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