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