]> granicus.if.org Git - postgresql/blob - src/backend/nodes/print.c
Update copyright for 2006. Update scripts.
[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-2006, 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.78 2006/03/05 15:58:28 momjian 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                 bool            typIsVarlena;
349                 char       *outputstr;
350
351                 if (c->constisnull)
352                 {
353                         printf("NULL");
354                         return;
355                 }
356
357                 getTypeOutputInfo(c->consttype,
358                                                   &typoutput, &typIsVarlena);
359
360                 outputstr = DatumGetCString(OidFunctionCall1(typoutput,
361                                                                                                          c->constvalue));
362                 printf("%s", outputstr);
363                 pfree(outputstr);
364         }
365         else if (IsA(expr, OpExpr))
366         {
367                 OpExpr     *e = (OpExpr *) expr;
368                 char       *opname;
369
370                 opname = get_opname(e->opno);
371                 if (list_length(e->args) > 1)
372                 {
373                         print_expr(get_leftop((Expr *) e), rtable);
374                         printf(" %s ", ((opname != NULL) ? opname : "(invalid operator)"));
375                         print_expr(get_rightop((Expr *) e), rtable);
376                 }
377                 else
378                 {
379                         /* we print prefix and postfix ops the same... */
380                         printf("%s ", ((opname != NULL) ? opname : "(invalid operator)"));
381                         print_expr(get_leftop((Expr *) e), rtable);
382                 }
383         }
384         else if (IsA(expr, FuncExpr))
385         {
386                 FuncExpr   *e = (FuncExpr *) expr;
387                 char       *funcname;
388                 ListCell   *l;
389
390                 funcname = get_func_name(e->funcid);
391                 printf("%s(", ((funcname != NULL) ? funcname : "(invalid function)"));
392                 foreach(l, e->args)
393                 {
394                         print_expr(lfirst(l), rtable);
395                         if (lnext(l))
396                                 printf(",");
397                 }
398                 printf(")");
399         }
400         else
401                 printf("unknown expr");
402 }
403
404 /*
405  * print_pathkeys -
406  *        pathkeys list of list of PathKeyItems
407  */
408 void
409 print_pathkeys(List *pathkeys, List *rtable)
410 {
411         ListCell   *i;
412
413         printf("(");
414         foreach(i, pathkeys)
415         {
416                 List       *pathkey = (List *) lfirst(i);
417                 ListCell   *k;
418
419                 printf("(");
420                 foreach(k, pathkey)
421                 {
422                         PathKeyItem *item = (PathKeyItem *) lfirst(k);
423
424                         print_expr(item->key, rtable);
425                         if (lnext(k))
426                                 printf(", ");
427                 }
428                 printf(")");
429                 if (lnext(i))
430                         printf(", ");
431         }
432         printf(")\n");
433 }
434
435 /*
436  * print_tl
437  *        print targetlist in a more legible way.
438  */
439 void
440 print_tl(List *tlist, List *rtable)
441 {
442         ListCell   *tl;
443
444         printf("(\n");
445         foreach(tl, tlist)
446         {
447                 TargetEntry *tle = (TargetEntry *) lfirst(tl);
448
449                 printf("\t%d %s\t", tle->resno,
450                            tle->resname ? tle->resname : "<null>");
451                 if (tle->ressortgroupref != 0)
452                         printf("(%u):\t", tle->ressortgroupref);
453                 else
454                         printf("    :\t");
455                 print_expr((Node *) tle->expr, rtable);
456                 printf("\n");
457         }
458         printf(")\n");
459 }
460
461 /*
462  * print_slot
463  *        print out the tuple with the given TupleTableSlot
464  */
465 void
466 print_slot(TupleTableSlot *slot)
467 {
468         if (TupIsNull(slot))
469         {
470                 printf("tuple is null.\n");
471                 return;
472         }
473         if (!slot->tts_tupleDescriptor)
474         {
475                 printf("no tuple descriptor.\n");
476                 return;
477         }
478
479         debugtup(slot, NULL);
480 }
481
482 static char *
483 plannode_type(Plan *p)
484 {
485         switch (nodeTag(p))
486         {
487                 case T_Plan:
488                         return "PLAN";
489                 case T_Result:
490                         return "RESULT";
491                 case T_Append:
492                         return "APPEND";
493                 case T_BitmapAnd:
494                         return "BITMAPAND";
495                 case T_BitmapOr:
496                         return "BITMAPOR";
497                 case T_Scan:
498                         return "SCAN";
499                 case T_SeqScan:
500                         return "SEQSCAN";
501                 case T_IndexScan:
502                         return "INDEXSCAN";
503                 case T_BitmapIndexScan:
504                         return "BITMAPINDEXSCAN";
505                 case T_BitmapHeapScan:
506                         return "BITMAPHEAPSCAN";
507                 case T_TidScan:
508                         return "TIDSCAN";
509                 case T_SubqueryScan:
510                         return "SUBQUERYSCAN";
511                 case T_FunctionScan:
512                         return "FUNCTIONSCAN";
513                 case T_Join:
514                         return "JOIN";
515                 case T_NestLoop:
516                         return "NESTLOOP";
517                 case T_MergeJoin:
518                         return "MERGEJOIN";
519                 case T_HashJoin:
520                         return "HASHJOIN";
521                 case T_Material:
522                         return "MATERIAL";
523                 case T_Sort:
524                         return "SORT";
525                 case T_Agg:
526                         return "AGG";
527                 case T_Unique:
528                         return "UNIQUE";
529                 case T_SetOp:
530                         return "SETOP";
531                 case T_Limit:
532                         return "LIMIT";
533                 case T_Hash:
534                         return "HASH";
535                 case T_Group:
536                         return "GROUP";
537                 default:
538                         return "UNKNOWN";
539         }
540 }
541
542 /*
543  * Recursively prints a simple text description of the plan tree
544  */
545 void
546 print_plan_recursive(Plan *p, Query *parsetree, int indentLevel, char *label)
547 {
548         int                     i;
549         char            extraInfo[NAMEDATALEN + 100];
550
551         if (!p)
552                 return;
553         for (i = 0; i < indentLevel; i++)
554                 printf(" ");
555         printf("%s%s :c=%.2f..%.2f :r=%.0f :w=%d ", label, plannode_type(p),
556                    p->startup_cost, p->total_cost,
557                    p->plan_rows, p->plan_width);
558         if (IsA(p, Scan) ||
559                 IsA(p, SeqScan) ||
560                 IsA(p, BitmapHeapScan))
561         {
562                 RangeTblEntry *rte;
563
564                 rte = rt_fetch(((Scan *) p)->scanrelid, parsetree->rtable);
565                 StrNCpy(extraInfo, rte->eref->aliasname, NAMEDATALEN);
566         }
567         else if (IsA(p, IndexScan))
568         {
569                 RangeTblEntry *rte;
570
571                 rte = rt_fetch(((IndexScan *) p)->scan.scanrelid, parsetree->rtable);
572                 StrNCpy(extraInfo, rte->eref->aliasname, NAMEDATALEN);
573         }
574         else if (IsA(p, FunctionScan))
575         {
576                 RangeTblEntry *rte;
577
578                 rte = rt_fetch(((FunctionScan *) p)->scan.scanrelid, parsetree->rtable);
579                 StrNCpy(extraInfo, rte->eref->aliasname, NAMEDATALEN);
580         }
581         else
582                 extraInfo[0] = '\0';
583         if (extraInfo[0] != '\0')
584                 printf(" ( %s )\n", extraInfo);
585         else
586                 printf("\n");
587         print_plan_recursive(p->lefttree, parsetree, indentLevel + 3, "l: ");
588         print_plan_recursive(p->righttree, parsetree, indentLevel + 3, "r: ");
589
590         if (IsA(p, Append))
591         {
592                 ListCell   *l;
593                 Append     *appendplan = (Append *) p;
594
595                 foreach(l, appendplan->appendplans)
596                 {
597                         Plan       *subnode = (Plan *) lfirst(l);
598
599                         print_plan_recursive(subnode, parsetree, indentLevel + 3, "a: ");
600                 }
601         }
602
603         if (IsA(p, BitmapAnd))
604         {
605                 ListCell   *l;
606                 BitmapAnd  *bitmapandplan = (BitmapAnd *) p;
607
608                 foreach(l, bitmapandplan->bitmapplans)
609                 {
610                         Plan       *subnode = (Plan *) lfirst(l);
611
612                         print_plan_recursive(subnode, parsetree, indentLevel + 3, "a: ");
613                 }
614         }
615
616         if (IsA(p, BitmapOr))
617         {
618                 ListCell   *l;
619                 BitmapOr   *bitmaporplan = (BitmapOr *) p;
620
621                 foreach(l, bitmaporplan->bitmapplans)
622                 {
623                         Plan       *subnode = (Plan *) lfirst(l);
624
625                         print_plan_recursive(subnode, parsetree, indentLevel + 3, "a: ");
626                 }
627         }
628 }
629
630 /*
631  * print_plan
632  *
633  * prints just the plan node types
634  */
635 void
636 print_plan(Plan *p, Query *parsetree)
637 {
638         print_plan_recursive(p, parsetree, 0, "");
639 }