]> granicus.if.org Git - postgresql/blobdiff - src/backend/nodes/print.c
Make some small planner API cleanups.
[postgresql] / src / backend / nodes / print.c
index ffba95949ad8ffd2b3eb0abfd98d21596c1e1dc6..6d6da5299faf0b38e2ab44a72941eadc28c87f71 100644 (file)
@@ -3,12 +3,12 @@
  * print.c
  *       various print routines (used mostly for debugging)
  *
- * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/nodes/print.c,v 1.35 2000/01/26 05:56:32 momjian Exp $
+ *       src/backend/nodes/print.c
  *
  * HISTORY
  *       AUTHOR                        DATE                    MAJOR EVENT
 #include "postgres.h"
 
 #include "access/printtup.h"
+#include "lib/stringinfo.h"
+#include "nodes/nodeFuncs.h"
 #include "nodes/print.h"
-#include "optimizer/clauses.h"
+#include "nodes/relation.h"
 #include "parser/parsetree.h"
 #include "utils/lsyscache.h"
 
-static char *plannode_type(Plan *p);
 
 /*
  * print
  *       print contents of Node to stdout
  */
 void
-print(void *obj)
+print(const void *obj)
 {
        char       *s;
+       char       *f;
 
        s = nodeToString(obj);
-       printf("%s\n", s);
+       f = format_node_dump(s);
+       pfree(s);
+       printf("%s\n", f);
        fflush(stdout);
-       return;
+       pfree(f);
 }
 
 /*
- * pretty print hack extraordinaire.  -ay 10/94
+ * pprint
+ *       pretty-print contents of Node to stdout
  */
 void
-pprint(void *obj)
+pprint(const void *obj)
 {
        char       *s;
+       char       *f;
+
+       s = nodeToString(obj);
+       f = pretty_format_node_dump(s);
+       pfree(s);
+       printf("%s\n", f);
+       fflush(stdout);
+       pfree(f);
+}
+
+/*
+ * elog_node_display
+ *       send pretty-printed contents of Node to postmaster log
+ */
+void
+elog_node_display(int lev, const char *title, const void *obj, bool pretty)
+{
+       char       *s;
+       char       *f;
+
+       s = nodeToString(obj);
+       if (pretty)
+               f = pretty_format_node_dump(s);
+       else
+               f = format_node_dump(s);
+       pfree(s);
+       ereport(lev,
+                       (errmsg_internal("%s:", title),
+                        errdetail_internal("%s", f)));
+       pfree(f);
+}
+
+/*
+ * Format a nodeToString output for display on a terminal.
+ *
+ * The result is a palloc'd string.
+ *
+ * This version just tries to break at whitespace.
+ */
+char *
+format_node_dump(const char *dump)
+{
+#define LINELEN                78
+       char            line[LINELEN + 1];
+       StringInfoData str;
        int                     i;
-       char            line[80];
-       int                     indentLev;
        int                     j;
+       int                     k;
 
-       s = nodeToString(obj);
+       initStringInfo(&str);
+       i = 0;
+       for (;;)
+       {
+               for (j = 0; j < LINELEN && dump[i] != '\0'; i++, j++)
+                       line[j] = dump[i];
+               if (dump[i] == '\0')
+                       break;
+               if (dump[i] == ' ')
+               {
+                       /* ok to break at adjacent space */
+                       i++;
+               }
+               else
+               {
+                       for (k = j - 1; k > 0; k--)
+                               if (line[k] == ' ')
+                                       break;
+                       if (k > 0)
+                       {
+                               /* back up; will reprint all after space */
+                               i -= (j - k - 1);
+                               j = k;
+                       }
+               }
+               line[j] = '\0';
+               appendStringInfo(&str, "%s\n", line);
+       }
+       if (j > 0)
+       {
+               line[j] = '\0';
+               appendStringInfo(&str, "%s\n", line);
+       }
+       return str.data;
+#undef LINELEN
+}
+
+/*
+ * Format a nodeToString output for display on a terminal.
+ *
+ * The result is a palloc'd string.
+ *
+ * This version tries to indent intelligently.
+ */
+char *
+pretty_format_node_dump(const char *dump)
+{
+#define INDENTSTOP     3
+#define MAXINDENT      60
+#define LINELEN                78
+       char            line[LINELEN + 1];
+       StringInfoData str;
+       int                     indentLev;
+       int                     indentDist;
+       int                     i;
+       int                     j;
 
-       indentLev = 0;
+       initStringInfo(&str);
+       indentLev = 0;                          /* logical indent level */
+       indentDist = 0;                         /* physical indent distance */
        i = 0;
        for (;;)
        {
-               for (j = 0; j < indentLev * 3; j++)
+               for (j = 0; j < indentDist; j++)
                        line[j] = ' ';
-               for (; j < 75 && s[i] != '\0'; i++, j++)
+               for (; j < LINELEN && dump[i] != '\0'; i++, j++)
                {
-                       line[j] = s[i];
+                       line[j] = dump[i];
                        switch (line[j])
                        {
                                case '}':
-                                       if (j != indentLev * 3)
+                                       if (j != indentDist)
                                        {
+                                               /* print data before the } */
                                                line[j] = '\0';
-                                               printf("%s\n", line);
-                                               line[indentLev * 3] = '\0';
-                                               printf("%s}\n", line);
+                                               appendStringInfo(&str, "%s\n", line);
                                        }
-                                       else
+                                       /* print the } at indentDist */
+                                       line[indentDist] = '}';
+                                       line[indentDist + 1] = '\0';
+                                       appendStringInfo(&str, "%s\n", line);
+                                       /* outdent */
+                                       if (indentLev > 0)
                                        {
-                                               line[j] = '\0';
-                                               printf("%s}\n", line);
+                                               indentLev--;
+                                               indentDist = Min(indentLev * INDENTSTOP, MAXINDENT);
                                        }
-                                       indentLev--;
-                                       j = indentLev * 3 - 1;          /* print the line before :
-                                                                                                * and resets */
+                                       j = indentDist - 1;
+                                       /* j will equal indentDist on next loop iteration */
+                                       /* suppress whitespace just after } */
+                                       while (dump[i + 1] == ' ')
+                                               i++;
                                        break;
                                case ')':
-                                       line[j + 1] = '\0';
-                                       printf("%s\n", line);
-                                       j = indentLev * 3 - 1;
+                                       /* force line break after ), unless another ) follows */
+                                       if (dump[i + 1] != ')')
+                                       {
+                                               line[j + 1] = '\0';
+                                               appendStringInfo(&str, "%s\n", line);
+                                               j = indentDist - 1;
+                                               while (dump[i + 1] == ' ')
+                                                       i++;
+                                       }
                                        break;
                                case '{':
+                                       /* force line break before { */
+                                       if (j != indentDist)
+                                       {
+                                               line[j] = '\0';
+                                               appendStringInfo(&str, "%s\n", line);
+                                       }
+                                       /* indent */
                                        indentLev++;
-                                       /* !!! FALLS THROUGH */
+                                       indentDist = Min(indentLev * INDENTSTOP, MAXINDENT);
+                                       for (j = 0; j < indentDist; j++)
+                                               line[j] = ' ';
+                                       line[j] = dump[i];
+                                       break;
                                case ':':
-                                       if (j != 0)
+                                       /* force line break before : */
+                                       if (j != indentDist)
                                        {
                                                line[j] = '\0';
-                                               printf("%s\n", line);
-                                               /* print the line before : and resets */
-                                               for (j = 0; j < indentLev * 3; j++)
-                                                       line[j] = ' ';
+                                               appendStringInfo(&str, "%s\n", line);
                                        }
-                                       line[j] = s[i];
+                                       j = indentDist;
+                                       line[j] = dump[i];
                                        break;
                        }
                }
                line[j] = '\0';
-               if (s[i] == '\0')
+               if (dump[i] == '\0')
                        break;
-               printf("%s\n", line);
+               appendStringInfo(&str, "%s\n", line);
        }
-       if (j != 0)
-               printf("%s\n", line);
-       fflush(stdout);
-       return;
+       if (j > 0)
+               appendStringInfo(&str, "%s\n", line);
+       return str.data;
+#undef INDENTSTOP
+#undef MAXINDENT
+#undef LINELEN
 }
 
 /*
@@ -121,21 +251,63 @@ pprint(void *obj)
  *       print contents of range table
  */
 void
-print_rt(List *rtable)
+print_rt(const List *rtable)
 {
-       List       *l;
+       const ListCell *l;
        int                     i = 1;
 
-       printf("resno\trelname(refname)\trelid\tinFromCl\n");
-       printf("-----\t----------------\t-----\t--------\n");
+       printf("resno\trefname  \trelid\tinFromCl\n");
+       printf("-----\t---------\t-----\t--------\n");
        foreach(l, rtable)
        {
                RangeTblEntry *rte = lfirst(l);
 
-               printf("%d\t%s(%s)\t%u\t%d\t%s\n",
-                          i, rte->relname, rte->refname, rte->relid,
-                          rte->inFromCl,
-                          (rte->inh ? "inh" : ""));
+               switch (rte->rtekind)
+               {
+                       case RTE_RELATION:
+                               printf("%d\t%s\t%u\t%c",
+                                          i, rte->eref->aliasname, rte->relid, rte->relkind);
+                               break;
+                       case RTE_SUBQUERY:
+                               printf("%d\t%s\t[subquery]",
+                                          i, rte->eref->aliasname);
+                               break;
+                       case RTE_JOIN:
+                               printf("%d\t%s\t[join]",
+                                          i, rte->eref->aliasname);
+                               break;
+                       case RTE_FUNCTION:
+                               printf("%d\t%s\t[rangefunction]",
+                                          i, rte->eref->aliasname);
+                               break;
+                       case RTE_TABLEFUNC:
+                               printf("%d\t%s\t[table function]",
+                                          i, rte->eref->aliasname);
+                               break;
+                       case RTE_VALUES:
+                               printf("%d\t%s\t[values list]",
+                                          i, rte->eref->aliasname);
+                               break;
+                       case RTE_CTE:
+                               printf("%d\t%s\t[cte]",
+                                          i, rte->eref->aliasname);
+                               break;
+                       case RTE_NAMEDTUPLESTORE:
+                               printf("%d\t%s\t[tuplestore]",
+                                          i, rte->eref->aliasname);
+                               break;
+                       case RTE_RESULT:
+                               printf("%d\t%s\t[result]",
+                                          i, rte->eref->aliasname);
+                               break;
+                       default:
+                               printf("%d\t%s\t[unknown rtekind]",
+                                          i, rte->eref->aliasname);
+               }
+
+               printf("\t%s\t%s\n",
+                          (rte->inh ? "inh" : ""),
+                          (rte->inFromCl ? "inFromCl" : ""));
                i++;
        }
 }
@@ -146,7 +318,7 @@ print_rt(List *rtable)
  *       print an expression
  */
 void
-print_expr(Node *expr, List *rtable)
+print_expr(const Node *expr, const List *rtable)
 {
        if (expr == NULL)
        {
@@ -156,78 +328,131 @@ print_expr(Node *expr, List *rtable)
 
        if (IsA(expr, Var))
        {
-               Var                *var = (Var *) expr;
-               RangeTblEntry *rt;
+               const Var  *var = (const Var *) expr;
                char       *relname,
                                   *attname;
 
                switch (var->varno)
                {
-                       case INNER:
+                       case INNER_VAR:
                                relname = "INNER";
                                attname = "?";
                                break;
-                       case OUTER:
+                       case OUTER_VAR:
                                relname = "OUTER";
                                attname = "?";
                                break;
+                       case INDEX_VAR:
+                               relname = "INDEX";
+                               attname = "?";
+                               break;
                        default:
                                {
-                                       rt = rt_fetch(var->varno, rtable);
-                                       relname = rt->relname;
-                                       if (rt->refname)
-                                               relname = rt->refname;  /* table renamed */
-                                       attname = get_attname(rt->relid, var->varattno);
+                                       RangeTblEntry *rte;
+
+                                       Assert(var->varno > 0 &&
+                                                  (int) var->varno <= list_length(rtable));
+                                       rte = rt_fetch(var->varno, rtable);
+                                       relname = rte->eref->aliasname;
+                                       attname = get_rte_attribute_name(rte, var->varattno);
                                }
                                break;
                }
                printf("%s.%s", relname, attname);
        }
-       else if (IsA(expr, Expr))
+       else if (IsA(expr, Const))
        {
-               Expr       *e = (Expr *) expr;
+               const Const *c = (const Const *) expr;
+               Oid                     typoutput;
+               bool            typIsVarlena;
+               char       *outputstr;
 
-               if (is_opclause(expr))
+               if (c->constisnull)
                {
-                       char       *opname;
+                       printf("NULL");
+                       return;
+               }
 
-                       print_expr((Node *) get_leftop(e), rtable);
-                       opname = get_opname(((Oper *) e->oper)->opno);
+               getTypeOutputInfo(c->consttype,
+                                                 &typoutput, &typIsVarlena);
+
+               outputstr = OidOutputFunctionCall(typoutput, c->constvalue);
+               printf("%s", outputstr);
+               pfree(outputstr);
+       }
+       else if (IsA(expr, OpExpr))
+       {
+               const OpExpr *e = (const OpExpr *) expr;
+               char       *opname;
+
+               opname = get_opname(e->opno);
+               if (list_length(e->args) > 1)
+               {
+                       print_expr(get_leftop((const Expr *) e), rtable);
                        printf(" %s ", ((opname != NULL) ? opname : "(invalid operator)"));
-                       print_expr((Node *) get_rightop(e), rtable);
+                       print_expr(get_rightop((const Expr *) e), rtable);
                }
                else
-                       printf("an expr");
+               {
+                       /* we print prefix and postfix ops the same... */
+                       printf("%s ", ((opname != NULL) ? opname : "(invalid operator)"));
+                       print_expr(get_leftop((const Expr *) e), rtable);
+               }
+       }
+       else if (IsA(expr, FuncExpr))
+       {
+               const FuncExpr *e = (const FuncExpr *) expr;
+               char       *funcname;
+               ListCell   *l;
+
+               funcname = get_func_name(e->funcid);
+               printf("%s(", ((funcname != NULL) ? funcname : "(invalid function)"));
+               foreach(l, e->args)
+               {
+                       print_expr(lfirst(l), rtable);
+                       if (lnext(l))
+                               printf(",");
+               }
+               printf(")");
        }
        else
-               printf("not an expr");
+               printf("unknown expr");
 }
 
 /*
  * print_pathkeys -
- *       pathkeys list of list of PathKeyItems
+ *       pathkeys list of PathKeys
  */
 void
-print_pathkeys(List *pathkeys, List *rtable)
+print_pathkeys(const List *pathkeys, const List *rtable)
 {
-       List       *i,
-                          *k;
+       const ListCell *i;
 
        printf("(");
        foreach(i, pathkeys)
        {
-               List       *pathkey = lfirst(i);
+               PathKey    *pathkey = (PathKey *) lfirst(i);
+               EquivalenceClass *eclass;
+               ListCell   *k;
+               bool            first = true;
+
+               eclass = pathkey->pk_eclass;
+               /* chase up, in case pathkey is non-canonical */
+               while (eclass->ec_merged)
+                       eclass = eclass->ec_merged;
 
                printf("(");
-               foreach(k, pathkey)
+               foreach(k, eclass->ec_members)
                {
-                       PathKeyItem     *item = lfirst(k);
+                       EquivalenceMember *mem = (EquivalenceMember *) lfirst(k);
 
-                       print_expr(item->key, rtable);
-                       if (lnext(k))
+                       if (first)
+                               first = false;
+                       else
                                printf(", ");
+                       print_expr((Node *) mem->em_expr, rtable);
                }
-               printf(") ");
+               printf(")");
                if (lnext(i))
                        printf(", ");
        }
@@ -239,21 +464,22 @@ print_pathkeys(List *pathkeys, List *rtable)
  *       print targetlist in a more legible way.
  */
 void
-print_tl(List *tlist, List *rtable)
+print_tl(const List *tlist, const List *rtable)
 {
-       List       *tl;
+       const ListCell *tl;
 
        printf("(\n");
        foreach(tl, tlist)
        {
-               TargetEntry *tle = lfirst(tl);
+               TargetEntry *tle = (TargetEntry *) lfirst(tl);
 
-               printf("\t%d %s\t", tle->resdom->resno, tle->resdom->resname);
-               if (tle->resdom->reskey != 0)
-                       printf("(%d):\t", tle->resdom->reskey);
+               printf("\t%d %s\t", tle->resno,
+                          tle->resname ? tle->resname : "<null>");
+               if (tle->ressortgroupref != 0)
+                       printf("(%u):\t", tle->ressortgroupref);
                else
                        printf("    :\t");
-               print_expr(tle->expr, rtable);
+               print_expr((Node *) tle->expr, rtable);
                printf("\n");
        }
        printf(")\n");
@@ -266,156 +492,16 @@ print_tl(List *tlist, List *rtable)
 void
 print_slot(TupleTableSlot *slot)
 {
-       if (!slot->val)
+       if (TupIsNull(slot))
        {
                printf("tuple is null.\n");
                return;
        }
-       if (!slot->ttc_tupleDescriptor)
+       if (!slot->tts_tupleDescriptor)
        {
                printf("no tuple descriptor.\n");
                return;
        }
 
-       debugtup(slot->val, slot->ttc_tupleDescriptor, NULL);
-}
-
-static char *
-plannode_type(Plan *p)
-{
-       switch (nodeTag(p))
-       {
-                       case T_Plan:
-                       return "PLAN";
-                       break;
-               case T_Result:
-                       return "RESULT";
-                       break;
-               case T_Append:
-                       return "APPEND";
-                       break;
-               case T_Scan:
-                       return "SCAN";
-                       break;
-               case T_SeqScan:
-                       return "SEQSCAN";
-                       break;
-               case T_IndexScan:
-                       return "INDEXSCAN";
-                       break;
-               case T_Join:
-                       return "JOIN";
-                       break;
-               case T_NestLoop:
-                       return "NESTLOOP";
-                       break;
-               case T_MergeJoin:
-                       return "MERGEJOIN";
-                       break;
-               case T_HashJoin:
-                       return "HASHJOIN";
-                       break;
-               case T_Noname:
-                       return "NONAME";
-                       break;
-               case T_Material:
-                       return "MATERIAL";
-                       break;
-               case T_Sort:
-                       return "SORT";
-                       break;
-               case T_Agg:
-                       return "AGG";
-                       break;
-               case T_Unique:
-                       return "UNIQUE";
-                       break;
-               case T_Hash:
-                       return "HASH";
-                       break;
-               case T_Choose:
-                       return "CHOOSE";
-                       break;
-               case T_Group:
-                       return "GROUP";
-                       break;
-               case T_TidScan:
-                       return "TIDSCAN";
-                       break;
-               default:
-                       return "UNKNOWN";
-                       break;
-       }
-}
-
-/*
-   prints the ascii description of the plan nodes
-   does this recursively by doing a depth-first traversal of the
-   plan tree.  for SeqScan and IndexScan, the name of the table is also
-   printed out
-
-*/
-void
-print_plan_recursive(Plan *p, Query *parsetree, int indentLevel, char *label)
-{
-       int                     i;
-       char            extraInfo[100];
-
-       if (!p)
-               return;
-       for (i = 0; i < indentLevel; i++)
-               printf(" ");
-       printf("%s%s :c=%.4f :r=%.0f :w=%d ", label, plannode_type(p),
-                  p->cost, p->plan_rows, p->plan_width);
-       if (IsA(p, Scan) ||IsA(p, SeqScan))
-       {
-               RangeTblEntry *rte;
-
-               rte = rt_fetch(((Scan *) p)->scanrelid, parsetree->rtable);
-               StrNCpy(extraInfo, rte->relname, NAMEDATALEN);
-       }
-       else if (IsA(p, IndexScan))
-       {
-               StrNCpy(extraInfo,
-                  ((RangeTblEntry *) (nth(((IndexScan *) p)->scan.scanrelid - 1,
-                                                                  parsetree->rtable)))->relname,
-                               NAMEDATALEN);
-       }
-       else
-               extraInfo[0] = '\0';
-       if (extraInfo[0] != '\0')
-               printf(" ( %s )\n", extraInfo);
-       else
-               printf("\n");
-       print_plan_recursive(p->lefttree, parsetree, indentLevel + 3, "l: ");
-       print_plan_recursive(p->righttree, parsetree, indentLevel + 3, "r: ");
-
-       if (nodeTag(p) == T_Append)
-       {
-               List       *lst;
-               int                     whichplan = 0;
-               Append     *appendplan = (Append *) p;
-
-               foreach(lst, appendplan->appendplans)
-               {
-                       Plan       *subnode = (Plan *) lfirst(lst);
-
-                       /*
-                        * I don't think we need to fiddle with the range table here,
-                        * bjm
-                        */
-                       print_plan_recursive(subnode, parsetree, indentLevel + 3, "a: ");
-
-                       whichplan++;
-               }
-       }
-}
-
-/* print_plan
-  prints just the plan node types */
-
-void
-print_plan(Plan *p, Query *parsetree)
-{
-       print_plan_recursive(p, parsetree, 0, "");
+       debugtup(slot, NULL);
 }