1 /*-------------------------------------------------------------------------
2 *
3 * stmtwalk.c
4 *
5 * iteration over plpgsql statements loop
6 *
7 * by Pavel Stehule 2013-2021
8 *
9 *-------------------------------------------------------------------------
10 */
11
12 #include "plpgsql_check.h"
13
14 #include "access/tupconvert.h"
15 #include "catalog/pg_collation.h"
16 #include "catalog/pg_type.h"
17 #include "nodes/nodeFuncs.h"
18 #include "nodes/value.h"
19 #include "parser/parse_node.h"
20 #include "parser/parser.h"
21 #include "common/keywords.h"
22
23 static void check_stmts(PLpgSQL_checkstate *cstate, List *stmts, int *closing, List **exceptions);
24 static PLpgSQL_stmt_stack_item * push_stmt_to_stmt_stack(PLpgSQL_checkstate *cstate);
25 static void pop_stmt_from_stmt_stack(PLpgSQL_checkstate *cstate);
26 static bool is_any_loop_stmt(PLpgSQL_stmt *stmt);
27 static PLpgSQL_stmt * find_nearest_loop(PLpgSQL_stmt_stack_item *current);
28 static PLpgSQL_stmt * find_stmt_with_label(char *label, PLpgSQL_stmt_stack_item *current);
29 static int possibly_closed(int c);
30 static int merge_closing(int c, int c_local, List **exceptions, List *exceptions_local, int err_code);
31 static bool exception_matches_conditions(int sqlerrstate, PLpgSQL_condition *cond);
32 static bool found_shadowed_variable(char *varname, PLpgSQL_stmt_stack_item *current, PLpgSQL_checkstate *cstate);
33
34 #if PG_VERSION_NUM >= 110000
35
36 static void check_dynamic_sql(PLpgSQL_checkstate *cstate, PLpgSQL_stmt *stmt, PLpgSQL_expr *query, bool into, PLpgSQL_variable *target, List *params);
37
38 #else
39
40 static void check_dynamic_sql(PLpgSQL_checkstate *cstate, PLpgSQL_stmt *stmt, PLpgSQL_expr *query, bool into, PLpgSQL_row *row, PLpgSQL_rec *rec, List *params);
41
42 #endif
43
44 #if PG_VERSION_NUM >= 110000
45
46 static void
check_variable(PLpgSQL_checkstate * cstate,PLpgSQL_variable * var)47 check_variable(PLpgSQL_checkstate *cstate, PLpgSQL_variable *var)
48 {
49 /* leave quickly when var is not defined */
50 if (var == NULL)
51 return;
52
53 if (var->dtype == PLPGSQL_DTYPE_ROW)
54 {
55 PLpgSQL_row *row = (PLpgSQL_row *) var;
56 int fnum;
57
58 for (fnum = 0; fnum < row->nfields; fnum++)
59 {
60 /* skip dropped columns */
61 if (row->varnos[fnum] < 0)
62 continue;
63
64 plpgsql_check_target(cstate, row->varnos[fnum], NULL, NULL);
65 }
66 plpgsql_check_record_variable_usage(cstate, row->dno, true);
67
68 return;
69 }
70
71 if (var->dtype == PLPGSQL_DTYPE_REC)
72 {
73 PLpgSQL_rec *rec = (PLpgSQL_rec *) var;
74
75 /*
76 * There are no checks done on records currently; just record that the
77 * variable is not unused.
78 */
79 plpgsql_check_record_variable_usage(cstate, rec->dno, true);
80
81 return;
82 }
83
84 elog(ERROR, "unsupported dtype %d", var->dtype);
85 }
86
87 #endif
88
89 bool
plpgsql_check_is_reserved_keyword(char * name)90 plpgsql_check_is_reserved_keyword(char *name)
91 {
92 int i;
93
94 #if PG_VERSION_NUM < 120000
95
96 for (i = 0; i < NumScanKeywords; i++)
97 {
98 if (ScanKeywords[i].category == RESERVED_KEYWORD &&
99 strcmp(name, ScanKeywords[i].name) == 0)
100 return true;
101 }
102
103 #else
104
105 for (i = 0; i < ScanKeywords.num_keywords; i++)
106 {
107 if (ScanKeywordCategories[i] == RESERVED_KEYWORD)
108 {
109 char *value;
110
111 value = unconstify(char *, GetScanKeyword(i, &ScanKeywords));
112 if (strcmp(name, value) == 0)
113 return true;
114 }
115 }
116
117 #endif
118
119 return false;
120 }
121
122 /*
123 * walk over all plpgsql statements - search and check expressions
124 *
125 */
126 void
plpgsql_check_stmt(PLpgSQL_checkstate * cstate,PLpgSQL_stmt * stmt,int * closing,List ** exceptions)127 plpgsql_check_stmt(PLpgSQL_checkstate *cstate, PLpgSQL_stmt *stmt, int *closing, List **exceptions)
128 {
129 TupleDesc tupdesc = NULL;
130 PLpgSQL_function *func;
131 ResourceOwner oldowner;
132 MemoryContext oldCxt = CurrentMemoryContext;
133 PLpgSQL_stmt_stack_item *outer_stmt;
134 plpgsql_check_pragma_vector pragma_vector;
135
136 if (stmt == NULL)
137 return;
138
139 if (cstate->stop_check)
140 return;
141
142 cstate->estate->err_stmt = stmt;
143 cstate->was_pragma = false;
144
145 func = cstate->estate->func;
146 pragma_vector = cstate->pragma_vector;
147
148 /*
149 * Attention - returns NULL, when there are not any outer level
150 */
151 outer_stmt = push_stmt_to_stmt_stack(cstate);
152
153 oldowner = CurrentResourceOwner;
154 BeginInternalSubTransaction(NULL);
155 MemoryContextSwitchTo(oldCxt);
156
157 PG_TRY();
158 {
159 switch (PLPGSQL_STMT_TYPES stmt->cmd_type)
160 {
161 case PLPGSQL_STMT_BLOCK:
162 {
163 PLpgSQL_stmt_block *stmt_block = (PLpgSQL_stmt_block *) stmt;
164 int i;
165 PLpgSQL_datum *d;
166
167 for (i = 0; i < stmt_block->n_initvars; i++)
168 {
169 char *refname;
170
171 d = func->datums[stmt_block->initvarnos[i]];
172
173 if (d->dtype == PLPGSQL_DTYPE_VAR ||
174 d->dtype == PLPGSQL_DTYPE_ROW ||
175 d->dtype == PLPGSQL_DTYPE_REC)
176 {
177 PLpgSQL_variable *var = (PLpgSQL_variable *) d;
178 StringInfoData str;
179
180 initStringInfo(&str);
181 appendStringInfo(&str, "during statement block local variable \"%s\" initialization on line %d",
182 var->refname,
183 var->lineno);
184
185 cstate->estate->err_text = str.data;
186
187 #if PG_VERSION_NUM >= 110000
188
189 if (var->default_val)
190 plpgsql_check_assignment(cstate,
191 var->default_val,
192 NULL,
193 NULL,
194 var->dno);
195
196 #else
197
198 if (d->dtype == PLPGSQL_DTYPE_VAR &&
199 ((PLpgSQL_var *) var)->default_val)
200 plpgsql_check_assignment(cstate,
201 ((PLpgSQL_var *) var)->default_val,
202 NULL,
203 NULL,
204 var->dno);
205
206
207 #endif
208
209 cstate->estate->err_text = NULL;
210 pfree(str.data);
211 }
212
213 refname = plpgsql_check_datum_get_refname(d);
214 if (refname != NULL)
215 {
216 ListCell *l;
217
218 if (plpgsql_check_is_reserved_keyword(refname))
219 {
220 StringInfoData str;
221
222 initStringInfo(&str);
223 appendStringInfo(&str, "name of variable \"%s\" is reserved keyword",
224 refname);
225
226 plpgsql_check_put_error(cstate,
227 0, 0,
228 str.data,
229 "The reserved keyword was used as variable name.",
230 NULL,
231 PLPGSQL_CHECK_WARNING_OTHERS,
232 0, NULL, NULL);
233 pfree(str.data);
234 }
235
236 foreach(l, cstate->argnames)
237 {
238 char *argname = (char *) lfirst(l);
239
240 if (strcmp(argname, refname) == 0)
241 {
242 StringInfoData str;
243
244 initStringInfo(&str);
245 appendStringInfo(&str, "parameter \"%s\" is overlapped",
246 refname);
247
248 plpgsql_check_put_error(cstate,
249 0, 0,
250 str.data,
251 "Local variable overlap function parameter.",
252 NULL,
253 PLPGSQL_CHECK_WARNING_OTHERS,
254 0, NULL, NULL);
255 pfree(str.data);
256 }
257 }
258
259 if (found_shadowed_variable(refname, outer_stmt, cstate))
260 {
261 StringInfoData str;
262
263 initStringInfo(&str);
264 appendStringInfo(&str, "variable \"%s\" shadows a previously defined variable",
265 refname);
266
267 plpgsql_check_put_error(cstate,
268 0, 0,
269 str.data,
270 NULL,
271 "SET plpgsql.extra_warnings TO 'shadowed_variables'",
272 PLPGSQL_CHECK_WARNING_EXTRA,
273 0, NULL, NULL);
274 pfree(str.data);
275 }
276 }
277 }
278
279 check_stmts(cstate, stmt_block->body, closing, exceptions);
280
281 if (stmt_block->exceptions)
282 {
283 int closing_local;
284 List *exceptions_local = NIL;
285 int closing_handlers = PLPGSQL_CHECK_UNKNOWN;
286 List *exceptions_transformed = NIL;
287
288 if (*closing == PLPGSQL_CHECK_CLOSED_BY_EXCEPTIONS)
289 {
290 ListCell *lc;
291 ListCell *l;
292 int errn = 0;
293 int *err_codes = NULL;
294 int nerr_codes = 0;
295
296 /* copy errcodes to a array */
297 nerr_codes = list_length(*exceptions);
298 err_codes = palloc(sizeof(int) * nerr_codes);
299
300 foreach(lc, *exceptions)
301 {
302 err_codes[errn++] = lfirst_int(lc);
303 }
304
305 foreach(l, stmt_block->exceptions->exc_list)
306 {
307 PLpgSQL_exception *exception = (PLpgSQL_exception *) lfirst(l);
308
309 /* RETURN in exception handler ~ is possible closing */
310 check_stmts(cstate, exception->action,
311 &closing_local, &exceptions_local);
312
313 if (*exceptions != NIL)
314 {
315 int idx;
316
317 for (idx = 0; idx < nerr_codes; idx++)
318 {
319 int err_code = err_codes[idx];
320
321 if (err_code != -1 &&
322 exception_matches_conditions(err_code, exception->conditions))
323 {
324 closing_handlers = merge_closing(closing_handlers, closing_local,
325 &exceptions_transformed, exceptions_local,
326 err_code);
327 *exceptions = list_delete_int(*exceptions, err_code);
328 err_codes[idx] = -1;
329 }
330 }
331 }
332 }
333
334 Assert(err_codes != NULL);
335 pfree(err_codes);
336
337 if (closing_handlers != PLPGSQL_CHECK_UNKNOWN)
338 {
339 *closing = closing_handlers;
340 if (closing_handlers == PLPGSQL_CHECK_CLOSED_BY_EXCEPTIONS)
341 *exceptions = list_concat_unique_int(*exceptions, exceptions_transformed);
342 else
343 *exceptions = NIL;
344 }
345 }
346 else
347 {
348 ListCell *l;
349
350 closing_handlers = *closing;
351
352 foreach(l, stmt_block->exceptions->exc_list)
353 {
354 PLpgSQL_exception *exception = (PLpgSQL_exception *) lfirst(l);
355
356 /* RETURN in exception handler ~ it is possible closing only */
357 check_stmts(cstate, exception->action,
358 &closing_local, &exceptions_local);
359
360 closing_handlers = merge_closing(closing_handlers, closing_local,
361 &exceptions_transformed, exceptions_local,
362 -1);
363 }
364
365 *closing = closing_handlers;
366
367 if (closing_handlers == PLPGSQL_CHECK_CLOSED_BY_EXCEPTIONS)
368 *exceptions = exceptions_transformed;
369 else
370 *exceptions = NIL;
371 }
372
373 /*
374 * Mark the hidden variables SQLSTATE and SQLERRM used
375 * even if they actually weren't. Not using them
376 * should practically never be a sign of a problem, so
377 * there's no point in annoying the user.
378 */
379 plpgsql_check_record_variable_usage(cstate, stmt_block->exceptions->sqlstate_varno, false);
380 plpgsql_check_record_variable_usage(cstate, stmt_block->exceptions->sqlerrm_varno, false);
381 }
382 }
383 break;
384
385 case PLPGSQL_STMT_ASSERT:
386 {
387 PLpgSQL_stmt_assert *stmt_assert = (PLpgSQL_stmt_assert *) stmt;
388
389 /*
390 * Should or should not to depends on plpgsql_check_asserts?
391 * I am thinking, so any code (active or inactive) should be valid,
392 * so I ignore plpgsql_check_asserts option.
393 */
394 plpgsql_check_expr_with_scalar_type(cstate,
395 stmt_assert->cond, BOOLOID, true);
396 if (stmt_assert->message != NULL)
397 plpgsql_check_expr(cstate, stmt_assert->message);
398 }
399 break;
400
401 case PLPGSQL_STMT_ASSIGN:
402 {
403 PLpgSQL_stmt_assign *stmt_assign = (PLpgSQL_stmt_assign *) stmt;
404 PLpgSQL_datum *d = (PLpgSQL_datum *) cstate->estate->datums[stmt_assign->varno];
405 StringInfoData str;
406
407 initStringInfo(&str);
408
409 if (d->dtype == PLPGSQL_DTYPE_VAR ||
410 d->dtype == PLPGSQL_DTYPE_ROW ||
411 d->dtype == PLPGSQL_DTYPE_REC)
412 {
413 PLpgSQL_variable *var = (PLpgSQL_variable *) d;
414
415 appendStringInfo(&str, "at assignment to variable \"%s\" declared on line %d",
416 var->refname,
417 var->lineno);
418
419 cstate->estate->err_text = str.data;
420 }
421 else if (d->dtype == PLPGSQL_DTYPE_RECFIELD)
422 {
423 PLpgSQL_recfield *recfield = (PLpgSQL_recfield *) d;
424 PLpgSQL_variable *var = (PLpgSQL_variable *) cstate->estate->datums[recfield->recparentno];
425
426 appendStringInfo(&str, "at assignment to field \"%s\" of variable \"%s\" declared on line %d",
427 recfield->fieldname,
428 var->refname,
429 var->lineno);
430
431 cstate->estate->err_text = str.data;
432 }
433
434 #if PG_VERSION_NUM < 140000
435
436 else if (d->dtype == PLPGSQL_DTYPE_ARRAYELEM)
437 {
438 PLpgSQL_arrayelem *elem = (PLpgSQL_arrayelem *) d;
439 PLpgSQL_variable *var = (PLpgSQL_variable *) cstate->estate->datums[elem->arrayparentno];
440
441 appendStringInfo(&str, "at assignment to element of variable \"%s\" declared on line %d",
442 var->refname,
443 var->lineno);
444
445 cstate->estate->err_text = str.data;
446 }
447
448 #endif
449
450 plpgsql_check_assignment(cstate, stmt_assign->expr, NULL, NULL,
451 stmt_assign->varno);
452
453 pfree(str.data);
454 cstate->estate->err_text = NULL;
455 }
456 break;
457
458 case PLPGSQL_STMT_IF:
459 {
460 PLpgSQL_stmt_if *stmt_if = (PLpgSQL_stmt_if *) stmt;
461 ListCell *l;
462 int closing_local;
463 int closing_all_paths = PLPGSQL_CHECK_UNKNOWN;
464 List *exceptions_local;
465
466 plpgsql_check_expr_with_scalar_type(cstate,
467 stmt_if->cond, BOOLOID, true);
468
469 check_stmts(cstate, stmt_if->then_body, &closing_local,
470 &exceptions_local);
471 closing_all_paths = merge_closing(closing_all_paths,
472 closing_local,
473 exceptions,
474 exceptions_local,
475 -1);
476
477 foreach(l, stmt_if->elsif_list)
478 {
479 PLpgSQL_if_elsif *elif = (PLpgSQL_if_elsif *) lfirst(l);
480
481 plpgsql_check_expr_with_scalar_type(cstate,
482 elif->cond, BOOLOID, true);
483 check_stmts(cstate, elif->stmts, &closing_local,
484 &exceptions_local);
485 closing_all_paths = merge_closing(closing_all_paths,
486 closing_local,
487 exceptions,
488 exceptions_local,
489 -1);
490 }
491
492 check_stmts(cstate, stmt_if->else_body, &closing_local,
493 &exceptions_local);
494 closing_all_paths = merge_closing(closing_all_paths,
495 closing_local,
496 exceptions,
497 exceptions_local,
498 -1);
499
500 if (stmt_if->else_body != NULL)
501 *closing = closing_all_paths;
502 else if (closing_all_paths == PLPGSQL_CHECK_UNCLOSED)
503 *closing = PLPGSQL_CHECK_UNCLOSED;
504 else
505 *closing = PLPGSQL_CHECK_POSSIBLY_CLOSED;
506 }
507 break;
508
509 case PLPGSQL_STMT_CASE:
510 {
511 PLpgSQL_stmt_case *stmt_case = (PLpgSQL_stmt_case *) stmt;
512 Oid result_oid;
513 int closing_local;
514 List *exceptions_local;
515 ListCell *l;
516 int closing_all_paths = PLPGSQL_CHECK_UNKNOWN;
517
518 if (stmt_case->t_expr != NULL)
519 {
520 PLpgSQL_var *t_var = (PLpgSQL_var *) cstate->estate->datums[stmt_case->t_varno];
521
522 /*
523 * we need to set hidden variable type
524 */
525 plpgsql_check_expr_generic(cstate, stmt_case->t_expr);
526
527 /* record all variables used by the query */
528 cstate->used_variables = bms_add_members(cstate->used_variables,
529 stmt_case->t_expr->paramnos);
530
531 tupdesc = plpgsql_check_expr_get_desc(cstate,
532 stmt_case->t_expr,
533 false, /* no element type */
534 true, /* expand record */
535 true, /* is expression */
536 NULL);
537 result_oid = TupleDescAttr(tupdesc, 0)->atttypid;
538
539 /*
540 * When expected datatype is different from real,
541 * change it. Note that what we're modifying here is
542 * an execution copy of the datum, so this doesn't
543 * affect the originally stored function parse tree.
544 */
545 if (t_var->datatype->typoid != result_oid)
546
547 #ifdef PLPGSQL_BUILD_DATATYPE_4
548
549 t_var->datatype = plpgsql_check__build_datatype_p(result_oid,
550 -1,
551 cstate->estate->func->fn_input_collation,
552 t_var->datatype->origtypname);
553
554 #else
555
556 t_var->datatype = plpgsql_check__build_datatype_p(result_oid,
557 -1,
558 cstate->estate->func->fn_input_collation);
559
560 #endif
561
562 ReleaseTupleDesc(tupdesc);
563 }
564
565 foreach(l, stmt_case->case_when_list)
566 {
567 PLpgSQL_case_when *cwt = (PLpgSQL_case_when *) lfirst(l);
568
569 plpgsql_check_expr(cstate, cwt->expr);
570 check_stmts(cstate, cwt->stmts, &closing_local, &exceptions_local);
571 closing_all_paths = merge_closing(closing_all_paths,
572 closing_local,
573 exceptions,
574 exceptions_local,
575 -1);
576 }
577
578 if (stmt_case->else_stmts)
579 {
580 check_stmts(cstate, stmt_case->else_stmts, &closing_local, &exceptions_local);
581 *closing = merge_closing(closing_all_paths,
582 closing_local,
583 exceptions,
584 exceptions_local,
585 -1);
586 }
587 else
588 /* is not ensured all path evaluation */
589 *closing = possibly_closed(closing_all_paths);
590 }
591 break;
592
593 case PLPGSQL_STMT_LOOP:
594 check_stmts(cstate, ((PLpgSQL_stmt_loop *) stmt)->body, closing, exceptions);
595 break;
596
597 case PLPGSQL_STMT_WHILE:
598 {
599 PLpgSQL_stmt_while *stmt_while = (PLpgSQL_stmt_while *) stmt;
600 int closing_local;
601 List *exceptions_local;
602
603 plpgsql_check_expr_with_scalar_type(cstate,
604 stmt_while->cond,
605 BOOLOID,
606 true);
607
608 /*
609 * When is not guaranteed execution (possible zero loops),
610 * then ignore closing info from body.
611 */
612 check_stmts(cstate, stmt_while->body, &closing_local, &exceptions_local);
613 *closing = possibly_closed(closing_local);
614 }
615 break;
616
617 case PLPGSQL_STMT_FORI:
618 {
619 PLpgSQL_stmt_fori *stmt_fori = (PLpgSQL_stmt_fori *) stmt;
620 int dno = stmt_fori->var->dno;
621 int closing_local;
622 List *exceptions_local;
623
624 /* prepare plan if desn't exist yet */
625 plpgsql_check_assignment(cstate, stmt_fori->lower, NULL, NULL, dno);
626 plpgsql_check_assignment(cstate, stmt_fori->upper, NULL, NULL, dno);
627
628 if (stmt_fori->step)
629 plpgsql_check_assignment(cstate, stmt_fori->step, NULL, NULL, dno);
630
631 /* this variable should not be updated */
632 cstate->protected_variables = bms_add_member(cstate->protected_variables, dno);
633 cstate->auto_variables = bms_add_member(cstate->auto_variables, dno);
634
635 check_stmts(cstate, stmt_fori->body, &closing_local, &exceptions_local);
636 *closing = possibly_closed(closing_local);
637 }
638 break;
639
640 case PLPGSQL_STMT_FORS:
641 {
642 PLpgSQL_stmt_fors *stmt_fors = (PLpgSQL_stmt_fors *) stmt;
643 int closing_local;
644 List *exceptions_local;
645
646 #if PG_VERSION_NUM >= 110000
647
648 check_variable(cstate, stmt_fors->var);
649
650 /* we need to set hidden variable type */
651 plpgsql_check_assignment_to_variable(cstate, stmt_fors->query,
652 stmt_fors->var, -1);
653
654 #else
655
656 plpgsql_check_row_or_rec(cstate, stmt_fors->row, stmt_fors->rec);
657
658 /* we need to set hidden variable type */
659 plpgsql_check_assignment(cstate, stmt_fors->query,
660 stmt_fors->rec, stmt_fors->row, -1);
661
662 #endif
663
664 check_stmts(cstate, stmt_fors->body, &closing_local, &exceptions_local);
665 *closing = possibly_closed(closing_local);
666 }
667 break;
668
669 case PLPGSQL_STMT_FORC:
670 {
671 PLpgSQL_stmt_forc *stmt_forc = (PLpgSQL_stmt_forc *) stmt;
672 PLpgSQL_var *var = (PLpgSQL_var *) func->datums[stmt_forc->curvar];
673 int closing_local;
674 List *exceptions_local;
675
676 #if PG_VERSION_NUM >= 110000
677
678 check_variable(cstate, stmt_forc->var);
679
680 #else
681
682 plpgsql_check_row_or_rec(cstate, stmt_forc->row, stmt_forc->rec);
683
684 #endif
685
686 plpgsql_check_expr_as_sqlstmt_data(cstate, stmt_forc->argquery);
687
688 #if PG_VERSION_NUM >= 110000
689
690 if (var->cursor_explicit_expr != NULL)
691 plpgsql_check_assignment_to_variable(cstate, var->cursor_explicit_expr,
692 stmt_forc->var, -1);
693
694 #else
695
696 if (var->cursor_explicit_expr != NULL)
697 plpgsql_check_assignment(cstate, var->cursor_explicit_expr,
698 stmt_forc->rec, stmt_forc->row, -1);
699
700 #endif
701
702 check_stmts(cstate, stmt_forc->body, &closing_local, &exceptions_local);
703 *closing = possibly_closed(closing_local);
704
705 cstate->used_variables = bms_add_member(cstate->used_variables,
706 stmt_forc->curvar);
707 }
708 break;
709
710 case PLPGSQL_STMT_DYNFORS:
711 {
712 PLpgSQL_stmt_dynfors *stmt_dynfors = (PLpgSQL_stmt_dynfors *) stmt;
713 int closing_local;
714 List *exceptions_local;
715
716 check_dynamic_sql(cstate,
717 stmt,
718 stmt_dynfors->query,
719 true,
720
721 #if PG_VERSION_NUM >= 110000
722
723 stmt_dynfors->var,
724
725 #else
726
727 stmt_dynfors->row,
728 stmt_dynfors->rec,
729
730 #endif
731
732 stmt_dynfors->params);
733
734 check_stmts(cstate, stmt_dynfors->body, &closing_local, &exceptions_local);
735 *closing = possibly_closed(closing_local);
736 }
737 break;
738
739 case PLPGSQL_STMT_FOREACH_A:
740 {
741 PLpgSQL_stmt_foreach_a *stmt_foreach_a = (PLpgSQL_stmt_foreach_a *) stmt;
742 bool use_element_type;
743 int closing_local;
744 List *exceptions_local;
745
746 plpgsql_check_target(cstate, stmt_foreach_a->varno, NULL, NULL);
747
748 /*
749 * When slice > 0, then result and target are a array.
750 * We shoudl to disable a array element refencing.
751 */
752 use_element_type = stmt_foreach_a->slice == 0;
753
754 plpgsql_check_assignment_with_possible_slices(cstate,
755 stmt_foreach_a->expr,
756 NULL, NULL,
757 stmt_foreach_a->varno,
758 use_element_type);
759
760 check_stmts(cstate, stmt_foreach_a->body, &closing_local, &exceptions_local);
761 *closing = possibly_closed(closing_local);
762 }
763 break;
764
765 case PLPGSQL_STMT_EXIT:
766 {
767 PLpgSQL_stmt_exit *stmt_exit = (PLpgSQL_stmt_exit *) stmt;
768
769 plpgsql_check_expr_with_scalar_type(cstate,
770 stmt_exit->cond,
771 BOOLOID,
772 false);
773
774 if (stmt_exit->label != NULL)
775 {
776 PLpgSQL_stmt *labeled_stmt = find_stmt_with_label(stmt_exit->label,
777 outer_stmt);
778 if (labeled_stmt == NULL)
779 ereport(ERROR,
780 (errcode(ERRCODE_SYNTAX_ERROR),
781 errmsg("label \"%s\" does not exist", stmt_exit->label)));
782
783 /* CONTINUE only allows loop labels */
784 if (!is_any_loop_stmt(labeled_stmt) && !stmt_exit->is_exit)
785 ereport(ERROR,
786 (errcode(ERRCODE_SYNTAX_ERROR),
787 errmsg("block label \"%s\" cannot be used in CONTINUE",
788 stmt_exit->label)));
789 }
790 else
791 {
792 if (find_nearest_loop(outer_stmt) == NULL)
793 ereport(ERROR,
794 (errcode(ERRCODE_SYNTAX_ERROR),
795 errmsg("%s cannot be used outside a loop",
796 plpgsql_check__stmt_typename_p((PLpgSQL_stmt *) stmt_exit))));
797 }
798 }
799 break;
800
801 case PLPGSQL_STMT_PERFORM:
802 plpgsql_check_expr_as_sqlstmt(cstate, ((PLpgSQL_stmt_perform *) stmt)->expr);
803
804 /*
805 * Note: if you want to raise warning when used expressions returns
806 * some value (other than VOID type), change previous command plpgsql_check_expr
807 * to following check_expr_with_expected_scalar_type. This should be
808 * not enabled by default, because PERFORM can be used with reason
809 * to ignore result.
810 *
811 * check_expr_with_expected_scalar_type(cstate,
812 * ((PLpgSQL_stmt_perform *) stmt)->expr,
813 * VOIDOID,
814 * true);
815 */
816
817 break;
818
819 case PLPGSQL_STMT_RETURN:
820 {
821 PLpgSQL_stmt_return *stmt_rt = (PLpgSQL_stmt_return *) stmt;
822
823 if (stmt_rt->retvarno >= 0)
824 {
825 PLpgSQL_datum *retvar = cstate->estate->datums[stmt_rt->retvarno];
826 PLpgSQL_execstate *estate = cstate->estate;
827
828 cstate->used_variables = bms_add_member(cstate->used_variables, stmt_rt->retvarno);
829
830 switch (retvar->dtype)
831 {
832 case PLPGSQL_DTYPE_VAR:
833 {
834 PLpgSQL_var *var = (PLpgSQL_var *) retvar;
835
836 plpgsql_check_assign_to_target_type(cstate,
837 cstate->estate->func->fn_rettype, -1,
838 var->datatype->typoid, false);
839 }
840 break;
841
842 case PLPGSQL_DTYPE_REC:
843 {
844 PLpgSQL_rec *rec = (PLpgSQL_rec *) retvar;
845
846 if (recvar_tupdesc(rec) && estate->rsi && IsA(estate->rsi, ReturnSetInfo))
847 {
848 TupleDesc rettupdesc = estate->rsi->expectedDesc;
849 TupleConversionMap *tupmap ;
850
851 tupmap = convert_tuples_by_position(recvar_tupdesc(rec), rettupdesc,
852 gettext_noop("returned record type does not match expected record type"));
853
854 if (tupmap)
855 free_conversion_map(tupmap);
856 }
857 }
858 break;
859
860 case PLPGSQL_DTYPE_ROW:
861 {
862 PLpgSQL_row *row = (PLpgSQL_row *) retvar;
863
864 if (row->rowtupdesc && estate->rsi && IsA(estate->rsi, ReturnSetInfo))
865 {
866 TupleDesc rettupdesc = estate->rsi->expectedDesc;
867 TupleConversionMap *tupmap ;
868
869 tupmap = convert_tuples_by_position(row->rowtupdesc, rettupdesc,
870 gettext_noop("returned record type does not match expected record type"));
871
872 if (tupmap)
873 free_conversion_map(tupmap);
874 }
875 }
876 break;
877
878 default:
879 ; /* nope */
880 }
881 }
882
883 *closing = PLPGSQL_CHECK_CLOSED;
884
885 if (stmt_rt->expr)
886 plpgsql_check_returned_expr(cstate, stmt_rt->expr, true);
887 }
888 break;
889
890 case PLPGSQL_STMT_RETURN_NEXT:
891 {
892 PLpgSQL_stmt_return_next *stmt_rn = (PLpgSQL_stmt_return_next *) stmt;
893
894 if (stmt_rn->retvarno >= 0)
895 {
896 PLpgSQL_datum *retvar = cstate->estate->datums[stmt_rn->retvarno];
897 PLpgSQL_execstate *estate = cstate->estate;
898 int natts;
899
900 cstate->used_variables = bms_add_member(cstate->used_variables, stmt_rn->retvarno);
901
902 if (!estate->retisset)
903 ereport(ERROR,
904 (errcode(ERRCODE_SYNTAX_ERROR),
905 errmsg("cannot use RETURN NEXT in a non-SETOF function")));
906
907 #if PG_VERSION_NUM >= 110000
908
909 tupdesc = estate->tuple_store_desc;
910
911 #else
912
913 tupdesc = estate->rettupdesc;
914
915 #endif
916
917 natts = tupdesc ? tupdesc->natts : 0;
918
919 switch (retvar->dtype)
920 {
921 case PLPGSQL_DTYPE_VAR:
922 {
923 PLpgSQL_var *var = (PLpgSQL_var *) retvar;
924
925 if (natts > 1)
926 ereport(ERROR,
927 (errcode(ERRCODE_DATATYPE_MISMATCH),
928 errmsg("wrong result type supplied in RETURN NEXT")));
929
930 plpgsql_check_assign_to_target_type(cstate,
931 cstate->estate->func->fn_rettype, -1,
932 var->datatype->typoid, false);
933 }
934 break;
935
936 case PLPGSQL_DTYPE_REC:
937 {
938 PLpgSQL_rec *rec = (PLpgSQL_rec *) retvar;
939 TupleConversionMap *tupmap;
940
941 if (!HeapTupleIsValid(recvar_tuple(rec)))
942 ereport(ERROR,
943 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
944 errmsg("record \"%s\" is not assigned yet",
945 rec->refname),
946 errdetail("The tuple structure of a not-yet-assigned"
947 " record is indeterminate.")));
948
949 if (tupdesc)
950 {
951 tupmap = convert_tuples_by_position(recvar_tupdesc(rec),
952 tupdesc,
953 gettext_noop("wrong record type supplied in RETURN NEXT"));
954 if (tupmap)
955 free_conversion_map(tupmap);
956 }
957 }
958 break;
959
960 case PLPGSQL_DTYPE_ROW:
961 {
962 PLpgSQL_row *row = (PLpgSQL_row *) retvar;
963 bool row_is_valid_result;
964
965 row_is_valid_result = true;
966
967 if (tupdesc)
968 {
969 if (row->nfields == natts)
970 {
971 int i;
972
973 for (i = 0; i < natts; i++)
974 {
975 PLpgSQL_var *var;
976
977 if (TupleDescAttr(tupdesc, i)->attisdropped)
978 continue;
979 if (row->varnos[i] < 0)
980 elog(ERROR, "dropped rowtype entry for non-dropped column");
981
982 var = (PLpgSQL_var *) (cstate->estate->datums[row->varnos[i]]);
983 if (var->datatype->typoid != TupleDescAttr(tupdesc, i)->atttypid)
984 {
985 row_is_valid_result = false;
986 break;
987 }
988 }
989 }
990 else
991 row_is_valid_result = false;
992
993 if (!row_is_valid_result)
994 ereport(ERROR,
995 (errcode(ERRCODE_DATATYPE_MISMATCH),
996 errmsg("wrong record type supplied in RETURN NEXT")));
997 }
998 }
999 break;
1000
1001 default:
1002 ; /* nope */
1003 }
1004 }
1005
1006 if (stmt_rn->expr)
1007 plpgsql_check_returned_expr(cstate, stmt_rn->expr, true);
1008 }
1009 break;
1010
1011 case PLPGSQL_STMT_RETURN_QUERY:
1012 {
1013 PLpgSQL_stmt_return_query *stmt_rq = (PLpgSQL_stmt_return_query *) stmt;
1014
1015 if (stmt_rq->query)
1016 {
1017 plpgsql_check_returned_expr(cstate, stmt_rq->query, false);
1018 cstate->found_return_query = true;
1019 }
1020
1021 if (stmt_rq->dynquery)
1022 {
1023 check_dynamic_sql(cstate,
1024 stmt,
1025 stmt_rq->dynquery,
1026 false,
1027
1028 #if PG_VERSION_NUM >= 110000
1029
1030 NULL,
1031
1032 #else
1033
1034 NULL, NULL,
1035
1036 #endif
1037
1038 stmt_rq->params);
1039 }
1040 }
1041 break;
1042
1043 case PLPGSQL_STMT_RAISE:
1044 {
1045 PLpgSQL_stmt_raise *stmt_raise = (PLpgSQL_stmt_raise *) stmt;
1046 ListCell *current_param;
1047 ListCell *l;
1048 char *cp;
1049 int err_code = 0;
1050
1051 if (stmt_raise->condname != NULL)
1052 err_code = plpgsql_check__recognize_err_condition_p(stmt_raise->condname, true);
1053
1054 foreach(l, stmt_raise->params)
1055 {
1056 plpgsql_check_expr(cstate, (PLpgSQL_expr *) lfirst(l));
1057 }
1058
1059 foreach(l, stmt_raise->options)
1060 {
1061 PLpgSQL_raise_option *opt = (PLpgSQL_raise_option *) lfirst(l);
1062
1063 plpgsql_check_expr(cstate, opt->expr);
1064
1065 if (opt->opt_type == PLPGSQL_RAISEOPTION_ERRCODE)
1066 {
1067 bool isnull;
1068 char *value;
1069
1070 value = plpgsql_check_expr_get_string(cstate, opt->expr, &isnull);
1071
1072 if (value != NULL)
1073 err_code = plpgsql_check__recognize_err_condition_p(value, true);
1074 else
1075 err_code = -1; /* cannot be calculated now */
1076 }
1077 }
1078
1079 current_param = list_head(stmt_raise->params);
1080
1081 /* ensure any single % has a own parameter */
1082 if (stmt_raise->message != NULL)
1083 {
1084 for (cp = stmt_raise->message; *cp; cp++)
1085 {
1086 if (cp[0] == '%')
1087 {
1088 if (cp[1] == '%')
1089 {
1090 cp++;
1091 continue;
1092 }
1093 if (current_param == NULL)
1094 ereport(ERROR,
1095 (errcode(ERRCODE_SYNTAX_ERROR),
1096 errmsg("too few parameters specified for RAISE")));
1097
1098 #if PG_VERSION_NUM >= 130000
1099
1100 current_param = lnext(stmt_raise->params, current_param);
1101
1102 #else
1103
1104 current_param = lnext(current_param);
1105
1106 #endif
1107
1108 }
1109 }
1110 }
1111 if (current_param != NULL)
1112 ereport(ERROR,
1113 (errcode(ERRCODE_SYNTAX_ERROR),
1114 errmsg("too many parameters specified for RAISE")));
1115
1116 if (stmt_raise->elog_level >= ERROR)
1117 {
1118 *closing = PLPGSQL_CHECK_CLOSED_BY_EXCEPTIONS;
1119 if (err_code == 0)
1120 err_code = ERRCODE_RAISE_EXCEPTION;
1121 else if (err_code == -1)
1122 err_code = 0; /* cannot be calculated */
1123 *exceptions = list_make1_int(err_code);
1124 }
1125 /* without any parameters it is reRAISE */
1126 if (stmt_raise->condname == NULL && stmt_raise->message == NULL &&
1127 stmt_raise->options == NIL)
1128 {
1129 *closing = PLPGSQL_CHECK_CLOSED_BY_EXCEPTIONS;
1130 /* should be enhanced in future */
1131 *exceptions = list_make1_int(-2); /* reRAISE */
1132 }
1133 }
1134 break;
1135
1136 case PLPGSQL_STMT_EXECSQL:
1137 {
1138 PLpgSQL_stmt_execsql *stmt_execsql = (PLpgSQL_stmt_execsql *) stmt;
1139
1140 if (stmt_execsql->into)
1141 {
1142
1143 #if PG_VERSION_NUM >= 110000
1144
1145 check_variable(cstate, stmt_execsql->target);
1146 plpgsql_check_assignment_to_variable(cstate, stmt_execsql->sqlstmt,
1147 stmt_execsql->target, -1);
1148
1149 #else
1150
1151 plpgsql_check_row_or_rec(cstate, stmt_execsql->row, stmt_execsql->rec);
1152 plpgsql_check_assignment(cstate, stmt_execsql->sqlstmt,
1153 stmt_execsql->rec, stmt_execsql->row, -1);
1154
1155 #endif
1156
1157 }
1158 else
1159 /* only statement */
1160 plpgsql_check_expr_as_sqlstmt_nodata(cstate, stmt_execsql->sqlstmt);
1161 }
1162 break;
1163
1164 case PLPGSQL_STMT_DYNEXECUTE:
1165 {
1166 PLpgSQL_stmt_dynexecute *stmt_dynexecute = (PLpgSQL_stmt_dynexecute *) stmt;
1167
1168 check_dynamic_sql(cstate,
1169 stmt,
1170 stmt_dynexecute->query,
1171 stmt_dynexecute->into,
1172
1173 #if PG_VERSION_NUM >= 110000
1174
1175 stmt_dynexecute->target,
1176
1177 #else
1178
1179 stmt_dynexecute->row,
1180 stmt_dynexecute->rec,
1181
1182 #endif
1183
1184 stmt_dynexecute->params);
1185 }
1186 break;
1187
1188 case PLPGSQL_STMT_OPEN:
1189 {
1190 PLpgSQL_stmt_open *stmt_open = (PLpgSQL_stmt_open *) stmt;
1191 PLpgSQL_var *var = (PLpgSQL_var *) (cstate->estate->datums[stmt_open->curvar]);
1192 ListCell *l;
1193
1194 plpgsql_check_expr_as_sqlstmt_data(cstate, var->cursor_explicit_expr);
1195 plpgsql_check_expr_as_sqlstmt_data(cstate, stmt_open->query);
1196
1197 if (stmt_open->query != NULL)
1198 var->cursor_explicit_expr = stmt_open->query;
1199
1200 plpgsql_check_expr_as_sqlstmt_data(cstate, stmt_open->argquery);
1201
1202 plpgsql_check_expr(cstate, stmt_open->dynquery);
1203
1204 foreach(l, stmt_open->params)
1205 {
1206 plpgsql_check_expr(cstate, (PLpgSQL_expr *) lfirst(l));
1207 }
1208
1209 cstate->modif_variables = bms_add_member(cstate->modif_variables,
1210 stmt_open->curvar);
1211 }
1212 break;
1213
1214 case PLPGSQL_STMT_GETDIAG:
1215 {
1216 PLpgSQL_stmt_getdiag *stmt_getdiag = (PLpgSQL_stmt_getdiag *) stmt;
1217 ListCell *lc;
1218
1219 foreach(lc, stmt_getdiag->diag_items)
1220 {
1221 PLpgSQL_diag_item *diag_item = (PLpgSQL_diag_item *) lfirst(lc);
1222
1223 plpgsql_check_target(cstate, diag_item->target, NULL, NULL);
1224 }
1225 }
1226 break;
1227
1228 case PLPGSQL_STMT_FETCH:
1229 {
1230 PLpgSQL_stmt_fetch *stmt_fetch = (PLpgSQL_stmt_fetch *) stmt;
1231 PLpgSQL_var *var = (PLpgSQL_var *) (cstate->estate->datums[stmt_fetch->curvar]);
1232
1233 #if PG_VERSION_NUM >= 110000
1234
1235 check_variable(cstate, stmt_fetch->target);
1236
1237 if (var != NULL && var->cursor_explicit_expr != NULL)
1238 plpgsql_check_assignment_to_variable(cstate, var->cursor_explicit_expr,
1239 stmt_fetch->target, -1);
1240
1241 #else
1242
1243 plpgsql_check_row_or_rec(cstate, stmt_fetch->row, stmt_fetch->rec);
1244
1245 if (var != NULL && var->cursor_explicit_expr != NULL)
1246 plpgsql_check_assignment(cstate, var->cursor_explicit_expr,
1247 stmt_fetch->rec, stmt_fetch->row, -1);
1248
1249 #endif
1250
1251 plpgsql_check_expr(cstate, stmt_fetch->expr);
1252
1253 cstate->used_variables = bms_add_member(cstate->used_variables, stmt_fetch->curvar);
1254 }
1255 break;
1256
1257 case PLPGSQL_STMT_CLOSE:
1258 cstate->used_variables = bms_add_member(cstate->used_variables,
1259 ((PLpgSQL_stmt_close *) stmt)->curvar);
1260
1261 break;
1262
1263 #if PG_VERSION_NUM >= 110000
1264
1265 #if PG_VERSION_NUM < 140000
1266 case PLPGSQL_STMT_SET:
1267 /*
1268 * We can not check this now, syntax should be ok.
1269 * The expression there has not plan.
1270 */
1271 break;
1272 #endif /* PG_VERSION_NUM < 140000 */
1273
1274 case PLPGSQL_STMT_COMMIT:
1275 case PLPGSQL_STMT_ROLLBACK:
1276 /* These commands are allowed only in procedures */
1277 if (!cstate->cinfo->is_procedure)
1278 ereport(ERROR,
1279 (errcode(ERRCODE_INVALID_TRANSACTION_TERMINATION),
1280 errmsg("invalid transaction termination")));
1281 break;
1282
1283 case PLPGSQL_STMT_CALL:
1284 {
1285 PLpgSQL_stmt_call *stmt_call = (PLpgSQL_stmt_call *) stmt;
1286 PLpgSQL_row *target;
1287 bool has_data;
1288
1289 has_data = plpgsql_check_expr_as_sqlstmt(cstate, stmt_call->expr);
1290
1291 /* any check_expr_xxx should be called before CallExprGetRowTarget */
1292 target = plpgsql_check_CallExprGetRowTarget(cstate, stmt_call->expr);
1293
1294 if (has_data != (target != NULL))
1295 elog(ERROR, "plpgsql internal error, broken CALL statement");
1296
1297 if (target != NULL)
1298 {
1299 check_variable(cstate, (PLpgSQL_variable *) target);
1300 plpgsql_check_assignment_to_variable(cstate, stmt_call->expr,
1301 (PLpgSQL_variable *) target, -1);
1302
1303 pfree(target->varnos);
1304 pfree(target);
1305 }
1306 }
1307 break;
1308
1309 #endif /* PG_VERSION_NUM >= 110000 */
1310
1311 default:
1312 elog(ERROR, "unrecognized cmd_type: %d", stmt->cmd_type);
1313 }
1314
1315 pop_stmt_from_stmt_stack(cstate);
1316
1317 ReleaseCurrentSubTransaction();
1318 MemoryContextSwitchTo(oldCxt);
1319 CurrentResourceOwner = oldowner;
1320
1321 SPI_restore_connection();
1322 }
1323 PG_CATCH();
1324 {
1325 ErrorData *edata;
1326
1327 MemoryContextSwitchTo(oldCxt);
1328 edata = CopyErrorData();
1329 FlushErrorState();
1330
1331 RollbackAndReleaseCurrentSubTransaction();
1332 MemoryContextSwitchTo(oldCxt);
1333 CurrentResourceOwner = oldowner;
1334
1335 pop_stmt_from_stmt_stack(cstate);
1336
1337 if (!cstate->pragma_vector.disable_check)
1338 {
1339 /*
1340 * If fatal_errors is true, we just propagate the error up to the
1341 * highest level. Otherwise the error is appended to our current list
1342 * of errors, and we continue checking.
1343 */
1344 if (cstate->cinfo->fatal_errors)
1345 ReThrowError(edata);
1346 else
1347 plpgsql_check_put_error_edata(cstate, edata);
1348 }
1349
1350 MemoryContextSwitchTo(oldCxt);
1351
1352 /* reconnect spi */
1353 SPI_restore_connection();
1354 }
1355 PG_END_TRY();
1356
1357 if (!cstate->was_pragma)
1358 cstate->pragma_vector = pragma_vector;
1359 else
1360 cstate->was_pragma = false;
1361 }
1362
1363 /*
1364 * Ensure check for all statements in list
1365 *
1366 */
1367 static void
check_stmts(PLpgSQL_checkstate * cstate,List * stmts,int * closing,List ** exceptions)1368 check_stmts(PLpgSQL_checkstate *cstate, List *stmts, int *closing, List **exceptions)
1369 {
1370 ListCell *lc;
1371 int closing_local;
1372 List *exceptions_local;
1373 volatile bool dead_code_alert = false;
1374 plpgsql_check_pragma_vector prev_pragma_vector = cstate->pragma_vector;
1375
1376 *closing = PLPGSQL_CHECK_UNCLOSED;
1377 *exceptions = NIL;
1378
1379 PG_TRY();
1380 {
1381 foreach(lc, stmts)
1382 {
1383 PLpgSQL_stmt *stmt = (PLpgSQL_stmt *) lfirst(lc);
1384
1385 closing_local = PLPGSQL_CHECK_UNCLOSED;
1386 exceptions_local = NIL;
1387
1388 plpgsql_check_stmt(cstate, stmt, &closing_local, &exceptions_local);
1389
1390 /* raise dead_code_alert only for visible statements */
1391 if (dead_code_alert && stmt->lineno > 0)
1392 {
1393 plpgsql_check_put_error(cstate,
1394 0, stmt->lineno,
1395 "unreachable code",
1396 NULL,
1397 NULL,
1398 PLPGSQL_CHECK_WARNING_EXTRA,
1399 0, NULL, NULL);
1400 /* don't raise this warning every line */
1401 dead_code_alert = false;
1402 }
1403
1404 if (closing_local == PLPGSQL_CHECK_CLOSED)
1405 {
1406 dead_code_alert = true;
1407 *closing = PLPGSQL_CHECK_CLOSED;
1408 *exceptions = NIL;
1409 }
1410 else if (closing_local == PLPGSQL_CHECK_CLOSED_BY_EXCEPTIONS)
1411 {
1412 dead_code_alert = true;
1413 if (*closing == PLPGSQL_CHECK_UNCLOSED ||
1414 *closing == PLPGSQL_CHECK_POSSIBLY_CLOSED ||
1415 *closing == PLPGSQL_CHECK_CLOSED_BY_EXCEPTIONS)
1416 {
1417 *closing = PLPGSQL_CHECK_CLOSED_BY_EXCEPTIONS;
1418 *exceptions = exceptions_local;
1419 }
1420 }
1421 else if (closing_local == PLPGSQL_CHECK_POSSIBLY_CLOSED)
1422 {
1423 if (*closing == PLPGSQL_CHECK_UNCLOSED)
1424 {
1425 *closing = PLPGSQL_CHECK_POSSIBLY_CLOSED;
1426 *exceptions = NIL;
1427 }
1428 }
1429 }
1430 }
1431 PG_CATCH();
1432 {
1433 cstate->pragma_vector = prev_pragma_vector;
1434 cstate->was_pragma = false;
1435
1436 PG_RE_THROW();
1437 }
1438 PG_END_TRY();
1439 }
1440
1441 /*
1442 * Add label to stack of labels
1443 */
1444 static PLpgSQL_stmt_stack_item *
push_stmt_to_stmt_stack(PLpgSQL_checkstate * cstate)1445 push_stmt_to_stmt_stack(PLpgSQL_checkstate *cstate)
1446 {
1447 PLpgSQL_stmt *stmt = cstate->estate->err_stmt;
1448 PLpgSQL_stmt_stack_item *stmt_stack_item;
1449 PLpgSQL_stmt_stack_item *current = cstate->top_stmt_stack;
1450
1451 stmt_stack_item = (PLpgSQL_stmt_stack_item *) palloc(sizeof(PLpgSQL_stmt_stack_item));
1452 stmt_stack_item->stmt = stmt;
1453
1454 switch (PLPGSQL_STMT_TYPES stmt->cmd_type)
1455 {
1456 case PLPGSQL_STMT_BLOCK:
1457 stmt_stack_item->label = ((PLpgSQL_stmt_block *) stmt)->label;
1458 break;
1459
1460 case PLPGSQL_STMT_EXIT:
1461 stmt_stack_item->label = ((PLpgSQL_stmt_exit *) stmt)->label;
1462 break;
1463
1464 case PLPGSQL_STMT_LOOP:
1465 stmt_stack_item->label = ((PLpgSQL_stmt_loop *) stmt)->label;
1466 break;
1467
1468 case PLPGSQL_STMT_WHILE:
1469 stmt_stack_item->label = ((PLpgSQL_stmt_while *) stmt)->label;
1470 break;
1471
1472 case PLPGSQL_STMT_FORI:
1473 stmt_stack_item->label = ((PLpgSQL_stmt_fori *) stmt)->label;
1474 break;
1475
1476 case PLPGSQL_STMT_FORS:
1477 stmt_stack_item->label = ((PLpgSQL_stmt_fors *) stmt)->label;
1478 break;
1479
1480 case PLPGSQL_STMT_FORC:
1481 stmt_stack_item->label = ((PLpgSQL_stmt_forc *) stmt)->label;
1482 break;
1483
1484 case PLPGSQL_STMT_DYNFORS:
1485 stmt_stack_item->label = ((PLpgSQL_stmt_dynfors *) stmt)->label;
1486 break;
1487
1488 case PLPGSQL_STMT_FOREACH_A:
1489 stmt_stack_item->label = ((PLpgSQL_stmt_foreach_a *) stmt)->label;
1490 break;
1491
1492 default:
1493 stmt_stack_item->label = NULL;
1494 }
1495
1496 stmt_stack_item->outer = current;
1497 cstate->top_stmt_stack = stmt_stack_item;
1498
1499 return current;
1500 }
1501
1502 static void
pop_stmt_from_stmt_stack(PLpgSQL_checkstate * cstate)1503 pop_stmt_from_stmt_stack(PLpgSQL_checkstate *cstate)
1504 {
1505 PLpgSQL_stmt_stack_item *current = cstate->top_stmt_stack;
1506
1507 Assert(cstate->top_stmt_stack != NULL);
1508
1509 cstate->top_stmt_stack = current->outer;
1510 pfree(current);
1511 }
1512
1513 /*
1514 * Returns true, when stmt is any loop statement
1515 */
1516 static bool
is_any_loop_stmt(PLpgSQL_stmt * stmt)1517 is_any_loop_stmt(PLpgSQL_stmt *stmt)
1518 {
1519 switch (PLPGSQL_STMT_TYPES stmt->cmd_type)
1520 {
1521 case PLPGSQL_STMT_LOOP:
1522 case PLPGSQL_STMT_WHILE:
1523 case PLPGSQL_STMT_FORI:
1524 case PLPGSQL_STMT_FORS:
1525 case PLPGSQL_STMT_FORC:
1526 case PLPGSQL_STMT_DYNFORS:
1527 case PLPGSQL_STMT_FOREACH_A:
1528 return true;
1529 default:
1530 return false;
1531 }
1532 }
1533
1534 /*
1535 * Searching a any statement related to CONTINUE/EXIT statement.
1536 * label cannot be NULL.
1537 */
1538 static PLpgSQL_stmt *
find_stmt_with_label(char * label,PLpgSQL_stmt_stack_item * current)1539 find_stmt_with_label(char *label, PLpgSQL_stmt_stack_item *current)
1540 {
1541 while (current != NULL)
1542 {
1543 if (current->label != NULL
1544 && strcmp(current->label, label) == 0)
1545 return current->stmt;
1546
1547 current = current->outer;
1548 }
1549
1550 return NULL;
1551 }
1552
1553 static PLpgSQL_stmt *
find_nearest_loop(PLpgSQL_stmt_stack_item * current)1554 find_nearest_loop(PLpgSQL_stmt_stack_item *current)
1555 {
1556 while (current != NULL)
1557 {
1558 if (is_any_loop_stmt(current->stmt))
1559 return current->stmt;
1560
1561 current = current->outer;
1562 }
1563
1564 return NULL;
1565 }
1566
1567 /*
1568 * returns false, when a variable doesn't shadows any other variable
1569 */
1570 static bool
found_shadowed_variable(char * varname,PLpgSQL_stmt_stack_item * current,PLpgSQL_checkstate * cstate)1571 found_shadowed_variable(char *varname, PLpgSQL_stmt_stack_item *current, PLpgSQL_checkstate *cstate)
1572 {
1573 while (current != NULL)
1574 {
1575 if (current->stmt->cmd_type == PLPGSQL_STMT_BLOCK)
1576 {
1577 PLpgSQL_stmt_block *stmt_block = (PLpgSQL_stmt_block *) current->stmt;
1578 int i;
1579 PLpgSQL_datum *d;
1580
1581 for (i = 0; i < stmt_block->n_initvars; i++)
1582 {
1583 char *refname;
1584
1585 d = cstate->estate->func->datums[stmt_block->initvarnos[i]];
1586 refname = plpgsql_check_datum_get_refname(d);
1587
1588 if (refname != NULL && strcmp(refname, varname) == 0)
1589 return true;
1590 }
1591 }
1592
1593 current = current->outer;
1594 }
1595
1596 return false;
1597 }
1598
1599 /*
1600 * Reduce ending states of execution paths.
1601 *
1602 */
1603 static int
possibly_closed(int c)1604 possibly_closed(int c)
1605 {
1606 switch (c)
1607 {
1608 case PLPGSQL_CHECK_CLOSED:
1609 case PLPGSQL_CHECK_CLOSED_BY_EXCEPTIONS:
1610 case PLPGSQL_CHECK_POSSIBLY_CLOSED:
1611 return PLPGSQL_CHECK_POSSIBLY_CLOSED;
1612 default:
1613 return PLPGSQL_CHECK_UNCLOSED;
1614 }
1615 }
1616
1617 /*
1618 * Deduce ending state of execution paths.
1619 *
1620 */
1621 static int
merge_closing(int c,int c_local,List ** exceptions,List * exceptions_local,int err_code)1622 merge_closing(int c, int c_local, List **exceptions, List *exceptions_local, int err_code)
1623 {
1624 *exceptions = NIL;
1625
1626 if (c == PLPGSQL_CHECK_UNKNOWN)
1627 {
1628 if (c_local == PLPGSQL_CHECK_CLOSED_BY_EXCEPTIONS)
1629 *exceptions = exceptions_local;
1630
1631 return c_local;
1632 }
1633
1634 if (c_local == PLPGSQL_CHECK_UNKNOWN)
1635 return c;
1636
1637 if (c == c_local)
1638 {
1639 if (c == PLPGSQL_CHECK_CLOSED_BY_EXCEPTIONS)
1640 {
1641
1642 if (err_code != -1)
1643 {
1644 ListCell *lc;
1645
1646 /* replace reRAISE symbol (-2) by real err_code */
1647 foreach(lc, exceptions_local)
1648 {
1649 int t_err_code = lfirst_int(lc);
1650
1651 *exceptions = list_append_unique_int(*exceptions,
1652 t_err_code != -2 ? t_err_code : err_code);
1653 }
1654 }
1655 else
1656 *exceptions = list_concat_unique_int(*exceptions, exceptions_local);
1657 }
1658
1659 return c_local;
1660 }
1661
1662 if (c == PLPGSQL_CHECK_CLOSED || c_local == PLPGSQL_CHECK_CLOSED)
1663 {
1664 if (c == PLPGSQL_CHECK_CLOSED_BY_EXCEPTIONS ||
1665 c_local == PLPGSQL_CHECK_CLOSED_BY_EXCEPTIONS)
1666 return PLPGSQL_CHECK_CLOSED;
1667 }
1668
1669 return PLPGSQL_CHECK_POSSIBLY_CLOSED;
1670 }
1671
1672 /*
1673 * Returns true, if exception with sqlerrstate is handled.
1674 *
1675 */
1676 static bool
exception_matches_conditions(int sqlerrstate,PLpgSQL_condition * cond)1677 exception_matches_conditions(int sqlerrstate, PLpgSQL_condition *cond)
1678 {
1679 for (; cond != NULL; cond = cond->next)
1680 {
1681 int _sqlerrstate = cond->sqlerrstate;
1682
1683 /*
1684 * OTHERS matches everything *except* query-canceled and
1685 * assert-failure. If you're foolish enough, you can match those
1686 * explicitly.
1687 */
1688 if (_sqlerrstate == 0)
1689 {
1690 if (sqlerrstate != ERRCODE_QUERY_CANCELED &&
1691 sqlerrstate != ERRCODE_ASSERT_FAILURE)
1692 return true;
1693 }
1694 /* Exact match? */
1695 else if (sqlerrstate == _sqlerrstate)
1696 return true;
1697 /* Category match? */
1698 else if (ERRCODE_IS_CATEGORY(_sqlerrstate) &&
1699 ERRCODE_TO_CATEGORY(sqlerrstate) == _sqlerrstate)
1700 return true;
1701 }
1702 return false;
1703 }
1704
1705 /*
1706 * Dynamic SQL processing.
1707 *
1708 * When dynamic query is constant, we can do same work like with
1709 * static SQL.
1710 */
1711
1712 typedef struct
1713 {
1714 List *args;
1715 PLpgSQL_checkstate *cstate;
1716 bool use_params;
1717 } DynSQLParams;
1718
1719 static Node *
dynsql_param_ref(ParseState * pstate,ParamRef * pref)1720 dynsql_param_ref(ParseState *pstate, ParamRef *pref)
1721 {
1722 DynSQLParams *params = (DynSQLParams *) pstate->p_ref_hook_state;
1723 List *args = params->args;
1724 int nargs = list_length(args);
1725 Param *param = NULL;
1726 PLpgSQL_expr *expr;
1727 TupleDesc tupdesc;
1728
1729 if (pref->number < 1 || pref->number > nargs)
1730 ereport(ERROR,
1731 (errcode(ERRCODE_UNDEFINED_PARAMETER),
1732 errmsg("there is no parameter $%d", pref->number),
1733 parser_errposition(pstate, pref->location)));
1734
1735 expr = (PLpgSQL_expr *) list_nth(args, pref->number - 1);
1736
1737 tupdesc = plpgsql_check_expr_get_desc(params->cstate,
1738 expr,
1739 false,
1740 false,
1741 true,
1742 NULL);
1743
1744 if (tupdesc)
1745 {
1746 param = makeNode(Param);
1747 param->paramkind = PARAM_EXTERN;
1748 param->paramid = pref->number;
1749 param->paramtype = TupleDescAttr(tupdesc, 0)->atttypid;
1750 param->location = pref->location;
1751
1752 /*
1753 * SPI_execute_with_args doesn't allow pass typmod.
1754 */
1755 param->paramtypmod = -1;
1756 param->paramcollid = InvalidOid;
1757
1758 ReleaseTupleDesc(tupdesc);
1759 }
1760 else
1761 elog(ERROR, "cannot to detect type of $%d parameter", pref->number);
1762
1763 params->use_params = true;
1764
1765 return (Node *) param;
1766 }
1767
1768 /*
1769 * Dynamic query requires own setup. In reality it is executed by
1770 * different SPI, here we need to emulate different environment.
1771 * Parameters are not mapped to function parameters, but to USING
1772 * clause expressions.
1773 */
1774 static void
dynsql_parser_setup(struct ParseState * pstate,DynSQLParams * params)1775 dynsql_parser_setup(struct ParseState *pstate, DynSQLParams *params)
1776 {
1777 pstate->p_pre_columnref_hook = NULL;
1778 pstate->p_post_columnref_hook = NULL;
1779 pstate->p_paramref_hook = dynsql_param_ref;
1780 pstate->p_ref_hook_state = (void *) params;
1781 }
1782
1783 /*
1784 * Returns true if record variable has assigned some type
1785 */
1786 static bool
has_assigned_tupdesc(PLpgSQL_checkstate * cstate,PLpgSQL_rec * rec)1787 has_assigned_tupdesc(PLpgSQL_checkstate *cstate, PLpgSQL_rec *rec)
1788 {
1789 PLpgSQL_rec *target = (PLpgSQL_rec *) (cstate->estate->datums[rec->dno]);
1790
1791 Assert(rec->dtype == PLPGSQL_DTYPE_REC);
1792
1793 if (recvar_tupdesc(target))
1794 return true;
1795
1796 return false;
1797 }
1798
1799 static void
check_dynamic_sql(PLpgSQL_checkstate * cstate,PLpgSQL_stmt * stmt,PLpgSQL_expr * query,bool into,PLpgSQL_variable * target,List * params)1800 check_dynamic_sql(PLpgSQL_checkstate *cstate,
1801 PLpgSQL_stmt *stmt,
1802 PLpgSQL_expr *query,
1803 bool into,
1804
1805 #if PG_VERSION_NUM >= 110000
1806
1807 PLpgSQL_variable *target,
1808
1809 #else
1810
1811 PLpgSQL_row *row,
1812 PLpgSQL_rec *rec,
1813
1814 #endif
1815
1816 List *params)
1817 {
1818 Node *expr_node;
1819 ListCell *l;
1820 int loc = -1;
1821 char *dynquery = NULL;
1822 bool prev_has_execute_stmt = cstate->has_execute_stmt;
1823 volatile bool expr_is_const = false;
1824
1825 volatile bool raise_unknown_rec_warning = false;
1826 volatile bool known_type_of_dynexpr = false;
1827
1828 /*
1829 * possible checks:
1830 *
1831 * 1. When expression is string literal, then we can check this query similary
1832 * like cursor query with parameters. When this query has not a parameters,
1833 * and it is not DDL, DML, then we can raise a performance warning'.
1834 *
1835 * 2. When expression is real expression, then we should to check any string
1836 * kind parameters if are sanitized by functions quote_ident, qoute_literal,
1837 * or format.
1838 *
1839 * 3. When expression is based on calling format function, and there are used
1840 * only placeholders %I and %L, then we can try to check syntax of embeded
1841 * query.
1842 */
1843
1844 cstate->has_execute_stmt = true;
1845
1846 foreach(l, params)
1847 {
1848 plpgsql_check_expr(cstate, (PLpgSQL_expr *) lfirst(l));
1849 }
1850
1851 plpgsql_check_expr(cstate, query);
1852 expr_node = plpgsql_check_expr_get_node(cstate, query, false);
1853
1854 if (IsA(expr_node, FuncExpr))
1855 {
1856 FuncExpr *fexpr = (FuncExpr *) expr_node;
1857
1858 if (fexpr->funcid == FORMAT_0PARAM_OID ||
1859 fexpr->funcid == FORMAT_NPARAM_OID)
1860 {
1861 if (fexpr->args && IsA(linitial(fexpr->args), Const))
1862 {
1863 StringInfoData sinfo;
1864 char c, *fmt;
1865 bool subst_is_ok = true;
1866 bool found_ident_placeholder = false;
1867 bool found_literal_placeholder = false;
1868
1869 expr_is_const = fexpr->funcid == FORMAT_0PARAM_OID;
1870 fmt = plpgsql_check_const_to_string((Const *) linitial(fexpr->args));
1871
1872 /*
1873 * The placeholders can be used only in FORMAT_NPARAM function,
1874 * but for simplicity and consistency we check FORMAT_0PARAM and
1875 * FORMAT_NPARAM together
1876 */
1877 initStringInfo(&sinfo);
1878
1879 while ((c = *fmt++))
1880 {
1881 if (c == '%')
1882 {
1883 c = *fmt++;
1884
1885 if (c == '%')
1886 {
1887 appendStringInfoChar(&sinfo, c);
1888 }
1889 else if (c == 'I')
1890 {
1891 appendStringInfoString(&sinfo, "\"%I\"");
1892 expr_is_const = false;
1893 found_ident_placeholder = true;
1894 }
1895 else if (c == 'L')
1896 {
1897 /*
1898 * Original idea was used external parameter,
1899 * but external parameters requires known type,
1900 * so most safe value is NULL instead.
1901 */
1902 appendStringInfo(&sinfo, " null ");
1903 found_literal_placeholder = false;
1904 expr_is_const = false;
1905 }
1906 else
1907 {
1908 /*
1909 * Because %s is used, we know nothing about form
1910 * of output string, and has not any sense to continue
1911 * in check.
1912 */
1913 subst_is_ok = false;
1914 expr_is_const = false;
1915 break;
1916 }
1917 }
1918 else
1919 appendStringInfoChar(&sinfo, c);
1920 }
1921
1922 if (subst_is_ok)
1923 {
1924 if (!found_literal_placeholder)
1925 {
1926
1927 #if PG_VERSION_NUM >= 140000
1928
1929 /* in this case we can do only basic parser check */
1930 raw_parser(sinfo.data, RAW_PARSE_DEFAULT);
1931
1932 #else
1933
1934 raw_parser(sinfo.data);
1935
1936 #endif
1937
1938 }
1939
1940 if (!found_ident_placeholder)
1941 dynquery = sinfo.data;
1942 }
1943 }
1944 }
1945 }
1946 else if (IsA(expr_node, Const))
1947 {
1948 expr_is_const = true;
1949 dynquery = plpgsql_check_const_to_string((Const *) expr_node);
1950 }
1951
1952 if (dynquery)
1953 {
1954 PLpgSQL_expr *dynexpr = NULL;
1955 DynSQLParams dsp;
1956 volatile bool is_mp;
1957 volatile bool is_ok = true;
1958
1959 dynexpr = palloc0(sizeof(PLpgSQL_expr));
1960
1961 #if PG_VERSION_NUM < 110000
1962
1963 dynexpr->dtype = PLPGSQL_DTYPE_EXPR;
1964 dynexpr->dno = -1;
1965
1966 #endif
1967
1968 #if PG_VERSION_NUM >= 140000
1969
1970 dynexpr->expr_rw_param = NULL;
1971
1972 #else
1973
1974 dynexpr->rwparam = -1;
1975
1976 #endif
1977
1978 dynexpr->query = dynquery;
1979
1980 dsp.args = params;
1981 dsp.cstate = cstate;
1982 dsp.use_params = false;
1983
1984 /*
1985 * When dynquery is not really constant, then there are
1986 * possible false alarms because we try to replace string
1987 * literal by parameter, so we can use it just for type
1988 * detection when check is ok.
1989 */
1990 if (expr_is_const)
1991 {
1992 PG_TRY();
1993 {
1994 cstate->allow_mp = true;
1995
1996 plpgsql_check_expr_generic_with_parser_setup(cstate,
1997 dynexpr,
1998 (ParserSetupHook) dynsql_parser_setup,
1999 &dsp);
2000
2001 is_mp = cstate->has_mp;
2002 cstate->has_mp = false;
2003 }
2004 PG_CATCH();
2005 {
2006 cstate->allow_mp = false;
2007 cstate->has_mp = false;
2008
2009 PG_RE_THROW();
2010 }
2011 PG_END_TRY();
2012 }
2013 else
2014 {
2015 MemoryContext oldCxt;
2016 ResourceOwner oldowner;
2017
2018 /*
2019 * When dynquery is not really constant, then there are
2020 * possible false alarms because we try to replace string
2021 * literal by parameter, so we can use it just for type
2022 * detection when check is ok.
2023 */
2024
2025 oldCxt = CurrentMemoryContext;
2026
2027 oldowner = CurrentResourceOwner;
2028 BeginInternalSubTransaction(NULL);
2029 MemoryContextSwitchTo(cstate->check_cxt);
2030
2031 PG_TRY();
2032 {
2033 cstate->allow_mp = true;
2034
2035 plpgsql_check_expr_generic_with_parser_setup(cstate,
2036 dynexpr,
2037 (ParserSetupHook) dynsql_parser_setup,
2038 &dsp);
2039
2040 is_mp = cstate->has_mp;
2041 cstate->has_mp = false;
2042
2043 RollbackAndReleaseCurrentSubTransaction();
2044 MemoryContextSwitchTo(oldCxt);
2045 CurrentResourceOwner = oldowner;
2046
2047 SPI_restore_connection();
2048 }
2049 PG_CATCH();
2050 {
2051 is_ok = false;
2052
2053 cstate->allow_mp = false;
2054 cstate->has_mp = false;
2055
2056 MemoryContextSwitchTo(oldCxt);
2057 FlushErrorState();
2058
2059 RollbackAndReleaseCurrentSubTransaction();
2060 MemoryContextSwitchTo(oldCxt);
2061 CurrentResourceOwner = oldowner;
2062 }
2063 PG_END_TRY();
2064 }
2065
2066 if (is_ok && expr_is_const && !is_mp && (!params || !dsp.use_params))
2067 {
2068
2069 /* probably useless dynamic command */
2070 plpgsql_check_put_error(cstate,
2071 0, 0,
2072 "immutable expression without parameters found",
2073 "the EXECUTE command is not necessary probably",
2074 "Don't use dynamic SQL when you can use static SQL.",
2075 PLPGSQL_CHECK_WARNING_PERFORMANCE,
2076 0, NULL, NULL);
2077 }
2078
2079 if (is_ok && params && !dsp.use_params)
2080 {
2081 plpgsql_check_put_error(cstate,
2082 0, 0,
2083 "values passed to EXECUTE statement by USING clause was not used",
2084 NULL,
2085 NULL,
2086 PLPGSQL_CHECK_WARNING_OTHERS,
2087 0, NULL, NULL);
2088 }
2089
2090 if (is_ok && dynexpr->plan)
2091 {
2092 known_type_of_dynexpr = true;
2093
2094 if (stmt->cmd_type == PLPGSQL_STMT_RETURN_QUERY)
2095 {
2096 plpgsql_check_returned_expr(cstate, dynexpr, false);
2097 cstate->found_return_query = true;
2098 }
2099 else if (into)
2100 {
2101
2102 #if PG_VERSION_NUM >= 110000
2103
2104 check_variable(cstate, target);
2105 plpgsql_check_assignment_to_variable(cstate, dynexpr, target, -1);
2106
2107 #else
2108
2109 plpgsql_check_row_or_rec(cstate, row, rec);
2110 plpgsql_check_assignment(cstate, dynexpr, rec, row, -1);
2111
2112 #endif
2113
2114 }
2115 }
2116
2117 /* this is not real dynamic SQL statement */
2118 if (!is_mp)
2119 cstate->has_execute_stmt = prev_has_execute_stmt;
2120 }
2121
2122 if (!expr_is_const)
2123 {
2124 /*
2125 * execute string is not constant (is not safe),
2126 * but we can check sanitize parameters.
2127 */
2128 if (cstate->cinfo->security_warnings &&
2129 plpgsql_check_is_sql_injection_vulnerable(cstate, query, expr_node, &loc))
2130 {
2131 if (loc != -1)
2132 plpgsql_check_put_error(cstate,
2133 0, 0,
2134 "text type variable is not sanitized",
2135 "The EXECUTE expression is SQL injection vulnerable.",
2136 "Use quote_ident, quote_literal or format function to secure variable.",
2137 PLPGSQL_CHECK_WARNING_SECURITY,
2138 loc,
2139 query->query,
2140 NULL);
2141 else
2142 plpgsql_check_put_error(cstate,
2143 0, 0,
2144 "the expression is not SQL injection safe",
2145 "Cannot ensure so dynamic EXECUTE statement is SQL injection secure.",
2146 "Use quote_ident, quote_literal or format function to secure variable.",
2147 PLPGSQL_CHECK_WARNING_SECURITY,
2148 -1,
2149 query->query,
2150 NULL);
2151 }
2152
2153 /* in this case we don't know number of output columns */
2154 if (stmt->cmd_type == PLPGSQL_STMT_RETURN_QUERY &&
2155 !known_type_of_dynexpr)
2156 {
2157 cstate->found_return_dyn_query = true;
2158 }
2159
2160 /*
2161 * In this case, we don't know a result type, and we should
2162 * to raise warning about this situation.
2163 */
2164 if (into && !known_type_of_dynexpr)
2165 {
2166
2167 #if PG_VERSION_NUM >= 110000
2168
2169 if (target->dtype == PLPGSQL_DTYPE_REC)
2170 raise_unknown_rec_warning = true;
2171
2172 #else
2173
2174 if (rec)
2175 raise_unknown_rec_warning = true;
2176
2177 #endif
2178
2179 }
2180 }
2181
2182 /* recheck if target rec var has assigned tupdesc */
2183 if (into)
2184 {
2185
2186 #if PG_VERSION_NUM >= 110000
2187
2188 check_variable(cstate, target);
2189
2190 if (raise_unknown_rec_warning ||
2191 (target->dtype == PLPGSQL_DTYPE_REC &&
2192 !has_assigned_tupdesc(cstate, (PLpgSQL_rec *) target)))
2193
2194 #else
2195
2196 plpgsql_check_row_or_rec(cstate, row, rec);
2197
2198 if (raise_unknown_rec_warning || (rec != NULL && !has_assigned_tupdesc(cstate, rec)))
2199
2200 #endif
2201
2202 {
2203
2204 #if PG_VERSION_NUM >= 110000
2205
2206 if (!bms_is_member(target->dno, cstate->typed_variables))
2207
2208 #else
2209
2210 if (!bms_is_member(rec->dno, cstate->typed_variables))
2211
2212 #endif
2213
2214 plpgsql_check_put_error(cstate,
2215 0, 0,
2216 "cannot determinate a result of dynamic SQL",
2217 "There is a risk of related false alarms.",
2218 "Don't use dynamic SQL and record type together, when you would check function.",
2219 PLPGSQL_CHECK_WARNING_OTHERS,
2220 0, NULL, NULL);
2221 }
2222 }
2223 }
2224