1998-08-22 14:38:39 +02:00
|
|
|
/**********************************************************************
|
|
|
|
* pl_handler.c - Handler for the PL/pgSQL
|
|
|
|
* procedural language
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
2004-03-22 04:15:33 +01:00
|
|
|
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_handler.c,v 1.21 2004/03/22 03:15:33 momjian Exp $
|
1998-08-22 14:38:39 +02:00
|
|
|
*
|
1998-09-01 06:40:42 +02:00
|
|
|
* This software is copyrighted by Jan Wieck - Hamburg.
|
1998-08-22 14:38:39 +02:00
|
|
|
*
|
1998-09-01 06:40:42 +02:00
|
|
|
* The author hereby grants permission to use, copy, modify,
|
|
|
|
* distribute, and license this software and its documentation
|
|
|
|
* for any purpose, provided that existing copyright notices are
|
|
|
|
* retained in all copies and that this notice is included
|
|
|
|
* verbatim in any distributions. No written agreement, license,
|
|
|
|
* or royalty fee is required for any of the authorized uses.
|
|
|
|
* Modifications to this software may be copyrighted by their
|
|
|
|
* author and need not follow the licensing terms described
|
|
|
|
* here, provided that the new terms are clearly indicated on
|
|
|
|
* the first page of each file where they apply.
|
1998-08-22 14:38:39 +02:00
|
|
|
*
|
1998-09-01 06:40:42 +02:00
|
|
|
* IN NO EVENT SHALL THE AUTHOR OR DISTRIBUTORS BE LIABLE TO ANY
|
|
|
|
* PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR
|
|
|
|
* CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS
|
|
|
|
* SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF, EVEN
|
|
|
|
* IF THE AUTHOR HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
|
|
|
* DAMAGE.
|
1998-08-22 14:38:39 +02:00
|
|
|
*
|
1998-09-01 06:40:42 +02:00
|
|
|
* THE AUTHOR AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY
|
|
|
|
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
|
|
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
|
|
|
* PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON
|
|
|
|
* AN "AS IS" BASIS, AND THE AUTHOR AND DISTRIBUTORS HAVE NO
|
|
|
|
* OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
|
|
|
|
* ENHANCEMENTS, OR MODIFICATIONS.
|
1998-08-22 14:38:39 +02:00
|
|
|
*
|
|
|
|
**********************************************************************/
|
|
|
|
|
|
|
|
#include "plpgsql.h"
|
|
|
|
#include "pl.tab.h"
|
|
|
|
|
|
|
|
#include "access/heapam.h"
|
|
|
|
#include "catalog/pg_proc.h"
|
|
|
|
#include "catalog/pg_type.h"
|
2000-05-28 19:56:29 +02:00
|
|
|
#include "utils/builtins.h"
|
2004-03-19 19:58:07 +01:00
|
|
|
#include "utils/lsyscache.h"
|
2000-05-28 19:56:29 +02:00
|
|
|
#include "utils/syscache.h"
|
1998-08-22 14:38:39 +02:00
|
|
|
|
2004-03-22 04:15:33 +01:00
|
|
|
extern DLLIMPORT bool check_function_bodies;
|
2004-03-19 19:58:07 +01:00
|
|
|
|
2003-07-31 20:36:46 +02:00
|
|
|
static int plpgsql_firstcall = 1;
|
|
|
|
|
|
|
|
static void plpgsql_init_all(void);
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* plpgsql_init() - postmaster-startup safe initialization
|
|
|
|
*
|
|
|
|
* DO NOT make this static --- it has to be callable by preload
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
plpgsql_init(void)
|
|
|
|
{
|
|
|
|
/* Do initialization only once */
|
|
|
|
if (!plpgsql_firstcall)
|
|
|
|
return;
|
|
|
|
|
|
|
|
plpgsql_HashTableInit();
|
|
|
|
|
2003-09-29 01:37:45 +02:00
|
|
|
RegisterEOXactCallback(plpgsql_eoxact, NULL);
|
|
|
|
|
2003-07-31 20:36:46 +02:00
|
|
|
plpgsql_firstcall = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* plpgsql_init_all() - Initialize all
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
plpgsql_init_all(void)
|
|
|
|
{
|
|
|
|
/* Execute any postmaster-startup safe initialization */
|
|
|
|
if (plpgsql_firstcall)
|
|
|
|
plpgsql_init();
|
|
|
|
|
|
|
|
/*
|
2003-08-04 02:43:34 +02:00
|
|
|
* Any other initialization that must be done each time a new backend
|
|
|
|
* starts -- currently none
|
2003-07-31 20:36:46 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
}
|
1998-08-22 14:38:39 +02:00
|
|
|
|
|
|
|
/* ----------
|
2000-05-28 19:56:29 +02:00
|
|
|
* plpgsql_call_handler
|
|
|
|
*
|
|
|
|
* The PostgreSQL function manager and trigger manager
|
|
|
|
* call this function for execution of PL/pgSQL procedures.
|
1998-08-22 14:38:39 +02:00
|
|
|
* ----------
|
|
|
|
*/
|
2000-11-20 21:36:57 +01:00
|
|
|
PG_FUNCTION_INFO_V1(plpgsql_call_handler);
|
|
|
|
|
1998-08-22 14:38:39 +02:00
|
|
|
Datum
|
2000-05-28 19:56:29 +02:00
|
|
|
plpgsql_call_handler(PG_FUNCTION_ARGS)
|
1998-08-22 14:38:39 +02:00
|
|
|
{
|
2000-05-28 19:56:29 +02:00
|
|
|
PLpgSQL_function *func;
|
1998-09-01 06:40:42 +02:00
|
|
|
Datum retval;
|
|
|
|
|
2003-07-31 20:36:46 +02:00
|
|
|
/* perform initialization */
|
|
|
|
plpgsql_init_all();
|
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
2000-05-28 19:56:29 +02:00
|
|
|
* Connect to SPI manager
|
1998-09-01 06:40:42 +02:00
|
|
|
*/
|
2000-05-28 19:56:29 +02:00
|
|
|
if (SPI_connect() != SPI_OK_CONNECT)
|
2003-07-27 19:10:07 +02:00
|
|
|
elog(ERROR, "SPI_connect failed");
|
1998-09-01 06:40:42 +02:00
|
|
|
|
2003-07-01 23:47:09 +02:00
|
|
|
/* Find or compile the function */
|
2004-03-19 19:58:07 +01:00
|
|
|
func = plpgsql_compile(fcinfo, false);
|
1998-09-01 06:40:42 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
|
|
|
* Determine if called as function or trigger and call appropriate
|
|
|
|
* subhandler
|
1998-09-01 06:40:42 +02:00
|
|
|
*/
|
2003-07-01 23:47:09 +02:00
|
|
|
if (CALLED_AS_TRIGGER(fcinfo))
|
2000-05-29 03:59:17 +02:00
|
|
|
retval = PointerGetDatum(plpgsql_exec_trigger(func,
|
2001-03-22 05:01:46 +01:00
|
|
|
(TriggerData *) fcinfo->context));
|
2000-05-28 19:56:29 +02:00
|
|
|
else
|
|
|
|
retval = plpgsql_exec_function(func, fcinfo);
|
1998-09-01 06:40:42 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
2000-05-28 19:56:29 +02:00
|
|
|
* Disconnect from SPI manager
|
1998-09-01 06:40:42 +02:00
|
|
|
*/
|
2000-05-28 19:56:29 +02:00
|
|
|
if (SPI_finish() != SPI_OK_FINISH)
|
2003-07-27 19:10:07 +02:00
|
|
|
elog(ERROR, "SPI_finish failed");
|
1998-09-01 06:40:42 +02:00
|
|
|
|
2000-05-28 19:56:29 +02:00
|
|
|
return retval;
|
1998-08-22 14:38:39 +02:00
|
|
|
}
|
2004-03-19 19:58:07 +01:00
|
|
|
|
|
|
|
/* ----------
|
|
|
|
* plpgsql_validator
|
|
|
|
*
|
|
|
|
* This function attempts to validate a PL/pgSQL function at
|
|
|
|
* CREATE FUNCTION time.
|
|
|
|
* ----------
|
|
|
|
*/
|
|
|
|
PG_FUNCTION_INFO_V1(plpgsql_validator);
|
|
|
|
|
|
|
|
Datum
|
|
|
|
plpgsql_validator(PG_FUNCTION_ARGS)
|
|
|
|
{
|
|
|
|
Oid funcoid = PG_GETARG_OID(0);
|
|
|
|
HeapTuple tuple;
|
|
|
|
Form_pg_proc proc;
|
|
|
|
char functyptype;
|
|
|
|
bool istrigger = false;
|
|
|
|
bool haspolyresult;
|
|
|
|
bool haspolyarg;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* perform initialization */
|
|
|
|
plpgsql_init_all();
|
|
|
|
|
|
|
|
/* Get the new function's pg_proc entry */
|
|
|
|
tuple = SearchSysCache(PROCOID,
|
|
|
|
ObjectIdGetDatum(funcoid),
|
|
|
|
0, 0, 0);
|
|
|
|
if (!HeapTupleIsValid(tuple))
|
|
|
|
elog(ERROR, "cache lookup failed for function %u", funcoid);
|
|
|
|
proc = (Form_pg_proc) GETSTRUCT(tuple);
|
|
|
|
|
|
|
|
functyptype = get_typtype(proc->prorettype);
|
|
|
|
|
|
|
|
/* Disallow pseudotype result */
|
|
|
|
/* except for TRIGGER, RECORD, VOID, ANYARRAY, or ANYELEMENT */
|
|
|
|
if (functyptype == 'p')
|
|
|
|
{
|
|
|
|
/* we assume OPAQUE with no arguments means a trigger */
|
|
|
|
if (proc->prorettype == TRIGGEROID ||
|
|
|
|
(proc->prorettype == OPAQUEOID && proc->pronargs == 0))
|
|
|
|
istrigger = true;
|
|
|
|
else if (proc->prorettype == ANYARRAYOID ||
|
|
|
|
proc->prorettype == ANYELEMENTOID)
|
|
|
|
haspolyresult = true;
|
|
|
|
else if (proc->prorettype != RECORDOID &&
|
|
|
|
proc->prorettype != VOIDOID)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
|
|
errmsg("plpgsql functions cannot return type %s",
|
|
|
|
format_type_be(proc->prorettype))));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Disallow pseudotypes in arguments */
|
|
|
|
/* except for ANYARRAY or ANYELEMENT */
|
|
|
|
haspolyarg = false;
|
|
|
|
for (i = 0; i < proc->pronargs; i++)
|
|
|
|
{
|
|
|
|
if (get_typtype(proc->proargtypes[i]) == 'p')
|
|
|
|
{
|
|
|
|
if (proc->proargtypes[i] == ANYARRAYOID ||
|
|
|
|
proc->proargtypes[i] == ANYELEMENTOID)
|
|
|
|
haspolyarg = true;
|
|
|
|
else
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
|
|
errmsg("plpgsql functions cannot take type %s",
|
|
|
|
format_type_be(proc->proargtypes[i]))));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Postpone body checks if !check_function_bodies */
|
|
|
|
if (check_function_bodies)
|
|
|
|
{
|
|
|
|
FunctionCallInfoData fake_fcinfo;
|
|
|
|
FmgrInfo flinfo;
|
|
|
|
TriggerData trigdata;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Connect to SPI manager (is this needed for compilation?)
|
|
|
|
*/
|
|
|
|
if (SPI_connect() != SPI_OK_CONNECT)
|
|
|
|
elog(ERROR, "SPI_connect failed");
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set up a fake fcinfo with just enough info to satisfy
|
|
|
|
* plpgsql_compile().
|
|
|
|
*/
|
|
|
|
MemSet(&fake_fcinfo, 0, sizeof(fake_fcinfo));
|
|
|
|
MemSet(&flinfo, 0, sizeof(flinfo));
|
|
|
|
fake_fcinfo.flinfo = &flinfo;
|
|
|
|
flinfo.fn_oid = funcoid;
|
|
|
|
flinfo.fn_mcxt = CurrentMemoryContext;
|
|
|
|
if (istrigger)
|
|
|
|
{
|
|
|
|
MemSet(&trigdata, 0, sizeof(trigdata));
|
|
|
|
trigdata.type = T_TriggerData;
|
|
|
|
fake_fcinfo.context = (Node *) &trigdata;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Test-compile the function */
|
|
|
|
plpgsql_compile(&fake_fcinfo, true);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Disconnect from SPI manager
|
|
|
|
*/
|
|
|
|
if (SPI_finish() != SPI_OK_FINISH)
|
|
|
|
elog(ERROR, "SPI_finish failed");
|
|
|
|
}
|
|
|
|
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
|
|
|
|
PG_RETURN_VOID();
|
|
|
|
}
|