]> granicus.if.org Git - postgresql/blob - src/backend/nodes/print.c
Infrastructure for I/O of composite types: arrange for the I/O routines
[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-2003, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *        $PostgreSQL: pgsql/src/backend/nodes/print.c,v 1.69 2004/06/06 00:41:26 tgl Exp $
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 #include "utils/syscache.h"
29
30 static char *plannode_type(Plan *p);
31
32 /*
33  * print
34  *        print contents of Node to stdout
35  */
36 void
37 print(void *obj)
38 {
39         char       *s;
40         char       *f;
41
42         s = nodeToString(obj);
43         f = format_node_dump(s);
44         pfree(s);
45         printf("%s\n", f);
46         fflush(stdout);
47         pfree(f);
48 }
49
50 /*
51  * pprint
52  *        pretty-print contents of Node to stdout
53  */
54 void
55 pprint(void *obj)
56 {
57         char       *s;
58         char       *f;
59
60         s = nodeToString(obj);
61         f = pretty_format_node_dump(s);
62         pfree(s);
63         printf("%s\n", f);
64         fflush(stdout);
65         pfree(f);
66 }
67
68 /*
69  * elog_node_display
70  *        send pretty-printed contents of Node to postmaster log
71  */
72 void
73 elog_node_display(int lev, const char *title, void *obj, bool pretty)
74 {
75         char       *s;
76         char       *f;
77
78         s = nodeToString(obj);
79         if (pretty)
80                 f = pretty_format_node_dump(s);
81         else
82                 f = format_node_dump(s);
83         pfree(s);
84         ereport(lev,
85                         (errmsg_internal("%s:", title),
86                          errdetail("%s", f)));
87         pfree(f);
88 }
89
90 /*
91  * Format a nodeToString output for display on a terminal.
92  *
93  * The result is a palloc'd string.
94  *
95  * This version just tries to break at whitespace.
96  */
97 char *
98 format_node_dump(const char *dump)
99 {
100 #define LINELEN         78
101         char            line[LINELEN + 1];
102         StringInfoData str;
103         int                     i;
104         int                     j;
105         int                     k;
106
107         initStringInfo(&str);
108         i = 0;
109         for (;;)
110         {
111                 for (j = 0; j < LINELEN && dump[i] != '\0'; i++, j++)
112                         line[j] = dump[i];
113                 if (dump[i] == '\0')
114                         break;
115                 if (dump[i] == ' ')
116                 {
117                         /* ok to break at adjacent space */
118                         i++;
119                 }
120                 else
121                 {
122                         for (k = j - 1; k > 0; k--)
123                                 if (line[k] == ' ')
124                                         break;
125                         if (k > 0)
126                         {
127                                 /* back up; will reprint all after space */
128                                 i -= (j - k - 1);
129                                 j = k;
130                         }
131                 }
132                 line[j] = '\0';
133                 appendStringInfo(&str, "%s\n", line);
134         }
135         if (j > 0)
136         {
137                 line[j] = '\0';
138                 appendStringInfo(&str, "%s\n", line);
139         }
140         return str.data;
141 #undef LINELEN
142 }
143
144 /*
145  * Format a nodeToString output for display on a terminal.
146  *
147  * The result is a palloc'd string.
148  *
149  * This version tries to indent intelligently.
150  */
151 char *
152 pretty_format_node_dump(const char *dump)
153 {
154 #define INDENTSTOP      3
155 #define MAXINDENT       60
156 #define LINELEN         78
157         char            line[LINELEN + 1];
158         StringInfoData str;
159         int                     indentLev;
160         int                     indentDist;
161         int                     i;
162         int                     j;
163
164         initStringInfo(&str);
165         indentLev = 0;                          /* logical indent level */
166         indentDist = 0;                         /* physical indent distance */
167         i = 0;
168         for (;;)
169         {
170                 for (j = 0; j < indentDist; j++)
171                         line[j] = ' ';
172                 for (; j < LINELEN && dump[i] != '\0'; i++, j++)
173                 {
174                         line[j] = dump[i];
175                         switch (line[j])
176                         {
177                                 case '}':
178                                         if (j != indentDist)
179                                         {
180                                                 /* print data before the } */
181                                                 line[j] = '\0';
182                                                 appendStringInfo(&str, "%s\n", line);
183                                         }
184                                         /* print the } at indentDist */
185                                         line[indentDist] = '}';
186                                         line[indentDist + 1] = '\0';
187                                         appendStringInfo(&str, "%s\n", line);
188                                         /* outdent */
189                                         if (indentLev > 0)
190                                         {
191                                                 indentLev--;
192                                                 indentDist = Min(indentLev * INDENTSTOP, MAXINDENT);
193                                         }
194                                         j = indentDist - 1;
195                                         /* j will equal indentDist on next loop iteration */
196                                         /* suppress whitespace just after } */
197                                         while (dump[i+1] == ' ')
198                                                 i++;
199                                         break;
200                                 case ')':
201                                         /* force line break after ), unless another ) follows */
202                                         if (dump[i+1] != ')')
203                                         {
204                                                 line[j + 1] = '\0';
205                                                 appendStringInfo(&str, "%s\n", line);
206                                                 j = indentDist - 1;
207                                                 while (dump[i+1] == ' ')
208                                                         i++;
209                                         }
210                                         break;
211                                 case '{':
212                                         /* force line break before { */
213                                         if (j != indentDist)
214                                         {
215                                                 line[j] = '\0';
216                                                 appendStringInfo(&str, "%s\n", line);
217                                         }
218                                         /* indent */
219                                         indentLev++;
220                                         indentDist = Min(indentLev * INDENTSTOP, MAXINDENT);
221                                         for (j = 0; j < indentDist; j++)
222                                                 line[j] = ' ';
223                                         line[j] = dump[i];
224                                         break;
225                                 case ':':
226                                         /* force line break before : */
227                                         if (j != indentDist)
228                                         {
229                                                 line[j] = '\0';
230                                                 appendStringInfo(&str, "%s\n", line);
231                                         }
232                                         j = indentDist;
233                                         line[j] = dump[i];
234                                         break;
235                         }
236                 }
237                 line[j] = '\0';
238                 if (dump[i] == '\0')
239                         break;
240                 appendStringInfo(&str, "%s\n", line);
241         }
242         if (j > 0)
243                 appendStringInfo(&str, "%s\n", line);
244         return str.data;
245 #undef INDENTSTOP
246 #undef MAXINDENT
247 #undef LINELEN
248 }
249
250 /*
251  * print_rt
252  *        print contents of range table
253  */
254 void
255 print_rt(List *rtable)
256 {
257         ListCell   *l;
258         int                     i = 1;
259
260         printf("resno\trefname  \trelid\tinFromCl\n");
261         printf("-----\t---------\t-----\t--------\n");
262         foreach(l, rtable)
263         {
264                 RangeTblEntry *rte = lfirst(l);
265
266                 switch (rte->rtekind)
267                 {
268                         case RTE_RELATION:
269                                 printf("%d\t%s\t%u",
270                                            i, rte->eref->aliasname, rte->relid);
271                                 break;
272                         case RTE_SUBQUERY:
273                                 printf("%d\t%s\t[subquery]",
274                                            i, rte->eref->aliasname);
275                                 break;
276                         case RTE_FUNCTION:
277                                 printf("%d\t%s\t[rangefunction]",
278                                            i, rte->eref->aliasname);
279                                 break;
280                         case RTE_JOIN:
281                                 printf("%d\t%s\t[join]",
282                                            i, rte->eref->aliasname);
283                                 break;
284                         case RTE_SPECIAL:
285                                 printf("%d\t%s\t[special]",
286                                            i, rte->eref->aliasname);
287                                 break;
288                         default:
289                                 printf("%d\t%s\t[unknown rtekind]",
290                                            i, rte->eref->aliasname);
291                 }
292
293                 printf("\t%s\t%s\n",
294                            (rte->inh ? "inh" : ""),
295                            (rte->inFromCl ? "inFromCl" : ""));
296                 i++;
297         }
298 }
299
300
301 /*
302  * print_expr
303  *        print an expression
304  */
305 void
306 print_expr(Node *expr, List *rtable)
307 {
308         if (expr == NULL)
309         {
310                 printf("<>");
311                 return;
312         }
313
314         if (IsA(expr, Var))
315         {
316                 Var                *var = (Var *) expr;
317                 char       *relname,
318                                    *attname;
319
320                 switch (var->varno)
321                 {
322                         case INNER:
323                                 relname = "INNER";
324                                 attname = "?";
325                                 break;
326                         case OUTER:
327                                 relname = "OUTER";
328                                 attname = "?";
329                                 break;
330                         default:
331                                 {
332                                         RangeTblEntry *rte;
333
334                                         Assert(var->varno > 0 &&
335                                                    (int) var->varno <= list_length(rtable));
336                                         rte = rt_fetch(var->varno, rtable);
337                                         relname = rte->eref->aliasname;
338                                         attname = get_rte_attribute_name(rte, var->varattno);
339                                 }
340                                 break;
341                 }
342                 printf("%s.%s", relname, attname);
343         }
344         else if (IsA(expr, Const))
345         {
346                 Const      *c = (Const *) expr;
347                 Oid                     typoutput;
348                 Oid                     typioparam;
349                 bool            typIsVarlena;
350                 char       *outputstr;
351
352                 if (c->constisnull)
353                 {
354                         printf("NULL");
355                         return;
356                 }
357
358                 getTypeOutputInfo(c->consttype,
359                                                   &typoutput, &typioparam, &typIsVarlena);
360
361                 outputstr = DatumGetCString(OidFunctionCall3(typoutput,
362                                                                                                          c->constvalue,
363                                                                                            ObjectIdGetDatum(typioparam),
364                                                                                                          Int32GetDatum(-1)));
365                 printf("%s", outputstr);
366                 pfree(outputstr);
367         }
368         else if (IsA(expr, OpExpr))
369         {
370                 OpExpr     *e = (OpExpr *) expr;
371                 char       *opname;
372
373                 opname = get_opname(e->opno);
374                 if (list_length(e->args) > 1)
375                 {
376                         print_expr(get_leftop((Expr *) e), rtable);
377                         printf(" %s ", ((opname != NULL) ? opname : "(invalid operator)"));
378                         print_expr(get_rightop((Expr *) e), rtable);
379                 }
380                 else
381                 {
382                         /* we print prefix and postfix ops the same... */
383                         printf("%s ", ((opname != NULL) ? opname : "(invalid operator)"));
384                         print_expr(get_leftop((Expr *) e), rtable);
385                 }
386         }
387         else if (IsA(expr, FuncExpr))
388         {
389                 FuncExpr   *e = (FuncExpr *) expr;
390                 char       *funcname;
391                 ListCell   *l;
392
393                 funcname = get_func_name(e->funcid);
394                 printf("%s(", ((funcname != NULL) ? funcname : "(invalid function)"));
395                 foreach(l, e->args)
396                 {
397                         print_expr(lfirst(l), rtable);
398                         if (lnext(l))
399                                 printf(",");
400                 }
401                 printf(")");
402         }
403         else
404                 printf("unknown expr");
405 }
406
407 /*
408  * print_pathkeys -
409  *        pathkeys list of list of PathKeyItems
410  */
411 void
412 print_pathkeys(List *pathkeys, List *rtable)
413 {
414         ListCell   *i;
415
416         printf("(");
417         foreach(i, pathkeys)
418         {
419                 List       *pathkey = (List *) lfirst(i);
420                 ListCell   *k;
421
422                 printf("(");
423                 foreach(k, pathkey)
424                 {
425                         PathKeyItem *item = (PathKeyItem *) lfirst(k);
426
427                         print_expr(item->key, rtable);
428                         if (lnext(k))
429                                 printf(", ");
430                 }
431                 printf(")");
432                 if (lnext(i))
433                         printf(", ");
434         }
435         printf(")\n");
436 }
437
438 /*
439  * print_tl
440  *        print targetlist in a more legible way.
441  */
442 void
443 print_tl(List *tlist, List *rtable)
444 {
445         ListCell   *tl;
446
447         printf("(\n");
448         foreach(tl, tlist)
449         {
450                 TargetEntry *tle = (TargetEntry *) lfirst(tl);
451
452                 printf("\t%d %s\t", tle->resdom->resno,
453                            tle->resdom->resname ? tle->resdom->resname : "<null>");
454                 if (tle->resdom->ressortgroupref != 0)
455                         printf("(%u):\t", tle->resdom->ressortgroupref);
456                 else
457                         printf("    :\t");
458                 print_expr((Node *) tle->expr, rtable);
459                 printf("\n");
460         }
461         printf(")\n");
462 }
463
464 /*
465  * print_slot
466  *        print out the tuple with the given TupleTableSlot
467  */
468 void
469 print_slot(TupleTableSlot *slot)
470 {
471         if (!slot->val)
472         {
473                 printf("tuple is null.\n");
474                 return;
475         }
476         if (!slot->ttc_tupleDescriptor)
477         {
478                 printf("no tuple descriptor.\n");
479                 return;
480         }
481
482         debugtup(slot->val, slot->ttc_tupleDescriptor, NULL);
483 }
484
485 static char *
486 plannode_type(Plan *p)
487 {
488         switch (nodeTag(p))
489         {
490                 case T_Plan:
491                         return "PLAN";
492                 case T_Result:
493                         return "RESULT";
494                 case T_Append:
495                         return "APPEND";
496                 case T_Scan:
497                         return "SCAN";
498                 case T_SeqScan:
499                         return "SEQSCAN";
500                 case T_IndexScan:
501                         return "INDEXSCAN";
502                 case T_TidScan:
503                         return "TIDSCAN";
504                 case T_SubqueryScan:
505                         return "SUBQUERYSCAN";
506                 case T_FunctionScan:
507                         return "FUNCTIONSCAN";
508                 case T_Join:
509                         return "JOIN";
510                 case T_NestLoop:
511                         return "NESTLOOP";
512                 case T_MergeJoin:
513                         return "MERGEJOIN";
514                 case T_HashJoin:
515                         return "HASHJOIN";
516                 case T_Material:
517                         return "MATERIAL";
518                 case T_Sort:
519                         return "SORT";
520                 case T_Agg:
521                         return "AGG";
522                 case T_Unique:
523                         return "UNIQUE";
524                 case T_SetOp:
525                         return "SETOP";
526                 case T_Limit:
527                         return "LIMIT";
528                 case T_Hash:
529                         return "HASH";
530                 case T_Group:
531                         return "GROUP";
532                 default:
533                         return "UNKNOWN";
534         }
535 }
536
537 /*
538  * Recursively prints a simple text description of the plan tree
539  */
540 void
541 print_plan_recursive(Plan *p, Query *parsetree, int indentLevel, char *label)
542 {
543         int                     i;
544         char            extraInfo[NAMEDATALEN + 100];
545
546         if (!p)
547                 return;
548         for (i = 0; i < indentLevel; i++)
549                 printf(" ");
550         printf("%s%s :c=%.2f..%.2f :r=%.0f :w=%d ", label, plannode_type(p),
551                    p->startup_cost, p->total_cost,
552                    p->plan_rows, p->plan_width);
553         if (IsA(p, Scan) ||
554                 IsA(p, SeqScan))
555         {
556                 RangeTblEntry *rte;
557
558                 rte = rt_fetch(((Scan *) p)->scanrelid, parsetree->rtable);
559                 StrNCpy(extraInfo, rte->eref->aliasname, NAMEDATALEN);
560         }
561         else if (IsA(p, IndexScan))
562         {
563                 RangeTblEntry *rte;
564
565                 rte = rt_fetch(((IndexScan *) p)->scan.scanrelid, parsetree->rtable);
566                 StrNCpy(extraInfo, rte->eref->aliasname, NAMEDATALEN);
567         }
568         else if (IsA(p, FunctionScan))
569         {
570                 RangeTblEntry *rte;
571
572                 rte = rt_fetch(((FunctionScan *) p)->scan.scanrelid, parsetree->rtable);
573                 StrNCpy(extraInfo, rte->eref->aliasname, NAMEDATALEN);
574         }
575         else
576                 extraInfo[0] = '\0';
577         if (extraInfo[0] != '\0')
578                 printf(" ( %s )\n", extraInfo);
579         else
580                 printf("\n");
581         print_plan_recursive(p->lefttree, parsetree, indentLevel + 3, "l: ");
582         print_plan_recursive(p->righttree, parsetree, indentLevel + 3, "r: ");
583
584         if (IsA(p, Append))
585         {
586                 ListCell   *l;
587                 int                     whichplan = 0;
588                 Append     *appendplan = (Append *) p;
589
590                 foreach(l, appendplan->appendplans)
591                 {
592                         Plan       *subnode = (Plan *) lfirst(l);
593
594                         /*
595                          * I don't think we need to fiddle with the range table here,
596                          * bjm
597                          */
598                         print_plan_recursive(subnode, parsetree, indentLevel + 3, "a: ");
599
600                         whichplan++;
601                 }
602         }
603 }
604
605 /* print_plan
606   prints just the plan node types */
607
608 void
609 print_plan(Plan *p, Query *parsetree)
610 {
611         print_plan_recursive(p, parsetree, 0, "");
612 }