Branch data Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * pl_funcs.c - Misc functions for the PL/pgSQL
4 : : * procedural language
5 : : *
6 : : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
7 : : * Portions Copyright (c) 1994, Regents of the University of California
8 : : *
9 : : *
10 : : * IDENTIFICATION
11 : : * src/pl/plpgsql/src/pl_funcs.c
12 : : *
13 : : *-------------------------------------------------------------------------
14 : : */
15 : :
16 : : #include "postgres.h"
17 : :
18 : : #include "plpgsql.h"
19 : : #include "utils/memutils.h"
20 : :
21 : : /* ----------
22 : : * Local variables for namespace handling
23 : : *
24 : : * The namespace structure actually forms a tree, of which only one linear
25 : : * list or "chain" (from the youngest item to the root) is accessible from
26 : : * any one plpgsql statement. During initial parsing of a function, ns_top
27 : : * points to the youngest item accessible from the block currently being
28 : : * parsed. We store the entire tree, however, since at runtime we will need
29 : : * to access the chain that's relevant to any one statement.
30 : : *
31 : : * Block boundaries in the namespace chain are marked by PLPGSQL_NSTYPE_LABEL
32 : : * items.
33 : : * ----------
34 : : */
35 : : static PLpgSQL_nsitem *ns_top = NULL;
36 : :
37 : :
38 : : /* ----------
39 : : * plpgsql_ns_init Initialize namespace processing for a new function
40 : : * ----------
41 : : */
42 : : void
43 : 6003 : plpgsql_ns_init(void)
44 : : {
45 : 6003 : ns_top = NULL;
46 : 6003 : }
47 : :
48 : :
49 : : /* ----------
50 : : * plpgsql_ns_push Create a new namespace level
51 : : * ----------
52 : : */
53 : : void
54 : 13315 : plpgsql_ns_push(const char *label, PLpgSQL_label_type label_type)
55 : : {
56 [ + + ]: 13315 : if (label == NULL)
57 : 7181 : label = "";
58 : 13315 : plpgsql_ns_additem(PLPGSQL_NSTYPE_LABEL, (int) label_type, label);
59 : 13315 : }
60 : :
61 : :
62 : : /* ----------
63 : : * plpgsql_ns_pop Pop entries back to (and including) the last label
64 : : * ----------
65 : : */
66 : : void
67 : 7195 : plpgsql_ns_pop(void)
68 : : {
69 : : Assert(ns_top != NULL);
70 [ + + ]: 12035 : while (ns_top->itemtype != PLPGSQL_NSTYPE_LABEL)
71 : 4840 : ns_top = ns_top->prev;
72 : 7195 : ns_top = ns_top->prev;
73 : 7195 : }
74 : :
75 : :
76 : : /* ----------
77 : : * plpgsql_ns_top Fetch the current namespace chain end
78 : : * ----------
79 : : */
80 : : PLpgSQL_nsitem *
81 : 59759 : plpgsql_ns_top(void)
82 : : {
83 : 59759 : return ns_top;
84 : : }
85 : :
86 : :
87 : : /* ----------
88 : : * plpgsql_ns_additem Add an item to the current namespace chain
89 : : * ----------
90 : : */
91 : : void
92 : 58607 : plpgsql_ns_additem(PLpgSQL_nsitem_type itemtype, int itemno, const char *name)
93 : : {
94 : : PLpgSQL_nsitem *nse;
95 : :
96 : : Assert(name != NULL);
97 : : /* first item added must be a label */
98 : : Assert(ns_top != NULL || itemtype == PLPGSQL_NSTYPE_LABEL);
99 : :
100 : 58607 : nse = palloc(offsetof(PLpgSQL_nsitem, name) + strlen(name) + 1);
101 : 58607 : nse->itemtype = itemtype;
102 : 58607 : nse->itemno = itemno;
103 : 58607 : nse->prev = ns_top;
104 : 58607 : strcpy(nse->name, name);
105 : 58607 : ns_top = nse;
106 : 58607 : }
107 : :
108 : :
109 : : /* ----------
110 : : * plpgsql_ns_lookup Lookup an identifier in the given namespace chain
111 : : *
112 : : * Note that this only searches for variables, not labels.
113 : : *
114 : : * If localmode is true, only the topmost block level is searched.
115 : : *
116 : : * name1 must be non-NULL. Pass NULL for name2 and/or name3 if parsing a name
117 : : * with fewer than three components.
118 : : *
119 : : * If names_used isn't NULL, *names_used receives the number of names
120 : : * matched: 0 if no match, 1 if name1 matched an unqualified variable name,
121 : : * 2 if name1 and name2 matched a block label + variable name.
122 : : *
123 : : * Note that name3 is never directly matched to anything. However, if it
124 : : * isn't NULL, we will disregard qualified matches to scalar variables.
125 : : * Similarly, if name2 isn't NULL, we disregard unqualified matches to
126 : : * scalar variables.
127 : : * ----------
128 : : */
129 : : PLpgSQL_nsitem *
130 : 57767 : plpgsql_ns_lookup(PLpgSQL_nsitem *ns_cur, bool localmode,
131 : : const char *name1, const char *name2, const char *name3,
132 : : int *names_used)
133 : : {
134 : : /* Outer loop iterates once per block level in the namespace chain */
135 [ + + ]: 113639 : while (ns_cur != NULL)
136 : : {
137 : : PLpgSQL_nsitem *nsitem;
138 : :
139 : : /* Check this level for unqualified match to variable name */
140 : 101992 : for (nsitem = ns_cur;
141 [ + + ]: 385909 : nsitem->itemtype != PLPGSQL_NSTYPE_LABEL;
142 : 283917 : nsitem = nsitem->prev)
143 : : {
144 [ + + ]: 320979 : if (strcmp(nsitem->name, name1) == 0)
145 : : {
146 [ + + ]: 37062 : if (name2 == NULL ||
147 [ + - ]: 8343 : nsitem->itemtype != PLPGSQL_NSTYPE_VAR)
148 : : {
149 [ + + ]: 37062 : if (names_used)
150 : 26521 : *names_used = 1;
151 : 37062 : return nsitem;
152 : : }
153 : : }
154 : : }
155 : :
156 : : /* Check this level for qualified match to variable name */
157 [ + + ]: 64930 : if (name2 != NULL &&
158 [ + + ]: 9523 : strcmp(nsitem->name, name1) == 0)
159 : : {
160 : 82 : for (nsitem = ns_cur;
161 [ + + ]: 142 : nsitem->itemtype != PLPGSQL_NSTYPE_LABEL;
162 : 60 : nsitem = nsitem->prev)
163 : : {
164 [ + + ]: 141 : if (strcmp(nsitem->name, name2) == 0)
165 : : {
166 [ + + ]: 81 : if (name3 == NULL ||
167 [ + - ]: 36 : nsitem->itemtype != PLPGSQL_NSTYPE_VAR)
168 : : {
169 [ + - ]: 81 : if (names_used)
170 : 81 : *names_used = 2;
171 : 81 : return nsitem;
172 : : }
173 : : }
174 : : }
175 : : }
176 : :
177 [ + + ]: 64849 : if (localmode)
178 : 8977 : break; /* do not look into upper levels */
179 : :
180 : 55872 : ns_cur = nsitem->prev;
181 : : }
182 : :
183 : : /* This is just to suppress possibly-uninitialized-variable warnings */
184 [ + + ]: 20624 : if (names_used)
185 : 3344 : *names_used = 0;
186 : 20624 : return NULL; /* No match found */
187 : : }
188 : :
189 : :
190 : : /* ----------
191 : : * plpgsql_ns_lookup_label Lookup a label in the given namespace chain
192 : : * ----------
193 : : */
194 : : PLpgSQL_nsitem *
195 : 16 : plpgsql_ns_lookup_label(PLpgSQL_nsitem *ns_cur, const char *name)
196 : : {
197 [ + + ]: 43 : while (ns_cur != NULL)
198 : : {
199 [ + + ]: 41 : if (ns_cur->itemtype == PLPGSQL_NSTYPE_LABEL &&
200 [ + + ]: 34 : strcmp(ns_cur->name, name) == 0)
201 : 14 : return ns_cur;
202 : 27 : ns_cur = ns_cur->prev;
203 : : }
204 : :
205 : 2 : return NULL; /* label not found */
206 : : }
207 : :
208 : :
209 : : /* ----------
210 : : * plpgsql_ns_find_nearest_loop Find innermost loop label in namespace chain
211 : : * ----------
212 : : */
213 : : PLpgSQL_nsitem *
214 : 125 : plpgsql_ns_find_nearest_loop(PLpgSQL_nsitem *ns_cur)
215 : : {
216 [ + + ]: 155 : while (ns_cur != NULL)
217 : : {
218 [ + + ]: 153 : if (ns_cur->itemtype == PLPGSQL_NSTYPE_LABEL &&
219 [ + + ]: 134 : ns_cur->itemno == PLPGSQL_LABEL_LOOP)
220 : 123 : return ns_cur;
221 : 30 : ns_cur = ns_cur->prev;
222 : : }
223 : :
224 : 2 : return NULL; /* no loop found */
225 : : }
226 : :
227 : :
228 : : /*
229 : : * Statement type as a string, for use in error messages etc.
230 : : */
231 : : const char *
232 : 14397 : plpgsql_stmt_typename(PLpgSQL_stmt *stmt)
233 : : {
234 [ - + - + : 14397 : switch (stmt->cmd_type)
- - + + -
+ - + + +
+ + + + +
+ + + - +
+ + + - ]
235 : : {
236 : 0 : case PLPGSQL_STMT_BLOCK:
237 : 0 : return _("statement block");
238 : 360 : case PLPGSQL_STMT_ASSIGN:
239 : 360 : return _("assignment");
240 : 0 : case PLPGSQL_STMT_IF:
241 : 0 : return "IF";
242 : 3 : case PLPGSQL_STMT_CASE:
243 : 3 : return "CASE";
244 : 0 : case PLPGSQL_STMT_LOOP:
245 : 0 : return "LOOP";
246 : 0 : case PLPGSQL_STMT_WHILE:
247 : 0 : return "WHILE";
248 : 3 : case PLPGSQL_STMT_FORI:
249 : 3 : return _("FOR with integer loop variable");
250 : 10 : case PLPGSQL_STMT_FORS:
251 : 10 : return _("FOR over SELECT rows");
252 : 0 : case PLPGSQL_STMT_FORC:
253 : 0 : return _("FOR over cursor");
254 : 12 : case PLPGSQL_STMT_FOREACH_A:
255 : 12 : return _("FOREACH over array");
256 : 0 : case PLPGSQL_STMT_EXIT:
257 [ # # ]: 0 : return ((PLpgSQL_stmt_exit *) stmt)->is_exit ? "EXIT" : "CONTINUE";
258 : 84 : case PLPGSQL_STMT_RETURN:
259 : 84 : return "RETURN";
260 : 2 : case PLPGSQL_STMT_RETURN_NEXT:
261 : 2 : return "RETURN NEXT";
262 : 9 : case PLPGSQL_STMT_RETURN_QUERY:
263 : 9 : return "RETURN QUERY";
264 : 10137 : case PLPGSQL_STMT_RAISE:
265 : 10137 : return "RAISE";
266 : 16 : case PLPGSQL_STMT_ASSERT:
267 : 16 : return "ASSERT";
268 : 2369 : case PLPGSQL_STMT_EXECSQL:
269 : 2369 : return _("SQL statement");
270 : 274 : case PLPGSQL_STMT_DYNEXECUTE:
271 : 274 : return "EXECUTE";
272 : 37 : case PLPGSQL_STMT_DYNFORS:
273 : 37 : return _("FOR over EXECUTE statement");
274 : 36 : case PLPGSQL_STMT_GETDIAG:
275 : 36 : return ((PLpgSQL_stmt_getdiag *) stmt)->is_stacked ?
276 [ + + ]: 36 : "GET STACKED DIAGNOSTICS" : "GET DIAGNOSTICS";
277 : 8 : case PLPGSQL_STMT_OPEN:
278 : 8 : return "OPEN";
279 : 4 : case PLPGSQL_STMT_FETCH:
280 [ - + ]: 4 : return ((PLpgSQL_stmt_fetch *) stmt)->is_move ? "MOVE" : "FETCH";
281 : 0 : case PLPGSQL_STMT_CLOSE:
282 : 0 : return "CLOSE";
283 : 980 : case PLPGSQL_STMT_PERFORM:
284 : 980 : return "PERFORM";
285 : 39 : case PLPGSQL_STMT_CALL:
286 [ + - ]: 39 : return ((PLpgSQL_stmt_call *) stmt)->is_call ? "CALL" : "DO";
287 : 11 : case PLPGSQL_STMT_COMMIT:
288 : 11 : return "COMMIT";
289 : 3 : case PLPGSQL_STMT_ROLLBACK:
290 : 3 : return "ROLLBACK";
291 : : }
292 : :
293 : 0 : return "unknown";
294 : : }
295 : :
296 : : /*
297 : : * GET DIAGNOSTICS item name as a string, for use in error messages etc.
298 : : */
299 : : const char *
300 : 0 : plpgsql_getdiag_kindname(PLpgSQL_getdiag_kind kind)
301 : : {
302 [ # # # # : 0 : switch (kind)
# # # # #
# # # #
# ]
303 : : {
304 : 0 : case PLPGSQL_GETDIAG_ROW_COUNT:
305 : 0 : return "ROW_COUNT";
306 : 0 : case PLPGSQL_GETDIAG_ROUTINE_OID:
307 : 0 : return "PG_ROUTINE_OID";
308 : 0 : case PLPGSQL_GETDIAG_CONTEXT:
309 : 0 : return "PG_CONTEXT";
310 : 0 : case PLPGSQL_GETDIAG_ERROR_CONTEXT:
311 : 0 : return "PG_EXCEPTION_CONTEXT";
312 : 0 : case PLPGSQL_GETDIAG_ERROR_DETAIL:
313 : 0 : return "PG_EXCEPTION_DETAIL";
314 : 0 : case PLPGSQL_GETDIAG_ERROR_HINT:
315 : 0 : return "PG_EXCEPTION_HINT";
316 : 0 : case PLPGSQL_GETDIAG_RETURNED_SQLSTATE:
317 : 0 : return "RETURNED_SQLSTATE";
318 : 0 : case PLPGSQL_GETDIAG_COLUMN_NAME:
319 : 0 : return "COLUMN_NAME";
320 : 0 : case PLPGSQL_GETDIAG_CONSTRAINT_NAME:
321 : 0 : return "CONSTRAINT_NAME";
322 : 0 : case PLPGSQL_GETDIAG_DATATYPE_NAME:
323 : 0 : return "PG_DATATYPE_NAME";
324 : 0 : case PLPGSQL_GETDIAG_MESSAGE_TEXT:
325 : 0 : return "MESSAGE_TEXT";
326 : 0 : case PLPGSQL_GETDIAG_TABLE_NAME:
327 : 0 : return "TABLE_NAME";
328 : 0 : case PLPGSQL_GETDIAG_SCHEMA_NAME:
329 : 0 : return "SCHEMA_NAME";
330 : : }
331 : :
332 : 0 : return "unknown";
333 : : }
334 : :
335 : :
336 : : /**********************************************************************
337 : : * Support for recursing through a PL/pgSQL statement tree
338 : : *
339 : : * The point of this code is to encapsulate knowledge of where the
340 : : * sub-statements and expressions are in a statement tree, avoiding
341 : : * duplication of code. The caller supplies two callbacks, one to
342 : : * be invoked on statements and one to be invoked on expressions.
343 : : * (The recursion should be started by invoking the statement callback
344 : : * on function->action.) The statement callback should do any
345 : : * statement-type-specific action it needs, then recurse by calling
346 : : * plpgsql_statement_tree_walker(). The expression callback can be a
347 : : * no-op if no per-expression behavior is needed.
348 : : **********************************************************************/
349 : : typedef void (*plpgsql_stmt_walker_callback) (PLpgSQL_stmt *stmt,
350 : : void *context);
351 : : typedef void (*plpgsql_expr_walker_callback) (PLpgSQL_expr *expr,
352 : : void *context);
353 : :
354 : : /*
355 : : * As in nodeFuncs.h, we respectfully decline to support the C standard's
356 : : * position that a pointer to struct is incompatible with "void *". Instead,
357 : : * silence related compiler warnings using casts in this macro wrapper.
358 : : */
359 : : #define plpgsql_statement_tree_walker(s, sw, ew, c) \
360 : : plpgsql_statement_tree_walker_impl(s, (plpgsql_stmt_walker_callback) (sw), \
361 : : (plpgsql_expr_walker_callback) (ew), c)
362 : :
363 : : pg_attribute_no_sanitize_function()
364 : : static void
365 : 8447 : plpgsql_statement_tree_walker_impl(PLpgSQL_stmt *stmt,
366 : : plpgsql_stmt_walker_callback stmt_callback,
367 : : plpgsql_expr_walker_callback expr_callback,
368 : : void *context)
369 : : {
370 : : #define S_WALK(st) stmt_callback(st, context)
371 : : #define E_WALK(ex) expr_callback(ex, context)
372 : : #define S_LIST_WALK(lst) foreach_ptr(PLpgSQL_stmt, st, lst) S_WALK(st)
373 : : #define E_LIST_WALK(lst) foreach_ptr(PLpgSQL_expr, ex, lst) E_WALK(ex)
374 : :
375 [ + + + + : 8447 : switch (stmt->cmd_type)
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + - ]
376 : : {
377 : 1932 : case PLPGSQL_STMT_BLOCK:
378 : : {
379 : 1932 : PLpgSQL_stmt_block *bstmt = (PLpgSQL_stmt_block *) stmt;
380 : :
381 [ + - + + : 8686 : S_LIST_WALK(bstmt->body);
+ + ]
382 [ + + ]: 1932 : if (bstmt->exceptions)
383 : : {
384 [ + - + + : 1117 : foreach_ptr(PLpgSQL_exception, exc, bstmt->exceptions->exc_list)
+ + ]
385 : : {
386 : : /* conditions list has no interesting sub-structure */
387 [ + + + + : 1131 : S_LIST_WALK(exc->action);
+ + ]
388 : : }
389 : : }
390 : 1932 : break;
391 : : }
392 : 612 : case PLPGSQL_STMT_ASSIGN:
393 : : {
394 : 612 : PLpgSQL_stmt_assign *astmt = (PLpgSQL_stmt_assign *) stmt;
395 : :
396 : 612 : E_WALK(astmt->expr);
397 : 612 : break;
398 : : }
399 : 237 : case PLPGSQL_STMT_IF:
400 : : {
401 : 237 : PLpgSQL_stmt_if *ifstmt = (PLpgSQL_stmt_if *) stmt;
402 : :
403 : 237 : E_WALK(ifstmt->cond);
404 [ + - + + : 849 : S_LIST_WALK(ifstmt->then_body);
+ + ]
405 [ - + - - : 474 : foreach_ptr(PLpgSQL_if_elsif, elif, ifstmt->elsif_list)
+ + ]
406 : : {
407 : 0 : E_WALK(elif->cond);
408 [ # # # # : 0 : S_LIST_WALK(elif->stmts);
# # ]
409 : : }
410 [ + + + + : 565 : S_LIST_WALK(ifstmt->else_body);
+ + ]
411 : 237 : break;
412 : : }
413 : 1 : case PLPGSQL_STMT_CASE:
414 : : {
415 : 1 : PLpgSQL_stmt_case *cstmt = (PLpgSQL_stmt_case *) stmt;
416 : :
417 : 1 : E_WALK(cstmt->t_expr);
418 [ + - + + : 7 : foreach_ptr(PLpgSQL_case_when, cwt, cstmt->case_when_list)
+ + ]
419 : : {
420 : 5 : E_WALK(cwt->expr);
421 [ + - + + : 15 : S_LIST_WALK(cwt->stmts);
+ + ]
422 : : }
423 [ - + - - : 2 : S_LIST_WALK(cstmt->else_stmts);
+ + ]
424 : 1 : break;
425 : : }
426 : 29 : case PLPGSQL_STMT_LOOP:
427 : : {
428 : 29 : PLpgSQL_stmt_loop *lstmt = (PLpgSQL_stmt_loop *) stmt;
429 : :
430 [ + - + + : 135 : S_LIST_WALK(lstmt->body);
+ + ]
431 : 29 : break;
432 : : }
433 : 35 : case PLPGSQL_STMT_WHILE:
434 : : {
435 : 35 : PLpgSQL_stmt_while *wstmt = (PLpgSQL_stmt_while *) stmt;
436 : :
437 : 35 : E_WALK(wstmt->cond);
438 [ + - + + : 147 : S_LIST_WALK(wstmt->body);
+ + ]
439 : 35 : break;
440 : : }
441 : 309 : case PLPGSQL_STMT_FORI:
442 : : {
443 : 309 : PLpgSQL_stmt_fori *fori = (PLpgSQL_stmt_fori *) stmt;
444 : :
445 : 309 : E_WALK(fori->lower);
446 : 309 : E_WALK(fori->upper);
447 : 309 : E_WALK(fori->step);
448 [ + - + + : 1187 : S_LIST_WALK(fori->body);
+ + ]
449 : 309 : break;
450 : : }
451 : 95 : case PLPGSQL_STMT_FORS:
452 : : {
453 : 95 : PLpgSQL_stmt_fors *fors = (PLpgSQL_stmt_fors *) stmt;
454 : :
455 [ + + + + : 535 : S_LIST_WALK(fors->body);
+ + ]
456 : 95 : E_WALK(fors->query);
457 : 95 : break;
458 : : }
459 : 33 : case PLPGSQL_STMT_FORC:
460 : : {
461 : 33 : PLpgSQL_stmt_forc *forc = (PLpgSQL_stmt_forc *) stmt;
462 : :
463 [ + - + + : 104 : S_LIST_WALK(forc->body);
+ + ]
464 : 33 : E_WALK(forc->argquery);
465 : 33 : break;
466 : : }
467 : 32 : case PLPGSQL_STMT_FOREACH_A:
468 : : {
469 : 32 : PLpgSQL_stmt_foreach_a *fstmt = (PLpgSQL_stmt_foreach_a *) stmt;
470 : :
471 : 32 : E_WALK(fstmt->expr);
472 [ + - + + : 96 : S_LIST_WALK(fstmt->body);
+ + ]
473 : 32 : break;
474 : : }
475 : 48 : case PLPGSQL_STMT_EXIT:
476 : : {
477 : 48 : PLpgSQL_stmt_exit *estmt = (PLpgSQL_stmt_exit *) stmt;
478 : :
479 : 48 : E_WALK(estmt->cond);
480 : 48 : break;
481 : : }
482 : 1706 : case PLPGSQL_STMT_RETURN:
483 : : {
484 : 1706 : PLpgSQL_stmt_return *rstmt = (PLpgSQL_stmt_return *) stmt;
485 : :
486 : 1706 : E_WALK(rstmt->expr);
487 : 1706 : break;
488 : : }
489 : 39 : case PLPGSQL_STMT_RETURN_NEXT:
490 : : {
491 : 39 : PLpgSQL_stmt_return_next *rstmt = (PLpgSQL_stmt_return_next *) stmt;
492 : :
493 : 39 : E_WALK(rstmt->expr);
494 : 39 : break;
495 : : }
496 : 25 : case PLPGSQL_STMT_RETURN_QUERY:
497 : : {
498 : 25 : PLpgSQL_stmt_return_query *rstmt = (PLpgSQL_stmt_return_query *) stmt;
499 : :
500 : 25 : E_WALK(rstmt->query);
501 : 25 : E_WALK(rstmt->dynquery);
502 [ - + - - : 50 : E_LIST_WALK(rstmt->params);
+ + ]
503 : 25 : break;
504 : : }
505 : 1126 : case PLPGSQL_STMT_RAISE:
506 : : {
507 : 1126 : PLpgSQL_stmt_raise *rstmt = (PLpgSQL_stmt_raise *) stmt;
508 : :
509 [ + + + + : 3582 : E_LIST_WALK(rstmt->params);
+ + ]
510 [ + + + + : 2417 : foreach_ptr(PLpgSQL_raise_option, opt, rstmt->options)
+ + ]
511 : : {
512 : 165 : E_WALK(opt->expr);
513 : : }
514 : 1126 : break;
515 : : }
516 : 28 : case PLPGSQL_STMT_ASSERT:
517 : : {
518 : 28 : PLpgSQL_stmt_assert *astmt = (PLpgSQL_stmt_assert *) stmt;
519 : :
520 : 28 : E_WALK(astmt->cond);
521 : 28 : E_WALK(astmt->message);
522 : 28 : break;
523 : : }
524 : 1058 : case PLPGSQL_STMT_EXECSQL:
525 : : {
526 : 1058 : PLpgSQL_stmt_execsql *xstmt = (PLpgSQL_stmt_execsql *) stmt;
527 : :
528 : 1058 : E_WALK(xstmt->sqlstmt);
529 : 1058 : break;
530 : : }
531 : 293 : case PLPGSQL_STMT_DYNEXECUTE:
532 : : {
533 : 293 : PLpgSQL_stmt_dynexecute *dstmt = (PLpgSQL_stmt_dynexecute *) stmt;
534 : :
535 : 293 : E_WALK(dstmt->query);
536 [ + + + + : 598 : E_LIST_WALK(dstmt->params);
+ + ]
537 : 293 : break;
538 : : }
539 : 52 : case PLPGSQL_STMT_DYNFORS:
540 : : {
541 : 52 : PLpgSQL_stmt_dynfors *dstmt = (PLpgSQL_stmt_dynfors *) stmt;
542 : :
543 [ + - + + : 156 : S_LIST_WALK(dstmt->body);
+ + ]
544 : 52 : E_WALK(dstmt->query);
545 [ - + - - : 104 : E_LIST_WALK(dstmt->params);
+ + ]
546 : 52 : break;
547 : : }
548 : 72 : case PLPGSQL_STMT_GETDIAG:
549 : : {
550 : : /* no interesting sub-structure */
551 : 72 : break;
552 : : }
553 : 29 : case PLPGSQL_STMT_OPEN:
554 : : {
555 : 29 : PLpgSQL_stmt_open *ostmt = (PLpgSQL_stmt_open *) stmt;
556 : :
557 : 29 : E_WALK(ostmt->argquery);
558 : 29 : E_WALK(ostmt->query);
559 : 29 : E_WALK(ostmt->dynquery);
560 [ - + - - : 58 : E_LIST_WALK(ostmt->params);
+ + ]
561 : 29 : break;
562 : : }
563 : 53 : case PLPGSQL_STMT_FETCH:
564 : : {
565 : 53 : PLpgSQL_stmt_fetch *fstmt = (PLpgSQL_stmt_fetch *) stmt;
566 : :
567 : 53 : E_WALK(fstmt->expr);
568 : 53 : break;
569 : : }
570 : 24 : case PLPGSQL_STMT_CLOSE:
571 : : {
572 : : /* no interesting sub-structure */
573 : 24 : break;
574 : : }
575 : 488 : case PLPGSQL_STMT_PERFORM:
576 : : {
577 : 488 : PLpgSQL_stmt_perform *pstmt = (PLpgSQL_stmt_perform *) stmt;
578 : :
579 : 488 : E_WALK(pstmt->expr);
580 : 488 : break;
581 : : }
582 : 44 : case PLPGSQL_STMT_CALL:
583 : : {
584 : 44 : PLpgSQL_stmt_call *cstmt = (PLpgSQL_stmt_call *) stmt;
585 : :
586 : 44 : E_WALK(cstmt->expr);
587 : 44 : break;
588 : : }
589 : 47 : case PLPGSQL_STMT_COMMIT:
590 : : case PLPGSQL_STMT_ROLLBACK:
591 : : {
592 : : /* no interesting sub-structure */
593 : 47 : break;
594 : : }
595 : 0 : default:
596 [ # # ]: 0 : elog(ERROR, "unrecognized cmd_type: %d", stmt->cmd_type);
597 : : break;
598 : : }
599 : 8447 : }
600 : :
601 : :
602 : : /**********************************************************************
603 : : * Mark assignment source expressions that have local target variables,
604 : : * that is, the target variable is declared within the exception block
605 : : * most closely containing the assignment itself. (Such target variables
606 : : * need not be preserved if the assignment's source expression raises an
607 : : * error, since the variable will no longer be accessible afterwards.
608 : : * Detecting this allows better optimization.)
609 : : *
610 : : * This code need not be called if the plpgsql function contains no exception
611 : : * blocks, because mark_expr_as_assignment_source will have set all the flags
612 : : * to true already. Also, we need not reconsider default-value expressions
613 : : * for variables, because variable declarations are necessarily within the
614 : : * nearest exception block. (In DECLARE ... BEGIN ... EXCEPTION ... END, the
615 : : * variable initializations are done before entering the exception scope.)
616 : : *
617 : : * Within the recursion, local_dnos is a Bitmapset of dnos of variables
618 : : * known to be declared within the current exception level.
619 : : **********************************************************************/
620 : : static void mark_stmt(PLpgSQL_stmt *stmt, Bitmapset *local_dnos);
621 : : static void mark_expr(PLpgSQL_expr *expr, Bitmapset *local_dnos);
622 : :
623 : : static void
624 : 1599 : mark_stmt(PLpgSQL_stmt *stmt, Bitmapset *local_dnos)
625 : : {
626 [ - + ]: 1599 : if (stmt == NULL)
627 : 0 : return;
628 [ + + ]: 1599 : if (stmt->cmd_type == PLPGSQL_STMT_BLOCK)
629 : : {
630 : 516 : PLpgSQL_stmt_block *block = (PLpgSQL_stmt_block *) stmt;
631 : :
632 [ + + ]: 516 : if (block->exceptions)
633 : : {
634 : : /*
635 : : * The block creates a new exception scope, so variables declared
636 : : * at outer levels are nonlocal. For that matter, so are any
637 : : * variables declared in the block's DECLARE section. Hence, we
638 : : * must pass down empty local_dnos.
639 : : */
640 : 286 : plpgsql_statement_tree_walker(stmt, mark_stmt, mark_expr, NULL);
641 : : }
642 : : else
643 : : {
644 : : /*
645 : : * Otherwise, the block does not create a new exception scope, and
646 : : * any variables it declares can also be considered local within
647 : : * it. Note that only initializable datum types (VAR, REC) are
648 : : * included in initvarnos; but that's sufficient for our purposes.
649 : : */
650 : 230 : local_dnos = bms_copy(local_dnos);
651 [ + + ]: 296 : for (int i = 0; i < block->n_initvars; i++)
652 : 66 : local_dnos = bms_add_member(local_dnos, block->initvarnos[i]);
653 : 230 : plpgsql_statement_tree_walker(stmt, mark_stmt, mark_expr,
654 : : local_dnos);
655 : 230 : bms_free(local_dnos);
656 : : }
657 : : }
658 : : else
659 : 1083 : plpgsql_statement_tree_walker(stmt, mark_stmt, mark_expr, local_dnos);
660 : : }
661 : :
662 : : static void
663 : 1128 : mark_expr(PLpgSQL_expr *expr, Bitmapset *local_dnos)
664 : : {
665 : : /*
666 : : * If this expression has an assignment target, check whether the target
667 : : * is local, and mark the expression accordingly.
668 : : */
669 [ + + + + ]: 1128 : if (expr && expr->target_param >= 0)
670 : 78 : expr->target_is_local = bms_is_member(expr->target_param, local_dnos);
671 : 1128 : }
672 : :
673 : : void
674 : 274 : plpgsql_mark_local_assignment_targets(PLpgSQL_function *func)
675 : : {
676 : : Bitmapset *local_dnos;
677 : :
678 : : /* Function parameters can be treated as local targets at outer level */
679 : 274 : local_dnos = NULL;
680 [ + + ]: 362 : for (int i = 0; i < func->fn_nargs; i++)
681 : 88 : local_dnos = bms_add_member(local_dnos, func->fn_argvarnos[i]);
682 : 274 : mark_stmt((PLpgSQL_stmt *) func->action, local_dnos);
683 : 274 : bms_free(local_dnos);
684 : 274 : }
685 : :
686 : :
687 : : /**********************************************************************
688 : : * Release memory when a PL/pgSQL function is no longer needed
689 : : *
690 : : * This code only needs to deal with cleaning up PLpgSQL_expr nodes,
691 : : * which may contain references to saved SPI Plans that must be freed.
692 : : * The function tree itself, along with subsidiary data, is freed in
693 : : * one swoop by freeing the function's permanent memory context.
694 : : **********************************************************************/
695 : : static void free_stmt(PLpgSQL_stmt *stmt, void *context);
696 : : static void free_expr(PLpgSQL_expr *expr, void *context);
697 : :
698 : : static void
699 : 6848 : free_stmt(PLpgSQL_stmt *stmt, void *context)
700 : : {
701 [ - + ]: 6848 : if (stmt == NULL)
702 : 0 : return;
703 : 6848 : plpgsql_statement_tree_walker(stmt, free_stmt, free_expr, NULL);
704 : : }
705 : :
706 : : static void
707 : 13981 : free_expr(PLpgSQL_expr *expr, void *context)
708 : : {
709 [ + + + + ]: 13981 : if (expr && expr->plan)
710 : : {
711 : 4188 : SPI_freeplan(expr->plan);
712 : 4188 : expr->plan = NULL;
713 : : }
714 : 13981 : }
715 : :
716 : : void
717 : 1313 : plpgsql_free_function_memory(PLpgSQL_function *func)
718 : : {
719 : : int i;
720 : :
721 : : /* Better not call this on an in-use function */
722 : : Assert(func->cfunc.use_count == 0);
723 : :
724 : : /* Release plans associated with variable declarations */
725 [ + + ]: 6181 : for (i = 0; i < func->ndatums; i++)
726 : : {
727 : 4868 : PLpgSQL_datum *d = func->datums[i];
728 : :
729 [ + + + + : 4868 : switch (d->dtype)
- ]
730 : : {
731 : 3576 : case PLPGSQL_DTYPE_VAR:
732 : : case PLPGSQL_DTYPE_PROMISE:
733 : : {
734 : 3576 : PLpgSQL_var *var = (PLpgSQL_var *) d;
735 : :
736 : 3576 : free_expr(var->default_val, NULL);
737 : 3576 : free_expr(var->cursor_explicit_expr, NULL);
738 : : }
739 : 3576 : break;
740 : 227 : case PLPGSQL_DTYPE_ROW:
741 : 227 : break;
742 : 499 : case PLPGSQL_DTYPE_REC:
743 : : {
744 : 499 : PLpgSQL_rec *rec = (PLpgSQL_rec *) d;
745 : :
746 : 499 : free_expr(rec->default_val, NULL);
747 : : }
748 : 499 : break;
749 : 566 : case PLPGSQL_DTYPE_RECFIELD:
750 : 566 : break;
751 : 0 : default:
752 [ # # ]: 0 : elog(ERROR, "unrecognized data type: %d", d->dtype);
753 : : }
754 : : }
755 : 1313 : func->ndatums = 0;
756 : :
757 : : /* Release plans in statement tree */
758 : 1313 : free_stmt((PLpgSQL_stmt *) func->action, NULL);
759 : 1313 : func->action = NULL;
760 : :
761 : : /*
762 : : * And finally, release all memory except the PLpgSQL_function struct
763 : : * itself (which has to be kept around because there may be multiple
764 : : * fn_extra pointers to it).
765 : : */
766 [ + - ]: 1313 : if (func->fn_cxt)
767 : 1313 : MemoryContextDelete(func->fn_cxt);
768 : 1313 : func->fn_cxt = NULL;
769 : 1313 : }
770 : :
771 : : /* Deletion callback used by funccache.c */
772 : : void
773 : 467 : plpgsql_delete_callback(CachedFunction *cfunc)
774 : : {
775 : 467 : plpgsql_free_function_memory((PLpgSQL_function *) cfunc);
776 : 467 : }
777 : :
778 : :
779 : : /**********************************************************************
780 : : * Debug functions for analyzing the compiled code
781 : : *
782 : : * Sadly, there doesn't seem to be any way to let plpgsql_statement_tree_walker
783 : : * bear some of the burden for this.
784 : : **********************************************************************/
785 : : static int dump_indent;
786 : :
787 : : static void dump_ind(void);
788 : : static void dump_stmt(PLpgSQL_stmt *stmt);
789 : : static void dump_block(PLpgSQL_stmt_block *block);
790 : : static void dump_assign(PLpgSQL_stmt_assign *stmt);
791 : : static void dump_if(PLpgSQL_stmt_if *stmt);
792 : : static void dump_case(PLpgSQL_stmt_case *stmt);
793 : : static void dump_loop(PLpgSQL_stmt_loop *stmt);
794 : : static void dump_while(PLpgSQL_stmt_while *stmt);
795 : : static void dump_fori(PLpgSQL_stmt_fori *stmt);
796 : : static void dump_fors(PLpgSQL_stmt_fors *stmt);
797 : : static void dump_forc(PLpgSQL_stmt_forc *stmt);
798 : : static void dump_foreach_a(PLpgSQL_stmt_foreach_a *stmt);
799 : : static void dump_exit(PLpgSQL_stmt_exit *stmt);
800 : : static void dump_return(PLpgSQL_stmt_return *stmt);
801 : : static void dump_return_next(PLpgSQL_stmt_return_next *stmt);
802 : : static void dump_return_query(PLpgSQL_stmt_return_query *stmt);
803 : : static void dump_raise(PLpgSQL_stmt_raise *stmt);
804 : : static void dump_assert(PLpgSQL_stmt_assert *stmt);
805 : : static void dump_execsql(PLpgSQL_stmt_execsql *stmt);
806 : : static void dump_dynexecute(PLpgSQL_stmt_dynexecute *stmt);
807 : : static void dump_dynfors(PLpgSQL_stmt_dynfors *stmt);
808 : : static void dump_getdiag(PLpgSQL_stmt_getdiag *stmt);
809 : : static void dump_open(PLpgSQL_stmt_open *stmt);
810 : : static void dump_fetch(PLpgSQL_stmt_fetch *stmt);
811 : : static void dump_cursor_direction(PLpgSQL_stmt_fetch *stmt);
812 : : static void dump_close(PLpgSQL_stmt_close *stmt);
813 : : static void dump_perform(PLpgSQL_stmt_perform *stmt);
814 : : static void dump_call(PLpgSQL_stmt_call *stmt);
815 : : static void dump_commit(PLpgSQL_stmt_commit *stmt);
816 : : static void dump_rollback(PLpgSQL_stmt_rollback *stmt);
817 : : static void dump_expr(PLpgSQL_expr *expr);
818 : :
819 : :
820 : : static void
821 : 0 : dump_ind(void)
822 : : {
823 : : int i;
824 : :
825 [ # # ]: 0 : for (i = 0; i < dump_indent; i++)
826 : 0 : printf(" ");
827 : 0 : }
828 : :
829 : : static void
830 : 0 : dump_stmt(PLpgSQL_stmt *stmt)
831 : : {
832 : 0 : printf("%3d:", stmt->lineno);
833 [ # # # # : 0 : switch (stmt->cmd_type)
# # # # #
# # # # #
# # # # #
# # # # #
# # # # ]
834 : : {
835 : 0 : case PLPGSQL_STMT_BLOCK:
836 : 0 : dump_block((PLpgSQL_stmt_block *) stmt);
837 : 0 : break;
838 : 0 : case PLPGSQL_STMT_ASSIGN:
839 : 0 : dump_assign((PLpgSQL_stmt_assign *) stmt);
840 : 0 : break;
841 : 0 : case PLPGSQL_STMT_IF:
842 : 0 : dump_if((PLpgSQL_stmt_if *) stmt);
843 : 0 : break;
844 : 0 : case PLPGSQL_STMT_CASE:
845 : 0 : dump_case((PLpgSQL_stmt_case *) stmt);
846 : 0 : break;
847 : 0 : case PLPGSQL_STMT_LOOP:
848 : 0 : dump_loop((PLpgSQL_stmt_loop *) stmt);
849 : 0 : break;
850 : 0 : case PLPGSQL_STMT_WHILE:
851 : 0 : dump_while((PLpgSQL_stmt_while *) stmt);
852 : 0 : break;
853 : 0 : case PLPGSQL_STMT_FORI:
854 : 0 : dump_fori((PLpgSQL_stmt_fori *) stmt);
855 : 0 : break;
856 : 0 : case PLPGSQL_STMT_FORS:
857 : 0 : dump_fors((PLpgSQL_stmt_fors *) stmt);
858 : 0 : break;
859 : 0 : case PLPGSQL_STMT_FORC:
860 : 0 : dump_forc((PLpgSQL_stmt_forc *) stmt);
861 : 0 : break;
862 : 0 : case PLPGSQL_STMT_FOREACH_A:
863 : 0 : dump_foreach_a((PLpgSQL_stmt_foreach_a *) stmt);
864 : 0 : break;
865 : 0 : case PLPGSQL_STMT_EXIT:
866 : 0 : dump_exit((PLpgSQL_stmt_exit *) stmt);
867 : 0 : break;
868 : 0 : case PLPGSQL_STMT_RETURN:
869 : 0 : dump_return((PLpgSQL_stmt_return *) stmt);
870 : 0 : break;
871 : 0 : case PLPGSQL_STMT_RETURN_NEXT:
872 : 0 : dump_return_next((PLpgSQL_stmt_return_next *) stmt);
873 : 0 : break;
874 : 0 : case PLPGSQL_STMT_RETURN_QUERY:
875 : 0 : dump_return_query((PLpgSQL_stmt_return_query *) stmt);
876 : 0 : break;
877 : 0 : case PLPGSQL_STMT_RAISE:
878 : 0 : dump_raise((PLpgSQL_stmt_raise *) stmt);
879 : 0 : break;
880 : 0 : case PLPGSQL_STMT_ASSERT:
881 : 0 : dump_assert((PLpgSQL_stmt_assert *) stmt);
882 : 0 : break;
883 : 0 : case PLPGSQL_STMT_EXECSQL:
884 : 0 : dump_execsql((PLpgSQL_stmt_execsql *) stmt);
885 : 0 : break;
886 : 0 : case PLPGSQL_STMT_DYNEXECUTE:
887 : 0 : dump_dynexecute((PLpgSQL_stmt_dynexecute *) stmt);
888 : 0 : break;
889 : 0 : case PLPGSQL_STMT_DYNFORS:
890 : 0 : dump_dynfors((PLpgSQL_stmt_dynfors *) stmt);
891 : 0 : break;
892 : 0 : case PLPGSQL_STMT_GETDIAG:
893 : 0 : dump_getdiag((PLpgSQL_stmt_getdiag *) stmt);
894 : 0 : break;
895 : 0 : case PLPGSQL_STMT_OPEN:
896 : 0 : dump_open((PLpgSQL_stmt_open *) stmt);
897 : 0 : break;
898 : 0 : case PLPGSQL_STMT_FETCH:
899 : 0 : dump_fetch((PLpgSQL_stmt_fetch *) stmt);
900 : 0 : break;
901 : 0 : case PLPGSQL_STMT_CLOSE:
902 : 0 : dump_close((PLpgSQL_stmt_close *) stmt);
903 : 0 : break;
904 : 0 : case PLPGSQL_STMT_PERFORM:
905 : 0 : dump_perform((PLpgSQL_stmt_perform *) stmt);
906 : 0 : break;
907 : 0 : case PLPGSQL_STMT_CALL:
908 : 0 : dump_call((PLpgSQL_stmt_call *) stmt);
909 : 0 : break;
910 : 0 : case PLPGSQL_STMT_COMMIT:
911 : 0 : dump_commit((PLpgSQL_stmt_commit *) stmt);
912 : 0 : break;
913 : 0 : case PLPGSQL_STMT_ROLLBACK:
914 : 0 : dump_rollback((PLpgSQL_stmt_rollback *) stmt);
915 : 0 : break;
916 : 0 : default:
917 [ # # ]: 0 : elog(ERROR, "unrecognized cmd_type: %d", stmt->cmd_type);
918 : : break;
919 : : }
920 : 0 : }
921 : :
922 : : static void
923 : 0 : dump_stmts(List *stmts)
924 : : {
925 : : ListCell *s;
926 : :
927 : 0 : dump_indent += 2;
928 [ # # # # : 0 : foreach(s, stmts)
# # ]
929 : 0 : dump_stmt((PLpgSQL_stmt *) lfirst(s));
930 : 0 : dump_indent -= 2;
931 : 0 : }
932 : :
933 : : static void
934 : 0 : dump_block(PLpgSQL_stmt_block *block)
935 : : {
936 : : char *name;
937 : :
938 [ # # ]: 0 : if (block->label == NULL)
939 : 0 : name = "*unnamed*";
940 : : else
941 : 0 : name = block->label;
942 : :
943 : 0 : dump_ind();
944 : 0 : printf("BLOCK <<%s>>\n", name);
945 : :
946 : 0 : dump_stmts(block->body);
947 : :
948 [ # # ]: 0 : if (block->exceptions)
949 : : {
950 : : ListCell *e;
951 : :
952 [ # # # # : 0 : foreach(e, block->exceptions->exc_list)
# # ]
953 : : {
954 : 0 : PLpgSQL_exception *exc = (PLpgSQL_exception *) lfirst(e);
955 : : PLpgSQL_condition *cond;
956 : :
957 : 0 : dump_ind();
958 : 0 : printf(" EXCEPTION WHEN ");
959 [ # # ]: 0 : for (cond = exc->conditions; cond; cond = cond->next)
960 : : {
961 [ # # ]: 0 : if (cond != exc->conditions)
962 : 0 : printf(" OR ");
963 : 0 : printf("%s", cond->condname);
964 : : }
965 : 0 : printf(" THEN\n");
966 : 0 : dump_stmts(exc->action);
967 : : }
968 : : }
969 : :
970 : 0 : dump_ind();
971 : 0 : printf(" END -- %s\n", name);
972 : 0 : }
973 : :
974 : : static void
975 : 0 : dump_assign(PLpgSQL_stmt_assign *stmt)
976 : : {
977 : 0 : dump_ind();
978 : 0 : printf("ASSIGN var %d := ", stmt->varno);
979 : 0 : dump_expr(stmt->expr);
980 : 0 : printf("\n");
981 : 0 : }
982 : :
983 : : static void
984 : 0 : dump_if(PLpgSQL_stmt_if *stmt)
985 : : {
986 : : ListCell *l;
987 : :
988 : 0 : dump_ind();
989 : 0 : printf("IF ");
990 : 0 : dump_expr(stmt->cond);
991 : 0 : printf(" THEN\n");
992 : 0 : dump_stmts(stmt->then_body);
993 [ # # # # : 0 : foreach(l, stmt->elsif_list)
# # ]
994 : : {
995 : 0 : PLpgSQL_if_elsif *elif = (PLpgSQL_if_elsif *) lfirst(l);
996 : :
997 : 0 : dump_ind();
998 : 0 : printf(" ELSIF ");
999 : 0 : dump_expr(elif->cond);
1000 : 0 : printf(" THEN\n");
1001 : 0 : dump_stmts(elif->stmts);
1002 : : }
1003 [ # # ]: 0 : if (stmt->else_body != NIL)
1004 : : {
1005 : 0 : dump_ind();
1006 : 0 : printf(" ELSE\n");
1007 : 0 : dump_stmts(stmt->else_body);
1008 : : }
1009 : 0 : dump_ind();
1010 : 0 : printf(" ENDIF\n");
1011 : 0 : }
1012 : :
1013 : : static void
1014 : 0 : dump_case(PLpgSQL_stmt_case *stmt)
1015 : : {
1016 : : ListCell *l;
1017 : :
1018 : 0 : dump_ind();
1019 : 0 : printf("CASE %d ", stmt->t_varno);
1020 [ # # ]: 0 : if (stmt->t_expr)
1021 : 0 : dump_expr(stmt->t_expr);
1022 : 0 : printf("\n");
1023 : 0 : dump_indent += 6;
1024 [ # # # # : 0 : foreach(l, stmt->case_when_list)
# # ]
1025 : : {
1026 : 0 : PLpgSQL_case_when *cwt = (PLpgSQL_case_when *) lfirst(l);
1027 : :
1028 : 0 : dump_ind();
1029 : 0 : printf("WHEN ");
1030 : 0 : dump_expr(cwt->expr);
1031 : 0 : printf("\n");
1032 : 0 : dump_ind();
1033 : 0 : printf("THEN\n");
1034 : 0 : dump_indent += 2;
1035 : 0 : dump_stmts(cwt->stmts);
1036 : 0 : dump_indent -= 2;
1037 : : }
1038 [ # # ]: 0 : if (stmt->have_else)
1039 : : {
1040 : 0 : dump_ind();
1041 : 0 : printf("ELSE\n");
1042 : 0 : dump_indent += 2;
1043 : 0 : dump_stmts(stmt->else_stmts);
1044 : 0 : dump_indent -= 2;
1045 : : }
1046 : 0 : dump_indent -= 6;
1047 : 0 : dump_ind();
1048 : 0 : printf(" ENDCASE\n");
1049 : 0 : }
1050 : :
1051 : : static void
1052 : 0 : dump_loop(PLpgSQL_stmt_loop *stmt)
1053 : : {
1054 : 0 : dump_ind();
1055 : 0 : printf("LOOP\n");
1056 : :
1057 : 0 : dump_stmts(stmt->body);
1058 : :
1059 : 0 : dump_ind();
1060 : 0 : printf(" ENDLOOP\n");
1061 : 0 : }
1062 : :
1063 : : static void
1064 : 0 : dump_while(PLpgSQL_stmt_while *stmt)
1065 : : {
1066 : 0 : dump_ind();
1067 : 0 : printf("WHILE ");
1068 : 0 : dump_expr(stmt->cond);
1069 : 0 : printf("\n");
1070 : :
1071 : 0 : dump_stmts(stmt->body);
1072 : :
1073 : 0 : dump_ind();
1074 : 0 : printf(" ENDWHILE\n");
1075 : 0 : }
1076 : :
1077 : : static void
1078 : 0 : dump_fori(PLpgSQL_stmt_fori *stmt)
1079 : : {
1080 : 0 : dump_ind();
1081 [ # # ]: 0 : printf("FORI %s %s\n", stmt->var->refname, (stmt->reverse) ? "REVERSE" : "NORMAL");
1082 : :
1083 : 0 : dump_indent += 2;
1084 : 0 : dump_ind();
1085 : 0 : printf(" lower = ");
1086 : 0 : dump_expr(stmt->lower);
1087 : 0 : printf("\n");
1088 : 0 : dump_ind();
1089 : 0 : printf(" upper = ");
1090 : 0 : dump_expr(stmt->upper);
1091 : 0 : printf("\n");
1092 [ # # ]: 0 : if (stmt->step)
1093 : : {
1094 : 0 : dump_ind();
1095 : 0 : printf(" step = ");
1096 : 0 : dump_expr(stmt->step);
1097 : 0 : printf("\n");
1098 : : }
1099 : 0 : dump_indent -= 2;
1100 : :
1101 : 0 : dump_stmts(stmt->body);
1102 : :
1103 : 0 : dump_ind();
1104 : 0 : printf(" ENDFORI\n");
1105 : 0 : }
1106 : :
1107 : : static void
1108 : 0 : dump_fors(PLpgSQL_stmt_fors *stmt)
1109 : : {
1110 : 0 : dump_ind();
1111 : 0 : printf("FORS %s ", stmt->var->refname);
1112 : 0 : dump_expr(stmt->query);
1113 : 0 : printf("\n");
1114 : :
1115 : 0 : dump_stmts(stmt->body);
1116 : :
1117 : 0 : dump_ind();
1118 : 0 : printf(" ENDFORS\n");
1119 : 0 : }
1120 : :
1121 : : static void
1122 : 0 : dump_forc(PLpgSQL_stmt_forc *stmt)
1123 : : {
1124 : 0 : dump_ind();
1125 : 0 : printf("FORC %s ", stmt->var->refname);
1126 : 0 : printf("curvar=%d\n", stmt->curvar);
1127 : :
1128 : 0 : dump_indent += 2;
1129 [ # # ]: 0 : if (stmt->argquery != NULL)
1130 : : {
1131 : 0 : dump_ind();
1132 : 0 : printf(" arguments = ");
1133 : 0 : dump_expr(stmt->argquery);
1134 : 0 : printf("\n");
1135 : : }
1136 : 0 : dump_indent -= 2;
1137 : :
1138 : 0 : dump_stmts(stmt->body);
1139 : :
1140 : 0 : dump_ind();
1141 : 0 : printf(" ENDFORC\n");
1142 : 0 : }
1143 : :
1144 : : static void
1145 : 0 : dump_foreach_a(PLpgSQL_stmt_foreach_a *stmt)
1146 : : {
1147 : 0 : dump_ind();
1148 : 0 : printf("FOREACHA var %d ", stmt->varno);
1149 [ # # ]: 0 : if (stmt->slice != 0)
1150 : 0 : printf("SLICE %d ", stmt->slice);
1151 : 0 : printf("IN ");
1152 : 0 : dump_expr(stmt->expr);
1153 : 0 : printf("\n");
1154 : :
1155 : 0 : dump_stmts(stmt->body);
1156 : :
1157 : 0 : dump_ind();
1158 : 0 : printf(" ENDFOREACHA");
1159 : 0 : }
1160 : :
1161 : : static void
1162 : 0 : dump_open(PLpgSQL_stmt_open *stmt)
1163 : : {
1164 : 0 : dump_ind();
1165 : 0 : printf("OPEN curvar=%d\n", stmt->curvar);
1166 : :
1167 : 0 : dump_indent += 2;
1168 [ # # ]: 0 : if (stmt->argquery != NULL)
1169 : : {
1170 : 0 : dump_ind();
1171 : 0 : printf(" arguments = '");
1172 : 0 : dump_expr(stmt->argquery);
1173 : 0 : printf("'\n");
1174 : : }
1175 [ # # ]: 0 : if (stmt->query != NULL)
1176 : : {
1177 : 0 : dump_ind();
1178 : 0 : printf(" query = '");
1179 : 0 : dump_expr(stmt->query);
1180 : 0 : printf("'\n");
1181 : : }
1182 [ # # ]: 0 : if (stmt->dynquery != NULL)
1183 : : {
1184 : 0 : dump_ind();
1185 : 0 : printf(" execute = '");
1186 : 0 : dump_expr(stmt->dynquery);
1187 : 0 : printf("'\n");
1188 : :
1189 [ # # ]: 0 : if (stmt->params != NIL)
1190 : : {
1191 : : ListCell *lc;
1192 : : int i;
1193 : :
1194 : 0 : dump_indent += 2;
1195 : 0 : dump_ind();
1196 : 0 : printf(" USING\n");
1197 : 0 : dump_indent += 2;
1198 : 0 : i = 1;
1199 [ # # # # : 0 : foreach(lc, stmt->params)
# # ]
1200 : : {
1201 : 0 : dump_ind();
1202 : 0 : printf(" parameter $%d: ", i++);
1203 : 0 : dump_expr((PLpgSQL_expr *) lfirst(lc));
1204 : 0 : printf("\n");
1205 : : }
1206 : 0 : dump_indent -= 4;
1207 : : }
1208 : : }
1209 : 0 : dump_indent -= 2;
1210 : 0 : }
1211 : :
1212 : : static void
1213 : 0 : dump_fetch(PLpgSQL_stmt_fetch *stmt)
1214 : : {
1215 : 0 : dump_ind();
1216 : :
1217 [ # # ]: 0 : if (!stmt->is_move)
1218 : : {
1219 : 0 : printf("FETCH curvar=%d\n", stmt->curvar);
1220 : 0 : dump_cursor_direction(stmt);
1221 : :
1222 : 0 : dump_indent += 2;
1223 [ # # ]: 0 : if (stmt->target != NULL)
1224 : : {
1225 : 0 : dump_ind();
1226 : 0 : printf(" target = %d %s\n",
1227 : : stmt->target->dno, stmt->target->refname);
1228 : : }
1229 : 0 : dump_indent -= 2;
1230 : : }
1231 : : else
1232 : : {
1233 : 0 : printf("MOVE curvar=%d\n", stmt->curvar);
1234 : 0 : dump_cursor_direction(stmt);
1235 : : }
1236 : 0 : }
1237 : :
1238 : : static void
1239 : 0 : dump_cursor_direction(PLpgSQL_stmt_fetch *stmt)
1240 : : {
1241 : 0 : dump_indent += 2;
1242 : 0 : dump_ind();
1243 [ # # # # : 0 : switch (stmt->direction)
# ]
1244 : : {
1245 : 0 : case FETCH_FORWARD:
1246 : 0 : printf(" FORWARD ");
1247 : 0 : break;
1248 : 0 : case FETCH_BACKWARD:
1249 : 0 : printf(" BACKWARD ");
1250 : 0 : break;
1251 : 0 : case FETCH_ABSOLUTE:
1252 : 0 : printf(" ABSOLUTE ");
1253 : 0 : break;
1254 : 0 : case FETCH_RELATIVE:
1255 : 0 : printf(" RELATIVE ");
1256 : 0 : break;
1257 : 0 : default:
1258 : 0 : printf("??? unknown cursor direction %d", stmt->direction);
1259 : : }
1260 : :
1261 [ # # ]: 0 : if (stmt->expr)
1262 : : {
1263 : 0 : dump_expr(stmt->expr);
1264 : 0 : printf("\n");
1265 : : }
1266 : : else
1267 : 0 : printf("%ld\n", stmt->how_many);
1268 : :
1269 : 0 : dump_indent -= 2;
1270 : 0 : }
1271 : :
1272 : : static void
1273 : 0 : dump_close(PLpgSQL_stmt_close *stmt)
1274 : : {
1275 : 0 : dump_ind();
1276 : 0 : printf("CLOSE curvar=%d\n", stmt->curvar);
1277 : 0 : }
1278 : :
1279 : : static void
1280 : 0 : dump_perform(PLpgSQL_stmt_perform *stmt)
1281 : : {
1282 : 0 : dump_ind();
1283 : 0 : printf("PERFORM expr = ");
1284 : 0 : dump_expr(stmt->expr);
1285 : 0 : printf("\n");
1286 : 0 : }
1287 : :
1288 : : static void
1289 : 0 : dump_call(PLpgSQL_stmt_call *stmt)
1290 : : {
1291 : 0 : dump_ind();
1292 [ # # ]: 0 : printf("%s expr = ", stmt->is_call ? "CALL" : "DO");
1293 : 0 : dump_expr(stmt->expr);
1294 : 0 : printf("\n");
1295 : 0 : }
1296 : :
1297 : : static void
1298 : 0 : dump_commit(PLpgSQL_stmt_commit *stmt)
1299 : : {
1300 : 0 : dump_ind();
1301 [ # # ]: 0 : if (stmt->chain)
1302 : 0 : printf("COMMIT AND CHAIN\n");
1303 : : else
1304 : 0 : printf("COMMIT\n");
1305 : 0 : }
1306 : :
1307 : : static void
1308 : 0 : dump_rollback(PLpgSQL_stmt_rollback *stmt)
1309 : : {
1310 : 0 : dump_ind();
1311 [ # # ]: 0 : if (stmt->chain)
1312 : 0 : printf("ROLLBACK AND CHAIN\n");
1313 : : else
1314 : 0 : printf("ROLLBACK\n");
1315 : 0 : }
1316 : :
1317 : : static void
1318 : 0 : dump_exit(PLpgSQL_stmt_exit *stmt)
1319 : : {
1320 : 0 : dump_ind();
1321 [ # # ]: 0 : printf("%s", stmt->is_exit ? "EXIT" : "CONTINUE");
1322 [ # # ]: 0 : if (stmt->label != NULL)
1323 : 0 : printf(" label='%s'", stmt->label);
1324 [ # # ]: 0 : if (stmt->cond != NULL)
1325 : : {
1326 : 0 : printf(" WHEN ");
1327 : 0 : dump_expr(stmt->cond);
1328 : : }
1329 : 0 : printf("\n");
1330 : 0 : }
1331 : :
1332 : : static void
1333 : 0 : dump_return(PLpgSQL_stmt_return *stmt)
1334 : : {
1335 : 0 : dump_ind();
1336 : 0 : printf("RETURN ");
1337 [ # # ]: 0 : if (stmt->retvarno >= 0)
1338 : 0 : printf("variable %d", stmt->retvarno);
1339 [ # # ]: 0 : else if (stmt->expr != NULL)
1340 : 0 : dump_expr(stmt->expr);
1341 : : else
1342 : 0 : printf("NULL");
1343 : 0 : printf("\n");
1344 : 0 : }
1345 : :
1346 : : static void
1347 : 0 : dump_return_next(PLpgSQL_stmt_return_next *stmt)
1348 : : {
1349 : 0 : dump_ind();
1350 : 0 : printf("RETURN NEXT ");
1351 [ # # ]: 0 : if (stmt->retvarno >= 0)
1352 : 0 : printf("variable %d", stmt->retvarno);
1353 [ # # ]: 0 : else if (stmt->expr != NULL)
1354 : 0 : dump_expr(stmt->expr);
1355 : : else
1356 : 0 : printf("NULL");
1357 : 0 : printf("\n");
1358 : 0 : }
1359 : :
1360 : : static void
1361 : 0 : dump_return_query(PLpgSQL_stmt_return_query *stmt)
1362 : : {
1363 : 0 : dump_ind();
1364 [ # # ]: 0 : if (stmt->query)
1365 : : {
1366 : 0 : printf("RETURN QUERY ");
1367 : 0 : dump_expr(stmt->query);
1368 : 0 : printf("\n");
1369 : : }
1370 : : else
1371 : : {
1372 : 0 : printf("RETURN QUERY EXECUTE ");
1373 : 0 : dump_expr(stmt->dynquery);
1374 : 0 : printf("\n");
1375 [ # # ]: 0 : if (stmt->params != NIL)
1376 : : {
1377 : : ListCell *lc;
1378 : : int i;
1379 : :
1380 : 0 : dump_indent += 2;
1381 : 0 : dump_ind();
1382 : 0 : printf(" USING\n");
1383 : 0 : dump_indent += 2;
1384 : 0 : i = 1;
1385 [ # # # # : 0 : foreach(lc, stmt->params)
# # ]
1386 : : {
1387 : 0 : dump_ind();
1388 : 0 : printf(" parameter $%d: ", i++);
1389 : 0 : dump_expr((PLpgSQL_expr *) lfirst(lc));
1390 : 0 : printf("\n");
1391 : : }
1392 : 0 : dump_indent -= 4;
1393 : : }
1394 : : }
1395 : 0 : }
1396 : :
1397 : : static void
1398 : 0 : dump_raise(PLpgSQL_stmt_raise *stmt)
1399 : : {
1400 : : ListCell *lc;
1401 : 0 : int i = 0;
1402 : :
1403 : 0 : dump_ind();
1404 : 0 : printf("RAISE level=%d", stmt->elog_level);
1405 [ # # ]: 0 : if (stmt->condname)
1406 : 0 : printf(" condname='%s'", stmt->condname);
1407 [ # # ]: 0 : if (stmt->message)
1408 : 0 : printf(" message='%s'", stmt->message);
1409 : 0 : printf("\n");
1410 : 0 : dump_indent += 2;
1411 [ # # # # : 0 : foreach(lc, stmt->params)
# # ]
1412 : : {
1413 : 0 : dump_ind();
1414 : 0 : printf(" parameter %d: ", i++);
1415 : 0 : dump_expr((PLpgSQL_expr *) lfirst(lc));
1416 : 0 : printf("\n");
1417 : : }
1418 [ # # ]: 0 : if (stmt->options)
1419 : : {
1420 : 0 : dump_ind();
1421 : 0 : printf(" USING\n");
1422 : 0 : dump_indent += 2;
1423 [ # # # # : 0 : foreach(lc, stmt->options)
# # ]
1424 : : {
1425 : 0 : PLpgSQL_raise_option *opt = (PLpgSQL_raise_option *) lfirst(lc);
1426 : :
1427 : 0 : dump_ind();
1428 [ # # # # : 0 : switch (opt->opt_type)
# # # # #
# ]
1429 : : {
1430 : 0 : case PLPGSQL_RAISEOPTION_ERRCODE:
1431 : 0 : printf(" ERRCODE = ");
1432 : 0 : break;
1433 : 0 : case PLPGSQL_RAISEOPTION_MESSAGE:
1434 : 0 : printf(" MESSAGE = ");
1435 : 0 : break;
1436 : 0 : case PLPGSQL_RAISEOPTION_DETAIL:
1437 : 0 : printf(" DETAIL = ");
1438 : 0 : break;
1439 : 0 : case PLPGSQL_RAISEOPTION_HINT:
1440 : 0 : printf(" HINT = ");
1441 : 0 : break;
1442 : 0 : case PLPGSQL_RAISEOPTION_COLUMN:
1443 : 0 : printf(" COLUMN = ");
1444 : 0 : break;
1445 : 0 : case PLPGSQL_RAISEOPTION_CONSTRAINT:
1446 : 0 : printf(" CONSTRAINT = ");
1447 : 0 : break;
1448 : 0 : case PLPGSQL_RAISEOPTION_DATATYPE:
1449 : 0 : printf(" DATATYPE = ");
1450 : 0 : break;
1451 : 0 : case PLPGSQL_RAISEOPTION_TABLE:
1452 : 0 : printf(" TABLE = ");
1453 : 0 : break;
1454 : 0 : case PLPGSQL_RAISEOPTION_SCHEMA:
1455 : 0 : printf(" SCHEMA = ");
1456 : 0 : break;
1457 : : }
1458 : 0 : dump_expr(opt->expr);
1459 : 0 : printf("\n");
1460 : : }
1461 : 0 : dump_indent -= 2;
1462 : : }
1463 : 0 : dump_indent -= 2;
1464 : 0 : }
1465 : :
1466 : : static void
1467 : 0 : dump_assert(PLpgSQL_stmt_assert *stmt)
1468 : : {
1469 : 0 : dump_ind();
1470 : 0 : printf("ASSERT ");
1471 : 0 : dump_expr(stmt->cond);
1472 : 0 : printf("\n");
1473 : :
1474 : 0 : dump_indent += 2;
1475 [ # # ]: 0 : if (stmt->message != NULL)
1476 : : {
1477 : 0 : dump_ind();
1478 : 0 : printf(" MESSAGE = ");
1479 : 0 : dump_expr(stmt->message);
1480 : 0 : printf("\n");
1481 : : }
1482 : 0 : dump_indent -= 2;
1483 : 0 : }
1484 : :
1485 : : static void
1486 : 0 : dump_execsql(PLpgSQL_stmt_execsql *stmt)
1487 : : {
1488 : 0 : dump_ind();
1489 : 0 : printf("EXECSQL ");
1490 : 0 : dump_expr(stmt->sqlstmt);
1491 : 0 : printf("\n");
1492 : :
1493 : 0 : dump_indent += 2;
1494 [ # # ]: 0 : if (stmt->target != NULL)
1495 : : {
1496 : 0 : dump_ind();
1497 [ # # ]: 0 : printf(" INTO%s target = %d %s\n",
1498 : : stmt->strict ? " STRICT" : "",
1499 : : stmt->target->dno, stmt->target->refname);
1500 : : }
1501 : 0 : dump_indent -= 2;
1502 : 0 : }
1503 : :
1504 : : static void
1505 : 0 : dump_dynexecute(PLpgSQL_stmt_dynexecute *stmt)
1506 : : {
1507 : 0 : dump_ind();
1508 : 0 : printf("EXECUTE ");
1509 : 0 : dump_expr(stmt->query);
1510 : 0 : printf("\n");
1511 : :
1512 : 0 : dump_indent += 2;
1513 [ # # ]: 0 : if (stmt->target != NULL)
1514 : : {
1515 : 0 : dump_ind();
1516 [ # # ]: 0 : printf(" INTO%s target = %d %s\n",
1517 : : stmt->strict ? " STRICT" : "",
1518 : : stmt->target->dno, stmt->target->refname);
1519 : : }
1520 [ # # ]: 0 : if (stmt->params != NIL)
1521 : : {
1522 : : ListCell *lc;
1523 : : int i;
1524 : :
1525 : 0 : dump_ind();
1526 : 0 : printf(" USING\n");
1527 : 0 : dump_indent += 2;
1528 : 0 : i = 1;
1529 [ # # # # : 0 : foreach(lc, stmt->params)
# # ]
1530 : : {
1531 : 0 : dump_ind();
1532 : 0 : printf(" parameter %d: ", i++);
1533 : 0 : dump_expr((PLpgSQL_expr *) lfirst(lc));
1534 : 0 : printf("\n");
1535 : : }
1536 : 0 : dump_indent -= 2;
1537 : : }
1538 : 0 : dump_indent -= 2;
1539 : 0 : }
1540 : :
1541 : : static void
1542 : 0 : dump_dynfors(PLpgSQL_stmt_dynfors *stmt)
1543 : : {
1544 : 0 : dump_ind();
1545 : 0 : printf("FORS %s EXECUTE ", stmt->var->refname);
1546 : 0 : dump_expr(stmt->query);
1547 : 0 : printf("\n");
1548 [ # # ]: 0 : if (stmt->params != NIL)
1549 : : {
1550 : : ListCell *lc;
1551 : : int i;
1552 : :
1553 : 0 : dump_indent += 2;
1554 : 0 : dump_ind();
1555 : 0 : printf(" USING\n");
1556 : 0 : dump_indent += 2;
1557 : 0 : i = 1;
1558 [ # # # # : 0 : foreach(lc, stmt->params)
# # ]
1559 : : {
1560 : 0 : dump_ind();
1561 : 0 : printf(" parameter $%d: ", i++);
1562 : 0 : dump_expr((PLpgSQL_expr *) lfirst(lc));
1563 : 0 : printf("\n");
1564 : : }
1565 : 0 : dump_indent -= 4;
1566 : : }
1567 : 0 : dump_stmts(stmt->body);
1568 : 0 : dump_ind();
1569 : 0 : printf(" ENDFORS\n");
1570 : 0 : }
1571 : :
1572 : : static void
1573 : 0 : dump_getdiag(PLpgSQL_stmt_getdiag *stmt)
1574 : : {
1575 : : ListCell *lc;
1576 : :
1577 : 0 : dump_ind();
1578 [ # # ]: 0 : printf("GET %s DIAGNOSTICS ", stmt->is_stacked ? "STACKED" : "CURRENT");
1579 [ # # # # : 0 : foreach(lc, stmt->diag_items)
# # ]
1580 : : {
1581 : 0 : PLpgSQL_diag_item *diag_item = (PLpgSQL_diag_item *) lfirst(lc);
1582 : :
1583 [ # # ]: 0 : if (lc != list_head(stmt->diag_items))
1584 : 0 : printf(", ");
1585 : :
1586 : 0 : printf("{var %d} = %s", diag_item->target,
1587 : : plpgsql_getdiag_kindname(diag_item->kind));
1588 : : }
1589 : 0 : printf("\n");
1590 : 0 : }
1591 : :
1592 : : static void
1593 : 0 : dump_expr(PLpgSQL_expr *expr)
1594 : : {
1595 : 0 : printf("'%s'", expr->query);
1596 [ # # ]: 0 : if (expr->target_param >= 0)
1597 [ # # ]: 0 : printf(" target %d%s", expr->target_param,
1598 : : expr->target_is_local ? " (local)" : "");
1599 : 0 : }
1600 : :
1601 : : void
1602 : 0 : plpgsql_dumptree(PLpgSQL_function *func)
1603 : : {
1604 : : int i;
1605 : : PLpgSQL_datum *d;
1606 : :
1607 : 0 : printf("\nExecution tree of successfully compiled PL/pgSQL function %s:\n",
1608 : : func->fn_signature);
1609 : :
1610 : 0 : printf("\nFunction's data area:\n");
1611 [ # # ]: 0 : for (i = 0; i < func->ndatums; i++)
1612 : : {
1613 : 0 : d = func->datums[i];
1614 : :
1615 : 0 : printf(" entry %d: ", i);
1616 [ # # # # : 0 : switch (d->dtype)
# ]
1617 : : {
1618 : 0 : case PLPGSQL_DTYPE_VAR:
1619 : : case PLPGSQL_DTYPE_PROMISE:
1620 : : {
1621 : 0 : PLpgSQL_var *var = (PLpgSQL_var *) d;
1622 : :
1623 : 0 : printf("VAR %-16s type %s (typoid %u) atttypmod %d\n",
1624 : : var->refname, var->datatype->typname,
1625 : : var->datatype->typoid,
1626 : : var->datatype->atttypmod);
1627 [ # # ]: 0 : if (var->isconst)
1628 : 0 : printf(" CONSTANT\n");
1629 [ # # ]: 0 : if (var->notnull)
1630 : 0 : printf(" NOT NULL\n");
1631 [ # # ]: 0 : if (var->default_val != NULL)
1632 : : {
1633 : 0 : printf(" DEFAULT ");
1634 : 0 : dump_expr(var->default_val);
1635 : 0 : printf("\n");
1636 : : }
1637 [ # # ]: 0 : if (var->cursor_explicit_expr != NULL)
1638 : : {
1639 [ # # ]: 0 : if (var->cursor_explicit_argrow >= 0)
1640 : 0 : printf(" CURSOR argument row %d\n", var->cursor_explicit_argrow);
1641 : :
1642 : 0 : printf(" CURSOR IS ");
1643 : 0 : dump_expr(var->cursor_explicit_expr);
1644 : 0 : printf("\n");
1645 : : }
1646 [ # # ]: 0 : if (var->promise != PLPGSQL_PROMISE_NONE)
1647 : 0 : printf(" PROMISE %d\n",
1648 : : (int) var->promise);
1649 : : }
1650 : 0 : break;
1651 : 0 : case PLPGSQL_DTYPE_ROW:
1652 : : {
1653 : 0 : PLpgSQL_row *row = (PLpgSQL_row *) d;
1654 : :
1655 : 0 : printf("ROW %-16s fields", row->refname);
1656 [ # # ]: 0 : for (int j = 0; j < row->nfields; j++)
1657 : : {
1658 : 0 : printf(" %s=var %d", row->fieldnames[j],
1659 : : row->varnos[j]);
1660 : : }
1661 : 0 : printf("\n");
1662 : : }
1663 : 0 : break;
1664 : 0 : case PLPGSQL_DTYPE_REC:
1665 : 0 : printf("REC %-16s typoid %u\n",
1666 : : ((PLpgSQL_rec *) d)->refname,
1667 : : ((PLpgSQL_rec *) d)->rectypeid);
1668 [ # # ]: 0 : if (((PLpgSQL_rec *) d)->isconst)
1669 : 0 : printf(" CONSTANT\n");
1670 [ # # ]: 0 : if (((PLpgSQL_rec *) d)->notnull)
1671 : 0 : printf(" NOT NULL\n");
1672 [ # # ]: 0 : if (((PLpgSQL_rec *) d)->default_val != NULL)
1673 : : {
1674 : 0 : printf(" DEFAULT ");
1675 : 0 : dump_expr(((PLpgSQL_rec *) d)->default_val);
1676 : 0 : printf("\n");
1677 : : }
1678 : 0 : break;
1679 : 0 : case PLPGSQL_DTYPE_RECFIELD:
1680 : 0 : printf("RECFIELD %-16s of REC %d\n",
1681 : : ((PLpgSQL_recfield *) d)->fieldname,
1682 : : ((PLpgSQL_recfield *) d)->recparentno);
1683 : 0 : break;
1684 : 0 : default:
1685 : 0 : printf("??? unknown data type %d\n", d->dtype);
1686 : : }
1687 : : }
1688 : 0 : printf("\nFunction's statements:\n");
1689 : :
1690 : 0 : dump_indent = 0;
1691 : 0 : printf("%3d:", func->action->lineno);
1692 : 0 : dump_block(func->action);
1693 : 0 : printf("\nEnd of execution tree of function %s\n\n", func->fn_signature);
1694 : 0 : fflush(stdout);
1695 : 0 : }
|