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