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