OSDN Git Service

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