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