2006-03-09 22:29:38 +01:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
1998-08-22 14:38:39 +02:00
|
|
|
* pl_handler.c - Handler for the PL/pgSQL
|
|
|
|
* procedural language
|
|
|
|
*
|
2009-01-01 18:24:05 +01:00
|
|
|
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
2006-03-09 22:29:38 +01:00
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
1998-08-22 14:38:39 +02:00
|
|
|
*
|
|
|
|
*
|
2006-03-09 22:29:38 +01:00
|
|
|
* IDENTIFICATION
|
2009-02-18 12:33:04 +01:00
|
|
|
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_handler.c,v 1.44 2009/02/18 11:33:04 petere Exp $
|
1998-08-22 14:38:39 +02:00
|
|
|
*
|
2006-03-09 22:29:38 +01:00
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
1998-08-22 14:38:39 +02:00
|
|
|
|
|
|
|
#include "plpgsql.h"
|
|
|
|
|
|
|
|
#include "catalog/pg_proc.h"
|
|
|
|
#include "catalog/pg_type.h"
|
2005-12-28 19:11:25 +01:00
|
|
|
#include "funcapi.h"
|
2000-05-28 19:56:29 +02:00
|
|
|
#include "utils/builtins.h"
|
2006-10-19 20:32:48 +02:00
|
|
|
#include "utils/guc.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
|
|
|
|
2006-05-31 00:12:16 +02:00
|
|
|
PG_MODULE_MAGIC;
|
|
|
|
|
2006-08-15 21:01:17 +02:00
|
|
|
PLpgSQL_plugin **plugin_ptr = NULL;
|
|
|
|
|
2003-07-31 20:36:46 +02:00
|
|
|
|
|
|
|
/*
|
2006-08-08 21:15:09 +02:00
|
|
|
* _PG_init() - library load-time initialization
|
2003-07-31 20:36:46 +02:00
|
|
|
*
|
2006-08-08 21:15:09 +02:00
|
|
|
* DO NOT make this static nor change its name!
|
2003-07-31 20:36:46 +02:00
|
|
|
*/
|
|
|
|
void
|
2006-08-08 21:15:09 +02:00
|
|
|
_PG_init(void)
|
2003-07-31 20:36:46 +02:00
|
|
|
{
|
2006-08-08 21:15:09 +02:00
|
|
|
/* Be sure we do initialization only once (should be redundant now) */
|
|
|
|
static bool inited = false;
|
|
|
|
|
|
|
|
if (inited)
|
2003-07-31 20:36:46 +02:00
|
|
|
return;
|
|
|
|
|
2008-12-11 08:34:09 +01:00
|
|
|
pg_bindtextdomain(TEXTDOMAIN);
|
2008-10-09 19:24:05 +02:00
|
|
|
|
2003-07-31 20:36:46 +02:00
|
|
|
plpgsql_HashTableInit();
|
2004-08-01 19:32:22 +02:00
|
|
|
RegisterXactCallback(plpgsql_xact_cb, NULL);
|
2007-01-28 17:15:49 +01:00
|
|
|
RegisterSubXactCallback(plpgsql_subxact_cb, NULL);
|
2003-07-31 20:36:46 +02:00
|
|
|
|
2006-08-15 21:01:17 +02:00
|
|
|
/* Set up a rendezvous point with optional instrumentation plugin */
|
|
|
|
plugin_ptr = (PLpgSQL_plugin **) find_rendezvous_variable("PLpgSQL_plugin");
|
|
|
|
|
2006-08-08 21:15:09 +02:00
|
|
|
inited = true;
|
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;
|
2004-07-31 22:55:45 +02:00
|
|
|
int rc;
|
1998-09-01 06:40:42 +02:00
|
|
|
|
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
|
|
|
*/
|
2004-07-31 22:55:45 +02:00
|
|
|
if ((rc = SPI_connect()) != SPI_OK_CONNECT)
|
|
|
|
elog(ERROR, "SPI_connect failed: %s", SPI_result_code_string(rc));
|
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
|
|
|
|
2007-01-30 23:05:13 +01:00
|
|
|
/* Mark the function as busy, so it can't be deleted from under us */
|
|
|
|
func->use_count++;
|
|
|
|
|
|
|
|
PG_TRY();
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Determine if called as function or trigger and call appropriate
|
|
|
|
* subhandler
|
|
|
|
*/
|
|
|
|
if (CALLED_AS_TRIGGER(fcinfo))
|
|
|
|
retval = PointerGetDatum(plpgsql_exec_trigger(func,
|
2005-10-15 04:49:52 +02:00
|
|
|
(TriggerData *) fcinfo->context));
|
2007-01-30 23:05:13 +01:00
|
|
|
else
|
|
|
|
retval = plpgsql_exec_function(func, fcinfo);
|
|
|
|
}
|
|
|
|
PG_CATCH();
|
|
|
|
{
|
|
|
|
/* Decrement use-count and propagate error */
|
|
|
|
func->use_count--;
|
|
|
|
PG_RE_THROW();
|
|
|
|
}
|
|
|
|
PG_END_TRY();
|
|
|
|
|
|
|
|
func->use_count--;
|
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
|
|
|
*/
|
2004-07-31 22:55:45 +02:00
|
|
|
if ((rc = SPI_finish()) != SPI_OK_FINISH)
|
|
|
|
elog(ERROR, "SPI_finish failed: %s", SPI_result_code_string(rc));
|
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;
|
2005-12-28 19:11:25 +01:00
|
|
|
int numargs;
|
|
|
|
Oid *argtypes;
|
|
|
|
char **argnames;
|
|
|
|
char *argmodes;
|
2004-03-19 19:58:07 +01:00
|
|
|
bool istrigger = false;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* 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 */
|
2007-04-02 05:49:42 +02:00
|
|
|
/* except for TRIGGER, RECORD, VOID, or polymorphic */
|
|
|
|
if (functyptype == TYPTYPE_PSEUDO)
|
2004-03-19 19:58:07 +01:00
|
|
|
{
|
|
|
|
/* 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 != RECORDOID &&
|
2005-12-28 19:11:25 +01:00
|
|
|
proc->prorettype != VOIDOID &&
|
2007-04-02 05:49:42 +02:00
|
|
|
!IsPolymorphicType(proc->prorettype))
|
2004-03-19 19:58:07 +01:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
2009-02-18 12:33:04 +01:00
|
|
|
errmsg("PL/pgSQL functions cannot return type %s",
|
2004-03-19 19:58:07 +01:00
|
|
|
format_type_be(proc->prorettype))));
|
|
|
|
}
|
|
|
|
|
2005-12-28 19:11:25 +01:00
|
|
|
/* Disallow pseudotypes in arguments (either IN or OUT) */
|
2007-04-02 05:49:42 +02:00
|
|
|
/* except for polymorphic */
|
2005-12-28 19:11:25 +01:00
|
|
|
numargs = get_func_arg_info(tuple,
|
|
|
|
&argtypes, &argnames, &argmodes);
|
|
|
|
for (i = 0; i < numargs; i++)
|
2004-03-19 19:58:07 +01:00
|
|
|
{
|
2007-04-02 05:49:42 +02:00
|
|
|
if (get_typtype(argtypes[i]) == TYPTYPE_PSEUDO)
|
2004-03-19 19:58:07 +01:00
|
|
|
{
|
2007-04-02 05:49:42 +02:00
|
|
|
if (!IsPolymorphicType(argtypes[i]))
|
2004-03-19 19:58:07 +01:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
2009-02-18 12:33:04 +01:00
|
|
|
errmsg("PL/pgSQL functions cannot accept type %s",
|
2006-10-04 02:30:14 +02:00
|
|
|
format_type_be(argtypes[i]))));
|
2004-03-19 19:58:07 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Postpone body checks if !check_function_bodies */
|
|
|
|
if (check_function_bodies)
|
|
|
|
{
|
|
|
|
FunctionCallInfoData fake_fcinfo;
|
|
|
|
FmgrInfo flinfo;
|
|
|
|
TriggerData trigdata;
|
2004-07-31 22:55:45 +02:00
|
|
|
int rc;
|
2004-03-19 19:58:07 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Connect to SPI manager (is this needed for compilation?)
|
|
|
|
*/
|
2004-07-31 22:55:45 +02:00
|
|
|
if ((rc = SPI_connect()) != SPI_OK_CONNECT)
|
|
|
|
elog(ERROR, "SPI_connect failed: %s", SPI_result_code_string(rc));
|
2004-03-19 19:58:07 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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
|
|
|
|
*/
|
2004-07-31 22:55:45 +02:00
|
|
|
if ((rc = SPI_finish()) != SPI_OK_FINISH)
|
|
|
|
elog(ERROR, "SPI_finish failed: %s", SPI_result_code_string(rc));
|
2004-03-19 19:58:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
|
|
|
|
PG_RETURN_VOID();
|
|
|
|
}
|