]> granicus.if.org Git - postgresql/blobdiff - src/backend/nodes/print.c
Make some small planner API cleanups.
[postgresql] / src / backend / nodes / print.c
index ebc5d536e7d2ff917af3a5eecd4aa31c792c8a14..6d6da5299faf0b38e2ab44a72941eadc28c87f71 100644 (file)
@@ -3,12 +3,12 @@
  * print.c
  *       various print routines (used mostly for debugging)
  *
- * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
+ * 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.52 2002/03/21 16:00:41 tgl Exp $
+ *       src/backend/nodes/print.c
  *
  * HISTORY
  *       AUTHOR                        DATE                    MAJOR EVENT
 #include "postgres.h"
 
 #include "access/printtup.h"
-#include "catalog/pg_type.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"
-#include "utils/syscache.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);
+       pfree(f);
+}
+
+/*
+ * pprint
+ *       pretty-print contents of Node to stdout
+ */
+void
+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);
 }
 
 /*
- * pretty print hack extraordinaire.  -ay 10/94
+ * elog_node_display
+ *       send pretty-printed contents of Node to postmaster log
  */
 void
-pprint(void *obj)
+elog_node_display(int lev, const char *title, const void *obj, bool pretty)
 {
-#define INDENTSTOP     3
-#define MAXINDENT      60
-#define LINELEN                80
        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[LINELEN];
+       int                     j;
+       int                     k;
+
+       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;
 
-       s = nodeToString(obj);
-
+       initStringInfo(&str);
        indentLev = 0;                          /* logical indent level */
        indentDist = 0;                         /* physical indent distance */
        i = 0;
@@ -69,9 +168,9 @@ pprint(void *obj)
        {
                for (j = 0; j < indentDist; j++)
                        line[j] = ' ';
-               for (; j < LINELEN-1 && 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 '}':
@@ -79,11 +178,12 @@ pprint(void *obj)
                                        {
                                                /* print data before the } */
                                                line[j] = '\0';
-                                               printf("%s\n", line);
+                                               appendStringInfo(&str, "%s\n", line);
                                        }
                                        /* print the } at indentDist */
-                                       line[indentDist] = '\0';
-                                       printf("%s}\n", line);
+                                       line[indentDist] = '}';
+                                       line[indentDist + 1] = '\0';
+                                       appendStringInfo(&str, "%s\n", line);
                                        /* outdent */
                                        if (indentLev > 0)
                                        {
@@ -92,48 +192,58 @@ pprint(void *obj)
                                        }
                                        j = indentDist - 1;
                                        /* j will equal indentDist on next loop iteration */
+                                       /* suppress whitespace just after } */
+                                       while (dump[i + 1] == ' ')
+                                               i++;
                                        break;
                                case ')':
-                                       /* force line break after ')' */
-                                       line[j + 1] = '\0';
-                                       printf("%s\n", line);
-                                       j = indentDist - 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';
-                                               printf("%s\n", line);
+                                               appendStringInfo(&str, "%s\n", line);
                                        }
                                        /* indent */
                                        indentLev++;
                                        indentDist = Min(indentLev * INDENTSTOP, MAXINDENT);
                                        for (j = 0; j < indentDist; j++)
                                                line[j] = ' ';
-                                       line[j] = s[i];
+                                       line[j] = dump[i];
                                        break;
                                case ':':
                                        /* force line break before : */
                                        if (j != indentDist)
                                        {
                                                line[j] = '\0';
-                                               printf("%s\n", line);
+                                               appendStringInfo(&str, "%s\n", line);
                                        }
                                        j = indentDist;
-                                       line[j] = s[i];
+                                       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);
-       pfree(s);
+       if (j > 0)
+               appendStringInfo(&str, "%s\n", line);
+       return str.data;
+#undef INDENTSTOP
+#undef MAXINDENT
+#undef LINELEN
 }
 
 /*
@@ -141,23 +251,60 @@ 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);
 
-               if (rte->relname)
-                       printf("%d\t%s (%s)\t%u",
-                                  i, rte->relname, rte->eref->aliasname, rte->relid);
-               else
-                       printf("%d\t[subquery] (%s)\t",
-                                  i, rte->eref->aliasname);
+               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" : ""));
@@ -171,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)
        {
@@ -181,26 +328,30 @@ print_expr(Node *expr, List *rtable)
 
        if (IsA(expr, Var))
        {
-               Var                *var = (Var *) expr;
+               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:
                                {
                                        RangeTblEntry *rte;
 
                                        Assert(var->varno > 0 &&
-                                                  (int) var->varno <= length(rtable));
+                                                  (int) var->varno <= list_length(rtable));
                                        rte = rt_fetch(var->varno, rtable);
                                        relname = rte->eref->aliasname;
                                        attname = get_rte_attribute_name(rte, var->varattno);
@@ -211,10 +362,9 @@ print_expr(Node *expr, List *rtable)
        }
        else if (IsA(expr, Const))
        {
-               Const      *c = (Const *) expr;
-               HeapTuple       typeTup;
+               const Const *c = (const Const *) expr;
                Oid                     typoutput;
-               Oid                     typelem;
+               bool            typIsVarlena;
                char       *outputstr;
 
                if (c->constisnull)
@@ -223,65 +373,84 @@ print_expr(Node *expr, List *rtable)
                        return;
                }
 
-               typeTup = SearchSysCache(TYPEOID,
-                                                                ObjectIdGetDatum(c->consttype),
-                                                                0, 0, 0);
-               if (!HeapTupleIsValid(typeTup))
-                       elog(ERROR, "Cache lookup for type %u failed", c->consttype);
-               typoutput = ((Form_pg_type) GETSTRUCT(typeTup))->typoutput;
-               typelem = ((Form_pg_type) GETSTRUCT(typeTup))->typelem;
-               ReleaseSysCache(typeTup);
-
-               outputstr = DatumGetCString(OidFunctionCall3(typoutput,
-                                                                                                        c->constvalue,
-                                                                                          ObjectIdGetDatum(typelem),
-                                                                                                        Int32GetDatum(-1)));
+               getTypeOutputInfo(c->consttype,
+                                                 &typoutput, &typIsVarlena);
+
+               outputstr = OidOutputFunctionCall(typoutput, c->constvalue);
                printf("%s", outputstr);
                pfree(outputstr);
        }
-       else if (IsA(expr, Expr))
+       else if (IsA(expr, OpExpr))
        {
-               Expr       *e = (Expr *) expr;
+               const OpExpr *e = (const OpExpr *) expr;
+               char       *opname;
 
-               if (is_opclause(expr))
+               opname = get_opname(e->opno);
+               if (list_length(e->args) > 1)
                {
-                       char       *opname;
-
-                       print_expr((Node *) get_leftop(e), rtable);
-                       opname = get_opname(((Oper *) e->oper)->opno);
+                       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(")");
                if (lnext(i))
@@ -295,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");
@@ -322,139 +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";
-               case T_Result:
-                       return "RESULT";
-               case T_Append:
-                       return "APPEND";
-               case T_Scan:
-                       return "SCAN";
-               case T_SeqScan:
-                       return "SEQSCAN";
-               case T_IndexScan:
-                       return "INDEXSCAN";
-               case T_TidScan:
-                       return "TIDSCAN";
-               case T_SubqueryScan:
-                       return "SUBQUERYSCAN";
-               case T_Join:
-                       return "JOIN";
-               case T_NestLoop:
-                       return "NESTLOOP";
-               case T_MergeJoin:
-                       return "MERGEJOIN";
-               case T_HashJoin:
-                       return "HASHJOIN";
-               case T_Material:
-                       return "MATERIAL";
-               case T_Sort:
-                       return "SORT";
-               case T_Agg:
-                       return "AGG";
-               case T_Unique:
-                       return "UNIQUE";
-               case T_SetOp:
-                       return "SETOP";
-               case T_Limit:
-                       return "LIMIT";
-               case T_Hash:
-                       return "HASH";
-               case T_Group:
-                       return "GROUP";
-               default:
-                       return "UNKNOWN";
-       }
-}
-
-/*
-   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[NAMEDATALEN + 100];
-
-       if (!p)
-               return;
-       for (i = 0; i < indentLevel; i++)
-               printf(" ");
-       printf("%s%s :c=%.2f..%.2f :r=%.0f :w=%d ", label, plannode_type(p),
-                  p->startup_cost, p->total_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))
-       {
-               RangeTblEntry *rte;
-
-               rte = rt_fetch(((IndexScan *) p)->scan.scanrelid, parsetree->rtable);
-               StrNCpy(extraInfo, rte->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 (IsA(p, 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);
 }