* 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.38 2000/04/12 17:15:16 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
}
/*
* 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->ref->relname, 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++;
}
}
* print an expression
*/
void
-print_expr(Node *expr, List *rtable)
+print_expr(const Node *expr, const List *rtable)
{
if (expr == NULL)
{
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->ref && rt->ref->relname)
- relname = rt->ref->relname; /* 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(", ");
}
* 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");
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=%.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))
- {
- 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);
}