postgresql/src/backend/nodes/print.c

507 lines
9.7 KiB
C

/*-------------------------------------------------------------------------
*
* 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
{
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);
}