1 /*-------------------------------------------------------------------------
2  *
3  * check_function.c
4  *
5  *			  workhorse functionality of this extension - expression
6  *			  and query validator
7  *
8  * by Pavel Stehule 2013-2021
9  *
10  *-------------------------------------------------------------------------
11  */
12 
13 #include "plpgsql_check.h"
14 
15 #include "catalog/pg_proc.h"
16 #include "catalog/pg_type.h"
17 #include "nodes/makefuncs.h"
18 #include "utils/guc.h"
19 #include "utils/memutils.h"
20 #include "utils/lsyscache.h"
21 #include "utils/rel.h"
22 #include "utils/syscache.h"
23 #include "utils/typcache.h"
24 
25 #if PG_VERSION_NUM >= 120000
26 
27 #include "access/heapam.h"
28 
29 #endif
30 
31 static HTAB *plpgsql_check_HashTable = NULL;
32 
33 bool plpgsql_check_other_warnings = false;
34 bool plpgsql_check_extra_warnings = false;
35 bool plpgsql_check_performance_warnings = false;
36 bool plpgsql_check_fatal_errors = true;
37 int plpgsql_check_mode = PLPGSQL_CHECK_MODE_BY_FUNCTION;
38 
39 /* ----------
40  * Hash table for checked functions
41  * ----------
42  */
43 
44 typedef struct plpgsql_hashent
45 {
46 	PLpgSQL_func_hashkey key;
47 	TransactionId fn_xmin;
48 	ItemPointerData fn_tid;
49 	bool is_checked;
50 } plpgsql_check_HashEnt;
51 
52 
53 static void function_check(PLpgSQL_function *func, PLpgSQL_checkstate *cstate);
54 static void trigger_check(PLpgSQL_function *func, Node *tdata, PLpgSQL_checkstate *cstate);
55 static void release_exprs(List *exprs);
56 static int load_configuration(HeapTuple procTuple, bool *reload_config);
57 static void init_datum_dno(PLpgSQL_checkstate *cstate, int dno, bool is_auto, bool is_protected);
58 static PLpgSQL_datum * copy_plpgsql_datum(PLpgSQL_checkstate *cstate, PLpgSQL_datum *datum);
59 static void setup_estate(PLpgSQL_execstate *estate, PLpgSQL_function *func, ReturnSetInfo *rsi, plpgsql_check_info *cinfo);
60 static void setup_cstate(PLpgSQL_checkstate *cstate, plpgsql_check_result_info *result_info,
61 	plpgsql_check_info *cinfo, bool is_active_mode, bool fake_rtd);
62 
63 /*
64  * Prepare list of OUT variables for later report
65  */
66 static void
collect_out_variables(PLpgSQL_function * func,PLpgSQL_checkstate * cstate)67 collect_out_variables(PLpgSQL_function *func, PLpgSQL_checkstate *cstate)
68 {
69 	cstate->out_variables = NULL;
70 
71 	if (func->out_param_varno != -1)
72 	{
73 		int		varno = func->out_param_varno;
74 		PLpgSQL_variable *var = (PLpgSQL_variable *) func->datums[varno];
75 
76 		if (var->dtype == PLPGSQL_DTYPE_ROW && is_internal_variable(cstate, var))
77 		{
78 			/* this function has more OUT parameters */
79 			PLpgSQL_row *row = (PLpgSQL_row*) var;
80 			int		fnum;
81 
82 			for (fnum = 0; fnum < row->nfields; fnum++)
83 				cstate->out_variables = bms_add_member(cstate->out_variables, row->varnos[fnum]);
84 		}
85 		else
86 			cstate->out_variables = bms_add_member(cstate->out_variables, varno);
87 	}
88 }
89 
90 /*
91  * Returns true, when routine should be closed by RETURN statement
92  *
93  */
94 static bool
return_is_required(plpgsql_check_info * cinfo)95 return_is_required(plpgsql_check_info *cinfo)
96 {
97 	if (cinfo->is_procedure)
98 		return false;
99 
100 	if (cinfo->rettype == VOIDOID)
101 		return false;
102 
103 	return true;
104 }
105 
106 /*
107  * own implementation - active mode
108  *
109  */
110 void
plpgsql_check_function_internal(plpgsql_check_result_info * ri,plpgsql_check_info * cinfo)111 plpgsql_check_function_internal(plpgsql_check_result_info *ri,
112 								plpgsql_check_info *cinfo)
113 {
114 	PLpgSQL_checkstate cstate;
115 	PLpgSQL_function *volatile function = NULL;
116 	int			save_nestlevel = 0;
117 	bool		reload_config;
118 
119 #if PG_VERSION_NUM >= 120000
120 
121 	LOCAL_FCINFO(fake_fcinfo, 0);
122 
123 #else
124 
125 	FunctionCallInfoData fake_fcinfo_data;
126 	FunctionCallInfo fake_fcinfo = &fake_fcinfo_data;
127 
128 #endif
129 
130 	FmgrInfo	flinfo;
131 	TriggerData trigdata;
132 	EventTriggerData etrigdata;
133 	Trigger tg_trigger;
134 	int			rc;
135 	ResourceOwner oldowner;
136 	PLpgSQL_execstate *cur_estate = NULL;
137 	MemoryContext old_cxt;
138 	PLpgSQL_execstate estate;
139 	ReturnSetInfo rsinfo;
140 	bool		fake_rtd;
141 
142 	/*
143 	 * Connect to SPI manager
144 	 */
145 	if ((rc = SPI_connect()) != SPI_OK_CONNECT)
146 		elog(ERROR, "SPI_connect failed: %s", SPI_result_code_string(rc));
147 
148 	plpgsql_check_setup_fcinfo(cinfo,
149 							   &flinfo,
150 							   fake_fcinfo,
151 							   &rsinfo,
152 							   &trigdata,
153 							   &etrigdata,
154 								&tg_trigger,
155 								&fake_rtd);
156 
157 	setup_cstate(&cstate, ri, cinfo, true, fake_rtd);
158 
159 	old_cxt = MemoryContextSwitchTo(cstate.check_cxt);
160 
161 	/*
162 	 * Copy argument names for later check, only when other warnings are required.
163 	 * Argument names are used for check parameter versus local variable collision.
164 	 */
165 	if (cinfo->other_warnings)
166 	{
167 		int		numargs;
168 		Oid		   *argtypes;
169 		char	  **argnames;
170 		char	   *argmodes;
171 		int			i;
172 
173 		numargs = get_func_arg_info(cinfo->proctuple,
174 							&argtypes, &argnames, &argmodes);
175 
176 		if (argnames != NULL)
177 		{
178 			for (i = 0; i < numargs; i++)
179 			{
180 				if (argnames[i][0] != '\0')
181 					cstate.argnames = lappend(cstate.argnames, argnames[i]);
182 			}
183 		}
184 	}
185 
186 	oldowner = CurrentResourceOwner;
187 
188 	PG_TRY();
189 	{
190 		BeginInternalSubTransaction(NULL);
191 		MemoryContextSwitchTo(cstate.check_cxt);
192 
193 		save_nestlevel = load_configuration(cinfo->proctuple, &reload_config);
194 
195 		/* have to wait for this decision to loaded configuration */
196 		if (plpgsql_check_mode != PLPGSQL_CHECK_MODE_DISABLED)
197 		{
198 			/* Get a compiled function */
199 			function = plpgsql_check__compile_p(fake_fcinfo, false);
200 
201 			collect_out_variables(function, &cstate);
202 
203 			/* Must save and restore prior value of cur_estate */
204 			cur_estate = function->cur_estate;
205 
206 			/* recheck trigtype */
207 
208 			Assert(function->fn_is_trigger == cinfo->trigtype);
209 
210 			setup_estate(&estate, function, (ReturnSetInfo *) fake_fcinfo->resultinfo, cinfo);
211 			cstate.estate = &estate;
212 
213 			/*
214 			 * Mark the function as busy, ensure higher than zero usage. There is no
215 			 * reason for protection function against delete, but I afraid of asserts.
216 			 */
217 			function->use_count++;
218 
219 			/* Create a fake runtime environment and process check */
220 			switch (cinfo->trigtype)
221 			{
222 				case PLPGSQL_DML_TRIGGER:
223 					trigger_check(function, (Node *) &trigdata, &cstate);
224 					break;
225 
226 				case PLPGSQL_EVENT_TRIGGER:
227 					trigger_check(function, (Node *) &etrigdata, &cstate);
228 					break;
229 
230 				case PLPGSQL_NOT_TRIGGER:
231 					function_check(function, &cstate);
232 					break;
233 			}
234 
235 			function->cur_estate = cur_estate;
236 			function->use_count--;
237 		}
238 		else
239 			elog(NOTICE, "plpgsql_check is disabled");
240 
241 		/*
242 		 * reload back a GUC. XXX: isn't this done automatically by subxact
243 		 * rollback?
244 		 */
245 		if (reload_config)
246 			AtEOXact_GUC(true, save_nestlevel);
247 
248 		RollbackAndReleaseCurrentSubTransaction();
249 		MemoryContextSwitchTo(cstate.check_cxt);
250 		CurrentResourceOwner = oldowner;
251 
252 		if (OidIsValid(cinfo->relid))
253 			relation_close(trigdata.tg_relation, AccessShareLock);
254 
255 		release_exprs(cstate.exprs);
256 
257 		SPI_restore_connection();
258 	}
259 	PG_CATCH();
260 	{
261 		ErrorData  *edata;
262 
263 		MemoryContextSwitchTo(cstate.check_cxt);
264 		edata = CopyErrorData();
265 		FlushErrorState();
266 
267 		RollbackAndReleaseCurrentSubTransaction();
268 		MemoryContextSwitchTo(cstate.check_cxt);
269 		CurrentResourceOwner = oldowner;
270 
271 		if (OidIsValid(cinfo->relid))
272 			relation_close(trigdata.tg_relation, AccessShareLock);
273 
274 		if (function)
275 		{
276 			function->cur_estate = cur_estate;
277 			function->use_count--;
278 			release_exprs(cstate.exprs);
279 		}
280 
281 		plpgsql_check_put_error_edata(&cstate, edata);
282 
283 		/* reconnect spi */
284 		SPI_restore_connection();
285 	}
286 	PG_END_TRY();
287 
288 	MemoryContextSwitchTo(old_cxt);
289 	MemoryContextDelete(cstate.check_cxt);
290 
291 	/*
292 	 * Disconnect from SPI manager
293 	 */
294 	if ((rc = SPI_finish()) != SPI_OK_FINISH)
295 		elog(ERROR, "SPI_finish failed: %s", SPI_result_code_string(rc));
296 }
297 
298 /*
299  * plpgsql_check_on_func_beg - passive mode
300  *
301  *      callback function - called by plgsql executor, when function is started
302  *      and local variables are initialized.
303  *
304  */
305 void
plpgsql_check_on_func_beg(PLpgSQL_execstate * estate,PLpgSQL_function * func)306 plpgsql_check_on_func_beg(PLpgSQL_execstate *estate, PLpgSQL_function *func)
307 {
308 	const char *err_text = estate->err_text;
309 	int closing;
310 	List		*exceptions;
311 
312 	if (plpgsql_check_tracer)
313 		plpgsql_check_tracer_on_func_beg(estate, func);
314 
315 	if (plpgsql_check_mode == PLPGSQL_CHECK_MODE_FRESH_START ||
316 		   plpgsql_check_mode == PLPGSQL_CHECK_MODE_EVERY_START)
317 	{
318 		int i;
319 		PLpgSQL_rec *saved_records;
320 		PLpgSQL_var *saved_vars;
321 		MemoryContext oldcontext,
322 					 old_cxt;
323 		ResourceOwner oldowner;
324 		plpgsql_check_result_info ri;
325 		plpgsql_check_info cinfo;
326 		PLpgSQL_checkstate cstate;
327 
328 		/*
329 		 * don't allow repeated execution on checked function
330 		 * when it is not requsted.
331 		 */
332 		if (plpgsql_check_mode == PLPGSQL_CHECK_MODE_FRESH_START &&
333 			plpgsql_check_is_checked(func))
334 			return;
335 
336 		plpgsql_check_mark_as_checked(func);
337 
338 		memset(&ri, 0, sizeof(ri));
339 		memset(&cinfo, 0, sizeof(cinfo));
340 
341 		if (OidIsValid(func->fn_oid))
342 		{
343 			HeapTuple	procTuple;
344 
345 			procTuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(func->fn_oid));
346 			if (!HeapTupleIsValid(procTuple))
347 				elog(ERROR, "cache lookup failed for function %u", func->fn_oid);
348 
349 			plpgsql_check_get_function_info(procTuple,
350 											&cinfo.rettype,
351 											&cinfo.volatility,
352 											&cinfo.trigtype,
353 											&cinfo.is_procedure);
354 
355 			ReleaseSysCache(procTuple);
356 
357 			cinfo.fn_oid = func->fn_oid;
358 		}
359 		else
360 			cinfo.volatility = PROVOLATILE_VOLATILE;
361 
362 		cinfo.fatal_errors = plpgsql_check_fatal_errors,
363 		cinfo.other_warnings = plpgsql_check_other_warnings,
364 		cinfo.performance_warnings = plpgsql_check_performance_warnings,
365 		cinfo.extra_warnings = plpgsql_check_extra_warnings,
366 
367 		ri.format = PLPGSQL_CHECK_FORMAT_ELOG;
368 
369 		setup_cstate(&cstate, &ri, &cinfo, false, false);
370 
371 		collect_out_variables(func, &cstate);
372 
373 		/* use real estate */
374 		cstate.estate = estate;
375 
376 		old_cxt = MemoryContextSwitchTo(cstate.check_cxt);
377 
378 		/*
379 		 * During the check stage a rec and vars variables are modified, so we should
380 		 * to save their content
381 		 */
382 		saved_records = palloc(sizeof(PLpgSQL_rec) * estate->ndatums);
383 		saved_vars = palloc(sizeof(PLpgSQL_var) * estate->ndatums);
384 
385 		for (i = 0; i < estate->ndatums; i++)
386 		{
387 			if (estate->datums[i]->dtype == PLPGSQL_DTYPE_REC)
388 			{
389 				PLpgSQL_rec *rec = (PLpgSQL_rec *) estate->datums[i];
390 
391 #if PG_VERSION_NUM >= 110000
392 
393 				memcpy(&saved_records[i], rec, sizeof(PLpgSQL_rec));
394 
395 				if (rec->erh)
396 				{
397 					/* work with dummy copy */
398 					rec->erh = make_expanded_record_from_exprecord(rec->erh, cstate.check_cxt);
399 				}
400 
401 #else
402 
403 				saved_records[i].tup = rec->tup;
404 				saved_records[i].tupdesc = rec->tupdesc;
405 				saved_records[i].freetup = rec->freetup;
406 				saved_records[i].freetupdesc = rec->freetupdesc;
407 
408 				/* don't release a original tupdesc and original tup */
409 				rec->freetup = false;
410 				rec->freetupdesc = false;
411 
412 #endif
413 
414 			}
415 			else if (estate->datums[i]->dtype == PLPGSQL_DTYPE_VAR)
416 			{
417 				PLpgSQL_var *var = (PLpgSQL_var *) estate->datums[i];
418 
419 				saved_vars[i].value = var->value;
420 				saved_vars[i].isnull = var->isnull;
421 				saved_vars[i].freeval = var->freeval;
422 
423 				var->freeval = false;
424 			}
425 		}
426 
427 		estate->err_text = NULL;
428 
429 		/*
430 		 * Raised exception should be trapped in outer functtion. Protection
431 		 * against outer trap is QUERY_CANCELED exception.
432 		 */
433 		oldcontext = CurrentMemoryContext;
434 		oldowner = CurrentResourceOwner;
435 
436 		PG_TRY();
437 		{
438 			/*
439 			 * Now check the toplevel block of statements
440 			 */
441 			plpgsql_check_stmt(&cstate, (PLpgSQL_stmt *) func->action, &closing, &exceptions);
442 
443 			estate->err_stmt = NULL;
444 
445 			if (!cstate.stop_check)
446 			{
447 				if (closing != PLPGSQL_CHECK_CLOSED && closing != PLPGSQL_CHECK_CLOSED_BY_EXCEPTIONS &&
448 					return_is_required(cstate.cinfo))
449 					plpgsql_check_put_error(&cstate,
450 									  ERRCODE_S_R_E_FUNCTION_EXECUTED_NO_RETURN_STATEMENT, 0,
451 									  "control reached end of function without RETURN",
452 									  NULL,
453 									  NULL,
454 									  closing == PLPGSQL_CHECK_UNCLOSED ?
455 											PLPGSQL_CHECK_ERROR : PLPGSQL_CHECK_WARNING_EXTRA,
456 									  0, NULL, NULL);
457 
458 				plpgsql_check_report_unused_variables(&cstate);
459 				plpgsql_check_report_too_high_volatility(&cstate);
460 			}
461 		}
462 		PG_CATCH();
463 		{
464 			ErrorData  *edata;
465 
466 			/* Save error info */
467 			MemoryContextSwitchTo(oldcontext);
468 			edata = CopyErrorData();
469 			FlushErrorState();
470 			CurrentResourceOwner = oldowner;
471 
472 			release_exprs(cstate.exprs);
473 
474 			edata->sqlerrcode = ERRCODE_QUERY_CANCELED;
475 			ReThrowError(edata);
476 		}
477 		PG_END_TRY();
478 
479 		estate->err_text = err_text;
480 		estate->err_stmt = NULL;
481 
482 		/* return back a original rec variables */
483 		for (i = 0; i < estate->ndatums; i++)
484 		{
485 			if (estate->datums[i]->dtype == PLPGSQL_DTYPE_REC)
486 			{
487 				PLpgSQL_rec *rec = (PLpgSQL_rec *) estate->datums[i];
488 
489 #if PG_VERSION_NUM >= 110000
490 
491 				memcpy(rec, &saved_records[i], sizeof(PLpgSQL_rec));
492 
493 #else
494 
495 				if (rec->freetupdesc)
496 					FreeTupleDesc(rec->tupdesc);
497 
498 				rec->tup = saved_records[i].tup;
499 				rec->tupdesc = saved_records[i].tupdesc;
500 				rec->freetup = saved_records[i].freetup;
501 				rec->freetupdesc = saved_records[i].freetupdesc;
502 
503 #endif
504 
505 			}
506 			else if (estate->datums[i]->dtype == PLPGSQL_DTYPE_VAR)
507 			{
508 				PLpgSQL_var *var = (PLpgSQL_var *) estate->datums[i];
509 
510 				var->value = saved_vars[i].value;
511 				var->isnull = saved_vars[i].isnull;
512 				var->freeval = saved_vars[i].freeval;
513 			}
514 		}
515 
516 		MemoryContextSwitchTo(old_cxt);
517 		MemoryContextDelete(cstate.check_cxt);
518 	}
519 }
520 
521 
522 /*
523  * Check function - it prepare variables and starts a prepare plan walker
524  *
525  */
526 static void
function_check(PLpgSQL_function * func,PLpgSQL_checkstate * cstate)527 function_check(PLpgSQL_function *func, PLpgSQL_checkstate *cstate)
528 {
529 	int			i;
530 	int closing = PLPGSQL_CHECK_UNCLOSED;
531 	List	   *exceptions;
532 	ListCell   *lc;
533 
534 	/*
535 	 * Make local execution copies of all the datums
536 	 */
537 	for (i = 0; i < cstate->estate->ndatums; i++)
538 		cstate->estate->datums[i] = copy_plpgsql_datum(cstate, func->datums[i]);
539 
540 	init_datum_dno(cstate, cstate->estate->found_varno, true, true);
541 
542 	/*
543 	 * check function's parameters to not be reserved keywords
544 	 */
545 	foreach(lc, cstate->argnames)
546 	{
547 		char	   *argname = (char *) lfirst(lc);
548 
549 		if (plpgsql_check_is_reserved_keyword(argname))
550 		{
551 			StringInfoData str;
552 
553 			initStringInfo(&str);
554 			appendStringInfo(&str, "name of parameter \"%s\" is reserved keyword",
555 						 argname);
556 
557 			plpgsql_check_put_error(cstate,
558 						  0, 0,
559 						  str.data,
560 						  "The reserved keyword was used as parameter name.",
561 						  NULL,
562 						  PLPGSQL_CHECK_WARNING_OTHERS,
563 						  0, NULL, NULL);
564 			pfree(str.data);
565 		}
566 	}
567 
568 	/*
569 	 * Store the actual call argument values (fake) into the appropriate
570 	 * variables
571 	 */
572 	for (i = 0; i < func->fn_nargs; i++)
573 	{
574 		init_datum_dno(cstate, func->fn_argvarnos[i], false, false);
575 	}
576 
577 	/*
578 	 * Now check the toplevel block of statements
579 	 */
580 	plpgsql_check_stmt(cstate, (PLpgSQL_stmt *) func->action, &closing, &exceptions);
581 
582 	/* clean state values - next errors are not related to any command */
583 	cstate->estate->err_stmt = NULL;
584 
585 	if (!cstate->stop_check)
586 	{
587 		if (closing != PLPGSQL_CHECK_CLOSED && closing != PLPGSQL_CHECK_CLOSED_BY_EXCEPTIONS &&
588 			return_is_required(cstate->cinfo))
589 			plpgsql_check_put_error(cstate,
590 							  ERRCODE_S_R_E_FUNCTION_EXECUTED_NO_RETURN_STATEMENT, 0,
591 							  "control reached end of function without RETURN",
592 							  NULL,
593 							  NULL,
594 							  closing == PLPGSQL_CHECK_UNCLOSED ? PLPGSQL_CHECK_ERROR : PLPGSQL_CHECK_WARNING_EXTRA,
595 							  0, NULL, NULL);
596 
597 		plpgsql_check_report_unused_variables(cstate);
598 		plpgsql_check_report_too_high_volatility(cstate);
599 	}
600 }
601 
602 /*
603  * Check trigger - prepare fake environments for testing trigger
604  *
605  */
606 static void
trigger_check(PLpgSQL_function * func,Node * tdata,PLpgSQL_checkstate * cstate)607 trigger_check(PLpgSQL_function *func, Node *tdata, PLpgSQL_checkstate *cstate)
608 {
609 	PLpgSQL_rec *rec_new,
610 			   *rec_old;
611 	int			i;
612 	int closing = PLPGSQL_CHECK_UNCLOSED;
613 	List	   *exceptions;
614 
615 	/*
616 	 * Make local execution copies of all the datums
617 	 */
618 	for (i = 0; i < cstate->estate->ndatums; i++)
619 		cstate->estate->datums[i] = copy_plpgsql_datum(cstate, func->datums[i]);
620 
621 	init_datum_dno(cstate, cstate->estate->found_varno, true, true);
622 
623 	if (IsA(tdata, TriggerData))
624 	{
625 		TriggerData *trigdata = (TriggerData *) tdata;
626 
627 		/*
628 		 * Put the OLD and NEW tuples into record variables
629 		 *
630 		 * We make the tupdescs available in both records even though only one
631 		 * may have a value.  This allows parsing of record references to
632 		 * succeed in functions that are used for multiple trigger types. For
633 		 * example, we might have a test like "if (TG_OP = 'INSERT' and
634 		 * NEW.foo = 'xyz')", which should parse regardless of the current
635 		 * trigger type.
636 		 */
637 #if PG_VERSION_NUM >= 110000
638 
639 		/*
640 		 * find all PROMISE VARIABLES and initit their
641 		 */
642 		for (i = 0; i < func->ndatums; i++)
643 		{
644 			PLpgSQL_datum *datum = func->datums[i];
645 
646 			if (datum->dtype == PLPGSQL_DTYPE_PROMISE)
647 				init_datum_dno(cstate, datum->dno, true, datum->dno != func->new_varno && datum->dno != func->old_varno);
648 		}
649 
650 		rec_new = (PLpgSQL_rec *) (cstate->estate->datums[func->new_varno]);
651 		plpgsql_check_recval_assign_tupdesc(cstate, rec_new, trigdata->tg_relation->rd_att, false);
652 		rec_old = (PLpgSQL_rec *) (cstate->estate->datums[func->old_varno]);
653 		plpgsql_check_recval_assign_tupdesc(cstate, rec_old, trigdata->tg_relation->rd_att, false);
654 
655 #else
656 
657 		rec_new = (PLpgSQL_rec *) (cstate->estate->datums[func->new_varno]);
658 		rec_new->freetup = false;
659 		rec_new->freetupdesc = false;
660 		plpgsql_check_assign_tupdesc_row_or_rec(cstate, NULL, rec_new, trigdata->tg_relation->rd_att, false);
661 
662 		rec_old = (PLpgSQL_rec *) (cstate->estate->datums[func->old_varno]);
663 		rec_old->freetup = false;
664 		rec_old->freetupdesc = false;
665 		plpgsql_check_assign_tupdesc_row_or_rec(cstate, NULL, rec_old, trigdata->tg_relation->rd_att, false);
666 
667 		/*
668 		 * Assign the special tg_ variables
669 		 */
670 		init_datum_dno(cstate, func->tg_op_varno, true, true);
671 		init_datum_dno(cstate, func->tg_name_varno, true, true);
672 		init_datum_dno(cstate, func->tg_when_varno, true, true);
673 		init_datum_dno(cstate, func->tg_level_varno, true, true);
674 		init_datum_dno(cstate, func->tg_relid_varno, true, true);
675 		init_datum_dno(cstate, func->tg_relname_varno, true, true);
676 		init_datum_dno(cstate, func->tg_table_name_varno, true, true);
677 		init_datum_dno(cstate, func->tg_table_schema_varno, true, true);
678 		init_datum_dno(cstate, func->tg_nargs_varno, true, true);
679 		init_datum_dno(cstate, func->tg_argv_varno, true, true);
680 
681 #endif
682 
683 	}
684 	else if (IsA(tdata, EventTriggerData))
685 	{
686 
687 #if PG_VERSION_NUM < 110000
688 
689 		init_datum_dno(cstate, func->tg_event_varno, true, true);
690 		init_datum_dno(cstate, func->tg_tag_varno, true, true);
691 
692 #endif
693 
694 	}
695 	else
696 		elog(ERROR, "unexpected environment");
697 
698 	/*
699 	 * Now check the toplevel block of statements
700 	 */
701 	plpgsql_check_stmt(cstate, (PLpgSQL_stmt *) func->action, &closing, &exceptions);
702 
703 	/* clean state values - next errors are not related to any command */
704 	cstate->estate->err_stmt = NULL;
705 
706 	if (!cstate->stop_check)
707 	{
708 		if (closing != PLPGSQL_CHECK_CLOSED && closing != PLPGSQL_CHECK_CLOSED_BY_EXCEPTIONS &&
709 			return_is_required(cstate->cinfo))
710 			plpgsql_check_put_error(cstate,
711 							  ERRCODE_S_R_E_FUNCTION_EXECUTED_NO_RETURN_STATEMENT, 0,
712 							  "control reached end of function without RETURN",
713 							  NULL,
714 							  NULL,
715 							  closing == PLPGSQL_CHECK_UNCLOSED ? PLPGSQL_CHECK_ERROR : PLPGSQL_CHECK_WARNING_EXTRA,
716 							  0, NULL, NULL);
717 
718 		plpgsql_check_report_unused_variables(cstate);
719 		plpgsql_check_report_too_high_volatility(cstate);
720 	}
721 }
722 
723 /*
724  * Returns true when some fields is polymorphics
725  */
726 static bool
is_polymorphic_tupdesc(TupleDesc tupdesc)727 is_polymorphic_tupdesc(TupleDesc tupdesc)
728 {
729 	int	i;
730 
731 	for (i = 0; i < tupdesc->natts; i++)
732 		if (IsPolymorphicType(TupleDescAttr(tupdesc, i)->atttypid))
733 			return true;
734 
735 	return false;
736 }
737 
738 /*
739  * Replaces polymorphic types by real type
740  */
741 static Oid
replace_polymorphic_type(plpgsql_check_info * cinfo,Oid typ,Oid anyelement_array_oid,bool is_array_anyelement,Oid anycompatible_array_oid,bool is_array_anycompatible,bool is_variadic)742 replace_polymorphic_type(plpgsql_check_info *cinfo,
743 								Oid typ,
744 								Oid anyelement_array_oid,
745 								bool is_array_anyelement,
746 								Oid anycompatible_array_oid,
747 								bool is_array_anycompatible,
748 								bool is_variadic)
749 {
750 	/* quite compiler warnings */
751 	(void) anycompatible_array_oid;
752 	(void) is_array_anycompatible;
753 
754 	if (OidIsValid(typ) && IsPolymorphicType(typ))
755 	{
756 		switch (typ)
757 		{
758 			case ANYELEMENTOID:
759 				typ = is_variadic ? anyelement_array_oid : cinfo->anyelementoid;
760 				break;
761 
762 			case ANYNONARRAYOID:
763 				if (is_array_anyelement)
764 					elog(ERROR, "anyelement type is a array (expected nonarray)");
765 				typ = is_variadic ? anyelement_array_oid : cinfo->anyelementoid;
766 				break;
767 
768 			case ANYENUMOID:	/* XXX dubious */
769 				if (!OidIsValid(cinfo->anyenumoid))
770 					elog(ERROR, "anyenumtype option should be specified (anyenum type is used)");
771 				if (!type_is_enum(cinfo->anyenumoid))
772 					elog(ERROR, "type specified by anyenumtype option is not enum");
773 				typ = cinfo->anyenumoid;
774 			break;
775 
776 			case ANYARRAYOID:
777 				typ = anyelement_array_oid;
778 				break;
779 
780 			case ANYRANGEOID:
781 				typ = is_variadic ? get_array_type(cinfo->anyrangeoid) : cinfo->anyrangeoid;
782 				break;
783 
784 #if PG_VERSION_NUM >= 130000
785 
786 			case ANYCOMPATIBLEOID:
787 				typ = is_variadic ? anycompatible_array_oid : cinfo->anycompatibleoid;
788 				break;
789 
790 			case ANYCOMPATIBLENONARRAYOID:
791 				if (is_array_anycompatible)
792 					elog(ERROR, "anycompatible type is a array (expected nonarray)");
793 				typ = is_variadic ? anycompatible_array_oid : cinfo->anycompatibleoid;
794 				break;
795 
796 			case ANYCOMPATIBLEARRAYOID:
797 				typ = anycompatible_array_oid;
798 				break;
799 
800 			case ANYCOMPATIBLERANGEOID:
801 				typ = is_variadic ? get_array_type(cinfo->anycompatiblerangeoid) : cinfo->anycompatiblerangeoid;
802 				break;
803 
804 #endif
805 
806 			default:
807 				/* fallback */
808 				typ = is_variadic ? INT4ARRAYOID : INT4OID;
809 		}
810 	}
811 
812 	return typ;
813 }
814 
815 /*
816  * Set up a fake fcinfo with just enough info to satisfy plpgsql_compile().
817  *
818  * There should be a different real argtypes for polymorphic params.
819  *
820  * When output fake_rtd is true, then we should to not compare result fields,
821  * because we know nothing about expected result.
822  */
823 void
plpgsql_check_setup_fcinfo(plpgsql_check_info * cinfo,FmgrInfo * flinfo,FunctionCallInfo fcinfo,ReturnSetInfo * rsinfo,TriggerData * trigdata,EventTriggerData * etrigdata,Trigger * tg_trigger,bool * fake_rtd)824 plpgsql_check_setup_fcinfo(plpgsql_check_info *cinfo,
825 						  FmgrInfo *flinfo,
826 						  FunctionCallInfo fcinfo,
827 						  ReturnSetInfo *rsinfo,
828 						  TriggerData *trigdata,
829 						  EventTriggerData *etrigdata,
830 						  Trigger *tg_trigger,
831 						  bool *fake_rtd)
832 {
833 	TupleDesc resultTupleDesc;
834 	int		nargs;
835 	Oid	   *argtypes;
836 	char  **argnames;
837 	char   *argmodes;
838 	Oid		rettype;
839 	bool	found_polymorphic = false;
840 
841 	*fake_rtd = false;
842 
843 	/* clean structures */
844 #if PG_VERSION_NUM >= 120000
845 
846 	MemSet(fcinfo, 0, SizeForFunctionCallInfo(0));
847 
848 #else
849 
850 	MemSet(fcinfo, 0, sizeof(FunctionCallInfoData));
851 
852 #endif
853 
854 	MemSet(flinfo, 0, sizeof(FmgrInfo));
855 	MemSet(rsinfo, 0, sizeof(ReturnSetInfo));
856 
857 	fcinfo->flinfo = flinfo;
858 	flinfo->fn_oid = cinfo->fn_oid;
859 	flinfo->fn_mcxt = CurrentMemoryContext;
860 
861 	rettype = cinfo->rettype;
862 
863 	if (cinfo->trigtype == PLPGSQL_DML_TRIGGER)
864 	{
865 		Assert(trigdata != NULL);
866 
867 		MemSet(trigdata, 0, sizeof(TriggerData));
868 		MemSet(tg_trigger, 0, sizeof(Trigger));
869 
870 		trigdata->type = T_TriggerData;
871 		trigdata->tg_trigger = tg_trigger;
872 
873 		fcinfo->context = (Node *) trigdata;
874 
875 		if (OidIsValid(cinfo->relid))
876 			trigdata->tg_relation = relation_open(cinfo->relid, AccessShareLock);
877 	}
878 	else if (cinfo->trigtype == PLPGSQL_EVENT_TRIGGER)
879 	{
880 		MemSet(etrigdata, 0, sizeof(etrigdata));
881 		etrigdata->type = T_EventTriggerData;
882 		fcinfo->context = (Node *) etrigdata;
883 	}
884 
885 	/* prepare call expression - used for polymorphic arguments */
886 	nargs = get_func_arg_info(cinfo->proctuple,
887 							  &argtypes,
888 							  &argnames,
889 							  &argmodes);
890 
891 	if (nargs > 0)
892 	{
893 		int		i;
894 
895 		for (i = 0; i < nargs; i++)
896 		{
897 			Oid		argtype = InvalidOid;
898 
899 			if (argmodes)
900 			{
901 				if (argmodes[i] == FUNC_PARAM_IN ||
902 					argmodes[i] == FUNC_PARAM_INOUT ||
903 					argmodes[i] == FUNC_PARAM_VARIADIC)
904 				argtype = argtypes[i];
905 			}
906 			else
907 				argtype = argtypes[i];
908 
909 			if (OidIsValid(argtype) && IsPolymorphicType(argtype))
910 			{
911 				found_polymorphic = true;
912 				break;
913 			}
914 		}
915 
916 		if (found_polymorphic)
917 		{
918 			List	   *args = NIL;
919 			Oid			anyelement_array_oid;
920 			Oid			anyelement_base_oid;
921 			bool		is_array_anyelement;
922 			Oid			anycompatible_array_oid;
923 			Oid			anycompatible_base_oid;
924 			bool		is_array_anycompatible;
925 
926 			anyelement_array_oid = get_array_type(cinfo->anyelementoid);
927 			anyelement_base_oid = getBaseType(cinfo->anyelementoid);
928 			is_array_anyelement = OidIsValid(get_element_type(anyelement_base_oid));
929 
930 #if PG_VERSION_NUM >= 130000
931 
932 			anycompatible_array_oid = get_array_type(cinfo->anycompatibleoid);
933 			anycompatible_base_oid = getBaseType(cinfo->anycompatibleoid);
934 			is_array_anycompatible = OidIsValid(get_element_type(anycompatible_base_oid));
935 
936 #else
937 
938 			anycompatible_array_oid = InvalidOid;
939 			anycompatible_base_oid = InvalidOid;
940 			is_array_anycompatible = false;
941 
942 			(void) anycompatible_base_oid;
943 
944 #endif
945 
946 			/*
947 			 * when polymorphic types are used, then we need to build fake fn_expr,
948 			 * to be in plpgsql_resolve_polymorphic_argtypes happy.
949 			 */
950 			for (i = 0; i < nargs; i++)
951 			{
952 				bool	is_variadic = false;
953 				Oid		argtype = InvalidOid;
954 
955 				if (argmodes)
956 				{
957 					if (argmodes[i] == FUNC_PARAM_IN ||
958 						argmodes[i] == FUNC_PARAM_INOUT ||
959 						argmodes[i] == FUNC_PARAM_VARIADIC)
960 					{
961 						argtype = argtypes[i];
962 						if (argmodes[i] == FUNC_PARAM_VARIADIC)
963 							is_variadic = true;
964 					}
965 				}
966 				else
967 					argtype = argtypes[i];
968 
969 				if (OidIsValid(argtype))
970 				{
971 					argtype = replace_polymorphic_type(cinfo,
972 													   argtype,
973 													   anyelement_array_oid,
974 													   is_array_anyelement,
975 													   anycompatible_array_oid,
976 													   is_array_anycompatible,
977 													   is_variadic);
978 
979 					args = lappend(args,
980 								   makeNullConst(argtype, -1, InvalidOid));
981 				}
982 			}
983 
984 			rettype =  replace_polymorphic_type(cinfo,
985 												rettype,
986 												anyelement_array_oid,
987 												is_array_anyelement,
988 												anycompatible_array_oid,
989 												is_array_anycompatible,
990 												false);
991 
992 			fcinfo->flinfo->fn_expr = (Node *) makeFuncExpr(cinfo->fn_oid,
993 															rettype,
994 															args,
995 															InvalidOid,
996 															InvalidOid,
997 															COERCE_EXPLICIT_CALL);
998 		}
999 	}
1000 
1001 	if (argtypes)
1002 		pfree(argtypes);
1003 	if (argnames)
1004 		pfree(argnames);
1005 	if (argmodes)
1006 		pfree(argmodes);
1007 
1008 	/*
1009 	 * prepare ReturnSetInfo
1010 	 *
1011 	 * necessary for RETURN NEXT and RETURN QUERY
1012 	 *
1013 	 */
1014 	resultTupleDesc = build_function_result_tupdesc_t(cinfo->proctuple);
1015 	if (resultTupleDesc)
1016 	{
1017 		/* we cannot to solve polymorphic params now */
1018 		if (is_polymorphic_tupdesc(resultTupleDesc))
1019 		{
1020 			FreeTupleDesc(resultTupleDesc);
1021 			resultTupleDesc = NULL;
1022 		}
1023 	}
1024 	else if (cinfo->rettype == TRIGGEROID
1025 
1026 #if PG_VERSION_NUM < 130000
1027 
1028 			|| cinfo->rettype == OPAQUEOID
1029 
1030 #endif
1031 
1032 			)
1033 	{
1034 		/* trigger - return value should be ROW or RECORD based on relid */
1035 		if (trigdata->tg_relation)
1036 			resultTupleDesc = CreateTupleDescCopy(trigdata->tg_relation->rd_att);
1037 	}
1038 	else if (!IsPolymorphicType(cinfo->rettype))
1039 	{
1040 		if (get_typtype(cinfo->rettype) == TYPTYPE_COMPOSITE)
1041 			resultTupleDesc = lookup_rowtype_tupdesc_copy(cinfo->rettype, -1);
1042 		else
1043 		{
1044 			*fake_rtd = cinfo->rettype == RECORDOID;
1045 
1046 #if PG_VERSION_NUM >= 120000
1047 
1048 			resultTupleDesc = CreateTemplateTupleDesc(1);
1049 
1050 #else
1051 
1052 			resultTupleDesc = CreateTemplateTupleDesc(1, false);
1053 
1054 #endif
1055 
1056 			TupleDescInitEntry(resultTupleDesc,
1057 							    (AttrNumber) 1, "__result__",
1058 							    cinfo->rettype, -1, 0);
1059 			resultTupleDesc = BlessTupleDesc(resultTupleDesc);
1060 		}
1061 	}
1062 	else
1063 	{
1064 		if (IsPolymorphicType(cinfo->rettype))
1065 		{
1066 			/*
1067 			 * ensure replacament of polymorphic rettype, but this
1068 			 * error is checked in validation stage, so this case
1069 			 * should not be possible.
1070 			 */
1071 			if (IsPolymorphicType(rettype))
1072 				elog(ERROR, "return type is still polymorphic");
1073 		}
1074 	}
1075 
1076 	if (resultTupleDesc)
1077 	{
1078 		fcinfo->resultinfo = (Node *) rsinfo;
1079 
1080 		rsinfo->type = T_ReturnSetInfo;
1081 		rsinfo->expectedDesc = resultTupleDesc;
1082 		rsinfo->allowedModes = (int) (SFRM_ValuePerCall | SFRM_Materialize);
1083 		rsinfo->returnMode = SFRM_ValuePerCall;
1084 
1085 		/*
1086 		 * ExprContext is created inside CurrentMemoryContext,
1087 		 * without any additional source allocation. It is released
1088 		 * on end of transaction.
1089 		 */
1090 		rsinfo->econtext = CreateStandaloneExprContext();
1091 	}
1092 }
1093 
1094 /* ----------
1095  * Initialize a plpgsql fake execution state
1096  * ----------
1097  */
1098 static void
setup_estate(PLpgSQL_execstate * estate,PLpgSQL_function * func,ReturnSetInfo * rsi,plpgsql_check_info * cinfo)1099 setup_estate(PLpgSQL_execstate *estate,
1100 					 PLpgSQL_function *func,
1101 					 ReturnSetInfo *rsi,
1102 					 plpgsql_check_info *cinfo)
1103 {
1104 	/* this link will be restored at exit from plpgsql_call_handler */
1105 	func->cur_estate = estate;
1106 
1107 	estate->func = func;
1108 
1109 	estate->retval = (Datum) 0;
1110 	estate->retisnull = true;
1111 	estate->rettype = InvalidOid;
1112 
1113 	estate->fn_rettype = func->fn_rettype;
1114 
1115 	estate->retistuple = func->fn_retistuple;
1116 	estate->retisset = func->fn_retset;
1117 
1118 	estate->readonly_func = func->fn_readonly;
1119 
1120 #if PG_VERSION_NUM < 110000
1121 
1122 	estate->rettupdesc = NULL;
1123 	estate->eval_econtext = NULL;
1124 
1125 #else
1126 
1127 	estate->eval_econtext = makeNode(ExprContext);
1128 	estate->eval_econtext->ecxt_per_tuple_memory = AllocSetContextCreate(CurrentMemoryContext,
1129 													"ExprContext",
1130 													ALLOCSET_DEFAULT_SIZES);
1131 	estate->datum_context = CurrentMemoryContext;
1132 
1133 #endif
1134 
1135 	estate->exitlabel = NULL;
1136 	estate->cur_error = NULL;
1137 
1138 	estate->tuple_store = NULL;
1139 	if (rsi)
1140 	{
1141 		estate->tuple_store_cxt = rsi->econtext->ecxt_per_query_memory;
1142 		estate->tuple_store_owner = CurrentResourceOwner;
1143 
1144 #if PG_VERSION_NUM >= 110000
1145 
1146 		estate->tuple_store_desc = rsi->expectedDesc;
1147 
1148 #else
1149 
1150 		if (estate->retisset)
1151 			estate->rettupdesc = rsi->expectedDesc;
1152 
1153 #endif
1154 
1155 	}
1156 	else
1157 	{
1158 		estate->tuple_store_cxt = NULL;
1159 		estate->tuple_store_owner = NULL;
1160 	}
1161 	estate->rsi = rsi;
1162 
1163 	estate->found_varno = func->found_varno;
1164 	estate->ndatums = func->ndatums;
1165 	estate->datums = palloc(sizeof(PLpgSQL_datum *) * estate->ndatums);
1166 	/* caller is expected to fill the datums array */
1167 
1168 	estate->eval_tuptable = NULL;
1169 	estate->eval_processed = 0;
1170 
1171 #if PG_VERSION_NUM < 120000
1172 
1173 	estate->eval_lastoid = InvalidOid;
1174 
1175 #endif
1176 
1177 	if (cinfo->oldtable)
1178 	{
1179 		EphemeralNamedRelation enr = palloc(sizeof(EphemeralNamedRelationData));
1180 		int rc PG_USED_FOR_ASSERTS_ONLY;
1181 
1182 		enr->md.name = cinfo->oldtable;
1183 		enr->md.reliddesc = cinfo->relid;
1184 		enr->md.tupdesc = NULL;
1185 		enr->md.enrtype = ENR_NAMED_TUPLESTORE;
1186 		enr->md.enrtuples = 0;
1187 		enr->reldata = NULL;
1188 
1189 		rc = SPI_register_relation(enr);
1190 		Assert(rc >= 0);
1191 	}
1192 
1193 	if (cinfo->newtable)
1194 	{
1195 		EphemeralNamedRelation enr = palloc(sizeof(EphemeralNamedRelationData));
1196 		int rc PG_USED_FOR_ASSERTS_ONLY;
1197 
1198 		enr->md.name = cinfo->newtable;
1199 		enr->md.reliddesc = cinfo->relid;
1200 		enr->md.tupdesc = NULL;
1201 		enr->md.enrtype = ENR_NAMED_TUPLESTORE;
1202 		enr->md.enrtuples = 0;
1203 		enr->reldata = NULL;
1204 
1205 		rc = SPI_register_relation(enr);
1206 		Assert(rc >= 0);
1207 	}
1208 
1209 	estate->err_stmt = NULL;
1210 	estate->err_text = NULL;
1211 
1212 	estate->plugin_info = NULL;
1213 }
1214 
1215 /*
1216  * prepare PLpgSQL_checkstate structure
1217  *
1218  */
1219 static void
setup_cstate(PLpgSQL_checkstate * cstate,plpgsql_check_result_info * result_info,plpgsql_check_info * cinfo,bool is_active_mode,bool fake_rtd)1220 setup_cstate(PLpgSQL_checkstate *cstate,
1221 			 plpgsql_check_result_info *result_info,
1222 			 plpgsql_check_info *cinfo,
1223 			 bool is_active_mode,
1224 			 bool fake_rtd)
1225 {
1226 	cstate->decl_volatility = cinfo->volatility;
1227 	cstate->has_execute_stmt = false;
1228 	cstate->volatility = PROVOLATILE_IMMUTABLE;
1229 	cstate->skip_volatility_check = (cinfo->rettype == TRIGGEROID ||
1230 
1231 #if PG_VERSION_NUM < 130000
1232 
1233 									 cinfo->rettype == OPAQUEOID ||
1234 
1235 #endif
1236 
1237 									 plpgsql_check_is_eventtriggeroid(cinfo->rettype));
1238 	cstate->estate = NULL;
1239 	cstate->result_info = result_info;
1240 	cstate->cinfo = cinfo;
1241 	cstate->argnames = NIL;
1242 	cstate->exprs = NIL;
1243 	cstate->used_variables = NULL;
1244 	cstate->modif_variables = NULL;
1245 	cstate->out_variables = NULL;
1246 	cstate->top_stmt_stack = NULL;
1247 
1248 	cstate->is_active_mode = is_active_mode;
1249 
1250 	cstate->func_oids = NULL;
1251 	cstate->rel_oids = NULL;
1252 
1253 #if PG_VERSION_NUM >= 110000
1254 
1255 	cstate->check_cxt = AllocSetContextCreate(CurrentMemoryContext,
1256 										 "plpgsql_check temporary cxt",
1257 										   ALLOCSET_DEFAULT_SIZES);
1258 
1259 #else
1260 
1261 	cstate->check_cxt = AllocSetContextCreate(CurrentMemoryContext,
1262 										 "plpgsql_check temporary cxt",
1263 										   ALLOCSET_DEFAULT_MINSIZE,
1264 										   ALLOCSET_DEFAULT_INITSIZE,
1265 										   ALLOCSET_DEFAULT_MAXSIZE);
1266 
1267 #endif
1268 
1269 	cstate->found_return_query = false;
1270 	cstate->found_return_dyn_query = false;
1271 
1272 	cstate->fake_rtd = fake_rtd;
1273 
1274 	cstate->safe_variables = NULL;
1275 	cstate->protected_variables = NULL;
1276 	cstate->auto_variables = NULL;
1277 	cstate->typed_variables = NULL;
1278 
1279 	cstate->stop_check = false;
1280 	cstate->allow_mp = false;
1281 
1282 	cstate->pragma_vector.disable_check = false;
1283 	cstate->pragma_vector.disable_tracer = false;
1284 	cstate->pragma_vector.disable_other_warnings = false;
1285 	cstate->pragma_vector.disable_performance_warnings = false;
1286 	cstate->pragma_vector.disable_extra_warnings = false;
1287 	cstate->pragma_vector.disable_security_warnings = false;
1288 
1289 	/* try to find oid of plpgsql_check pragma function */
1290 	cstate->pragma_foid = plpgsql_check_pragma_func_oid();
1291 }
1292 
1293 /*
1294  * Loads function's configuration
1295  *
1296  * Before checking function we have to load configuration related to
1297  * function. This is function manager job, but we don't use it for checking.
1298  */
1299 static int
load_configuration(HeapTuple procTuple,bool * reload_config)1300 load_configuration(HeapTuple procTuple, bool *reload_config)
1301 {
1302 	Datum		datum;
1303 	bool		isnull;
1304 	int			new_nest_level;
1305 
1306 	*reload_config = false;
1307 	new_nest_level = 0;
1308 
1309 	datum = SysCacheGetAttr(PROCOID, procTuple, Anum_pg_proc_proconfig, &isnull);
1310 	if (!isnull)
1311 	{
1312 		ArrayType  *set_items;
1313 
1314 		/* Set per-function configuration parameters */
1315 		set_items = DatumGetArrayTypeP(datum);
1316 
1317 		if (set_items != NULL)
1318 		{						/* Need a new GUC nesting level */
1319 			new_nest_level = NewGUCNestLevel();
1320 			*reload_config = true;
1321 			ProcessGUCArray(set_items,
1322 							(superuser() ? PGC_SUSET : PGC_USERSET),
1323 							PGC_S_SESSION,
1324 							GUC_ACTION_SAVE);
1325 		}
1326 	}
1327 	return new_nest_level;
1328 }
1329 
1330 /*
1331  * Release all plans created in check time
1332  *
1333  */
1334 static void
release_exprs(List * exprs)1335 release_exprs(List *exprs)
1336 {
1337 	ListCell *l;
1338 
1339 	foreach(l, exprs)
1340 	{
1341 		PLpgSQL_expr *expr = (PLpgSQL_expr *) lfirst(l);
1342 
1343 		SPI_freeplan(expr->plan);
1344 		expr->plan = NULL;
1345 	}
1346 }
1347 
1348 /*
1349  * Initialize plpgsql datum to NULL. This routine is used only for function
1350  * and trigger parameters so it should not support all dtypes.
1351  *
1352  */
1353 static void
init_datum_dno(PLpgSQL_checkstate * cstate,int dno,bool is_auto,bool is_protected)1354 init_datum_dno(PLpgSQL_checkstate *cstate, int dno, bool is_auto, bool is_protected)
1355 {
1356 	switch (cstate->estate->datums[dno]->dtype)
1357 	{
1358 
1359 #if PG_VERSION_NUM >= 110000
1360 
1361 		case PLPGSQL_DTYPE_PROMISE:
1362 
1363 #endif
1364 
1365 		case PLPGSQL_DTYPE_VAR:
1366 			{
1367 				PLpgSQL_var *var = (PLpgSQL_var *) cstate->estate->datums[dno];
1368 
1369 				var->value = (Datum) 0;
1370 				var->isnull = true;
1371 				var->freeval = false;
1372 			}
1373 			break;
1374 
1375 #if PG_VERSION_NUM >= 110000
1376 
1377 		case PLPGSQL_DTYPE_REC:
1378 			{
1379 				PLpgSQL_rec *rec = (PLpgSQL_rec *) cstate->estate->datums[dno];
1380 
1381 				plpgsql_check_recval_init(rec);
1382 				plpgsql_check_recval_assign_tupdesc(cstate, rec, NULL, false);
1383 			}
1384 			break;
1385 
1386 #endif
1387 
1388 		case PLPGSQL_DTYPE_ROW:
1389 			{
1390 				PLpgSQL_row *row = (PLpgSQL_row *) cstate->estate->datums[dno];
1391 				int			fnum;
1392 
1393 				for (fnum = 0; fnum < row->nfields; fnum++)
1394 				{
1395 					if (row->varnos[fnum] < 0)
1396 						continue;		/* skip dropped column in row struct */
1397 
1398 					init_datum_dno(cstate, row->varnos[fnum], is_auto, is_protected);
1399 				}
1400 			}
1401 			break;
1402 
1403 		default:
1404 			elog(ERROR, "unexpected dtype: %d", cstate->estate->datums[dno]->dtype);
1405 	}
1406 
1407 	if (is_protected)
1408 		cstate->protected_variables = bms_add_member(cstate->protected_variables, dno);
1409 	if (is_auto)
1410 		cstate->auto_variables = bms_add_member(cstate->auto_variables, dno);
1411 }
1412 
1413 /*
1414  * initializing local execution variables
1415  *
1416  */
1417 static PLpgSQL_datum *
copy_plpgsql_datum(PLpgSQL_checkstate * cstate,PLpgSQL_datum * datum)1418 copy_plpgsql_datum(PLpgSQL_checkstate *cstate, PLpgSQL_datum *datum)
1419 {
1420 	PLpgSQL_datum *result;
1421 
1422 	switch (datum->dtype)
1423 	{
1424 		case PLPGSQL_DTYPE_VAR:
1425 
1426 #if PG_VERSION_NUM >= 110000
1427 
1428 		case PLPGSQL_DTYPE_PROMISE:
1429 
1430 #endif
1431 
1432 			{
1433 				PLpgSQL_var *new = palloc(sizeof(PLpgSQL_var));
1434 
1435 				memcpy(new, datum, sizeof(PLpgSQL_var));
1436 				/* Ensure the value is null (possibly not needed?) */
1437 				new->value = 0;
1438 				new->isnull = true;
1439 				new->freeval = false;
1440 
1441 				result = (PLpgSQL_datum *) new;
1442 			}
1443 			break;
1444 
1445 		case PLPGSQL_DTYPE_REC:
1446 			{
1447 				PLpgSQL_rec *new = palloc(sizeof(PLpgSQL_rec));
1448 
1449 				memcpy(new, datum, sizeof(PLpgSQL_rec));
1450 
1451 				/* Ensure the value is well initialized with correct type */
1452 				plpgsql_check_recval_init(new);
1453 				plpgsql_check_recval_assign_tupdesc(cstate, new, NULL, false);
1454 
1455 				result = (PLpgSQL_datum *) new;
1456 			}
1457 			break;
1458 
1459 		case PLPGSQL_DTYPE_ROW:
1460 		case PLPGSQL_DTYPE_RECFIELD:
1461 
1462 #if PG_VERSION_NUM < 140000
1463 
1464 		case PLPGSQL_DTYPE_ARRAYELEM:
1465 
1466 #endif
1467 
1468 			/*
1469 			 * These datum records are read-only at runtime, so no need to
1470 			 * copy them (well, ARRAYELEM contains some cached type data, but
1471 			 * we'd just as soon centralize the caching anyway)
1472 			 */
1473 			result = datum;
1474 			break;
1475 
1476 		default:
1477 			elog(ERROR, "unrecognized dtype: %d", datum->dtype);
1478 			result = NULL;		/* keep compiler quiet */
1479 			break;
1480 	}
1481 
1482 	return result;
1483 }
1484 
1485 void
plpgsql_check_HashTableInit(void)1486 plpgsql_check_HashTableInit(void)
1487 {
1488 	HASHCTL		ctl;
1489 
1490 	/* don't allow double-initialization */
1491 	Assert(plpgsql_check_HashTable == NULL);
1492 
1493 	memset(&ctl, 0, sizeof(ctl));
1494 	ctl.keysize = sizeof(PLpgSQL_func_hashkey);
1495 	ctl.entrysize = sizeof(plpgsql_check_HashEnt);
1496 	plpgsql_check_HashTable = hash_create("plpgsql_check function cache",
1497 									FUNCS_PER_USER,
1498 									&ctl,
1499 									HASH_ELEM | HASH_BLOBS);
1500 }
1501 
1502 /*
1503  * Returns true, when function is marked as checked already
1504  *
1505  */
1506 bool
plpgsql_check_is_checked(PLpgSQL_function * func)1507 plpgsql_check_is_checked(PLpgSQL_function *func)
1508 {
1509 	plpgsql_check_HashEnt *hentry;
1510 
1511 	hentry = (plpgsql_check_HashEnt *) hash_search(plpgsql_check_HashTable,
1512 											 (void *) func->fn_hashkey,
1513 											 HASH_FIND,
1514 											 NULL);
1515 
1516 	if (hentry != NULL && hentry->fn_xmin == func->fn_xmin &&
1517 			  ItemPointerEquals(&hentry->fn_tid, &func->fn_tid))
1518 		return hentry->is_checked;
1519 
1520 	return false;
1521 }
1522 
1523 /*
1524  * Protect function agains repeated checking
1525  *
1526  */
1527 void
plpgsql_check_mark_as_checked(PLpgSQL_function * func)1528 plpgsql_check_mark_as_checked(PLpgSQL_function *func)
1529 {
1530 	plpgsql_check_HashEnt *hentry;
1531 	bool		found;
1532 
1533 	/* don't try to mark anonymous code blocks */
1534 	if (func->fn_oid != InvalidOid)
1535 	{
1536 		hentry = (plpgsql_check_HashEnt *) hash_search(plpgsql_check_HashTable,
1537 												 (void *) func->fn_hashkey,
1538 												 HASH_ENTER,
1539 												 &found);
1540 
1541 		hentry->fn_xmin = func->fn_xmin;
1542 		hentry->fn_tid = func->fn_tid;
1543 
1544 		hentry->is_checked = true;
1545 	}
1546 }
1547