1996-07-09 08:22:35 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
1999-02-14 00:22:53 +01:00
|
|
|
* print.c
|
1997-09-07 07:04:48 +02:00
|
|
|
* various print routines (used mostly for debugging)
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
2019-01-02 18:44:25 +01:00
|
|
|
* Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
|
2000-01-26 06:58:53 +01:00
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
2010-09-20 22:08:53 +02:00
|
|
|
* src/backend/nodes/print.c
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
* HISTORY
|
1997-09-07 07:04:48 +02:00
|
|
|
* AUTHOR DATE MAJOR EVENT
|
|
|
|
* Andrew Yu Oct 26, 1994 file creation
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
1996-11-06 09:54:17 +01:00
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
#include "postgres.h"
|
1996-11-06 09:54:17 +01:00
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
#include "access/printtup.h"
|
2012-08-29 01:02:00 +02:00
|
|
|
#include "lib/stringinfo.h"
|
2019-01-29 21:26:44 +01:00
|
|
|
#include "nodes/nodeFuncs.h"
|
2019-01-29 22:49:25 +01:00
|
|
|
#include "nodes/pathnodes.h"
|
1996-11-10 04:06:38 +01:00
|
|
|
#include "nodes/print.h"
|
1996-07-09 08:22:35 +02:00
|
|
|
#include "parser/parsetree.h"
|
|
|
|
#include "utils/lsyscache.h"
|
1997-08-19 23:40:56 +02:00
|
|
|
|
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
/*
|
1999-02-14 00:22:53 +01:00
|
|
|
* print
|
1997-09-07 07:04:48 +02:00
|
|
|
* print contents of Node to stdout
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
|
|
|
void
|
2011-12-07 20:46:56 +01:00
|
|
|
print(const void *obj)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
char *s;
|
2002-03-24 05:31:09 +01:00
|
|
|
char *f;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
s = nodeToString(obj);
|
2002-03-24 05:31:09 +01:00
|
|
|
f = format_node_dump(s);
|
2001-12-19 23:35:35 +01:00
|
|
|
pfree(s);
|
2002-03-24 05:31:09 +01:00
|
|
|
printf("%s\n", f);
|
|
|
|
fflush(stdout);
|
|
|
|
pfree(f);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2002-03-24 05:31:09 +01:00
|
|
|
* pprint
|
|
|
|
* pretty-print contents of Node to stdout
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
|
|
|
void
|
2011-12-07 20:46:56 +01:00
|
|
|
pprint(const void *obj)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
char *s;
|
2002-03-24 05:31:09 +01:00
|
|
|
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
|
2011-12-07 20:46:56 +01:00
|
|
|
elog_node_display(int lev, const char *title, const void *obj, bool pretty)
|
2002-03-24 05:31:09 +01:00
|
|
|
{
|
|
|
|
char *s;
|
|
|
|
char *f;
|
|
|
|
|
|
|
|
s = nodeToString(obj);
|
|
|
|
if (pretty)
|
|
|
|
f = pretty_format_node_dump(s);
|
|
|
|
else
|
|
|
|
f = format_node_dump(s);
|
|
|
|
pfree(s);
|
2003-07-23 01:30:39 +02:00
|
|
|
ereport(lev,
|
|
|
|
(errmsg_internal("%s:", title),
|
2011-07-16 20:21:12 +02:00
|
|
|
errdetail_internal("%s", f)));
|
2002-03-24 05:31:09 +01:00
|
|
|
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
|
2002-09-04 22:31:48 +02:00
|
|
|
char line[LINELEN + 1];
|
2002-03-24 05:31:09 +01:00
|
|
|
StringInfoData str;
|
1997-09-08 04:41:22 +02:00
|
|
|
int i;
|
2002-03-24 05:31:09 +01:00
|
|
|
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
|
|
|
|
{
|
2002-09-04 22:31:48 +02:00
|
|
|
for (k = j - 1; k > 0; k--)
|
2002-03-24 05:31:09 +01:00
|
|
|
if (line[k] == ' ')
|
|
|
|
break;
|
|
|
|
if (k > 0)
|
|
|
|
{
|
|
|
|
/* back up; will reprint all after space */
|
2002-09-04 22:31:48 +02:00
|
|
|
i -= (j - k - 1);
|
2002-03-24 05:31:09 +01:00
|
|
|
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
|
2002-09-04 22:31:48 +02:00
|
|
|
char line[LINELEN + 1];
|
2002-03-24 05:31:09 +01:00
|
|
|
StringInfoData str;
|
1997-09-08 04:41:22 +02:00
|
|
|
int indentLev;
|
2001-12-19 23:35:35 +01:00
|
|
|
int indentDist;
|
2002-03-24 05:31:09 +01:00
|
|
|
int i;
|
1997-09-08 04:41:22 +02:00
|
|
|
int j;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2002-03-24 05:31:09 +01:00
|
|
|
initStringInfo(&str);
|
2001-12-19 23:35:35 +01:00
|
|
|
indentLev = 0; /* logical indent level */
|
|
|
|
indentDist = 0; /* physical indent distance */
|
1997-09-07 07:04:48 +02:00
|
|
|
i = 0;
|
|
|
|
for (;;)
|
|
|
|
{
|
2001-12-19 23:35:35 +01:00
|
|
|
for (j = 0; j < indentDist; j++)
|
1996-07-09 08:22:35 +02:00
|
|
|
line[j] = ' ';
|
2002-03-24 05:31:09 +01:00
|
|
|
for (; j < LINELEN && dump[i] != '\0'; i++, j++)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2002-03-24 05:31:09 +01:00
|
|
|
line[j] = dump[i];
|
1997-09-07 07:04:48 +02:00
|
|
|
switch (line[j])
|
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
case '}':
|
2001-12-19 23:35:35 +01:00
|
|
|
if (j != indentDist)
|
1997-09-08 04:41:22 +02:00
|
|
|
{
|
2001-12-19 23:35:35 +01:00
|
|
|
/* print data before the } */
|
1997-09-08 04:41:22 +02:00
|
|
|
line[j] = '\0';
|
2002-03-24 05:31:09 +01:00
|
|
|
appendStringInfo(&str, "%s\n", line);
|
1997-09-08 04:41:22 +02:00
|
|
|
}
|
2001-12-19 23:35:35 +01:00
|
|
|
/* print the } at indentDist */
|
2002-03-24 05:31:09 +01:00
|
|
|
line[indentDist] = '}';
|
2002-09-04 22:31:48 +02:00
|
|
|
line[indentDist + 1] = '\0';
|
2002-03-24 05:31:09 +01:00
|
|
|
appendStringInfo(&str, "%s\n", line);
|
2001-12-19 23:35:35 +01:00
|
|
|
/* outdent */
|
|
|
|
if (indentLev > 0)
|
1997-09-08 04:41:22 +02:00
|
|
|
{
|
2001-12-19 23:35:35 +01:00
|
|
|
indentLev--;
|
2001-12-20 03:39:26 +01:00
|
|
|
indentDist = Min(indentLev * INDENTSTOP, MAXINDENT);
|
1997-09-08 04:41:22 +02:00
|
|
|
}
|
2001-12-19 23:35:35 +01:00
|
|
|
j = indentDist - 1;
|
|
|
|
/* j will equal indentDist on next loop iteration */
|
2004-05-08 23:21:18 +02:00
|
|
|
/* suppress whitespace just after } */
|
2004-08-29 07:07:03 +02:00
|
|
|
while (dump[i + 1] == ' ')
|
2004-05-08 23:21:18 +02:00
|
|
|
i++;
|
1997-09-08 04:41:22 +02:00
|
|
|
break;
|
|
|
|
case ')':
|
2004-05-08 23:21:18 +02:00
|
|
|
/* force line break after ), unless another ) follows */
|
2004-08-29 07:07:03 +02:00
|
|
|
if (dump[i + 1] != ')')
|
2004-05-08 23:21:18 +02:00
|
|
|
{
|
|
|
|
line[j + 1] = '\0';
|
|
|
|
appendStringInfo(&str, "%s\n", line);
|
|
|
|
j = indentDist - 1;
|
2004-08-29 07:07:03 +02:00
|
|
|
while (dump[i + 1] == ' ')
|
2004-05-08 23:21:18 +02:00
|
|
|
i++;
|
|
|
|
}
|
1997-09-08 04:41:22 +02:00
|
|
|
break;
|
|
|
|
case '{':
|
2001-12-19 23:35:35 +01:00
|
|
|
/* force line break before { */
|
|
|
|
if (j != indentDist)
|
|
|
|
{
|
|
|
|
line[j] = '\0';
|
2002-03-24 05:31:09 +01:00
|
|
|
appendStringInfo(&str, "%s\n", line);
|
2001-12-19 23:35:35 +01:00
|
|
|
}
|
|
|
|
/* indent */
|
1997-09-08 04:41:22 +02:00
|
|
|
indentLev++;
|
2001-12-20 03:39:26 +01:00
|
|
|
indentDist = Min(indentLev * INDENTSTOP, MAXINDENT);
|
2001-12-19 23:35:35 +01:00
|
|
|
for (j = 0; j < indentDist; j++)
|
|
|
|
line[j] = ' ';
|
2002-03-24 05:31:09 +01:00
|
|
|
line[j] = dump[i];
|
2001-12-19 23:35:35 +01:00
|
|
|
break;
|
1997-09-08 04:41:22 +02:00
|
|
|
case ':':
|
2001-12-19 23:35:35 +01:00
|
|
|
/* force line break before : */
|
|
|
|
if (j != indentDist)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
line[j] = '\0';
|
2002-03-24 05:31:09 +01:00
|
|
|
appendStringInfo(&str, "%s\n", line);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
2001-12-19 23:35:35 +01:00
|
|
|
j = indentDist;
|
2002-03-24 05:31:09 +01:00
|
|
|
line[j] = dump[i];
|
1997-09-08 04:41:22 +02:00
|
|
|
break;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
line[j] = '\0';
|
2002-03-24 05:31:09 +01:00
|
|
|
if (dump[i] == '\0')
|
1997-09-07 07:04:48 +02:00
|
|
|
break;
|
2002-03-24 05:31:09 +01:00
|
|
|
appendStringInfo(&str, "%s\n", line);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
2002-03-24 05:31:09 +01:00
|
|
|
if (j > 0)
|
|
|
|
appendStringInfo(&str, "%s\n", line);
|
|
|
|
return str.data;
|
|
|
|
#undef INDENTSTOP
|
|
|
|
#undef MAXINDENT
|
|
|
|
#undef LINELEN
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
1999-02-14 00:22:53 +01:00
|
|
|
* print_rt
|
1997-09-07 07:04:48 +02:00
|
|
|
* print contents of range table
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
|
|
|
void
|
2011-12-07 20:46:56 +01:00
|
|
|
print_rt(const List *rtable)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2012-06-10 21:20:04 +02:00
|
|
|
const ListCell *l;
|
1997-09-08 04:41:22 +02:00
|
|
|
int i = 1;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2002-03-22 03:56:37 +01:00
|
|
|
printf("resno\trefname \trelid\tinFromCl\n");
|
|
|
|
printf("-----\t---------\t-----\t--------\n");
|
1997-09-07 07:04:48 +02:00
|
|
|
foreach(l, rtable)
|
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
RangeTblEntry *rte = lfirst(l);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2002-05-12 22:10:05 +02:00
|
|
|
switch (rte->rtekind)
|
|
|
|
{
|
|
|
|
case RTE_RELATION:
|
2011-02-23 01:23:23 +01:00
|
|
|
printf("%d\t%s\t%u\t%c",
|
|
|
|
i, rte->eref->aliasname, rte->relid, rte->relkind);
|
2002-05-12 22:10:05 +02:00
|
|
|
break;
|
|
|
|
case RTE_SUBQUERY:
|
|
|
|
printf("%d\t%s\t[subquery]",
|
|
|
|
i, rte->eref->aliasname);
|
|
|
|
break;
|
2008-10-04 23:56:55 +02:00
|
|
|
case RTE_JOIN:
|
|
|
|
printf("%d\t%s\t[join]",
|
|
|
|
i, rte->eref->aliasname);
|
|
|
|
break;
|
2002-05-12 22:10:05 +02:00
|
|
|
case RTE_FUNCTION:
|
|
|
|
printf("%d\t%s\t[rangefunction]",
|
2017-03-08 16:39:37 +01:00
|
|
|
i, rte->eref->aliasname);
|
|
|
|
break;
|
|
|
|
case RTE_TABLEFUNC:
|
|
|
|
printf("%d\t%s\t[table function]",
|
2002-05-12 22:10:05 +02:00
|
|
|
i, rte->eref->aliasname);
|
|
|
|
break;
|
2006-08-02 03:59:48 +02:00
|
|
|
case RTE_VALUES:
|
|
|
|
printf("%d\t%s\t[values list]",
|
|
|
|
i, rte->eref->aliasname);
|
|
|
|
break;
|
2008-10-04 23:56:55 +02:00
|
|
|
case RTE_CTE:
|
|
|
|
printf("%d\t%s\t[cte]",
|
2002-05-12 22:10:05 +02:00
|
|
|
i, rte->eref->aliasname);
|
|
|
|
break;
|
2017-04-01 06:17:18 +02:00
|
|
|
case RTE_NAMEDTUPLESTORE:
|
|
|
|
printf("%d\t%s\t[tuplestore]",
|
|
|
|
i, rte->eref->aliasname);
|
|
|
|
break;
|
In the planner, replace an empty FROM clause with a dummy RTE.
The fact that "SELECT expression" has no base relations has long been a
thorn in the side of the planner. It makes it hard to flatten a sub-query
that looks like that, or is a trivial VALUES() item, because the planner
generally uses relid sets to identify sub-relations, and such a sub-query
would have an empty relid set if we flattened it. prepjointree.c contains
some baroque logic that works around this in certain special cases --- but
there is a much better answer. We can replace an empty FROM clause with a
dummy RTE that acts like a table of one row and no columns, and then there
are no such corner cases to worry about. Instead we need some logic to
get rid of useless dummy RTEs, but that's simpler and covers more cases
than what was there before.
For really trivial cases, where the query is just "SELECT expression" and
nothing else, there's a hazard that adding the extra RTE makes for a
noticeable slowdown; even though it's not much processing, there's not
that much for the planner to do overall. However testing says that the
penalty is very small, close to the noise level. In more complex queries,
this is able to find optimizations that we could not find before.
The new RTE type is called RTE_RESULT, since the "scan" plan type it
gives rise to is a Result node (the same plan we produced for a "SELECT
expression" query before). To avoid confusion, rename the old ResultPath
path type to GroupResultPath, reflecting that it's only used in degenerate
grouping cases where we know the query produces just one grouped row.
(It wouldn't work to unify the two cases, because there are different
rules about where the associated quals live during query_planner.)
Note: although this touches readfuncs.c, I don't think a catversion
bump is required, because the added case can't occur in stored rules,
only plans.
Patch by me, reviewed by David Rowley and Mark Dilger
Discussion: https://postgr.es/m/15944.1521127664@sss.pgh.pa.us
2019-01-28 23:54:10 +01:00
|
|
|
case RTE_RESULT:
|
|
|
|
printf("%d\t%s\t[result]",
|
|
|
|
i, rte->eref->aliasname);
|
|
|
|
break;
|
2002-05-12 22:10:05 +02:00
|
|
|
default:
|
|
|
|
printf("%d\t%s\t[unknown rtekind]",
|
|
|
|
i, rte->eref->aliasname);
|
|
|
|
}
|
|
|
|
|
2000-09-29 20:21:41 +02:00
|
|
|
printf("\t%s\t%s\n",
|
|
|
|
(rte->inh ? "inh" : ""),
|
|
|
|
(rte->inFromCl ? "inFromCl" : ""));
|
1997-09-07 07:04:48 +02:00
|
|
|
i++;
|
|
|
|
}
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
1999-02-14 00:22:53 +01:00
|
|
|
* print_expr
|
1997-09-07 07:04:48 +02:00
|
|
|
* print an expression
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
|
|
|
void
|
2011-12-07 20:46:56 +01:00
|
|
|
print_expr(const Node *expr, const List *rtable)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-07 07:04:48 +02:00
|
|
|
if (expr == NULL)
|
|
|
|
{
|
1998-01-07 16:32:47 +01:00
|
|
|
printf("<>");
|
1997-09-07 07:04:48 +02:00
|
|
|
return;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
if (IsA(expr, Var))
|
|
|
|
{
|
2012-06-10 21:20:04 +02:00
|
|
|
const Var *var = (const Var *) expr;
|
1997-09-08 04:41:22 +02:00
|
|
|
char *relname,
|
|
|
|
*attname;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
switch (var->varno)
|
|
|
|
{
|
2011-10-11 20:20:06 +02:00
|
|
|
case INNER_VAR:
|
1997-09-08 04:41:22 +02:00
|
|
|
relname = "INNER";
|
|
|
|
attname = "?";
|
|
|
|
break;
|
2011-10-11 20:20:06 +02:00
|
|
|
case OUTER_VAR:
|
1997-09-08 04:41:22 +02:00
|
|
|
relname = "OUTER";
|
|
|
|
attname = "?";
|
|
|
|
break;
|
2011-10-11 20:20:06 +02:00
|
|
|
case INDEX_VAR:
|
|
|
|
relname = "INDEX";
|
|
|
|
attname = "?";
|
|
|
|
break;
|
1997-09-08 04:41:22 +02:00
|
|
|
default:
|
|
|
|
{
|
2000-09-25 20:14:55 +02:00
|
|
|
RangeTblEntry *rte;
|
2000-09-12 23:07:18 +02:00
|
|
|
|
2000-09-25 20:14:55 +02:00
|
|
|
Assert(var->varno > 0 &&
|
2004-05-31 01:40:41 +02:00
|
|
|
(int) var->varno <= list_length(rtable));
|
2000-09-25 20:14:55 +02:00
|
|
|
rte = rt_fetch(var->varno, rtable);
|
2002-03-21 17:02:16 +01:00
|
|
|
relname = rte->eref->aliasname;
|
2000-09-25 20:14:55 +02:00
|
|
|
attname = get_rte_attribute_name(rte, var->varattno);
|
1997-09-08 04:41:22 +02:00
|
|
|
}
|
|
|
|
break;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
printf("%s.%s", relname, attname);
|
|
|
|
}
|
2001-10-18 18:11:42 +02:00
|
|
|
else if (IsA(expr, Const))
|
|
|
|
{
|
2012-06-10 21:20:04 +02:00
|
|
|
const Const *c = (const Const *) expr;
|
2001-10-18 18:11:42 +02:00
|
|
|
Oid typoutput;
|
2004-06-06 02:41:28 +02:00
|
|
|
bool typIsVarlena;
|
2001-10-18 18:11:42 +02:00
|
|
|
char *outputstr;
|
|
|
|
|
|
|
|
if (c->constisnull)
|
|
|
|
{
|
|
|
|
printf("NULL");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2004-06-06 02:41:28 +02:00
|
|
|
getTypeOutputInfo(c->consttype,
|
2005-05-01 20:56:19 +02:00
|
|
|
&typoutput, &typIsVarlena);
|
2001-10-18 18:11:42 +02:00
|
|
|
|
2006-04-04 21:35:37 +02:00
|
|
|
outputstr = OidOutputFunctionCall(typoutput, c->constvalue);
|
2001-10-18 18:11:42 +02:00
|
|
|
printf("%s", outputstr);
|
|
|
|
pfree(outputstr);
|
|
|
|
}
|
2003-01-22 20:26:35 +01:00
|
|
|
else if (IsA(expr, OpExpr))
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2012-06-10 21:20:04 +02:00
|
|
|
const OpExpr *e = (const OpExpr *) expr;
|
2003-01-22 20:26:35 +01:00
|
|
|
char *opname;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2003-01-22 20:26:35 +01:00
|
|
|
opname = get_opname(e->opno);
|
2004-05-31 01:40:41 +02:00
|
|
|
if (list_length(e->args) > 1)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2011-12-07 20:46:56 +01:00
|
|
|
print_expr(get_leftop((const Expr *) e), rtable);
|
1998-09-01 06:40:42 +02:00
|
|
|
printf(" %s ", ((opname != NULL) ? opname : "(invalid operator)"));
|
2011-12-07 20:46:56 +01:00
|
|
|
print_expr(get_rightop((const Expr *) e), rtable);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
else
|
2003-01-22 20:26:35 +01:00
|
|
|
{
|
|
|
|
/* we print prefix and postfix ops the same... */
|
|
|
|
printf("%s ", ((opname != NULL) ? opname : "(invalid operator)"));
|
2011-12-07 20:46:56 +01:00
|
|
|
print_expr(get_leftop((const Expr *) e), rtable);
|
2003-01-22 20:26:35 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (IsA(expr, FuncExpr))
|
|
|
|
{
|
2012-06-10 21:20:04 +02:00
|
|
|
const FuncExpr *e = (const FuncExpr *) expr;
|
2003-01-22 20:26:35 +01:00
|
|
|
char *funcname;
|
2004-05-26 06:41:50 +02:00
|
|
|
ListCell *l;
|
2003-01-22 20:26:35 +01:00
|
|
|
|
|
|
|
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(")");
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
else
|
2003-01-22 20:26:35 +01:00
|
|
|
printf("unknown expr");
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
1999-02-10 04:52:54 +01:00
|
|
|
* print_pathkeys -
|
2007-01-20 21:45:41 +01:00
|
|
|
* pathkeys list of PathKeys
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
|
|
|
void
|
2011-12-07 20:46:56 +01:00
|
|
|
print_pathkeys(const List *pathkeys, const List *rtable)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2012-06-10 21:20:04 +02:00
|
|
|
const ListCell *i;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
printf("(");
|
1999-02-20 20:02:43 +01:00
|
|
|
foreach(i, pathkeys)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2007-11-15 22:14:46 +01:00
|
|
|
PathKey *pathkey = (PathKey *) lfirst(i);
|
2007-01-20 21:45:41 +01:00
|
|
|
EquivalenceClass *eclass;
|
2004-05-26 06:41:50 +02:00
|
|
|
ListCell *k;
|
2007-01-20 21:45:41 +01:00
|
|
|
bool first = true;
|
|
|
|
|
|
|
|
eclass = pathkey->pk_eclass;
|
|
|
|
/* chase up, in case pathkey is non-canonical */
|
|
|
|
while (eclass->ec_merged)
|
|
|
|
eclass = eclass->ec_merged;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-02-20 20:02:43 +01:00
|
|
|
printf("(");
|
2007-01-20 21:45:41 +01:00
|
|
|
foreach(k, eclass->ec_members)
|
1999-02-20 20:02:43 +01:00
|
|
|
{
|
2007-01-20 21:45:41 +01:00
|
|
|
EquivalenceMember *mem = (EquivalenceMember *) lfirst(k);
|
1999-05-25 18:15:34 +02:00
|
|
|
|
2007-01-20 21:45:41 +01:00
|
|
|
if (first)
|
|
|
|
first = false;
|
|
|
|
else
|
1999-02-20 20:02:43 +01:00
|
|
|
printf(", ");
|
2007-01-20 21:45:41 +01:00
|
|
|
print_expr((Node *) mem->em_expr, rtable);
|
1999-02-20 20:02:43 +01:00
|
|
|
}
|
2001-10-18 18:11:42 +02:00
|
|
|
printf(")");
|
1999-02-20 20:02:43 +01:00
|
|
|
if (lnext(i))
|
1997-09-07 07:04:48 +02:00
|
|
|
printf(", ");
|
|
|
|
}
|
|
|
|
printf(")\n");
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
1999-05-25 18:15:34 +02:00
|
|
|
* print_tl
|
1997-09-07 07:04:48 +02:00
|
|
|
* print targetlist in a more legible way.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
1997-09-07 07:04:48 +02:00
|
|
|
void
|
2011-12-07 20:46:56 +01:00
|
|
|
print_tl(const List *tlist, const List *rtable)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2012-06-10 21:20:04 +02:00
|
|
|
const ListCell *tl;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
printf("(\n");
|
|
|
|
foreach(tl, tlist)
|
|
|
|
{
|
2004-05-26 06:41:50 +02:00
|
|
|
TargetEntry *tle = (TargetEntry *) lfirst(tl);
|
1996-07-09 08:22:35 +02:00
|
|
|
|
2005-04-06 18:34:07 +02:00
|
|
|
printf("\t%d %s\t", tle->resno,
|
|
|
|
tle->resname ? tle->resname : "<null>");
|
|
|
|
if (tle->ressortgroupref != 0)
|
|
|
|
printf("(%u):\t", tle->ressortgroupref);
|
1997-09-07 07:04:48 +02:00
|
|
|
else
|
|
|
|
printf(" :\t");
|
2002-12-12 16:49:42 +01:00
|
|
|
print_expr((Node *) tle->expr, rtable);
|
1997-09-07 07:04:48 +02:00
|
|
|
printf("\n");
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
printf(")\n");
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
1999-02-14 00:22:53 +01:00
|
|
|
* print_slot
|
1997-09-07 07:04:48 +02:00
|
|
|
* print out the tuple with the given TupleTableSlot
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
|
|
|
void
|
1997-09-08 23:56:23 +02:00
|
|
|
print_slot(TupleTableSlot *slot)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2005-03-16 22:38:10 +01:00
|
|
|
if (TupIsNull(slot))
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
|
|
|
printf("tuple is null.\n");
|
|
|
|
return;
|
|
|
|
}
|
2005-03-16 22:38:10 +01:00
|
|
|
if (!slot->tts_tupleDescriptor)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
|
|
|
printf("no tuple descriptor.\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2005-03-16 22:38:10 +01:00
|
|
|
debugtup(slot, NULL);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|