summaryrefslogtreecommitdiffstats
path: root/src/backend/nodes/print.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/nodes/print.c')
-rw-r--r--src/backend/nodes/print.c507
1 files changed, 507 insertions, 0 deletions
diff --git a/src/backend/nodes/print.c b/src/backend/nodes/print.c
new file mode 100644
index 0000000..4247672
--- /dev/null
+++ b/src/backend/nodes/print.c
@@ -0,0 +1,507 @@
+/*-------------------------------------------------------------------------
+ *
+ * print.c
+ * various print routines (used mostly for debugging)
+ *
+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/nodes/print.c
+ *
+ * HISTORY
+ * AUTHOR DATE MAJOR EVENT
+ * Andrew Yu Oct 26, 1994 file creation
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/printtup.h"
+#include "lib/stringinfo.h"
+#include "nodes/nodeFuncs.h"
+#include "nodes/pathnodes.h"
+#include "nodes/print.h"
+#include "parser/parsetree.h"
+#include "utils/lsyscache.h"
+
+
+/*
+ * print
+ * print contents of Node to stdout
+ */
+void
+print(const void *obj)
+{
+ char *s;
+ char *f;
+
+ s = nodeToString(obj);
+ 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);
+}
+
+/*
+ * 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;
+ 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;
+
+ initStringInfo(&str);
+ indentLev = 0; /* logical indent level */
+ indentDist = 0; /* physical indent distance */
+ i = 0;
+ for (;;)
+ {
+ for (j = 0; j < indentDist; j++)
+ line[j] = ' ';
+ for (; j < LINELEN && dump[i] != '\0'; i++, j++)
+ {
+ line[j] = dump[i];
+ switch (line[j])
+ {
+ case '}':
+ if (j != indentDist)
+ {
+ /* print data before the } */
+ line[j] = '\0';
+ appendStringInfo(&str, "%s\n", line);
+ }
+ /* print the } at indentDist */
+ line[indentDist] = '}';
+ line[indentDist + 1] = '\0';
+ appendStringInfo(&str, "%s\n", line);
+ /* outdent */
+ if (indentLev > 0)
+ {
+ indentLev--;
+ indentDist = Min(indentLev * INDENTSTOP, MAXINDENT);
+ }
+ 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 ), 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++;
+ indentDist = Min(indentLev * INDENTSTOP, MAXINDENT);
+ for (j = 0; j < indentDist; j++)
+ line[j] = ' ';
+ line[j] = dump[i];
+ break;
+ case ':':
+ /* force line break before : */
+ if (j != indentDist)
+ {
+ line[j] = '\0';
+ appendStringInfo(&str, "%s\n", line);
+ }
+ j = indentDist;
+ line[j] = dump[i];
+ break;
+ }
+ }
+ line[j] = '\0';
+ if (dump[i] == '\0')
+ break;
+ appendStringInfo(&str, "%s\n", line);
+ }
+ if (j > 0)
+ appendStringInfo(&str, "%s\n", line);
+ return str.data;
+#undef INDENTSTOP
+#undef MAXINDENT
+#undef LINELEN
+}
+
+/*
+ * print_rt
+ * print contents of range table
+ */
+void
+print_rt(const List *rtable)
+{
+ const ListCell *l;
+ int i = 1;
+
+ printf("resno\trefname \trelid\tinFromCl\n");
+ printf("-----\t---------\t-----\t--------\n");
+ foreach(l, rtable)
+ {
+ RangeTblEntry *rte = lfirst(l);
+
+ 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_expr
+ * print an expression
+ */
+void
+print_expr(const Node *expr, const List *rtable)
+{
+ if (expr == NULL)
+ {
+ printf("<>");
+ return;
+ }
+
+ if (IsA(expr, Var))
+ {
+ const Var *var = (const Var *) expr;
+ char *relname,
+ *attname;
+
+ switch (var->varno)
+ {
+ case INNER_VAR:
+ relname = "INNER";
+ attname = "?";
+ break;
+ case OUTER_VAR:
+ relname = "OUTER";
+ attname = "?";
+ break;
+ case INDEX_VAR:
+ relname = "INDEX";
+ attname = "?";
+ break;
+ default:
+ {
+ 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, Const))
+ {
+ const Const *c = (const Const *) expr;
+ Oid typoutput;
+ bool typIsVarlena;
+ char *outputstr;
+
+ if (c->constisnull)
+ {
+ printf("NULL");
+ return;
+ }
+
+ 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(get_rightop((const Expr *) e), rtable);
+ }
+ else
+ {
+ /* 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(e->args, l))
+ printf(",");
+ }
+ printf(")");
+ }
+ else
+ printf("unknown expr");
+}
+
+/*
+ * print_pathkeys -
+ * pathkeys list of PathKeys
+ */
+void
+print_pathkeys(const List *pathkeys, const List *rtable)
+{
+ const ListCell *i;
+
+ printf("(");
+ foreach(i, pathkeys)
+ {
+ 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, eclass->ec_members)
+ {
+ EquivalenceMember *mem = (EquivalenceMember *) lfirst(k);
+
+ if (first)
+ first = false;
+ else
+ printf(", ");
+ print_expr((Node *) mem->em_expr, rtable);
+ }
+ printf(")");
+ if (lnext(pathkeys, i))
+ printf(", ");
+ }
+ printf(")\n");
+}
+
+/*
+ * print_tl
+ * print targetlist in a more legible way.
+ */
+void
+print_tl(const List *tlist, const List *rtable)
+{
+ const ListCell *tl;
+
+ printf("(\n");
+ foreach(tl, tlist)
+ {
+ TargetEntry *tle = (TargetEntry *) lfirst(tl);
+
+ 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((Node *) tle->expr, rtable);
+ printf("\n");
+ }
+ printf(")\n");
+}
+
+/*
+ * print_slot
+ * print out the tuple with the given TupleTableSlot
+ */
+void
+print_slot(TupleTableSlot *slot)
+{
+ if (TupIsNull(slot))
+ {
+ printf("tuple is null.\n");
+ return;
+ }
+ if (!slot->tts_tupleDescriptor)
+ {
+ printf("no tuple descriptor.\n");
+ return;
+ }
+
+ debugtup(slot, NULL);
+}