1 | /*------------------------------------------------------------------------- |
2 | * |
3 | * print.c |
4 | * various print routines (used mostly for debugging) |
5 | * |
6 | * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group |
7 | * Portions Copyright (c) 1994, Regents of the University of California |
8 | * |
9 | * |
10 | * IDENTIFICATION |
11 | * src/backend/nodes/print.c |
12 | * |
13 | * HISTORY |
14 | * AUTHOR DATE MAJOR EVENT |
15 | * Andrew Yu Oct 26, 1994 file creation |
16 | * |
17 | *------------------------------------------------------------------------- |
18 | */ |
19 | |
20 | #include "postgres.h" |
21 | |
22 | #include "access/printtup.h" |
23 | #include "lib/stringinfo.h" |
24 | #include "nodes/nodeFuncs.h" |
25 | #include "nodes/pathnodes.h" |
26 | #include "nodes/print.h" |
27 | #include "parser/parsetree.h" |
28 | #include "utils/lsyscache.h" |
29 | |
30 | |
31 | /* |
32 | * print |
33 | * print contents of Node to stdout |
34 | */ |
35 | void |
36 | print(const void *obj) |
37 | { |
38 | char *s; |
39 | char *f; |
40 | |
41 | s = nodeToString(obj); |
42 | f = format_node_dump(s); |
43 | pfree(s); |
44 | printf("%s\n" , f); |
45 | fflush(stdout); |
46 | pfree(f); |
47 | } |
48 | |
49 | /* |
50 | * pprint |
51 | * pretty-print contents of Node to stdout |
52 | */ |
53 | void |
54 | pprint(const void *obj) |
55 | { |
56 | char *s; |
57 | char *f; |
58 | |
59 | s = nodeToString(obj); |
60 | f = pretty_format_node_dump(s); |
61 | pfree(s); |
62 | printf("%s\n" , f); |
63 | fflush(stdout); |
64 | pfree(f); |
65 | } |
66 | |
67 | /* |
68 | * elog_node_display |
69 | * send pretty-printed contents of Node to postmaster log |
70 | */ |
71 | void |
72 | elog_node_display(int lev, const char *title, const void *obj, bool pretty) |
73 | { |
74 | char *s; |
75 | char *f; |
76 | |
77 | s = nodeToString(obj); |
78 | if (pretty) |
79 | f = pretty_format_node_dump(s); |
80 | else |
81 | f = format_node_dump(s); |
82 | pfree(s); |
83 | ereport(lev, |
84 | (errmsg_internal("%s:" , title), |
85 | errdetail_internal("%s" , f))); |
86 | pfree(f); |
87 | } |
88 | |
89 | /* |
90 | * Format a nodeToString output for display on a terminal. |
91 | * |
92 | * The result is a palloc'd string. |
93 | * |
94 | * This version just tries to break at whitespace. |
95 | */ |
96 | char * |
97 | format_node_dump(const char *dump) |
98 | { |
99 | #define LINELEN 78 |
100 | char line[LINELEN + 1]; |
101 | StringInfoData str; |
102 | int i; |
103 | int j; |
104 | int k; |
105 | |
106 | initStringInfo(&str); |
107 | i = 0; |
108 | for (;;) |
109 | { |
110 | for (j = 0; j < LINELEN && dump[i] != '\0'; i++, j++) |
111 | line[j] = dump[i]; |
112 | if (dump[i] == '\0') |
113 | break; |
114 | if (dump[i] == ' ') |
115 | { |
116 | /* ok to break at adjacent space */ |
117 | i++; |
118 | } |
119 | else |
120 | { |
121 | for (k = j - 1; k > 0; k--) |
122 | if (line[k] == ' ') |
123 | break; |
124 | if (k > 0) |
125 | { |
126 | /* back up; will reprint all after space */ |
127 | i -= (j - k - 1); |
128 | j = k; |
129 | } |
130 | } |
131 | line[j] = '\0'; |
132 | appendStringInfo(&str, "%s\n" , line); |
133 | } |
134 | if (j > 0) |
135 | { |
136 | line[j] = '\0'; |
137 | appendStringInfo(&str, "%s\n" , line); |
138 | } |
139 | return str.data; |
140 | #undef LINELEN |
141 | } |
142 | |
143 | /* |
144 | * Format a nodeToString output for display on a terminal. |
145 | * |
146 | * The result is a palloc'd string. |
147 | * |
148 | * This version tries to indent intelligently. |
149 | */ |
150 | char * |
151 | pretty_format_node_dump(const char *dump) |
152 | { |
153 | #define INDENTSTOP 3 |
154 | #define MAXINDENT 60 |
155 | #define LINELEN 78 |
156 | char line[LINELEN + 1]; |
157 | StringInfoData str; |
158 | int indentLev; |
159 | int indentDist; |
160 | int i; |
161 | int j; |
162 | |
163 | initStringInfo(&str); |
164 | indentLev = 0; /* logical indent level */ |
165 | indentDist = 0; /* physical indent distance */ |
166 | i = 0; |
167 | for (;;) |
168 | { |
169 | for (j = 0; j < indentDist; j++) |
170 | line[j] = ' '; |
171 | for (; j < LINELEN && dump[i] != '\0'; i++, j++) |
172 | { |
173 | line[j] = dump[i]; |
174 | switch (line[j]) |
175 | { |
176 | case '}': |
177 | if (j != indentDist) |
178 | { |
179 | /* print data before the } */ |
180 | line[j] = '\0'; |
181 | appendStringInfo(&str, "%s\n" , line); |
182 | } |
183 | /* print the } at indentDist */ |
184 | line[indentDist] = '}'; |
185 | line[indentDist + 1] = '\0'; |
186 | appendStringInfo(&str, "%s\n" , line); |
187 | /* outdent */ |
188 | if (indentLev > 0) |
189 | { |
190 | indentLev--; |
191 | indentDist = Min(indentLev * INDENTSTOP, MAXINDENT); |
192 | } |
193 | j = indentDist - 1; |
194 | /* j will equal indentDist on next loop iteration */ |
195 | /* suppress whitespace just after } */ |
196 | while (dump[i + 1] == ' ') |
197 | i++; |
198 | break; |
199 | case ')': |
200 | /* force line break after ), unless another ) follows */ |
201 | if (dump[i + 1] != ')') |
202 | { |
203 | line[j + 1] = '\0'; |
204 | appendStringInfo(&str, "%s\n" , line); |
205 | j = indentDist - 1; |
206 | while (dump[i + 1] == ' ') |
207 | i++; |
208 | } |
209 | break; |
210 | case '{': |
211 | /* force line break before { */ |
212 | if (j != indentDist) |
213 | { |
214 | line[j] = '\0'; |
215 | appendStringInfo(&str, "%s\n" , line); |
216 | } |
217 | /* indent */ |
218 | indentLev++; |
219 | indentDist = Min(indentLev * INDENTSTOP, MAXINDENT); |
220 | for (j = 0; j < indentDist; j++) |
221 | line[j] = ' '; |
222 | line[j] = dump[i]; |
223 | break; |
224 | case ':': |
225 | /* force line break before : */ |
226 | if (j != indentDist) |
227 | { |
228 | line[j] = '\0'; |
229 | appendStringInfo(&str, "%s\n" , line); |
230 | } |
231 | j = indentDist; |
232 | line[j] = dump[i]; |
233 | break; |
234 | } |
235 | } |
236 | line[j] = '\0'; |
237 | if (dump[i] == '\0') |
238 | break; |
239 | appendStringInfo(&str, "%s\n" , line); |
240 | } |
241 | if (j > 0) |
242 | appendStringInfo(&str, "%s\n" , line); |
243 | return str.data; |
244 | #undef INDENTSTOP |
245 | #undef MAXINDENT |
246 | #undef LINELEN |
247 | } |
248 | |
249 | /* |
250 | * print_rt |
251 | * print contents of range table |
252 | */ |
253 | void |
254 | print_rt(const List *rtable) |
255 | { |
256 | const ListCell *l; |
257 | int i = 1; |
258 | |
259 | printf("resno\trefname \trelid\tinFromCl\n" ); |
260 | printf("-----\t---------\t-----\t--------\n" ); |
261 | foreach(l, rtable) |
262 | { |
263 | RangeTblEntry *rte = lfirst(l); |
264 | |
265 | switch (rte->rtekind) |
266 | { |
267 | case RTE_RELATION: |
268 | printf("%d\t%s\t%u\t%c" , |
269 | i, rte->eref->aliasname, rte->relid, rte->relkind); |
270 | break; |
271 | case RTE_SUBQUERY: |
272 | printf("%d\t%s\t[subquery]" , |
273 | i, rte->eref->aliasname); |
274 | break; |
275 | case RTE_JOIN: |
276 | printf("%d\t%s\t[join]" , |
277 | i, rte->eref->aliasname); |
278 | break; |
279 | case RTE_FUNCTION: |
280 | printf("%d\t%s\t[rangefunction]" , |
281 | i, rte->eref->aliasname); |
282 | break; |
283 | case RTE_TABLEFUNC: |
284 | printf("%d\t%s\t[table function]" , |
285 | i, rte->eref->aliasname); |
286 | break; |
287 | case RTE_VALUES: |
288 | printf("%d\t%s\t[values list]" , |
289 | i, rte->eref->aliasname); |
290 | break; |
291 | case RTE_CTE: |
292 | printf("%d\t%s\t[cte]" , |
293 | i, rte->eref->aliasname); |
294 | break; |
295 | case RTE_NAMEDTUPLESTORE: |
296 | printf("%d\t%s\t[tuplestore]" , |
297 | i, rte->eref->aliasname); |
298 | break; |
299 | case RTE_RESULT: |
300 | printf("%d\t%s\t[result]" , |
301 | i, rte->eref->aliasname); |
302 | break; |
303 | default: |
304 | printf("%d\t%s\t[unknown rtekind]" , |
305 | i, rte->eref->aliasname); |
306 | } |
307 | |
308 | printf("\t%s\t%s\n" , |
309 | (rte->inh ? "inh" : "" ), |
310 | (rte->inFromCl ? "inFromCl" : "" )); |
311 | i++; |
312 | } |
313 | } |
314 | |
315 | |
316 | /* |
317 | * print_expr |
318 | * print an expression |
319 | */ |
320 | void |
321 | print_expr(const Node *expr, const List *rtable) |
322 | { |
323 | if (expr == NULL) |
324 | { |
325 | printf("<>" ); |
326 | return; |
327 | } |
328 | |
329 | if (IsA(expr, Var)) |
330 | { |
331 | const Var *var = (const Var *) expr; |
332 | char *relname, |
333 | *attname; |
334 | |
335 | switch (var->varno) |
336 | { |
337 | case INNER_VAR: |
338 | relname = "INNER" ; |
339 | attname = "?" ; |
340 | break; |
341 | case OUTER_VAR: |
342 | relname = "OUTER" ; |
343 | attname = "?" ; |
344 | break; |
345 | case INDEX_VAR: |
346 | relname = "INDEX" ; |
347 | attname = "?" ; |
348 | break; |
349 | default: |
350 | { |
351 | RangeTblEntry *rte; |
352 | |
353 | Assert(var->varno > 0 && |
354 | (int) var->varno <= list_length(rtable)); |
355 | rte = rt_fetch(var->varno, rtable); |
356 | relname = rte->eref->aliasname; |
357 | attname = get_rte_attribute_name(rte, var->varattno); |
358 | } |
359 | break; |
360 | } |
361 | printf("%s.%s" , relname, attname); |
362 | } |
363 | else if (IsA(expr, Const)) |
364 | { |
365 | const Const *c = (const Const *) expr; |
366 | Oid typoutput; |
367 | bool typIsVarlena; |
368 | char *outputstr; |
369 | |
370 | if (c->constisnull) |
371 | { |
372 | printf("NULL" ); |
373 | return; |
374 | } |
375 | |
376 | getTypeOutputInfo(c->consttype, |
377 | &typoutput, &typIsVarlena); |
378 | |
379 | outputstr = OidOutputFunctionCall(typoutput, c->constvalue); |
380 | printf("%s" , outputstr); |
381 | pfree(outputstr); |
382 | } |
383 | else if (IsA(expr, OpExpr)) |
384 | { |
385 | const OpExpr *e = (const OpExpr *) expr; |
386 | char *opname; |
387 | |
388 | opname = get_opname(e->opno); |
389 | if (list_length(e->args) > 1) |
390 | { |
391 | print_expr(get_leftop((const Expr *) e), rtable); |
392 | printf(" %s " , ((opname != NULL) ? opname : "(invalid operator)" )); |
393 | print_expr(get_rightop((const Expr *) e), rtable); |
394 | } |
395 | else |
396 | { |
397 | /* we print prefix and postfix ops the same... */ |
398 | printf("%s " , ((opname != NULL) ? opname : "(invalid operator)" )); |
399 | print_expr(get_leftop((const Expr *) e), rtable); |
400 | } |
401 | } |
402 | else if (IsA(expr, FuncExpr)) |
403 | { |
404 | const FuncExpr *e = (const FuncExpr *) expr; |
405 | char *funcname; |
406 | ListCell *l; |
407 | |
408 | funcname = get_func_name(e->funcid); |
409 | printf("%s(" , ((funcname != NULL) ? funcname : "(invalid function)" )); |
410 | foreach(l, e->args) |
411 | { |
412 | print_expr(lfirst(l), rtable); |
413 | if (lnext(l)) |
414 | printf("," ); |
415 | } |
416 | printf(")" ); |
417 | } |
418 | else |
419 | printf("unknown expr" ); |
420 | } |
421 | |
422 | /* |
423 | * print_pathkeys - |
424 | * pathkeys list of PathKeys |
425 | */ |
426 | void |
427 | print_pathkeys(const List *pathkeys, const List *rtable) |
428 | { |
429 | const ListCell *i; |
430 | |
431 | printf("(" ); |
432 | foreach(i, pathkeys) |
433 | { |
434 | PathKey *pathkey = (PathKey *) lfirst(i); |
435 | EquivalenceClass *eclass; |
436 | ListCell *k; |
437 | bool first = true; |
438 | |
439 | eclass = pathkey->pk_eclass; |
440 | /* chase up, in case pathkey is non-canonical */ |
441 | while (eclass->ec_merged) |
442 | eclass = eclass->ec_merged; |
443 | |
444 | printf("(" ); |
445 | foreach(k, eclass->ec_members) |
446 | { |
447 | EquivalenceMember *mem = (EquivalenceMember *) lfirst(k); |
448 | |
449 | if (first) |
450 | first = false; |
451 | else |
452 | printf(", " ); |
453 | print_expr((Node *) mem->em_expr, rtable); |
454 | } |
455 | printf(")" ); |
456 | if (lnext(i)) |
457 | printf(", " ); |
458 | } |
459 | printf(")\n" ); |
460 | } |
461 | |
462 | /* |
463 | * print_tl |
464 | * print targetlist in a more legible way. |
465 | */ |
466 | void |
467 | print_tl(const List *tlist, const List *rtable) |
468 | { |
469 | const ListCell *tl; |
470 | |
471 | printf("(\n" ); |
472 | foreach(tl, tlist) |
473 | { |
474 | TargetEntry *tle = (TargetEntry *) lfirst(tl); |
475 | |
476 | printf("\t%d %s\t" , tle->resno, |
477 | tle->resname ? tle->resname : "<null>" ); |
478 | if (tle->ressortgroupref != 0) |
479 | printf("(%u):\t" , tle->ressortgroupref); |
480 | else |
481 | printf(" :\t" ); |
482 | print_expr((Node *) tle->expr, rtable); |
483 | printf("\n" ); |
484 | } |
485 | printf(")\n" ); |
486 | } |
487 | |
488 | /* |
489 | * print_slot |
490 | * print out the tuple with the given TupleTableSlot |
491 | */ |
492 | void |
493 | print_slot(TupleTableSlot *slot) |
494 | { |
495 | if (TupIsNull(slot)) |
496 | { |
497 | printf("tuple is null.\n" ); |
498 | return; |
499 | } |
500 | if (!slot->tts_tupleDescriptor) |
501 | { |
502 | printf("no tuple descriptor.\n" ); |
503 | return; |
504 | } |
505 | |
506 | debugtup(slot, NULL); |
507 | } |
508 | |