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