1/*-------------------------------------------------------------------------
3 * pl_handler.c - Handler for the PL/pgSQL
6 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * src/pl/plpgsql/src/pl_handler.c
13 *-------------------------------------------------------------------------
39/* Custom GUC variable */
77 /* Need a modifiable copy of string */
80 /* Parse string into list of identifiers */
83 /* syntax error in list */
92 char *tok = (
char *)
lfirst(l);
123 *myextra = extrachecks;
143 * _PG_init() - library load-time initialization
145 * DO NOT make this static nor change its name!
150 /* Be sure we do initialization only once (should be redundant now) */
151 static bool inited =
false;
159 gettext_noop(
"Sets handling of conflicts between PL/pgSQL variable names and table column names."),
168 gettext_noop(
"Print information about parameters in the DETAIL part of the error messages generated on INTO ... STRICT failures."),
176 gettext_noop(
"Perform checks given in ASSERT statements."),
184 gettext_noop(
"List of programming constructs that should produce a warning."),
194 gettext_noop(
"List of programming constructs that should produce an error."),
208 /* Set up a rendezvous point with optional instrumentation plugin */
215 * plpgsql_call_handler
217 * The PostgreSQL function manager and trigger manager
218 * call this function for execution of PL/pgSQL procedures.
233 nonatomic = fcinfo->context &&
238 * Connect to SPI manager
242 /* Find or compile the function */
245 /* Must save and restore prior value of cur_estate */
248 /* Mark the function as busy, so it can't be deleted from under us */
252 * If we'll need a procedure-lifespan resowner to execute any CALL or DO
253 * statements, create it now. Since this resowner is not tied to any
254 * parent, failing to free it would result in process-lifespan leaks.
255 * Therefore, be very wary of adding any code between here and the PG_TRY
265 * Determine if called as function or trigger and call appropriate
275 /* there's no return value in this case */
285 /* Decrement use-count, restore cur_estate */
289 /* Be sure to release the procedure resowner if any */
290 if (procedure_resowner)
299 * Disconnect from SPI manager
308 * plpgsql_inline_handler
310 * Called by PostgreSQL to execute an anonymous code block
322 EState *simple_eval_estate;
328 * Connect to SPI manager
332 /* Compile the anonymous code block */
335 /* Mark the function as busy, just pro forma */
339 * Set up a fake fcinfo with just enough info to satisfy
340 * plpgsql_exec_function(). In particular note that this sets things up
341 * with no arguments passed.
344 MemSet(&flinfo, 0,
sizeof(flinfo));
345 fake_fcinfo->flinfo = &flinfo;
350 * Create a private EState and resowner for simple-expression execution.
351 * Notice that these are NOT tied to transaction-level resources; they
352 * must survive any COMMIT/ROLLBACK the DO block executes, since we will
353 * unconditionally try to clean them up below. (Hence, be wary of adding
354 * anything that could fail between here and the PG_TRY block.) See the
355 * comments for shared_simple_eval_estate.
357 * Because this resowner isn't tied to the calling transaction, we can
358 * also use it as the "procedure" resowner for any CALL statements. That
359 * helps reduce the opportunities for failure here.
362 simple_eval_resowner =
365 /* And run the function */
370 simple_eval_resowner,
371 simple_eval_resowner,
/* see above */
377 * We need to clean up what would otherwise be long-lived resources
378 * accumulated by the failed DO block, principally cached plans for
379 * statements (which can be flushed by plpgsql_free_function_memory),
380 * execution trees for simple expressions, which are in the private
381 * EState, and cached-plan refcounts held by the private resowner.
383 * Before releasing the private EState, we must clean up any
384 * simple_econtext_stack entries pointing into it, which we can do by
385 * invoking the subxact callback. (It will be called again later if
386 * some outer control level does a subtransaction abort, but no harm
387 * is done.) We cheat a bit knowing that plpgsql_subxact_cb does not
388 * pay attention to its parentSubid argument.
394 /* Clean up the private EState and resowner */
399 /* Function should now have no remaining use-counts ... */
403 /* ... so we can free subsidiary storage */
406 /* And propagate the error */
411 /* Clean up the private EState and resowner */
416 /* Function should now have no remaining use-counts ... */
420 /* ... so we can free subsidiary storage */
424 * Disconnect from SPI manager
435 * This function attempts to validate a PL/pgSQL function at
436 * CREATE FUNCTION time.
452 bool is_dml_trigger =
false;
453 bool is_event_trigger =
false;
459 /* Get the new function's pg_proc entry */
462 elog(
ERROR,
"cache lookup failed for function %u", funcoid);
467 /* Disallow pseudotype result */
468 /* except for TRIGGER, EVTTRIGGER, RECORD, VOID, or polymorphic */
469 if (functyptype == TYPTYPE_PSEUDO)
471 if (proc->prorettype == TRIGGEROID)
472 is_dml_trigger =
true;
473 else if (proc->prorettype == EVENT_TRIGGEROID)
474 is_event_trigger =
true;
475 else if (proc->prorettype != RECORDOID &&
476 proc->prorettype != VOIDOID &&
477 !IsPolymorphicType(proc->prorettype))
479 (
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
480 errmsg(
"PL/pgSQL functions cannot return type %s",
484 /* Disallow pseudotypes in arguments (either IN or OUT) */
485 /* except for RECORD and polymorphic */
487 &argtypes, &argnames, &argmodes);
488 for (
i = 0;
i < numargs;
i++)
492 if (argtypes[
i] != RECORDOID &&
493 !IsPolymorphicType(argtypes[
i]))
495 (
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
496 errmsg(
"PL/pgSQL functions cannot accept type %s",
501 /* Postpone body checks if !check_function_bodies */
511 * Connect to SPI manager (is this needed for compilation?)
516 * Set up a fake fcinfo with just enough info to satisfy
520 MemSet(&flinfo, 0,
sizeof(flinfo));
521 fake_fcinfo->flinfo = &flinfo;
526 MemSet(&trigdata, 0,
sizeof(trigdata));
527 trigdata.
type = T_TriggerData;
528 fake_fcinfo->context = (
Node *) &trigdata;
530 else if (is_event_trigger)
532 MemSet(&etrigdata, 0,
sizeof(etrigdata));
533 etrigdata.
type = T_EventTriggerData;
534 fake_fcinfo->context = (
Node *) &etrigdata;
537 /* Test-compile the function */
541 * Disconnect from SPI manager
#define MemSet(start, val, len)
void ** find_rendezvous_variable(const char *varName)
int errcode(int sqlerrcode)
int errmsg(const char *fmt,...)
#define ereport(elevel,...)
#define CALLED_AS_EVENT_TRIGGER(fcinfo)
void FreeExecutorState(EState *estate)
EState * CreateExecutorState(void)
bool CheckFunctionValidatorAccess(Oid validatorOid, Oid functionOid)
#define SizeForFunctionCallInfo(nargs)
#define PG_GETARG_DATUM(n)
#define LOCAL_FCINFO(name, nargs)
int get_func_arg_info(HeapTuple procTup, Oid **p_argtypes, char ***p_argnames, char **p_argmodes)
void DefineCustomEnumVariable(const char *name, const char *short_desc, const char *long_desc, int *valueAddr, int bootValue, const struct config_enum_entry *options, GucContext context, int flags, GucEnumCheckHook check_hook, GucEnumAssignHook assign_hook, GucShowHook show_hook)
void DefineCustomStringVariable(const char *name, const char *short_desc, const char *long_desc, char **valueAddr, const char *bootValue, GucContext context, int flags, GucStringCheckHook check_hook, GucStringAssignHook assign_hook, GucShowHook show_hook)
void * guc_malloc(int elevel, size_t size)
void DefineCustomBoolVariable(const char *name, const char *short_desc, const char *long_desc, bool *valueAddr, bool bootValue, GucContext context, int flags, GucBoolCheckHook check_hook, GucBoolAssignHook assign_hook, GucShowHook show_hook)
void MarkGUCPrefixReserved(const char *className)
#define GUC_check_errdetail
bool check_function_bodies
Assert(PointerIsAligned(start, uint64))
#define HeapTupleIsValid(tuple)
static void * GETSTRUCT(const HeapTupleData *tuple)
void list_free(List *list)
char get_typtype(Oid typid)
char * pstrdup(const char *in)
void pfree(void *pointer)
MemoryContext CurrentMemoryContext
void pg_bindtextdomain(const char *domain)
#define IsA(nodeptr, _type_)
#define castNode(_type_, nodeptr)
FormData_pg_proc * Form_pg_proc
static rewind_source * source
PLpgSQL_function * plpgsql_compile(FunctionCallInfo fcinfo, bool forValidator)
PLpgSQL_function * plpgsql_compile_inline(char *proc_source)
Datum plpgsql_exec_function(PLpgSQL_function *func, FunctionCallInfo fcinfo, EState *simple_eval_estate, ResourceOwner simple_eval_resowner, ResourceOwner procedure_resowner, bool atomic)
HeapTuple plpgsql_exec_trigger(PLpgSQL_function *func, TriggerData *trigdata)
void plpgsql_subxact_cb(SubXactEvent event, SubTransactionId mySubid, SubTransactionId parentSubid, void *arg)
void plpgsql_exec_event_trigger(PLpgSQL_function *func, EventTriggerData *trigdata)
void plpgsql_xact_cb(XactEvent event, void *arg)
void plpgsql_free_function_memory(PLpgSQL_function *func)
int plpgsql_variable_conflict
static void plpgsql_extra_errors_assign_hook(const char *newvalue, void *extra)
bool plpgsql_check_asserts
static const struct config_enum_entry variable_conflict_options[]
int plpgsql_extra_warnings
Datum plpgsql_call_handler(PG_FUNCTION_ARGS)
static char * plpgsql_extra_errors_string
Datum plpgsql_inline_handler(PG_FUNCTION_ARGS)
PG_FUNCTION_INFO_V1(plpgsql_call_handler)
PG_MODULE_MAGIC_EXT(.name="plpgsql",.version=PG_VERSION)
bool plpgsql_print_strict_params
PLpgSQL_plugin ** plpgsql_plugin_ptr
static void plpgsql_extra_warnings_assign_hook(const char *newvalue, void *extra)
static bool plpgsql_extra_checks_check_hook(char **newvalue, void **extra, GucSource source)
static char * plpgsql_extra_warnings_string
Datum plpgsql_validator(PG_FUNCTION_ARGS)
void ReleaseAllPlanCacheRefsInOwner(ResourceOwner owner)
#define PLPGSQL_XCHECK_SHADOWVAR
#define PLPGSQL_XCHECK_ALL
@ PLPGSQL_RESOLVE_VARIABLE
#define PLPGSQL_XCHECK_NONE
#define PLPGSQL_XCHECK_TOOMANYROWS
#define PLPGSQL_XCHECK_STRICTMULTIASSIGNMENT
int pg_strcasecmp(const char *s1, const char *s2)
static Datum PointerGetDatum(const void *X)
static Datum ObjectIdGetDatum(Oid X)
static Pointer DatumGetPointer(Datum X)
ResourceOwner ResourceOwnerCreate(ResourceOwner parent, const char *name)
void ResourceOwnerDelete(ResourceOwner owner)
const char * SPI_result_code_string(int code)
int SPI_connect_ext(int options)
#define SPI_OPT_NONATOMIC
bool requires_procedure_resowner
struct PLpgSQL_execstate * cur_estate
void ReleaseSysCache(HeapTuple tuple)
HeapTuple SearchSysCache1(int cacheId, Datum key1)
#define CALLED_AS_TRIGGER(fcinfo)
bool SplitIdentifierString(char *rawstring, char separator, List **namelist)
SubTransactionId GetCurrentSubTransactionId(void)
void RegisterXactCallback(XactCallback callback, void *arg)
void RegisterSubXactCallback(SubXactCallback callback, void *arg)
@ SUBXACT_EVENT_ABORT_SUB