1 /*-------------------------------------------------------------------------
2  *
is_error<T: ::std::error::Error + Send + Sync>()3  * pl_handler.c		- Handler for the PL/pgSQL
4  *			  procedural language
5  *
6  * Portions Copyright (c) 1996-2020, 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
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
127 plpgsql_extra_warnings_assign_hook(const char *newvalue, void *extra)
128 {
129 	plpgsql_extra_warnings = *((int *) extra);
130 }
131 
132 static void
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
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
222 plpgsql_call_handler(PG_FUNCTION_ARGS)
223 {
224 	bool		nonatomic;
225 	PLpgSQL_function *func;
226 	PLpgSQL_execstate *save_cur_estate;
227 	Datum		retval;
228 	int			rc;
229 
230 	nonatomic = fcinfo->context &&
231 		IsA(fcinfo->context, CallContext) &&
232 		!castNode(CallContext, fcinfo->context)->atomic;
233 
234 	/*
235 	 * Connect to SPI manager
236 	 */
237 	if ((rc = SPI_connect_ext(nonatomic ? SPI_OPT_NONATOMIC : 0)) != SPI_OK_CONNECT)
238 		elog(ERROR, "SPI_connect failed: %s", SPI_result_code_string(rc));
239 
240 	/* Find or compile the function */
241 	func = plpgsql_compile(fcinfo, false);
242 
243 	/* Must save and restore prior value of cur_estate */
244 	save_cur_estate = func->cur_estate;
245 
246 	/* Mark the function as busy, so it can't be deleted from under us */
247 	func->use_count++;
248 
249 	PG_TRY();
250 	{
251 		/*
252 		 * Determine if called as function or trigger and call appropriate
253 		 * subhandler
254 		 */
255 		if (CALLED_AS_TRIGGER(fcinfo))
256 			retval = PointerGetDatum(plpgsql_exec_trigger(func,
257 														  (TriggerData *) fcinfo->context));
258 		else if (CALLED_AS_EVENT_TRIGGER(fcinfo))
259 		{
260 			plpgsql_exec_event_trigger(func,
261 									   (EventTriggerData *) fcinfo->context);
262 			retval = (Datum) 0;
263 		}
264 		else
265 			retval = plpgsql_exec_function(func, fcinfo,
266 										   NULL, NULL,
267 										   !nonatomic);
268 	}
269 	PG_FINALLY();
270 	{
271 		/* Decrement use-count, restore cur_estate */
272 		func->use_count--;
273 		func->cur_estate = save_cur_estate;
274 	}
275 	PG_END_TRY();
276 
277 	/*
278 	 * Disconnect from SPI manager
279 	 */
280 	if ((rc = SPI_finish()) != SPI_OK_FINISH)
281 		elog(ERROR, "SPI_finish failed: %s", SPI_result_code_string(rc));
282 
283 	return retval;
284 }
285 
286 /* ----------
287  * plpgsql_inline_handler
288  *
289  * Called by PostgreSQL to execute an anonymous code block
290  * ----------
291  */
292 PG_FUNCTION_INFO_V1(plpgsql_inline_handler);
293 
294 Datum
295 plpgsql_inline_handler(PG_FUNCTION_ARGS)
296 {
297 	LOCAL_FCINFO(fake_fcinfo, 0);
298 	InlineCodeBlock *codeblock = castNode(InlineCodeBlock, DatumGetPointer(PG_GETARG_DATUM(0)));
299 	PLpgSQL_function *func;
300 	FmgrInfo	flinfo;
301 	EState	   *simple_eval_estate;
302 	ResourceOwner simple_eval_resowner;
303 	Datum		retval;
304 	int			rc;
305 
306 	/*
307 	 * Connect to SPI manager
308 	 */
309 	if ((rc = SPI_connect_ext(codeblock->atomic ? 0 : SPI_OPT_NONATOMIC)) != SPI_OK_CONNECT)
310 		elog(ERROR, "SPI_connect failed: %s", SPI_result_code_string(rc));
311 
312 	/* Compile the anonymous code block */
313 	func = plpgsql_compile_inline(codeblock->source_text);
314 
315 	/* Mark the function as busy, just pro forma */
316 	func->use_count++;
317 
318 	/*
319 	 * Set up a fake fcinfo with just enough info to satisfy
320 	 * plpgsql_exec_function().  In particular note that this sets things up
321 	 * with no arguments passed.
322 	 */
323 	MemSet(fake_fcinfo, 0, SizeForFunctionCallInfo(0));
324 	MemSet(&flinfo, 0, sizeof(flinfo));
325 	fake_fcinfo->flinfo = &flinfo;
326 	flinfo.fn_oid = InvalidOid;
327 	flinfo.fn_mcxt = CurrentMemoryContext;
328 
329 	/*
330 	 * Create a private EState and resowner for simple-expression execution.
331 	 * Notice that these are NOT tied to transaction-level resources; they
332 	 * must survive any COMMIT/ROLLBACK the DO block executes, since we will
333 	 * unconditionally try to clean them up below.  (Hence, be wary of adding
334 	 * anything that could fail between here and the PG_TRY block.)  See the
335 	 * comments for shared_simple_eval_estate.
336 	 */
337 	simple_eval_estate = CreateExecutorState();
338 	simple_eval_resowner =
339 		ResourceOwnerCreate(NULL, "PL/pgSQL DO block simple expressions");
340 
341 	/* And run the function */
342 	PG_TRY();
343 	{
344 		retval = plpgsql_exec_function(func, fake_fcinfo,
345 									   simple_eval_estate,
346 									   simple_eval_resowner,
347 									   codeblock->atomic);
348 	}
349 	PG_CATCH();
350 	{
351 		/*
352 		 * We need to clean up what would otherwise be long-lived resources
353 		 * accumulated by the failed DO block, principally cached plans for
354 		 * statements (which can be flushed by plpgsql_free_function_memory),
355 		 * execution trees for simple expressions, which are in the private
356 		 * EState, and cached-plan refcounts held by the private resowner.
357 		 *
358 		 * Before releasing the private EState, we must clean up any
359 		 * simple_econtext_stack entries pointing into it, which we can do by
360 		 * invoking the subxact callback.  (It will be called again later if
361 		 * some outer control level does a subtransaction abort, but no harm
362 		 * is done.)  We cheat a bit knowing that plpgsql_subxact_cb does not
363 		 * pay attention to its parentSubid argument.
364 		 */
365 		plpgsql_subxact_cb(SUBXACT_EVENT_ABORT_SUB,
366 						   GetCurrentSubTransactionId(),
367 						   0, NULL);
368 
369 		/* Clean up the private EState and resowner */
370 		FreeExecutorState(simple_eval_estate);
371 		ResourceOwnerReleaseAllPlanCacheRefs(simple_eval_resowner);
372 		ResourceOwnerDelete(simple_eval_resowner);
373 
374 		/* Function should now have no remaining use-counts ... */
375 		func->use_count--;
376 		Assert(func->use_count == 0);
377 
378 		/* ... so we can free subsidiary storage */
379 		plpgsql_free_function_memory(func);
380 
381 		/* And propagate the error */
382 		PG_RE_THROW();
383 	}
384 	PG_END_TRY();
385 
386 	/* Clean up the private EState and resowner */
387 	FreeExecutorState(simple_eval_estate);
388 	ResourceOwnerReleaseAllPlanCacheRefs(simple_eval_resowner);
389 	ResourceOwnerDelete(simple_eval_resowner);
390 
391 	/* Function should now have no remaining use-counts ... */
392 	func->use_count--;
393 	Assert(func->use_count == 0);
394 
395 	/* ... so we can free subsidiary storage */
396 	plpgsql_free_function_memory(func);
397 
398 	/*
399 	 * Disconnect from SPI manager
400 	 */
401 	if ((rc = SPI_finish()) != SPI_OK_FINISH)
402 		elog(ERROR, "SPI_finish failed: %s", SPI_result_code_string(rc));
403 
404 	return retval;
405 }
406 
407 /* ----------
408  * plpgsql_validator
409  *
410  * This function attempts to validate a PL/pgSQL function at
411  * CREATE FUNCTION time.
412  * ----------
413  */
414 PG_FUNCTION_INFO_V1(plpgsql_validator);
415 
416 Datum
417 plpgsql_validator(PG_FUNCTION_ARGS)
418 {
419 	Oid			funcoid = PG_GETARG_OID(0);
420 	HeapTuple	tuple;
421 	Form_pg_proc proc;
422 	char		functyptype;
423 	int			numargs;
424 	Oid		   *argtypes;
425 	char	  **argnames;
426 	char	   *argmodes;
427 	bool		is_dml_trigger = false;
428 	bool		is_event_trigger = false;
429 	int			i;
430 
431 	if (!CheckFunctionValidatorAccess(fcinfo->flinfo->fn_oid, funcoid))
432 		PG_RETURN_VOID();
433 
434 	/* Get the new function's pg_proc entry */
435 	tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcoid));
436 	if (!HeapTupleIsValid(tuple))
437 		elog(ERROR, "cache lookup failed for function %u", funcoid);
438 	proc = (Form_pg_proc) GETSTRUCT(tuple);
439 
440 	functyptype = get_typtype(proc->prorettype);
441 
442 	/* Disallow pseudotype result */
443 	/* except for TRIGGER, EVTTRIGGER, RECORD, VOID, or polymorphic */
444 	if (functyptype == TYPTYPE_PSEUDO)
445 	{
446 		if (proc->prorettype == TRIGGEROID)
447 			is_dml_trigger = true;
448 		else if (proc->prorettype == EVTTRIGGEROID)
449 			is_event_trigger = true;
450 		else if (proc->prorettype != RECORDOID &&
451 				 proc->prorettype != VOIDOID &&
452 				 !IsPolymorphicType(proc->prorettype))
453 			ereport(ERROR,
454 					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
455 					 errmsg("PL/pgSQL functions cannot return type %s",
456 							format_type_be(proc->prorettype))));
457 	}
458 
459 	/* Disallow pseudotypes in arguments (either IN or OUT) */
460 	/* except for RECORD and polymorphic */
461 	numargs = get_func_arg_info(tuple,
462 								&argtypes, &argnames, &argmodes);
463 	for (i = 0; i < numargs; i++)
464 	{
465 		if (get_typtype(argtypes[i]) == TYPTYPE_PSEUDO)
466 		{
467 			if (argtypes[i] != RECORDOID &&
468 				!IsPolymorphicType(argtypes[i]))
469 				ereport(ERROR,
470 						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
471 						 errmsg("PL/pgSQL functions cannot accept type %s",
472 								format_type_be(argtypes[i]))));
473 		}
474 	}
475 
476 	/* Postpone body checks if !check_function_bodies */
477 	if (check_function_bodies)
478 	{
479 		LOCAL_FCINFO(fake_fcinfo, 0);
480 		FmgrInfo	flinfo;
481 		int			rc;
482 		TriggerData trigdata;
483 		EventTriggerData etrigdata;
484 
485 		/*
486 		 * Connect to SPI manager (is this needed for compilation?)
487 		 */
488 		if ((rc = SPI_connect()) != SPI_OK_CONNECT)
489 			elog(ERROR, "SPI_connect failed: %s", SPI_result_code_string(rc));
490 
491 		/*
492 		 * Set up a fake fcinfo with just enough info to satisfy
493 		 * plpgsql_compile().
494 		 */
495 		MemSet(fake_fcinfo, 0, SizeForFunctionCallInfo(0));
496 		MemSet(&flinfo, 0, sizeof(flinfo));
497 		fake_fcinfo->flinfo = &flinfo;
498 		flinfo.fn_oid = funcoid;
499 		flinfo.fn_mcxt = CurrentMemoryContext;
500 		if (is_dml_trigger)
501 		{
502 			MemSet(&trigdata, 0, sizeof(trigdata));
503 			trigdata.type = T_TriggerData;
504 			fake_fcinfo->context = (Node *) &trigdata;
505 		}
506 		else if (is_event_trigger)
507 		{
508 			MemSet(&etrigdata, 0, sizeof(etrigdata));
509 			etrigdata.type = T_EventTriggerData;
510 			fake_fcinfo->context = (Node *) &etrigdata;
511 		}
512 
513 		/* Test-compile the function */
514 		plpgsql_compile(fake_fcinfo, true);
515 
516 		/*
517 		 * Disconnect from SPI manager
518 		 */
519 		if ((rc = SPI_finish()) != SPI_OK_FINISH)
520 			elog(ERROR, "SPI_finish failed: %s", SPI_result_code_string(rc));
521 	}
522 
523 	ReleaseSysCache(tuple);
524 
525 	PG_RETURN_VOID();
526 }
527