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