Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * pl_handler.c - Handler for the PL/pgSQL
4 : * procedural language
5 : *
6 : * Portions Copyright (c) 1996-2024, 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 3354 : PG_MODULE_MAGIC;
35 :
36 : /* Custom GUC variable */
37 : static const struct config_enum_entry variable_conflict_options[] = {
38 : {"error", PLPGSQL_RESOLVE_ERROR, false},
39 : {"use_variable", PLPGSQL_RESOLVE_VARIABLE, false},
40 : {"use_column", PLPGSQL_RESOLVE_COLUMN, false},
41 : {NULL, 0, false}
42 : };
43 :
44 : int plpgsql_variable_conflict = PLPGSQL_RESOLVE_ERROR;
45 :
46 : bool plpgsql_print_strict_params = false;
47 :
48 : bool plpgsql_check_asserts = true;
49 :
50 : char *plpgsql_extra_warnings_string = NULL;
51 : char *plpgsql_extra_errors_string = NULL;
52 : int plpgsql_extra_warnings;
53 : int plpgsql_extra_errors;
54 :
55 : /* Hook for plugins */
56 : PLpgSQL_plugin **plpgsql_plugin_ptr = NULL;
57 :
58 :
59 : static bool
60 6774 : plpgsql_extra_checks_check_hook(char **newvalue, void **extra, GucSource source)
61 : {
62 : char *rawstring;
63 : List *elemlist;
64 : ListCell *l;
65 6774 : int extrachecks = 0;
66 : int *myextra;
67 :
68 6774 : if (pg_strcasecmp(*newvalue, "all") == 0)
69 12 : extrachecks = PLPGSQL_XCHECK_ALL;
70 6762 : else if (pg_strcasecmp(*newvalue, "none") == 0)
71 6720 : extrachecks = PLPGSQL_XCHECK_NONE;
72 : else
73 : {
74 : /* Need a modifiable copy of string */
75 42 : rawstring = pstrdup(*newvalue);
76 :
77 : /* Parse string into list of identifiers */
78 42 : if (!SplitIdentifierString(rawstring, ',', &elemlist))
79 : {
80 : /* syntax error in list */
81 0 : GUC_check_errdetail("List syntax is invalid.");
82 0 : pfree(rawstring);
83 0 : list_free(elemlist);
84 0 : return false;
85 : }
86 :
87 84 : foreach(l, elemlist)
88 : {
89 42 : char *tok = (char *) lfirst(l);
90 :
91 42 : if (pg_strcasecmp(tok, "shadowed_variables") == 0)
92 18 : extrachecks |= PLPGSQL_XCHECK_SHADOWVAR;
93 24 : else if (pg_strcasecmp(tok, "too_many_rows") == 0)
94 12 : extrachecks |= PLPGSQL_XCHECK_TOOMANYROWS;
95 12 : else if (pg_strcasecmp(tok, "strict_multi_assignment") == 0)
96 12 : extrachecks |= PLPGSQL_XCHECK_STRICTMULTIASSIGNMENT;
97 0 : else if (pg_strcasecmp(tok, "all") == 0 || pg_strcasecmp(tok, "none") == 0)
98 : {
99 0 : GUC_check_errdetail("Key word \"%s\" cannot be combined with other key words.", tok);
100 0 : pfree(rawstring);
101 0 : list_free(elemlist);
102 0 : return false;
103 : }
104 : else
105 : {
106 0 : GUC_check_errdetail("Unrecognized key word: \"%s\".", tok);
107 0 : pfree(rawstring);
108 0 : list_free(elemlist);
109 0 : return false;
110 : }
111 : }
112 :
113 42 : pfree(rawstring);
114 42 : list_free(elemlist);
115 : }
116 :
117 6774 : myextra = (int *) guc_malloc(LOG, sizeof(int));
118 6774 : if (!myextra)
119 0 : return false;
120 6774 : *myextra = extrachecks;
121 6774 : *extra = (void *) myextra;
122 :
123 6774 : return true;
124 : }
125 :
126 : static void
127 3408 : plpgsql_extra_warnings_assign_hook(const char *newvalue, void *extra)
128 : {
129 3408 : plpgsql_extra_warnings = *((int *) extra);
130 3408 : }
131 :
132 : static void
133 3402 : plpgsql_extra_errors_assign_hook(const char *newvalue, void *extra)
134 : {
135 3402 : plpgsql_extra_errors = *((int *) extra);
136 3402 : }
137 :
138 :
139 : /*
140 : * _PG_init() - library load-time initialization
141 : *
142 : * DO NOT make this static nor change its name!
143 : */
144 : void
145 3354 : _PG_init(void)
146 : {
147 : /* Be sure we do initialization only once (should be redundant now) */
148 : static bool inited = false;
149 :
150 3354 : if (inited)
151 0 : return;
152 :
153 3354 : pg_bindtextdomain(TEXTDOMAIN);
154 :
155 3354 : DefineCustomEnumVariable("plpgsql.variable_conflict",
156 : gettext_noop("Sets handling of conflicts between PL/pgSQL variable names and table column names."),
157 : NULL,
158 : &plpgsql_variable_conflict,
159 : PLPGSQL_RESOLVE_ERROR,
160 : variable_conflict_options,
161 : PGC_SUSET, 0,
162 : NULL, NULL, NULL);
163 :
164 3354 : DefineCustomBoolVariable("plpgsql.print_strict_params",
165 : gettext_noop("Print information about parameters in the DETAIL part of the error messages generated on INTO ... STRICT failures."),
166 : NULL,
167 : &plpgsql_print_strict_params,
168 : false,
169 : PGC_USERSET, 0,
170 : NULL, NULL, NULL);
171 :
172 3354 : DefineCustomBoolVariable("plpgsql.check_asserts",
173 : gettext_noop("Perform checks given in ASSERT statements."),
174 : NULL,
175 : &plpgsql_check_asserts,
176 : true,
177 : PGC_USERSET, 0,
178 : NULL, NULL, NULL);
179 :
180 3354 : DefineCustomStringVariable("plpgsql.extra_warnings",
181 : gettext_noop("List of programming constructs that should produce a warning."),
182 : NULL,
183 : &plpgsql_extra_warnings_string,
184 : "none",
185 : PGC_USERSET, GUC_LIST_INPUT,
186 : plpgsql_extra_checks_check_hook,
187 : plpgsql_extra_warnings_assign_hook,
188 : NULL);
189 :
190 3354 : DefineCustomStringVariable("plpgsql.extra_errors",
191 : gettext_noop("List of programming constructs that should produce an error."),
192 : NULL,
193 : &plpgsql_extra_errors_string,
194 : "none",
195 : PGC_USERSET, GUC_LIST_INPUT,
196 : plpgsql_extra_checks_check_hook,
197 : plpgsql_extra_errors_assign_hook,
198 : NULL);
199 :
200 3354 : MarkGUCPrefixReserved("plpgsql");
201 :
202 3354 : plpgsql_HashTableInit();
203 3354 : RegisterXactCallback(plpgsql_xact_cb, NULL);
204 3354 : RegisterSubXactCallback(plpgsql_subxact_cb, NULL);
205 :
206 : /* Set up a rendezvous point with optional instrumentation plugin */
207 3354 : plpgsql_plugin_ptr = (PLpgSQL_plugin **) find_rendezvous_variable("PLpgSQL_plugin");
208 :
209 3354 : inited = true;
210 : }
211 :
212 : /* ----------
213 : * plpgsql_call_handler
214 : *
215 : * The PostgreSQL function manager and trigger manager
216 : * call this function for execution of PL/pgSQL procedures.
217 : * ----------
218 : */
219 922 : PG_FUNCTION_INFO_V1(plpgsql_call_handler);
220 :
221 : Datum
222 80890 : plpgsql_call_handler(PG_FUNCTION_ARGS)
223 : {
224 : bool nonatomic;
225 : PLpgSQL_function *func;
226 : PLpgSQL_execstate *save_cur_estate;
227 : ResourceOwner procedure_resowner;
228 80890 : volatile Datum retval = (Datum) 0;
229 : int rc;
230 :
231 179040 : nonatomic = fcinfo->context &&
232 81062 : IsA(fcinfo->context, CallContext) &&
233 172 : !castNode(CallContext, fcinfo->context)->atomic;
234 :
235 : /*
236 : * Connect to SPI manager
237 : */
238 80890 : if ((rc = SPI_connect_ext(nonatomic ? SPI_OPT_NONATOMIC : 0)) != SPI_OK_CONNECT)
239 0 : elog(ERROR, "SPI_connect failed: %s", SPI_result_code_string(rc));
240 :
241 : /* Find or compile the function */
242 80890 : func = plpgsql_compile(fcinfo, false);
243 :
244 : /* Must save and restore prior value of cur_estate */
245 80876 : save_cur_estate = func->cur_estate;
246 :
247 : /* Mark the function as busy, so it can't be deleted from under us */
248 80876 : func->use_count++;
249 :
250 : /*
251 : * If we'll need a procedure-lifespan resowner to execute any CALL or DO
252 : * statements, create it now. Since this resowner is not tied to any
253 : * parent, failing to free it would result in process-lifespan leaks.
254 : * Therefore, be very wary of adding any code between here and the PG_TRY
255 : * block.
256 : */
257 80876 : procedure_resowner =
258 156 : (nonatomic && func->requires_procedure_resowner) ?
259 81032 : ResourceOwnerCreate(NULL, "PL/pgSQL procedure resources") : NULL;
260 :
261 80876 : PG_TRY();
262 : {
263 : /*
264 : * Determine if called as function or trigger and call appropriate
265 : * subhandler
266 : */
267 80876 : if (CALLED_AS_TRIGGER(fcinfo))
268 14888 : retval = PointerGetDatum(plpgsql_exec_trigger(func,
269 15078 : (TriggerData *) fcinfo->context));
270 65798 : else if (CALLED_AS_EVENT_TRIGGER(fcinfo))
271 : {
272 1698 : plpgsql_exec_event_trigger(func,
273 1698 : (EventTriggerData *) fcinfo->context);
274 : /* there's no return value in this case */
275 : }
276 : else
277 63474 : retval = plpgsql_exec_function(func, fcinfo,
278 : NULL, NULL,
279 : procedure_resowner,
280 64100 : !nonatomic);
281 : }
282 840 : PG_FINALLY();
283 : {
284 : /* Decrement use-count, restore cur_estate */
285 80876 : func->use_count--;
286 80876 : func->cur_estate = save_cur_estate;
287 :
288 : /* Be sure to release the procedure resowner if any */
289 80876 : if (procedure_resowner)
290 : {
291 28 : ReleaseAllPlanCacheRefsInOwner(procedure_resowner);
292 28 : ResourceOwnerDelete(procedure_resowner);
293 : }
294 : }
295 80876 : PG_END_TRY();
296 :
297 : /*
298 : * Disconnect from SPI manager
299 : */
300 80036 : if ((rc = SPI_finish()) != SPI_OK_FINISH)
301 0 : elog(ERROR, "SPI_finish failed: %s", SPI_result_code_string(rc));
302 :
303 80036 : return retval;
304 : }
305 :
306 : /* ----------
307 : * plpgsql_inline_handler
308 : *
309 : * Called by PostgreSQL to execute an anonymous code block
310 : * ----------
311 : */
312 290 : PG_FUNCTION_INFO_V1(plpgsql_inline_handler);
313 :
314 : Datum
315 1000 : plpgsql_inline_handler(PG_FUNCTION_ARGS)
316 : {
317 1000 : LOCAL_FCINFO(fake_fcinfo, 0);
318 1000 : InlineCodeBlock *codeblock = castNode(InlineCodeBlock, DatumGetPointer(PG_GETARG_DATUM(0)));
319 : PLpgSQL_function *func;
320 : FmgrInfo flinfo;
321 : EState *simple_eval_estate;
322 : ResourceOwner simple_eval_resowner;
323 : Datum retval;
324 : int rc;
325 :
326 : /*
327 : * Connect to SPI manager
328 : */
329 1000 : if ((rc = SPI_connect_ext(codeblock->atomic ? 0 : SPI_OPT_NONATOMIC)) != SPI_OK_CONNECT)
330 0 : elog(ERROR, "SPI_connect failed: %s", SPI_result_code_string(rc));
331 :
332 : /* Compile the anonymous code block */
333 1000 : func = plpgsql_compile_inline(codeblock->source_text);
334 :
335 : /* Mark the function as busy, just pro forma */
336 944 : func->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 4720 : MemSet(fake_fcinfo, 0, SizeForFunctionCallInfo(0));
344 6608 : MemSet(&flinfo, 0, sizeof(flinfo));
345 944 : fake_fcinfo->flinfo = &flinfo;
346 944 : flinfo.fn_oid = InvalidOid;
347 944 : 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 944 : simple_eval_estate = CreateExecutorState();
362 : simple_eval_resowner =
363 944 : ResourceOwnerCreate(NULL, "PL/pgSQL DO block simple expressions");
364 :
365 : /* And run the function */
366 944 : PG_TRY();
367 : {
368 644 : retval = plpgsql_exec_function(func, fake_fcinfo,
369 : simple_eval_estate,
370 : simple_eval_resowner,
371 : simple_eval_resowner, /* see above */
372 944 : codeblock->atomic);
373 : }
374 300 : 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 300 : plpgsql_subxact_cb(SUBXACT_EVENT_ABORT_SUB,
391 : GetCurrentSubTransactionId(),
392 : 0, NULL);
393 :
394 : /* Clean up the private EState and resowner */
395 300 : FreeExecutorState(simple_eval_estate);
396 300 : ReleaseAllPlanCacheRefsInOwner(simple_eval_resowner);
397 300 : ResourceOwnerDelete(simple_eval_resowner);
398 :
399 : /* Function should now have no remaining use-counts ... */
400 300 : func->use_count--;
401 : Assert(func->use_count == 0);
402 :
403 : /* ... so we can free subsidiary storage */
404 300 : plpgsql_free_function_memory(func);
405 :
406 : /* And propagate the error */
407 300 : PG_RE_THROW();
408 : }
409 644 : PG_END_TRY();
410 :
411 : /* Clean up the private EState and resowner */
412 644 : FreeExecutorState(simple_eval_estate);
413 644 : ReleaseAllPlanCacheRefsInOwner(simple_eval_resowner);
414 644 : ResourceOwnerDelete(simple_eval_resowner);
415 :
416 : /* Function should now have no remaining use-counts ... */
417 644 : func->use_count--;
418 : Assert(func->use_count == 0);
419 :
420 : /* ... so we can free subsidiary storage */
421 644 : plpgsql_free_function_memory(func);
422 :
423 : /*
424 : * Disconnect from SPI manager
425 : */
426 644 : if ((rc = SPI_finish()) != SPI_OK_FINISH)
427 0 : elog(ERROR, "SPI_finish failed: %s", SPI_result_code_string(rc));
428 :
429 644 : 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 642 : PG_FUNCTION_INFO_V1(plpgsql_validator);
440 :
441 : Datum
442 4424 : plpgsql_validator(PG_FUNCTION_ARGS)
443 : {
444 4424 : 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 4424 : bool is_dml_trigger = false;
453 4424 : bool is_event_trigger = false;
454 : int i;
455 :
456 4424 : if (!CheckFunctionValidatorAccess(fcinfo->flinfo->fn_oid, funcoid))
457 0 : PG_RETURN_VOID();
458 :
459 : /* Get the new function's pg_proc entry */
460 4424 : tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcoid));
461 4424 : if (!HeapTupleIsValid(tuple))
462 0 : elog(ERROR, "cache lookup failed for function %u", funcoid);
463 4424 : proc = (Form_pg_proc) GETSTRUCT(tuple);
464 :
465 4424 : functyptype = get_typtype(proc->prorettype);
466 :
467 : /* Disallow pseudotype result */
468 : /* except for TRIGGER, EVTTRIGGER, RECORD, VOID, or polymorphic */
469 4424 : if (functyptype == TYPTYPE_PSEUDO)
470 : {
471 2670 : if (proc->prorettype == TRIGGEROID)
472 1304 : is_dml_trigger = true;
473 1366 : else if (proc->prorettype == EVENT_TRIGGEROID)
474 138 : is_event_trigger = true;
475 1228 : else if (proc->prorettype != RECORDOID &&
476 1040 : 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 4424 : numargs = get_func_arg_info(tuple,
487 : &argtypes, &argnames, &argmodes);
488 7412 : for (i = 0; i < numargs; i++)
489 : {
490 2988 : if (get_typtype(argtypes[i]) == TYPTYPE_PSEUDO)
491 : {
492 594 : if (argtypes[i] != RECORDOID &&
493 590 : !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 4424 : if (check_function_bodies)
503 : {
504 4140 : 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 4140 : if ((rc = SPI_connect()) != SPI_OK_CONNECT)
514 0 : elog(ERROR, "SPI_connect failed: %s", SPI_result_code_string(rc));
515 :
516 : /*
517 : * Set up a fake fcinfo with just enough info to satisfy
518 : * plpgsql_compile().
519 : */
520 20700 : MemSet(fake_fcinfo, 0, SizeForFunctionCallInfo(0));
521 28980 : MemSet(&flinfo, 0, sizeof(flinfo));
522 4140 : fake_fcinfo->flinfo = &flinfo;
523 4140 : flinfo.fn_oid = funcoid;
524 4140 : flinfo.fn_mcxt = CurrentMemoryContext;
525 4140 : if (is_dml_trigger)
526 : {
527 13200 : MemSet(&trigdata, 0, sizeof(trigdata));
528 1200 : trigdata.type = T_TriggerData;
529 1200 : fake_fcinfo->context = (Node *) &trigdata;
530 : }
531 2940 : else if (is_event_trigger)
532 : {
533 590 : MemSet(&etrigdata, 0, sizeof(etrigdata));
534 118 : etrigdata.type = T_EventTriggerData;
535 118 : fake_fcinfo->context = (Node *) &etrigdata;
536 : }
537 :
538 : /* Test-compile the function */
539 4140 : plpgsql_compile(fake_fcinfo, true);
540 :
541 : /*
542 : * Disconnect from SPI manager
543 : */
544 4032 : if ((rc = SPI_finish()) != SPI_OK_FINISH)
545 0 : elog(ERROR, "SPI_finish failed: %s", SPI_result_code_string(rc));
546 : }
547 :
548 4316 : ReleaseSysCache(tuple);
549 :
550 4316 : PG_RETURN_VOID();
551 : }
|