postgresql/src/pl/plpgsql/src/pl_handler.c

251 lines
6.5 KiB
C
Raw Normal View History

/**********************************************************************
* pl_handler.c - Handler for the PL/pgSQL
* procedural language
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_handler.c,v 1.21 2004/03/22 03:15:33 momjian Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
* 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.
*
* 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.
*
* 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.
*
**********************************************************************/
#include "plpgsql.h"
#include "pl.tab.h"
#include "access/heapam.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
extern DLLIMPORT bool check_function_bodies;
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();
RegisterEOXactCallback(plpgsql_eoxact, NULL);
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
*/
}
/* ----------
* plpgsql_call_handler
*
* The PostgreSQL function manager and trigger manager
* call this function for execution of PL/pgSQL procedures.
* ----------
*/
PG_FUNCTION_INFO_V1(plpgsql_call_handler);
Datum
plpgsql_call_handler(PG_FUNCTION_ARGS)
{
PLpgSQL_function *func;
Datum retval;
/* perform initialization */
plpgsql_init_all();
/*
* Connect to SPI manager
*/
if (SPI_connect() != SPI_OK_CONNECT)
2003-07-27 19:10:07 +02:00
elog(ERROR, "SPI_connect failed");
/* Find or compile the function */
func = plpgsql_compile(fcinfo, false);
/*
* Determine if called as function or trigger and call appropriate
* subhandler
*/
if (CALLED_AS_TRIGGER(fcinfo))
retval = PointerGetDatum(plpgsql_exec_trigger(func,
2001-03-22 05:01:46 +01:00
(TriggerData *) fcinfo->context));
else
retval = plpgsql_exec_function(func, fcinfo);
/*
* Disconnect from SPI manager
*/
if (SPI_finish() != SPI_OK_FINISH)
2003-07-27 19:10:07 +02:00
elog(ERROR, "SPI_finish failed");
return retval;
}
/* ----------
* 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();
}