]> granicus.if.org Git - postgresql/blob - src/backend/nodes/print.c
Make some small planner API cleanups.
[postgresql] / src / backend / nodes / print.c
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/print.h"
26 #include "nodes/relation.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 }