Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * pl_handler.c - Handler for the PL/pgSQL
4 : * procedural language
5 : *
6 : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
7 : * Portions Copyright (c) 1994, Regents of the University of California
8 : *
9 : *
10 : * IDENTIFICATION
11 : * src/pl/plpgsql/src/pl_handler.c
12 : *
13 : *-------------------------------------------------------------------------
14 : */
15 :
16 : #include "postgres.h"
17 :
18 : #include "access/htup_details.h"
19 : #include "catalog/pg_proc.h"
20 : #include "catalog/pg_type.h"
21 : #include "funcapi.h"
22 : #include "miscadmin.h"
23 : #include "plpgsql.h"
24 : #include "utils/builtins.h"
25 : #include "utils/guc.h"
26 : #include "utils/lsyscache.h"
27 : #include "utils/syscache.h"
28 : #include "utils/varlena.h"
29 :
30 : static bool plpgsql_extra_checks_check_hook(char **newvalue, void **extra, GucSource source);
31 : static void plpgsql_extra_warnings_assign_hook(const char *newvalue, void *extra);
32 : static void plpgsql_extra_errors_assign_hook(const char *newvalue, void *extra);
33 :
34 3634 : PG_MODULE_MAGIC_EXT(
35 : .name = "plpgsql",
36 : .version = PG_VERSION
37 : );
38 :
39 : /* Custom GUC variable */
40 : static const struct config_enum_entry variable_conflict_options[] = {
41 : {"error", PLPGSQL_RESOLVE_ERROR, false},
42 : {"use_variable", PLPGSQL_RESOLVE_VARIABLE, false},
43 : {"use_column", PLPGSQL_RESOLVE_COLUMN, false},
44 : {NULL, 0, false}
45 : };
46 :
47 : int plpgsql_variable_conflict = PLPGSQL_RESOLVE_ERROR;
48 :
49 : bool plpgsql_print_strict_params = false;
50 :
51 : bool plpgsql_check_asserts = true;
52 :
53 : static char *plpgsql_extra_warnings_string = NULL;
54 : static char *plpgsql_extra_errors_string = NULL;
55 : int plpgsql_extra_warnings;
56 : int plpgsql_extra_errors;
57 :
58 : /* Hook for plugins */
59 : PLpgSQL_plugin **plpgsql_plugin_ptr = NULL;
60 :
61 :
62 : static bool
63 7334 : plpgsql_extra_checks_check_hook(char **newvalue, void **extra, GucSource source)
64 : {
65 : char *rawstring;
66 : List *elemlist;
67 : ListCell *l;
68 7334 : int extrachecks = 0;
69 : int *myextra;
70 :
71 7334 : if (pg_strcasecmp(*newvalue, "all") == 0)
72 12 : extrachecks = PLPGSQL_XCHECK_ALL;
73 7322 : else if (pg_strcasecmp(*newvalue, "none") == 0)
74 7280 : extrachecks = PLPGSQL_XCHECK_NONE;
75 : else
76 : {
77 : /* Need a modifiable copy of string */
78 42 : rawstring = pstrdup(*newvalue);
79 :
80 : /* Parse string into list of identifiers */
81 42 : if (!SplitIdentifierString(rawstring, ',', &elemlist))
82 : {
83 : /* syntax error in list */
84 0 : GUC_check_errdetail("List syntax is invalid.");
85 0 : pfree(rawstring);
86 0 : list_free(elemlist);
87 0 : return false;
88 : }
89 :
90 84 : foreach(l, elemlist)
91 : {
92 42 : char *tok = (char *) lfirst(l);
93 :
94 42 : if (pg_strcasecmp(tok, "shadowed_variables") == 0)
95 18 : extrachecks |= PLPGSQL_XCHECK_SHADOWVAR;
96 24 : else if (pg_strcasecmp(tok, "too_many_rows") == 0)
97 12 : extrachecks |= PLPGSQL_XCHECK_TOOMANYROWS;
98 12 : else if (pg_strcasecmp(tok, "strict_multi_assignment") == 0)
99 12 : extrachecks |= PLPGSQL_XCHECK_STRICTMULTIASSIGNMENT;
100 0 : else if (pg_strcasecmp(tok, "all") == 0 || pg_strcasecmp(tok, "none") == 0)
101 : {
102 0 : GUC_check_errdetail("Key word \"%s\" cannot be combined with other key words.", tok);
103 0 : pfree(rawstring);
104 0 : list_free(elemlist);
105 0 : return false;
106 : }
107 : else
108 : {
109 0 : GUC_check_errdetail("Unrecognized key word: \"%s\".", tok);
110 0 : pfree(rawstring);
111 0 : list_free(elemlist);
112 0 : return false;
113 : }
114 : }
115 :
116 42 : pfree(rawstring);
117 42 : list_free(elemlist);
118 : }
119 :
120 7334 : myextra = (int *) guc_malloc(LOG, sizeof(int));
121 7334 : if (!myextra)
122 0 : return false;
123 7334 : *myextra = extrachecks;
124 7334 : *extra = myextra;
125 :
126 7334 : return true;
127 : }
128 :
129 : static void
130 3688 : plpgsql_extra_warnings_assign_hook(const char *newvalue, void *extra)
131 : {
132 3688 : plpgsql_extra_warnings = *((int *) extra);
133 3688 : }
134 :
135 : static void
136 3682 : plpgsql_extra_errors_assign_hook(const char *newvalue, void *extra)
137 : {
138 3682 : plpgsql_extra_errors = *((int *) extra);
139 3682 : }
140 :
141 :
142 : /*
143 : * _PG_init() - library load-time initialization
144 : *
145 : * DO NOT make this static nor change its name!
146 : */
147 : void
148 3634 : _PG_init(void)
149 : {
150 : /* Be sure we do initialization only once (should be redundant now) */
151 : static bool inited = false;
152 :
153 3634 : if (inited)
154 0 : return;
155 :
156 3634 : pg_bindtextdomain(TEXTDOMAIN);
157 :
158 3634 : DefineCustomEnumVariable("plpgsql.variable_conflict",
159 : gettext_noop("Sets handling of conflicts between PL/pgSQL variable names and table column names."),
160 : NULL,
161 : &plpgsql_variable_conflict,
162 : PLPGSQL_RESOLVE_ERROR,
163 : variable_conflict_options,
164 : PGC_SUSET, 0,
165 : NULL, NULL, NULL);
166 :
167 3634 : DefineCustomBoolVariable("plpgsql.print_strict_params",
168 : gettext_noop("Print information about parameters in the DETAIL part of the error messages generated on INTO ... STRICT failures."),
169 : NULL,
170 : &plpgsql_print_strict_params,
171 : false,
172 : PGC_USERSET, 0,
173 : NULL, NULL, NULL);
174 :
175 3634 : DefineCustomBoolVariable("plpgsql.check_asserts",
176 : gettext_noop("Perform checks given in ASSERT statements."),
177 : NULL,
178 : &plpgsql_check_asserts,
179 : true,
180 : PGC_USERSET, 0,
181 : NULL, NULL, NULL);
182 :
183 3634 : DefineCustomStringVariable("plpgsql.extra_warnings",
184 : gettext_noop("List of programming constructs that should produce a warning."),
185 : NULL,
186 : &plpgsql_extra_warnings_string,
187 : "none",
188 : PGC_USERSET, GUC_LIST_INPUT,
189 : plpgsql_extra_checks_check_hook,
190 : plpgsql_extra_warnings_assign_hook,
191 : NULL);
192 :
193 3634 : DefineCustomStringVariable("plpgsql.extra_errors",
194 : gettext_noop("List of programming constructs that should produce an error."),
195 : NULL,
196 : &plpgsql_extra_errors_string,
197 : "none",
198 : PGC_USERSET, GUC_LIST_INPUT,
199 : plpgsql_extra_checks_check_hook,
200 : plpgsql_extra_errors_assign_hook,
201 : NULL);
202 :
203 3634 : MarkGUCPrefixReserved("plpgsql");
204 :
205 3634 : RegisterXactCallback(plpgsql_xact_cb, NULL);
206 3634 : RegisterSubXactCallback(plpgsql_subxact_cb, NULL);
207 :
208 : /* Set up a rendezvous point with optional instrumentation plugin */
209 3634 : plpgsql_plugin_ptr = (PLpgSQL_plugin **) find_rendezvous_variable("PLpgSQL_plugin");
210 :
211 3634 : inited = true;
212 : }
213 :
214 : /* ----------
215 : * plpgsql_call_handler
216 : *
217 : * The PostgreSQL function manager and trigger manager
218 : * call this function for execution of PL/pgSQL procedures.
219 : * ----------
220 : */
221 1116 : PG_FUNCTION_INFO_V1(plpgsql_call_handler);
222 :
223 : Datum
224 85468 : plpgsql_call_handler(PG_FUNCTION_ARGS)
225 : {
226 : bool nonatomic;
227 : PLpgSQL_function *func;
228 : PLpgSQL_execstate *save_cur_estate;
229 : ResourceOwner procedure_resowner;
230 85468 : volatile Datum retval = (Datum) 0;
231 : int rc;
232 :
233 188766 : nonatomic = fcinfo->context &&
234 85680 : IsA(fcinfo->context, CallContext) &&
235 212 : !castNode(CallContext, fcinfo->context)->atomic;
236 :
237 : /*
238 : * Connect to SPI manager
239 : */
240 85468 : SPI_connect_ext(nonatomic ? SPI_OPT_NONATOMIC : 0);
241 :
242 : /* Find or compile the function */
243 85468 : func = plpgsql_compile(fcinfo, false);
244 :
245 : /* Must save and restore prior value of cur_estate */
246 85454 : save_cur_estate = func->cur_estate;
247 :
248 : /* Mark the function as busy, so it can't be deleted from under us */
249 85454 : func->cfunc.use_count++;
250 :
251 : /*
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
256 : * block.
257 : */
258 85454 : procedure_resowner =
259 188 : (nonatomic && func->requires_procedure_resowner) ?
260 85642 : ResourceOwnerCreate(NULL, "PL/pgSQL procedure resources") : NULL;
261 :
262 85454 : PG_TRY();
263 : {
264 : /*
265 : * Determine if called as function or trigger and call appropriate
266 : * subhandler
267 : */
268 85454 : if (CALLED_AS_TRIGGER(fcinfo))
269 15194 : retval = PointerGetDatum(plpgsql_exec_trigger(func,
270 15402 : (TriggerData *) fcinfo->context));
271 70052 : else if (CALLED_AS_EVENT_TRIGGER(fcinfo))
272 : {
273 1904 : plpgsql_exec_event_trigger(func,
274 1904 : (EventTriggerData *) fcinfo->context);
275 : /* there's no return value in this case */
276 : }
277 : else
278 67502 : retval = plpgsql_exec_function(func, fcinfo,
279 : NULL, NULL,
280 : procedure_resowner,
281 68148 : !nonatomic);
282 : }
283 878 : PG_FINALLY();
284 : {
285 : /* Decrement use-count, restore cur_estate */
286 85454 : func->cfunc.use_count--;
287 85454 : func->cur_estate = save_cur_estate;
288 :
289 : /* Be sure to release the procedure resowner if any */
290 85454 : if (procedure_resowner)
291 : {
292 28 : ReleaseAllPlanCacheRefsInOwner(procedure_resowner);
293 28 : ResourceOwnerDelete(procedure_resowner);
294 : }
295 : }
296 85454 : PG_END_TRY();
297 :
298 : /*
299 : * Disconnect from SPI manager
300 : */
301 84576 : if ((rc = SPI_finish()) != SPI_OK_FINISH)
302 0 : elog(ERROR, "SPI_finish failed: %s", SPI_result_code_string(rc));
303 :
304 84576 : return retval;
305 : }
306 :
307 : /* ----------
308 : * plpgsql_inline_handler
309 : *
310 : * Called by PostgreSQL to execute an anonymous code block
311 : * ----------
312 : */
313 348 : PG_FUNCTION_INFO_V1(plpgsql_inline_handler);
314 :
315 : Datum
316 1506 : plpgsql_inline_handler(PG_FUNCTION_ARGS)
317 : {
318 1506 : LOCAL_FCINFO(fake_fcinfo, 0);
319 1506 : InlineCodeBlock *codeblock = castNode(InlineCodeBlock, DatumGetPointer(PG_GETARG_DATUM(0)));
320 : PLpgSQL_function *func;
321 : FmgrInfo flinfo;
322 : EState *simple_eval_estate;
323 : ResourceOwner simple_eval_resowner;
324 : Datum retval;
325 : int rc;
326 :
327 : /*
328 : * Connect to SPI manager
329 : */
330 1506 : SPI_connect_ext(codeblock->atomic ? 0 : SPI_OPT_NONATOMIC);
331 :
332 : /* Compile the anonymous code block */
333 1506 : func = plpgsql_compile_inline(codeblock->source_text);
334 :
335 : /* Mark the function as busy, just pro forma */
336 1450 : func->cfunc.use_count++;
337 :
338 : /*
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.
342 : */
343 7250 : MemSet(fake_fcinfo, 0, SizeForFunctionCallInfo(0));
344 10150 : MemSet(&flinfo, 0, sizeof(flinfo));
345 1450 : fake_fcinfo->flinfo = &flinfo;
346 1450 : flinfo.fn_oid = InvalidOid;
347 1450 : flinfo.fn_mcxt = CurrentMemoryContext;
348 :
349 : /*
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.
356 : *
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.
360 : */
361 1450 : simple_eval_estate = CreateExecutorState();
362 : simple_eval_resowner =
363 1450 : ResourceOwnerCreate(NULL, "PL/pgSQL DO block simple expressions");
364 :
365 : /* And run the function */
366 1450 : PG_TRY();
367 : {
368 1142 : retval = plpgsql_exec_function(func, fake_fcinfo,
369 : simple_eval_estate,
370 : simple_eval_resowner,
371 : simple_eval_resowner, /* see above */
372 1450 : codeblock->atomic);
373 : }
374 308 : PG_CATCH();
375 : {
376 : /*
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.
382 : *
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.
389 : */
390 308 : plpgsql_subxact_cb(SUBXACT_EVENT_ABORT_SUB,
391 : GetCurrentSubTransactionId(),
392 : 0, NULL);
393 :
394 : /* Clean up the private EState and resowner */
395 308 : FreeExecutorState(simple_eval_estate);
396 308 : ReleaseAllPlanCacheRefsInOwner(simple_eval_resowner);
397 308 : ResourceOwnerDelete(simple_eval_resowner);
398 :
399 : /* Function should now have no remaining use-counts ... */
400 308 : func->cfunc.use_count--;
401 : Assert(func->cfunc.use_count == 0);
402 :
403 : /* ... so we can free subsidiary storage */
404 308 : plpgsql_free_function_memory(func);
405 :
406 : /* And propagate the error */
407 308 : PG_RE_THROW();
408 : }
409 1142 : PG_END_TRY();
410 :
411 : /* Clean up the private EState and resowner */
412 1142 : FreeExecutorState(simple_eval_estate);
413 1142 : ReleaseAllPlanCacheRefsInOwner(simple_eval_resowner);
414 1142 : ResourceOwnerDelete(simple_eval_resowner);
415 :
416 : /* Function should now have no remaining use-counts ... */
417 1142 : func->cfunc.use_count--;
418 : Assert(func->cfunc.use_count == 0);
419 :
420 : /* ... so we can free subsidiary storage */
421 1142 : plpgsql_free_function_memory(func);
422 :
423 : /*
424 : * Disconnect from SPI manager
425 : */
426 1142 : if ((rc = SPI_finish()) != SPI_OK_FINISH)
427 0 : elog(ERROR, "SPI_finish failed: %s", SPI_result_code_string(rc));
428 :
429 1142 : return retval;
430 : }
431 :
432 : /* ----------
433 : * plpgsql_validator
434 : *
435 : * This function attempts to validate a PL/pgSQL function at
436 : * CREATE FUNCTION time.
437 : * ----------
438 : */
439 720 : PG_FUNCTION_INFO_V1(plpgsql_validator);
440 :
441 : Datum
442 4706 : plpgsql_validator(PG_FUNCTION_ARGS)
443 : {
444 4706 : Oid funcoid = PG_GETARG_OID(0);
445 : HeapTuple tuple;
446 : Form_pg_proc proc;
447 : char functyptype;
448 : int numargs;
449 : Oid *argtypes;
450 : char **argnames;
451 : char *argmodes;
452 4706 : bool is_dml_trigger = false;
453 4706 : bool is_event_trigger = false;
454 : int i;
455 :
456 4706 : if (!CheckFunctionValidatorAccess(fcinfo->flinfo->fn_oid, funcoid))
457 0 : PG_RETURN_VOID();
458 :
459 : /* Get the new function's pg_proc entry */
460 4706 : tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcoid));
461 4706 : if (!HeapTupleIsValid(tuple))
462 0 : elog(ERROR, "cache lookup failed for function %u", funcoid);
463 4706 : proc = (Form_pg_proc) GETSTRUCT(tuple);
464 :
465 4706 : functyptype = get_typtype(proc->prorettype);
466 :
467 : /* Disallow pseudotype result */
468 : /* except for TRIGGER, EVTTRIGGER, RECORD, VOID, or polymorphic */
469 4706 : if (functyptype == TYPTYPE_PSEUDO)
470 : {
471 2838 : if (proc->prorettype == TRIGGEROID)
472 1418 : is_dml_trigger = true;
473 1420 : else if (proc->prorettype == EVENT_TRIGGEROID)
474 144 : is_event_trigger = true;
475 1276 : else if (proc->prorettype != RECORDOID &&
476 1084 : proc->prorettype != VOIDOID &&
477 76 : !IsPolymorphicType(proc->prorettype))
478 0 : ereport(ERROR,
479 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
480 : errmsg("PL/pgSQL functions cannot return type %s",
481 : format_type_be(proc->prorettype))));
482 : }
483 :
484 : /* Disallow pseudotypes in arguments (either IN or OUT) */
485 : /* except for RECORD and polymorphic */
486 4706 : numargs = get_func_arg_info(tuple,
487 : &argtypes, &argnames, &argmodes);
488 7994 : for (i = 0; i < numargs; i++)
489 : {
490 3288 : if (get_typtype(argtypes[i]) == TYPTYPE_PSEUDO)
491 : {
492 654 : if (argtypes[i] != RECORDOID &&
493 632 : !IsPolymorphicType(argtypes[i]))
494 0 : ereport(ERROR,
495 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
496 : errmsg("PL/pgSQL functions cannot accept type %s",
497 : format_type_be(argtypes[i]))));
498 : }
499 : }
500 :
501 : /* Postpone body checks if !check_function_bodies */
502 4706 : if (check_function_bodies)
503 : {
504 4412 : LOCAL_FCINFO(fake_fcinfo, 0);
505 : FmgrInfo flinfo;
506 : int rc;
507 : TriggerData trigdata;
508 : EventTriggerData etrigdata;
509 :
510 : /*
511 : * Connect to SPI manager (is this needed for compilation?)
512 : */
513 4412 : SPI_connect();
514 :
515 : /*
516 : * Set up a fake fcinfo with just enough info to satisfy
517 : * plpgsql_compile().
518 : */
519 22060 : MemSet(fake_fcinfo, 0, SizeForFunctionCallInfo(0));
520 30884 : MemSet(&flinfo, 0, sizeof(flinfo));
521 4412 : fake_fcinfo->flinfo = &flinfo;
522 4412 : flinfo.fn_oid = funcoid;
523 4412 : flinfo.fn_mcxt = CurrentMemoryContext;
524 4412 : if (is_dml_trigger)
525 : {
526 14366 : MemSet(&trigdata, 0, sizeof(trigdata));
527 1306 : trigdata.type = T_TriggerData;
528 1306 : fake_fcinfo->context = (Node *) &trigdata;
529 : }
530 3106 : else if (is_event_trigger)
531 : {
532 620 : MemSet(&etrigdata, 0, sizeof(etrigdata));
533 124 : etrigdata.type = T_EventTriggerData;
534 124 : fake_fcinfo->context = (Node *) &etrigdata;
535 : }
536 :
537 : /* Test-compile the function */
538 4412 : plpgsql_compile(fake_fcinfo, true);
539 :
540 : /*
541 : * Disconnect from SPI manager
542 : */
543 4304 : if ((rc = SPI_finish()) != SPI_OK_FINISH)
544 0 : elog(ERROR, "SPI_finish failed: %s", SPI_result_code_string(rc));
545 : }
546 :
547 4598 : ReleaseSysCache(tuple);
548 :
549 4598 : PG_RETURN_VOID();
550 : }
|