mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-08-31 16:07:23 +02:00
495 lines
9.4 KiB
C
495 lines
9.4 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* print.c
|
|
* various print routines (used mostly for debugging)
|
|
*
|
|
* Portions Copyright (c) 1996-2014, 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/print.h"
|
|
#include "optimizer/clauses.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_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;
|
|
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(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(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);
|
|
}
|