PostgreSQL Source Code git master
print.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * print.c
4 * various print routines (used mostly for debugging)
5 *
6 * Portions Copyright (c) 1996-2025, 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 */
35void
36print(const void *obj)
37{
38 char *s;
39 char *f;
40
42 f = format_node_dump(s);
43 pfree(s);
44 printf("%s\n", f);
46 pfree(f);
47}
48
49/*
50 * pprint
51 * pretty-print contents of Node to stdout
52 */
53void
54pprint(const void *obj)
55{
56 char *s;
57 char *f;
58
61 pfree(s);
62 printf("%s\n", f);
64 pfree(f);
65}
66
67/*
68 * elog_node_display
69 * send pretty-printed contents of Node to postmaster log
70 */
71void
72elog_node_display(int lev, const char *title, const void *obj, bool pretty)
73{
74 char *s;
75 char *f;
76
78 if (pretty)
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 */
96char *
97format_node_dump(const char *dump)
98{
99#define LINELEN 78
100 char line[LINELEN + 1];
102 int i;
103 int j;
104 int k;
105
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 */
150char *
151pretty_format_node_dump(const char *dump)
152{
153#define INDENTSTOP 3
154#define MAXINDENT 60
155#define LINELEN 78
156 char line[LINELEN + 1];
158 int indentLev;
159 int indentDist;
160 int i;
161 int j;
162
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 */
253void
254print_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;
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 case RTE_GROUP:
304 printf("%d\t%s\t[group]",
305 i, rte->eref->aliasname);
306 break;
307 default:
308 printf("%d\t%s\t[unknown rtekind]",
309 i, rte->eref->aliasname);
310 }
311
312 printf("\t%s\t%s\n",
313 (rte->inh ? "inh" : ""),
314 (rte->inFromCl ? "inFromCl" : ""));
315 i++;
316 }
317}
318
319
320/*
321 * print_expr
322 * print an expression
323 */
324void
325print_expr(const Node *expr, const List *rtable)
326{
327 if (expr == NULL)
328 {
329 printf("<>");
330 return;
331 }
332
333 if (IsA(expr, Var))
334 {
335 const Var *var = (const Var *) expr;
336 char *relname,
337 *attname;
338
339 switch (var->varno)
340 {
341 case INNER_VAR:
342 relname = "INNER";
343 attname = "?";
344 break;
345 case OUTER_VAR:
346 relname = "OUTER";
347 attname = "?";
348 break;
349 case INDEX_VAR:
350 relname = "INDEX";
351 attname = "?";
352 break;
353 default:
354 {
355 RangeTblEntry *rte;
356
357 Assert(var->varno > 0 &&
358 (int) var->varno <= list_length(rtable));
359 rte = rt_fetch(var->varno, rtable);
360 relname = rte->eref->aliasname;
362 }
363 break;
364 }
365 printf("%s.%s", relname, attname);
366 }
367 else if (IsA(expr, Const))
368 {
369 const Const *c = (const Const *) expr;
370 Oid typoutput;
371 bool typIsVarlena;
372 char *outputstr;
373
374 if (c->constisnull)
375 {
376 printf("NULL");
377 return;
378 }
379
380 getTypeOutputInfo(c->consttype,
381 &typoutput, &typIsVarlena);
382
383 outputstr = OidOutputFunctionCall(typoutput, c->constvalue);
384 printf("%s", outputstr);
385 pfree(outputstr);
386 }
387 else if (IsA(expr, OpExpr))
388 {
389 const OpExpr *e = (const OpExpr *) expr;
390 char *opname;
391
392 opname = get_opname(e->opno);
393 if (list_length(e->args) > 1)
394 {
395 print_expr(get_leftop((const Expr *) e), rtable);
396 printf(" %s ", ((opname != NULL) ? opname : "(invalid operator)"));
397 print_expr(get_rightop((const Expr *) e), rtable);
398 }
399 else
400 {
401 printf("%s ", ((opname != NULL) ? opname : "(invalid operator)"));
402 print_expr(get_leftop((const Expr *) e), rtable);
403 }
404 }
405 else if (IsA(expr, FuncExpr))
406 {
407 const FuncExpr *e = (const FuncExpr *) expr;
408 char *funcname;
409 ListCell *l;
410
411 funcname = get_func_name(e->funcid);
412 printf("%s(", ((funcname != NULL) ? funcname : "(invalid function)"));
413 foreach(l, e->args)
414 {
415 print_expr(lfirst(l), rtable);
416 if (lnext(e->args, l))
417 printf(",");
418 }
419 printf(")");
420 }
421 else
422 printf("unknown expr");
423}
424
425/*
426 * print_pathkeys -
427 * pathkeys list of PathKeys
428 */
429void
430print_pathkeys(const List *pathkeys, const List *rtable)
431{
432 const ListCell *i;
433
434 printf("(");
435 foreach(i, pathkeys)
436 {
437 PathKey *pathkey = (PathKey *) lfirst(i);
439 ListCell *k;
440 bool first = true;
441
442 eclass = pathkey->pk_eclass;
443 /* chase up, in case pathkey is non-canonical */
444 while (eclass->ec_merged)
445 eclass = eclass->ec_merged;
446
447 printf("(");
448 foreach(k, eclass->ec_members)
449 {
451
452 if (first)
453 first = false;
454 else
455 printf(", ");
456 print_expr((Node *) mem->em_expr, rtable);
457 }
458 printf(")");
459 if (lnext(pathkeys, i))
460 printf(", ");
461 }
462 printf(")\n");
463}
464
465/*
466 * print_tl
467 * print targetlist in a more legible way.
468 */
469void
470print_tl(const List *tlist, const List *rtable)
471{
472 const ListCell *tl;
473
474 printf("(\n");
475 foreach(tl, tlist)
476 {
477 TargetEntry *tle = (TargetEntry *) lfirst(tl);
478
479 printf("\t%d %s\t", tle->resno,
480 tle->resname ? tle->resname : "<null>");
481 if (tle->ressortgroupref != 0)
482 printf("(%u):\t", tle->ressortgroupref);
483 else
484 printf(" :\t");
485 print_expr((Node *) tle->expr, rtable);
486 printf("\n");
487 }
488 printf(")\n");
489}
490
491/*
492 * print_slot
493 * print out the tuple with the given TupleTableSlot
494 */
495void
497{
498 if (TupIsNull(slot))
499 {
500 printf("tuple is null.\n");
501 return;
502 }
503 if (!slot->tts_tupleDescriptor)
504 {
505 printf("no tuple descriptor.\n");
506 return;
507 }
508
509 debugtup(slot, NULL);
510}
#define MAXINDENT
void pprint(const void *obj)
Definition: print.c:54
char * pretty_format_node_dump(const char *dump)
Definition: print.c:151
void print_pathkeys(const List *pathkeys, const List *rtable)
Definition: print.c:430
void print_rt(const List *rtable)
Definition: print.c:254
void print_slot(TupleTableSlot *slot)
Definition: print.c:496
void print(const void *obj)
Definition: print.c:36
#define INDENTSTOP
#define LINELEN
void elog_node_display(int lev, const char *title, const void *obj, bool pretty)
Definition: print.c:72
void print_tl(const List *tlist, const List *rtable)
Definition: print.c:470
void print_expr(const Node *expr, const List *rtable)
Definition: print.c:325
char * format_node_dump(const char *dump)
Definition: print.c:97
#define Min(x, y)
Definition: c.h:961
#define Assert(condition)
Definition: c.h:815
int errmsg_internal(const char *fmt,...)
Definition: elog.c:1157
int errdetail_internal(const char *fmt,...)
Definition: elog.c:1230
#define ereport(elevel,...)
Definition: elog.h:149
char * OidOutputFunctionCall(Oid functionId, Datum val)
Definition: fmgr.c:1763
const char * str
#define funcname
Definition: indent_codes.h:69
int j
Definition: isn.c:73
int i
Definition: isn.c:72
static void const char fflush(stdout)
void getTypeOutputInfo(Oid type, Oid *typOutput, bool *typIsVarlena)
Definition: lsyscache.c:2934
char * get_opname(Oid opno)
Definition: lsyscache.c:1337
char * get_func_name(Oid funcid)
Definition: lsyscache.c:1635
void pfree(void *pointer)
Definition: mcxt.c:1521
static Node * get_rightop(const void *clause)
Definition: nodeFuncs.h:95
static Node * get_leftop(const void *clause)
Definition: nodeFuncs.h:83
#define IsA(nodeptr, _type_)
Definition: nodes.h:158
char * nodeToStringWithLocations(const void *obj)
Definition: outfuncs.c:800
char * get_rte_attribute_name(RangeTblEntry *rte, AttrNumber attnum)
@ RTE_JOIN
Definition: parsenodes.h:1028
@ RTE_CTE
Definition: parsenodes.h:1032
@ RTE_NAMEDTUPLESTORE
Definition: parsenodes.h:1033
@ RTE_VALUES
Definition: parsenodes.h:1031
@ RTE_SUBQUERY
Definition: parsenodes.h:1027
@ RTE_RESULT
Definition: parsenodes.h:1034
@ RTE_FUNCTION
Definition: parsenodes.h:1029
@ RTE_TABLEFUNC
Definition: parsenodes.h:1030
@ RTE_GROUP
Definition: parsenodes.h:1037
@ RTE_RELATION
Definition: parsenodes.h:1026
#define rt_fetch(rangetable_index, rangetable)
Definition: parsetree.h:31
NameData attname
Definition: pg_attribute.h:41
NameData relname
Definition: pg_class.h:38
#define lfirst(lc)
Definition: pg_list.h:172
static int list_length(const List *l)
Definition: pg_list.h:152
static ListCell * lnext(const List *l, const ListCell *c)
Definition: pg_list.h:343
#define printf(...)
Definition: port.h:245
unsigned int Oid
Definition: postgres_ext.h:32
char * c
e
Definition: preproc-init.c:82
#define OUTER_VAR
Definition: primnodes.h:243
#define INNER_VAR
Definition: primnodes.h:242
#define INDEX_VAR
Definition: primnodes.h:244
bool debugtup(TupleTableSlot *slot, DestReceiver *self)
Definition: printtup.c:462
static struct cvec * eclass(struct vars *v, chr c, int cases)
Definition: regc_locale.c:500
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition: stringinfo.c:145
void initStringInfo(StringInfo str)
Definition: stringinfo.c:97
Definition: pg_list.h:54
Definition: nodes.h:129
RTEKind rtekind
Definition: parsenodes.h:1056
Expr * expr
Definition: primnodes.h:2219
AttrNumber resno
Definition: primnodes.h:2221
Index ressortgroupref
Definition: primnodes.h:2225
TupleDesc tts_tupleDescriptor
Definition: tuptable.h:123
Definition: primnodes.h:262
AttrNumber varattno
Definition: primnodes.h:274
int varno
Definition: primnodes.h:269
#define TupIsNull(slot)
Definition: tuptable.h:306