1 /*-------------------------------------------------------------------------
2 *
3 * pl_handler.c - Handler for the PL/pgSQL
4 * procedural language
5 *
6 * Portions Copyright (c) 1996-2021, 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 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
plpgsql_extra_checks_check_hook(char ** newvalue,void ** extra,GucSource source)60 plpgsql_extra_checks_check_hook(char **newvalue, void **extra, GucSource source)
61 {
62 char *rawstring;
63 List *elemlist;
64 ListCell *l;
65 int extrachecks = 0;
66 int *myextra;
67
68 if (pg_strcasecmp(*newvalue, "all") == 0)
69 extrachecks = PLPGSQL_XCHECK_ALL;
70 else if (pg_strcasecmp(*newvalue, "none") == 0)
71 extrachecks = PLPGSQL_XCHECK_NONE;
72 else
73 {
74 /* Need a modifiable copy of string */
75 rawstring = pstrdup(*newvalue);
76
77 /* Parse string into list of identifiers */
78 if (!SplitIdentifierString(rawstring, ',', &elemlist))
79 {
80 /* syntax error in list */
81 GUC_check_errdetail("List syntax is invalid.");
82 pfree(rawstring);
83 list_free(elemlist);
84 return false;
85 }
86
87 foreach(l, elemlist)
88 {
89 char *tok = (char *) lfirst(l);
90
91 if (pg_strcasecmp(tok, "shadowed_variables") == 0)
92 extrachecks |= PLPGSQL_XCHECK_SHADOWVAR;
93 else if (pg_strcasecmp(tok, "too_many_rows") == 0)
94 extrachecks |= PLPGSQL_XCHECK_TOOMANYROWS;
95 else if (pg_strcasecmp(tok, "strict_multi_assignment") == 0)
96 extrachecks |= PLPGSQL_XCHECK_STRICTMULTIASSIGNMENT;
97 else if (pg_strcasecmp(tok, "all") == 0 || pg_strcasecmp(tok, "none") == 0)
98 {
99 GUC_check_errdetail("Key word \"%s\" cannot be combined with other key words.", tok);
100 pfree(rawstring);
101 list_free(elemlist);
102 return false;
103 }
104 else
105 {
106 GUC_check_errdetail("Unrecognized key word: \"%s\".", tok);
107 pfree(rawstring);
108 list_free(elemlist);
109 return false;
110 }
111 }
112
113 pfree(rawstring);
114 list_free(elemlist);
115 }
116
117 myextra = (int *) malloc(sizeof(int));
118 if (!myextra)
119 return false;
120 *myextra = extrachecks;
121 *extra = (void *) myextra;
122
123 return true;
124 }
125
126 static void
plpgsql_extra_warnings_assign_hook(const char * newvalue,void * extra)127 plpgsql_extra_warnings_assign_hook(const char *newvalue, void *extra)
128 {
129 plpgsql_extra_warnings = *((int *) extra);
130 }
131
132 static void
plpgsql_extra_errors_assign_hook(const char * newvalue,void * extra)133 plpgsql_extra_errors_assign_hook(const char *newvalue, void *extra)
134 {
135 plpgsql_extra_errors = *((int *) extra);
136 }
137
138
139 /*
140 * _PG_init() - library load-time initialization
141 *
142 * DO NOT make this static nor change its name!
143 */
144 void
_PG_init(void)145 _PG_init(void)
146 {
147 /* Be sure we do initialization only once (should be redundant now) */
148 static bool inited = false;
149
150 if (inited)
151 return;
152
153 pg_bindtextdomain(TEXTDOMAIN);
154
155 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 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 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 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 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 EmitWarningsOnPlaceholders("plpgsql");
201
202 plpgsql_HashTableInit();
203 RegisterXactCallback(plpgsql_xact_cb, NULL);
204 RegisterSubXactCallback(plpgsql_subxact_cb, NULL);
205
206 /* Set up a rendezvous point with optional instrumentation plugin */
207 plpgsql_plugin_ptr = (PLpgSQL_plugin **) find_rendezvous_variable("PLpgSQL_plugin");
208
209 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 PG_FUNCTION_INFO_V1(plpgsql_call_handler);
220
221 Datum
plpgsql_call_handler(PG_FUNCTION_ARGS)222 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 volatile Datum retval = (Datum) 0;
229 int rc;
230
231 nonatomic = fcinfo->context &&
232 IsA(fcinfo->context, CallContext) &&
233 !castNode(CallContext, fcinfo->context)->atomic;
234
235 /*
236 * Connect to SPI manager
237 */
238 if ((rc = SPI_connect_ext(nonatomic ? SPI_OPT_NONATOMIC : 0)) != SPI_OK_CONNECT)
239 elog(ERROR, "SPI_connect failed: %s", SPI_result_code_string(rc));
240
241 /* Find or compile the function */
242 func = plpgsql_compile(fcinfo, false);
243
244 /* Must save and restore prior value of cur_estate */
245 save_cur_estate = func->cur_estate;
246
247 /* Mark the function as busy, so it can't be deleted from under us */
248 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 procedure_resowner =
258 (nonatomic && func->requires_procedure_resowner) ?
259 ResourceOwnerCreate(NULL, "PL/pgSQL procedure resources") : NULL;
260
261 PG_TRY();
262 {
263 /*
264 * Determine if called as function or trigger and call appropriate
265 * subhandler
266 */
267 if (CALLED_AS_TRIGGER(fcinfo))
268 retval = PointerGetDatum(plpgsql_exec_trigger(func,
269 (TriggerData *) fcinfo->context));
270 else if (CALLED_AS_EVENT_TRIGGER(fcinfo))
271 {
272 plpgsql_exec_event_trigger(func,
273 (EventTriggerData *) fcinfo->context);
274 /* there's no return value in this case */
275 }
276 else
277 retval = plpgsql_exec_function(func, fcinfo,
278 NULL, NULL,
279 procedure_resowner,
280 !nonatomic);
281 }
282 PG_FINALLY();
283 {
284 /* Decrement use-count, restore cur_estate */
285 func->use_count--;
286 func->cur_estate = save_cur_estate;
287
288 /* Be sure to release the procedure resowner if any */
289 if (procedure_resowner)
290 {
291 ResourceOwnerReleaseAllPlanCacheRefs(procedure_resowner);
292 ResourceOwnerDelete(procedure_resowner);
293 }
294 }
295 PG_END_TRY();
296
297 /*
298 * Disconnect from SPI manager
299 */
300 if ((rc = SPI_finish()) != SPI_OK_FINISH)
301 elog(ERROR, "SPI_finish failed: %s", SPI_result_code_string(rc));
302
303 return retval;
304 }
305
306 /* ----------
307 * plpgsql_inline_handler
308 *
309 * Called by PostgreSQL to execute an anonymous code block
310 * ----------
311 */
312 PG_FUNCTION_INFO_V1(plpgsql_inline_handler);
313
314 Datum
plpgsql_inline_handler(PG_FUNCTION_ARGS)315 plpgsql_inline_handler(PG_FUNCTION_ARGS)
316 {
317 LOCAL_FCINFO(fake_fcinfo, 0);
318 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 if ((rc = SPI_connect_ext(codeblock->atomic ? 0 : SPI_OPT_NONATOMIC)) != SPI_OK_CONNECT)
330 elog(ERROR, "SPI_connect failed: %s", SPI_result_code_string(rc));
331
332 /* Compile the anonymous code block */
333 func = plpgsql_compile_inline(codeblock->source_text);
334
335 /* Mark the function as busy, just pro forma */
336 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 MemSet(fake_fcinfo, 0, SizeForFunctionCallInfo(0));
344 MemSet(&flinfo, 0, sizeof(flinfo));
345 fake_fcinfo->flinfo = &flinfo;
346 flinfo.fn_oid = InvalidOid;
347 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 simple_eval_estate = CreateExecutorState();
362 simple_eval_resowner =
363 ResourceOwnerCreate(NULL, "PL/pgSQL DO block simple expressions");
364
365 /* And run the function */
366 PG_TRY();
367 {
368 retval = plpgsql_exec_function(func, fake_fcinfo,
369 simple_eval_estate,
370 simple_eval_resowner,
371 simple_eval_resowner, /* see above */
372 codeblock->atomic);
373 }
374 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 plpgsql_subxact_cb(SUBXACT_EVENT_ABORT_SUB,
391 GetCurrentSubTransactionId(),
392 0, NULL);
393
394 /* Clean up the private EState and resowner */
395 FreeExecutorState(simple_eval_estate);
396 ResourceOwnerReleaseAllPlanCacheRefs(simple_eval_resowner);
397 ResourceOwnerDelete(simple_eval_resowner);
398
399 /* Function should now have no remaining use-counts ... */
400 func->use_count--;
401 Assert(func->use_count == 0);
402
403 /* ... so we can free subsidiary storage */
404 plpgsql_free_function_memory(func);
405
406 /* And propagate the error */
407 PG_RE_THROW();
408 }
409 PG_END_TRY();
410
411 /* Clean up the private EState and resowner */
412 FreeExecutorState(simple_eval_estate);
413 ResourceOwnerReleaseAllPlanCacheRefs(simple_eval_resowner);
414 ResourceOwnerDelete(simple_eval_resowner);
415
416 /* Function should now have no remaining use-counts ... */
417 func->use_count--;
418 Assert(func->use_count == 0);
419
420 /* ... so we can free subsidiary storage */
421 plpgsql_free_function_memory(func);
422
423 /*
424 * Disconnect from SPI manager
425 */
426 if ((rc = SPI_finish()) != SPI_OK_FINISH)
427 elog(ERROR, "SPI_finish failed: %s", SPI_result_code_string(rc));
428
429 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 PG_FUNCTION_INFO_V1(plpgsql_validator);
440
441 Datum
plpgsql_validator(PG_FUNCTION_ARGS)442 plpgsql_validator(PG_FUNCTION_ARGS)
443 {
444 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 bool is_dml_trigger = false;
453 bool is_event_trigger = false;
454 int i;
455
456 if (!CheckFunctionValidatorAccess(fcinfo->flinfo->fn_oid, funcoid))
457 PG_RETURN_VOID();
458
459 /* Get the new function's pg_proc entry */
460 tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcoid));
461 if (!HeapTupleIsValid(tuple))
462 elog(ERROR, "cache lookup failed for function %u", funcoid);
463 proc = (Form_pg_proc) GETSTRUCT(tuple);
464
465 functyptype = get_typtype(proc->prorettype);
466
467 /* Disallow pseudotype result */
468 /* except for TRIGGER, EVTTRIGGER, RECORD, VOID, or polymorphic */
469 if (functyptype == TYPTYPE_PSEUDO)
470 {
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))
478 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 numargs = get_func_arg_info(tuple,
487 &argtypes, &argnames, &argmodes);
488 for (i = 0; i < numargs; i++)
489 {
490 if (get_typtype(argtypes[i]) == TYPTYPE_PSEUDO)
491 {
492 if (argtypes[i] != RECORDOID &&
493 !IsPolymorphicType(argtypes[i]))
494 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 if (check_function_bodies)
503 {
504 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 if ((rc = SPI_connect()) != SPI_OK_CONNECT)
514 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 MemSet(fake_fcinfo, 0, SizeForFunctionCallInfo(0));
521 MemSet(&flinfo, 0, sizeof(flinfo));
522 fake_fcinfo->flinfo = &flinfo;
523 flinfo.fn_oid = funcoid;
524 flinfo.fn_mcxt = CurrentMemoryContext;
525 if (is_dml_trigger)
526 {
527 MemSet(&trigdata, 0, sizeof(trigdata));
528 trigdata.type = T_TriggerData;
529 fake_fcinfo->context = (Node *) &trigdata;
530 }
531 else if (is_event_trigger)
532 {
533 MemSet(&etrigdata, 0, sizeof(etrigdata));
534 etrigdata.type = T_EventTriggerData;
535 fake_fcinfo->context = (Node *) &etrigdata;
536 }
537
538 /* Test-compile the function */
539 plpgsql_compile(fake_fcinfo, true);
540
541 /*
542 * Disconnect from SPI manager
543 */
544 if ((rc = SPI_finish()) != SPI_OK_FINISH)
545 elog(ERROR, "SPI_finish failed: %s", SPI_result_code_string(rc));
546 }
547
548 ReleaseSysCache(tuple);
549
550 PG_RETURN_VOID();
551 }
552