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 2334 : 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 4712 : plpgsql_extra_checks_check_hook(char **newvalue, void **extra, GucSource source)
64 : {
65 : char *rawstring;
66 : List *elemlist;
67 : ListCell *l;
68 4712 : int extrachecks = 0;
69 : int *myextra;
70 :
71 4712 : if (pg_strcasecmp(*newvalue, "all") == 0)
72 8 : extrachecks = PLPGSQL_XCHECK_ALL;
73 4704 : else if (pg_strcasecmp(*newvalue, "none") == 0)
74 4676 : extrachecks = PLPGSQL_XCHECK_NONE;
75 : else
76 : {
77 : /* Need a modifiable copy of string */
78 28 : rawstring = pstrdup(*newvalue);
79 :
80 : /* Parse string into list of identifiers */
81 28 : 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 56 : foreach(l, elemlist)
91 : {
92 28 : char *tok = (char *) lfirst(l);
93 :
94 28 : if (pg_strcasecmp(tok, "shadowed_variables") == 0)
95 12 : extrachecks |= PLPGSQL_XCHECK_SHADOWVAR;
96 16 : else if (pg_strcasecmp(tok, "too_many_rows") == 0)
97 8 : extrachecks |= PLPGSQL_XCHECK_TOOMANYROWS;
98 8 : else if (pg_strcasecmp(tok, "strict_multi_assignment") == 0)
99 8 : 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 28 : pfree(rawstring);
117 28 : list_free(elemlist);
118 : }
119 :
120 4712 : myextra = (int *) guc_malloc(LOG, sizeof(int));
121 4712 : if (!myextra)
122 0 : return false;
123 4712 : *myextra = extrachecks;
124 4712 : *extra = myextra;
125 :
126 4712 : return true;
127 : }
128 :
129 : static void
130 2370 : plpgsql_extra_warnings_assign_hook(const char *newvalue, void *extra)
131 : {
132 2370 : plpgsql_extra_warnings = *((int *) extra);
133 2370 : }
134 :
135 : static void
136 2366 : plpgsql_extra_errors_assign_hook(const char *newvalue, void *extra)
137 : {
138 2366 : plpgsql_extra_errors = *((int *) extra);
139 2366 : }
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 2334 : _PG_init(void)
149 : {
150 : /* Be sure we do initialization only once (should be redundant now) */
151 : static bool inited = false;
152 :
153 2334 : if (inited)
154 0 : return;
155 :
156 2334 : pg_bindtextdomain(TEXTDOMAIN);
157 :
158 2334 : 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 2334 : 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 2334 : 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 2334 : 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 2334 : 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 2334 : MarkGUCPrefixReserved("plpgsql");
204 :
205 2334 : RegisterXactCallback(plpgsql_xact_cb, NULL);
206 2334 : RegisterSubXactCallback(plpgsql_subxact_cb, NULL);
207 :
208 : /* Set up a rendezvous point with optional instrumentation plugin */
209 2334 : plpgsql_plugin_ptr = (PLpgSQL_plugin **) find_rendezvous_variable("PLpgSQL_plugin");
210 :
211 2334 : 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 662 : PG_FUNCTION_INFO_V1(plpgsql_call_handler);
222 :
223 : Datum
224 54462 : 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 54462 : volatile Datum retval = (Datum) 0;
231 : int rc;
232 :
233 120729 : nonatomic = fcinfo->context &&
234 54570 : IsA(fcinfo->context, CallContext) &&
235 108 : !castNode(CallContext, fcinfo->context)->atomic;
236 :
237 : /*
238 : * Connect to SPI manager
239 : */
240 54462 : SPI_connect_ext(nonatomic ? SPI_OPT_NONATOMIC : 0);
241 :
242 : /* Find or compile the function */
243 54462 : func = plpgsql_compile(fcinfo, false);
244 :
245 : /* Must save and restore prior value of cur_estate */
246 54453 : save_cur_estate = func->cur_estate;
247 :
248 : /* Mark the function as busy, so it can't be deleted from under us */
249 54453 : 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 54453 : procedure_resowner =
259 96 : (nonatomic && func->requires_procedure_resowner) ?
260 54549 : ResourceOwnerCreate(NULL, "PL/pgSQL procedure resources") : NULL;
261 :
262 54453 : PG_TRY();
263 : {
264 : /*
265 : * Determine if called as function or trigger and call appropriate
266 : * subhandler
267 : */
268 54453 : if (CALLED_AS_TRIGGER(fcinfo))
269 10314 : retval = PointerGetDatum(plpgsql_exec_trigger(func,
270 : (TriggerData *) fcinfo->context));
271 44139 : else if (CALLED_AS_EVENT_TRIGGER(fcinfo))
272 : {
273 1175 : plpgsql_exec_event_trigger(func,
274 1175 : (EventTriggerData *) fcinfo->context);
275 : /* there's no return value in this case */
276 : }
277 : else
278 42558 : retval = plpgsql_exec_function(func, fcinfo,
279 : NULL, NULL,
280 : procedure_resowner,
281 42964 : !nonatomic);
282 : }
283 560 : PG_FINALLY();
284 : {
285 : /* Decrement use-count, restore cur_estate */
286 54453 : func->cfunc.use_count--;
287 54453 : func->cur_estate = save_cur_estate;
288 :
289 : /* Be sure to release the procedure resowner if any */
290 54453 : if (procedure_resowner)
291 : {
292 14 : ReleaseAllPlanCacheRefsInOwner(procedure_resowner);
293 14 : ResourceOwnerDelete(procedure_resowner);
294 : }
295 : }
296 54453 : PG_END_TRY();
297 :
298 : /*
299 : * Disconnect from SPI manager
300 : */
301 53893 : if ((rc = SPI_finish()) != SPI_OK_FINISH)
302 0 : elog(ERROR, "SPI_finish failed: %s", SPI_result_code_string(rc));
303 :
304 53893 : return retval;
305 : }
306 :
307 : /* ----------
308 : * plpgsql_inline_handler
309 : *
310 : * Called by PostgreSQL to execute an anonymous code block
311 : * ----------
312 : */
313 217 : PG_FUNCTION_INFO_V1(plpgsql_inline_handler);
314 :
315 : Datum
316 837 : plpgsql_inline_handler(PG_FUNCTION_ARGS)
317 : {
318 837 : LOCAL_FCINFO(fake_fcinfo, 0);
319 837 : 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 837 : SPI_connect_ext(codeblock->atomic ? 0 : SPI_OPT_NONATOMIC);
331 :
332 : /* Compile the anonymous code block */
333 837 : func = plpgsql_compile_inline(codeblock->source_text);
334 :
335 : /* Mark the function as busy, just pro forma */
336 808 : 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 4040 : MemSet(fake_fcinfo, 0, SizeForFunctionCallInfo(0));
344 5656 : MemSet(&flinfo, 0, sizeof(flinfo));
345 808 : fake_fcinfo->flinfo = &flinfo;
346 808 : flinfo.fn_oid = InvalidOid;
347 808 : 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 808 : simple_eval_estate = CreateExecutorState();
362 : simple_eval_resowner =
363 808 : ResourceOwnerCreate(NULL, "PL/pgSQL DO block simple expressions");
364 :
365 : /* And run the function */
366 808 : PG_TRY();
367 : {
368 622 : retval = plpgsql_exec_function(func, fake_fcinfo,
369 : simple_eval_estate,
370 : simple_eval_resowner,
371 : simple_eval_resowner, /* see above */
372 808 : codeblock->atomic);
373 : }
374 186 : 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 186 : plpgsql_subxact_cb(SUBXACT_EVENT_ABORT_SUB,
391 : GetCurrentSubTransactionId(),
392 : 0, NULL);
393 :
394 : /* Clean up the private EState and resowner */
395 186 : FreeExecutorState(simple_eval_estate);
396 186 : ReleaseAllPlanCacheRefsInOwner(simple_eval_resowner);
397 186 : ResourceOwnerDelete(simple_eval_resowner);
398 :
399 : /* Function should now have no remaining use-counts ... */
400 186 : func->cfunc.use_count--;
401 : Assert(func->cfunc.use_count == 0);
402 :
403 : /* ... so we can free subsidiary storage */
404 186 : plpgsql_free_function_memory(func);
405 :
406 : /* And propagate the error */
407 186 : PG_RE_THROW();
408 : }
409 622 : PG_END_TRY();
410 :
411 : /* Clean up the private EState and resowner */
412 622 : FreeExecutorState(simple_eval_estate);
413 622 : ReleaseAllPlanCacheRefsInOwner(simple_eval_resowner);
414 622 : ResourceOwnerDelete(simple_eval_resowner);
415 :
416 : /* Function should now have no remaining use-counts ... */
417 622 : func->cfunc.use_count--;
418 : Assert(func->cfunc.use_count == 0);
419 :
420 : /* ... so we can free subsidiary storage */
421 622 : plpgsql_free_function_memory(func);
422 :
423 : /*
424 : * Disconnect from SPI manager
425 : */
426 622 : if ((rc = SPI_finish()) != SPI_OK_FINISH)
427 0 : elog(ERROR, "SPI_finish failed: %s", SPI_result_code_string(rc));
428 :
429 622 : 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 467 : PG_FUNCTION_INFO_V1(plpgsql_validator);
440 :
441 : Datum
442 2913 : plpgsql_validator(PG_FUNCTION_ARGS)
443 : {
444 2913 : 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 2913 : bool is_dml_trigger = false;
453 2913 : bool is_event_trigger = false;
454 : int i;
455 :
456 2913 : if (!CheckFunctionValidatorAccess(fcinfo->flinfo->fn_oid, funcoid))
457 0 : PG_RETURN_VOID();
458 :
459 : /* Get the new function's pg_proc entry */
460 2913 : tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcoid));
461 2913 : if (!HeapTupleIsValid(tuple))
462 0 : elog(ERROR, "cache lookup failed for function %u", funcoid);
463 2913 : proc = (Form_pg_proc) GETSTRUCT(tuple);
464 :
465 2913 : functyptype = get_typtype(proc->prorettype);
466 :
467 : /* Disallow pseudotype result */
468 : /* except for TRIGGER, EVTTRIGGER, RECORD, VOID, or polymorphic */
469 2913 : if (functyptype == TYPTYPE_PSEUDO)
470 : {
471 1741 : if (proc->prorettype == TRIGGEROID)
472 876 : is_dml_trigger = true;
473 865 : else if (proc->prorettype == EVENT_TRIGGEROID)
474 91 : is_event_trigger = true;
475 774 : else if (proc->prorettype != RECORDOID &&
476 656 : proc->prorettype != VOIDOID &&
477 50 : !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 2913 : numargs = get_func_arg_info(tuple,
487 : &argtypes, &argnames, &argmodes);
488 4929 : for (i = 0; i < numargs; i++)
489 : {
490 2016 : if (get_typtype(argtypes[i]) == TYPTYPE_PSEUDO)
491 : {
492 370 : if (argtypes[i] != RECORDOID &&
493 356 : !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 2913 : if (check_function_bodies)
503 : {
504 2759 : 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 2759 : SPI_connect();
514 :
515 : /*
516 : * Set up a fake fcinfo with just enough info to satisfy
517 : * plpgsql_compile().
518 : */
519 13795 : MemSet(fake_fcinfo, 0, SizeForFunctionCallInfo(0));
520 19313 : MemSet(&flinfo, 0, sizeof(flinfo));
521 2759 : fake_fcinfo->flinfo = &flinfo;
522 2759 : flinfo.fn_oid = funcoid;
523 2759 : flinfo.fn_mcxt = CurrentMemoryContext;
524 2759 : if (is_dml_trigger)
525 : {
526 8987 : MemSet(&trigdata, 0, sizeof(trigdata));
527 817 : trigdata.type = T_TriggerData;
528 817 : fake_fcinfo->context = (Node *) &trigdata;
529 : }
530 1942 : else if (is_event_trigger)
531 : {
532 405 : MemSet(&etrigdata, 0, sizeof(etrigdata));
533 81 : etrigdata.type = T_EventTriggerData;
534 81 : fake_fcinfo->context = (Node *) &etrigdata;
535 : }
536 :
537 : /* Test-compile the function */
538 2759 : plpgsql_compile(fake_fcinfo, true);
539 :
540 : /*
541 : * Disconnect from SPI manager
542 : */
543 2690 : if ((rc = SPI_finish()) != SPI_OK_FINISH)
544 0 : elog(ERROR, "SPI_finish failed: %s", SPI_result_code_string(rc));
545 : }
546 :
547 2844 : ReleaseSysCache(tuple);
548 :
549 2844 : PG_RETURN_VOID();
550 : }
|