
Read/out support in 5ca0fe5c8ad7 was missing/incomplete, per Tom Lane. Again, as far as core is concerned, this is not only dead code but also untested; however, third parties may come to rely on it, so the standard features should work. Discussion: https://postgr.es/m/1548311.1657636605@sss.pgh.pa.us
900 lines
23 KiB
C
900 lines
23 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* outfuncs.c
|
|
* Output functions for Postgres tree nodes.
|
|
*
|
|
* Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
*
|
|
* IDENTIFICATION
|
|
* src/backend/nodes/outfuncs.c
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
#include "postgres.h"
|
|
|
|
#include <ctype.h>
|
|
|
|
#include "lib/stringinfo.h"
|
|
#include "miscadmin.h"
|
|
#include "nodes/bitmapset.h"
|
|
#include "nodes/nodes.h"
|
|
#include "nodes/pg_list.h"
|
|
#include "utils/datum.h"
|
|
|
|
static void outChar(StringInfo str, char c);
|
|
|
|
|
|
/*
|
|
* Macros to simplify output of different kinds of fields. Use these
|
|
* wherever possible to reduce the chance for silly typos. Note that these
|
|
* hard-wire conventions about the names of the local variables in an Out
|
|
* routine.
|
|
*/
|
|
|
|
/* Write the label for the node type */
|
|
#define WRITE_NODE_TYPE(nodelabel) \
|
|
appendStringInfoString(str, nodelabel)
|
|
|
|
/* Write an integer field (anything written as ":fldname %d") */
|
|
#define WRITE_INT_FIELD(fldname) \
|
|
appendStringInfo(str, " :" CppAsString(fldname) " %d", node->fldname)
|
|
|
|
/* Write an unsigned integer field (anything written as ":fldname %u") */
|
|
#define WRITE_UINT_FIELD(fldname) \
|
|
appendStringInfo(str, " :" CppAsString(fldname) " %u", node->fldname)
|
|
|
|
/* Write an unsigned integer field (anything written with UINT64_FORMAT) */
|
|
#define WRITE_UINT64_FIELD(fldname) \
|
|
appendStringInfo(str, " :" CppAsString(fldname) " " UINT64_FORMAT, \
|
|
node->fldname)
|
|
|
|
/* Write an OID field (don't hard-wire assumption that OID is same as uint) */
|
|
#define WRITE_OID_FIELD(fldname) \
|
|
appendStringInfo(str, " :" CppAsString(fldname) " %u", node->fldname)
|
|
|
|
/* Write a long-integer field */
|
|
#define WRITE_LONG_FIELD(fldname) \
|
|
appendStringInfo(str, " :" CppAsString(fldname) " %ld", node->fldname)
|
|
|
|
/* Write a char field (ie, one ascii character) */
|
|
#define WRITE_CHAR_FIELD(fldname) \
|
|
(appendStringInfo(str, " :" CppAsString(fldname) " "), \
|
|
outChar(str, node->fldname))
|
|
|
|
/* Write an enumerated-type field as an integer code */
|
|
#define WRITE_ENUM_FIELD(fldname, enumtype) \
|
|
appendStringInfo(str, " :" CppAsString(fldname) " %d", \
|
|
(int) node->fldname)
|
|
|
|
/* Write a float field --- caller must give format to define precision */
|
|
#define WRITE_FLOAT_FIELD(fldname,format) \
|
|
appendStringInfo(str, " :" CppAsString(fldname) " " format, node->fldname)
|
|
|
|
/* Write a boolean field */
|
|
#define WRITE_BOOL_FIELD(fldname) \
|
|
appendStringInfo(str, " :" CppAsString(fldname) " %s", \
|
|
booltostr(node->fldname))
|
|
|
|
/* Write a character-string (possibly NULL) field */
|
|
#define WRITE_STRING_FIELD(fldname) \
|
|
(appendStringInfoString(str, " :" CppAsString(fldname) " "), \
|
|
outToken(str, node->fldname))
|
|
|
|
/* Write a parse location field (actually same as INT case) */
|
|
#define WRITE_LOCATION_FIELD(fldname) \
|
|
appendStringInfo(str, " :" CppAsString(fldname) " %d", node->fldname)
|
|
|
|
/* Write a Node field */
|
|
#define WRITE_NODE_FIELD(fldname) \
|
|
(appendStringInfoString(str, " :" CppAsString(fldname) " "), \
|
|
outNode(str, node->fldname))
|
|
|
|
/* Write a bitmapset field */
|
|
#define WRITE_BITMAPSET_FIELD(fldname) \
|
|
(appendStringInfoString(str, " :" CppAsString(fldname) " "), \
|
|
outBitmapset(str, node->fldname))
|
|
|
|
#define WRITE_ATTRNUMBER_ARRAY(fldname, len) \
|
|
do { \
|
|
appendStringInfoString(str, " :" CppAsString(fldname) " "); \
|
|
for (int i = 0; i < len; i++) \
|
|
appendStringInfo(str, " %d", node->fldname[i]); \
|
|
} while(0)
|
|
|
|
#define WRITE_OID_ARRAY(fldname, len) \
|
|
do { \
|
|
appendStringInfoString(str, " :" CppAsString(fldname) " "); \
|
|
for (int i = 0; i < len; i++) \
|
|
appendStringInfo(str, " %u", node->fldname[i]); \
|
|
} while(0)
|
|
|
|
/*
|
|
* This macro supports the case that the field is NULL. For the other array
|
|
* macros, that is currently not needed.
|
|
*/
|
|
#define WRITE_INDEX_ARRAY(fldname, len) \
|
|
do { \
|
|
appendStringInfoString(str, " :" CppAsString(fldname) " "); \
|
|
if (node->fldname) \
|
|
for (int i = 0; i < len; i++) \
|
|
appendStringInfo(str, " %u", node->fldname[i]); \
|
|
else \
|
|
appendStringInfoString(str, "<>"); \
|
|
} while(0)
|
|
|
|
#define WRITE_INT_ARRAY(fldname, len) \
|
|
do { \
|
|
appendStringInfoString(str, " :" CppAsString(fldname) " "); \
|
|
for (int i = 0; i < len; i++) \
|
|
appendStringInfo(str, " %d", node->fldname[i]); \
|
|
} while(0)
|
|
|
|
#define WRITE_BOOL_ARRAY(fldname, len) \
|
|
do { \
|
|
appendStringInfoString(str, " :" CppAsString(fldname) " "); \
|
|
for (int i = 0; i < len; i++) \
|
|
appendStringInfo(str, " %s", booltostr(node->fldname[i])); \
|
|
} while(0)
|
|
|
|
|
|
#define booltostr(x) ((x) ? "true" : "false")
|
|
|
|
|
|
/*
|
|
* outToken
|
|
* Convert an ordinary string (eg, an identifier) into a form that
|
|
* will be decoded back to a plain token by read.c's functions.
|
|
*
|
|
* If a null or empty string is given, it is encoded as "<>".
|
|
*/
|
|
void
|
|
outToken(StringInfo str, const char *s)
|
|
{
|
|
if (s == NULL || *s == '\0')
|
|
{
|
|
appendStringInfoString(str, "<>");
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Look for characters or patterns that are treated specially by read.c
|
|
* (either in pg_strtok() or in nodeRead()), and therefore need a
|
|
* protective backslash.
|
|
*/
|
|
/* These characters only need to be quoted at the start of the string */
|
|
if (*s == '<' ||
|
|
*s == '"' ||
|
|
isdigit((unsigned char) *s) ||
|
|
((*s == '+' || *s == '-') &&
|
|
(isdigit((unsigned char) s[1]) || s[1] == '.')))
|
|
appendStringInfoChar(str, '\\');
|
|
while (*s)
|
|
{
|
|
/* These chars must be backslashed anywhere in the string */
|
|
if (*s == ' ' || *s == '\n' || *s == '\t' ||
|
|
*s == '(' || *s == ')' || *s == '{' || *s == '}' ||
|
|
*s == '\\')
|
|
appendStringInfoChar(str, '\\');
|
|
appendStringInfoChar(str, *s++);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Convert one char. Goes through outToken() so that special characters are
|
|
* escaped.
|
|
*/
|
|
static void
|
|
outChar(StringInfo str, char c)
|
|
{
|
|
char in[2];
|
|
|
|
in[0] = c;
|
|
in[1] = '\0';
|
|
|
|
outToken(str, in);
|
|
}
|
|
|
|
static void
|
|
_outList(StringInfo str, const List *node)
|
|
{
|
|
const ListCell *lc;
|
|
|
|
appendStringInfoChar(str, '(');
|
|
|
|
if (IsA(node, IntList))
|
|
appendStringInfoChar(str, 'i');
|
|
else if (IsA(node, OidList))
|
|
appendStringInfoChar(str, 'o');
|
|
else if (IsA(node, XidList))
|
|
appendStringInfoChar(str, 'x');
|
|
|
|
foreach(lc, node)
|
|
{
|
|
/*
|
|
* For the sake of backward compatibility, we emit a slightly
|
|
* different whitespace format for lists of nodes vs. other types of
|
|
* lists. XXX: is this necessary?
|
|
*/
|
|
if (IsA(node, List))
|
|
{
|
|
outNode(str, lfirst(lc));
|
|
if (lnext(node, lc))
|
|
appendStringInfoChar(str, ' ');
|
|
}
|
|
else if (IsA(node, IntList))
|
|
appendStringInfo(str, " %d", lfirst_int(lc));
|
|
else if (IsA(node, OidList))
|
|
appendStringInfo(str, " %u", lfirst_oid(lc));
|
|
else if (IsA(node, XidList))
|
|
appendStringInfo(str, " %u", lfirst_xid(lc));
|
|
else
|
|
elog(ERROR, "unrecognized list node type: %d",
|
|
(int) node->type);
|
|
}
|
|
|
|
appendStringInfoChar(str, ')');
|
|
}
|
|
|
|
/*
|
|
* outBitmapset -
|
|
* converts a bitmap set of integers
|
|
*
|
|
* Note: the output format is "(b int int ...)", similar to an integer List.
|
|
*/
|
|
void
|
|
outBitmapset(StringInfo str, const Bitmapset *bms)
|
|
{
|
|
int x;
|
|
|
|
appendStringInfoChar(str, '(');
|
|
appendStringInfoChar(str, 'b');
|
|
x = -1;
|
|
while ((x = bms_next_member(bms, x)) >= 0)
|
|
appendStringInfo(str, " %d", x);
|
|
appendStringInfoChar(str, ')');
|
|
}
|
|
|
|
/*
|
|
* Print the value of a Datum given its type.
|
|
*/
|
|
void
|
|
outDatum(StringInfo str, Datum value, int typlen, bool typbyval)
|
|
{
|
|
Size length,
|
|
i;
|
|
char *s;
|
|
|
|
length = datumGetSize(value, typbyval, typlen);
|
|
|
|
if (typbyval)
|
|
{
|
|
s = (char *) (&value);
|
|
appendStringInfo(str, "%u [ ", (unsigned int) length);
|
|
for (i = 0; i < (Size) sizeof(Datum); i++)
|
|
appendStringInfo(str, "%d ", (int) (s[i]));
|
|
appendStringInfoChar(str, ']');
|
|
}
|
|
else
|
|
{
|
|
s = (char *) DatumGetPointer(value);
|
|
if (!PointerIsValid(s))
|
|
appendStringInfoString(str, "0 [ ]");
|
|
else
|
|
{
|
|
appendStringInfo(str, "%u [ ", (unsigned int) length);
|
|
for (i = 0; i < length; i++)
|
|
appendStringInfo(str, "%d ", (int) (s[i]));
|
|
appendStringInfoChar(str, ']');
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#include "outfuncs.funcs.c"
|
|
|
|
|
|
/*
|
|
* Support functions for nodes with custom_read_write attribute or
|
|
* special_read_write attribute
|
|
*/
|
|
|
|
static void
|
|
_outConst(StringInfo str, const Const *node)
|
|
{
|
|
WRITE_NODE_TYPE("CONST");
|
|
|
|
WRITE_OID_FIELD(consttype);
|
|
WRITE_INT_FIELD(consttypmod);
|
|
WRITE_OID_FIELD(constcollid);
|
|
WRITE_INT_FIELD(constlen);
|
|
WRITE_BOOL_FIELD(constbyval);
|
|
WRITE_BOOL_FIELD(constisnull);
|
|
WRITE_LOCATION_FIELD(location);
|
|
|
|
appendStringInfoString(str, " :constvalue ");
|
|
if (node->constisnull)
|
|
appendStringInfoString(str, "<>");
|
|
else
|
|
outDatum(str, node->constvalue, node->constlen, node->constbyval);
|
|
}
|
|
|
|
static void
|
|
_outBoolExpr(StringInfo str, const BoolExpr *node)
|
|
{
|
|
char *opstr = NULL;
|
|
|
|
WRITE_NODE_TYPE("BOOLEXPR");
|
|
|
|
/* do-it-yourself enum representation */
|
|
switch (node->boolop)
|
|
{
|
|
case AND_EXPR:
|
|
opstr = "and";
|
|
break;
|
|
case OR_EXPR:
|
|
opstr = "or";
|
|
break;
|
|
case NOT_EXPR:
|
|
opstr = "not";
|
|
break;
|
|
}
|
|
appendStringInfoString(str, " :boolop ");
|
|
outToken(str, opstr);
|
|
|
|
WRITE_NODE_FIELD(args);
|
|
WRITE_LOCATION_FIELD(location);
|
|
}
|
|
|
|
static void
|
|
_outForeignKeyOptInfo(StringInfo str, const ForeignKeyOptInfo *node)
|
|
{
|
|
int i;
|
|
|
|
WRITE_NODE_TYPE("FOREIGNKEYOPTINFO");
|
|
|
|
WRITE_UINT_FIELD(con_relid);
|
|
WRITE_UINT_FIELD(ref_relid);
|
|
WRITE_INT_FIELD(nkeys);
|
|
WRITE_ATTRNUMBER_ARRAY(conkey, node->nkeys);
|
|
WRITE_ATTRNUMBER_ARRAY(confkey, node->nkeys);
|
|
WRITE_OID_ARRAY(conpfeqop, node->nkeys);
|
|
WRITE_INT_FIELD(nmatched_ec);
|
|
WRITE_INT_FIELD(nconst_ec);
|
|
WRITE_INT_FIELD(nmatched_rcols);
|
|
WRITE_INT_FIELD(nmatched_ri);
|
|
/* for compactness, just print the number of matches per column: */
|
|
appendStringInfoString(str, " :eclass");
|
|
for (i = 0; i < node->nkeys; i++)
|
|
appendStringInfo(str, " %d", (node->eclass[i] != NULL));
|
|
appendStringInfoString(str, " :rinfos");
|
|
for (i = 0; i < node->nkeys; i++)
|
|
appendStringInfo(str, " %d", list_length(node->rinfos[i]));
|
|
}
|
|
|
|
static void
|
|
_outEquivalenceClass(StringInfo str, const EquivalenceClass *node)
|
|
{
|
|
/*
|
|
* To simplify reading, we just chase up to the topmost merged EC and
|
|
* print that, without bothering to show the merge-ees separately.
|
|
*/
|
|
while (node->ec_merged)
|
|
node = node->ec_merged;
|
|
|
|
WRITE_NODE_TYPE("EQUIVALENCECLASS");
|
|
|
|
WRITE_NODE_FIELD(ec_opfamilies);
|
|
WRITE_OID_FIELD(ec_collation);
|
|
WRITE_NODE_FIELD(ec_members);
|
|
WRITE_NODE_FIELD(ec_sources);
|
|
WRITE_NODE_FIELD(ec_derives);
|
|
WRITE_BITMAPSET_FIELD(ec_relids);
|
|
WRITE_BOOL_FIELD(ec_has_const);
|
|
WRITE_BOOL_FIELD(ec_has_volatile);
|
|
WRITE_BOOL_FIELD(ec_below_outer_join);
|
|
WRITE_BOOL_FIELD(ec_broken);
|
|
WRITE_UINT_FIELD(ec_sortref);
|
|
WRITE_UINT_FIELD(ec_min_security);
|
|
WRITE_UINT_FIELD(ec_max_security);
|
|
}
|
|
|
|
static void
|
|
_outExtensibleNode(StringInfo str, const ExtensibleNode *node)
|
|
{
|
|
const ExtensibleNodeMethods *methods;
|
|
|
|
methods = GetExtensibleNodeMethods(node->extnodename, false);
|
|
|
|
WRITE_NODE_TYPE("EXTENSIBLENODE");
|
|
|
|
WRITE_STRING_FIELD(extnodename);
|
|
|
|
/* serialize the private fields */
|
|
methods->nodeOut(str, node);
|
|
}
|
|
|
|
static void
|
|
_outQuery(StringInfo str, const Query *node)
|
|
{
|
|
WRITE_NODE_TYPE("QUERY");
|
|
|
|
WRITE_ENUM_FIELD(commandType, CmdType);
|
|
WRITE_ENUM_FIELD(querySource, QuerySource);
|
|
/* we intentionally do not print the queryId field */
|
|
WRITE_BOOL_FIELD(canSetTag);
|
|
|
|
/*
|
|
* Hack to work around missing outfuncs routines for a lot of the
|
|
* utility-statement node types. (The only one we actually *need* for
|
|
* rules support is NotifyStmt.) Someday we ought to support 'em all, but
|
|
* for the meantime do this to avoid getting lots of warnings when running
|
|
* with debug_print_parse on.
|
|
*/
|
|
if (node->utilityStmt)
|
|
{
|
|
switch (nodeTag(node->utilityStmt))
|
|
{
|
|
case T_CreateStmt:
|
|
case T_IndexStmt:
|
|
case T_NotifyStmt:
|
|
case T_DeclareCursorStmt:
|
|
WRITE_NODE_FIELD(utilityStmt);
|
|
break;
|
|
default:
|
|
appendStringInfoString(str, " :utilityStmt ?");
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
appendStringInfoString(str, " :utilityStmt <>");
|
|
|
|
WRITE_INT_FIELD(resultRelation);
|
|
WRITE_BOOL_FIELD(hasAggs);
|
|
WRITE_BOOL_FIELD(hasWindowFuncs);
|
|
WRITE_BOOL_FIELD(hasTargetSRFs);
|
|
WRITE_BOOL_FIELD(hasSubLinks);
|
|
WRITE_BOOL_FIELD(hasDistinctOn);
|
|
WRITE_BOOL_FIELD(hasRecursive);
|
|
WRITE_BOOL_FIELD(hasModifyingCTE);
|
|
WRITE_BOOL_FIELD(hasForUpdate);
|
|
WRITE_BOOL_FIELD(hasRowSecurity);
|
|
WRITE_BOOL_FIELD(isReturn);
|
|
WRITE_NODE_FIELD(cteList);
|
|
WRITE_NODE_FIELD(rtable);
|
|
WRITE_NODE_FIELD(jointree);
|
|
WRITE_NODE_FIELD(targetList);
|
|
WRITE_ENUM_FIELD(override, OverridingKind);
|
|
WRITE_NODE_FIELD(onConflict);
|
|
WRITE_NODE_FIELD(returningList);
|
|
WRITE_NODE_FIELD(groupClause);
|
|
WRITE_BOOL_FIELD(groupDistinct);
|
|
WRITE_NODE_FIELD(groupingSets);
|
|
WRITE_NODE_FIELD(havingQual);
|
|
WRITE_NODE_FIELD(windowClause);
|
|
WRITE_NODE_FIELD(distinctClause);
|
|
WRITE_NODE_FIELD(sortClause);
|
|
WRITE_NODE_FIELD(limitOffset);
|
|
WRITE_NODE_FIELD(limitCount);
|
|
WRITE_ENUM_FIELD(limitOption, LimitOption);
|
|
WRITE_NODE_FIELD(rowMarks);
|
|
WRITE_NODE_FIELD(setOperations);
|
|
WRITE_NODE_FIELD(constraintDeps);
|
|
WRITE_NODE_FIELD(withCheckOptions);
|
|
WRITE_NODE_FIELD(mergeActionList);
|
|
WRITE_BOOL_FIELD(mergeUseOuterJoin);
|
|
WRITE_LOCATION_FIELD(stmt_location);
|
|
WRITE_INT_FIELD(stmt_len);
|
|
}
|
|
|
|
static void
|
|
_outRangeTblEntry(StringInfo str, const RangeTblEntry *node)
|
|
{
|
|
WRITE_NODE_TYPE("RANGETBLENTRY");
|
|
|
|
/* put alias + eref first to make dump more legible */
|
|
WRITE_NODE_FIELD(alias);
|
|
WRITE_NODE_FIELD(eref);
|
|
WRITE_ENUM_FIELD(rtekind, RTEKind);
|
|
|
|
switch (node->rtekind)
|
|
{
|
|
case RTE_RELATION:
|
|
WRITE_OID_FIELD(relid);
|
|
WRITE_CHAR_FIELD(relkind);
|
|
WRITE_INT_FIELD(rellockmode);
|
|
WRITE_NODE_FIELD(tablesample);
|
|
break;
|
|
case RTE_SUBQUERY:
|
|
WRITE_NODE_FIELD(subquery);
|
|
WRITE_BOOL_FIELD(security_barrier);
|
|
break;
|
|
case RTE_JOIN:
|
|
WRITE_ENUM_FIELD(jointype, JoinType);
|
|
WRITE_INT_FIELD(joinmergedcols);
|
|
WRITE_NODE_FIELD(joinaliasvars);
|
|
WRITE_NODE_FIELD(joinleftcols);
|
|
WRITE_NODE_FIELD(joinrightcols);
|
|
WRITE_NODE_FIELD(join_using_alias);
|
|
break;
|
|
case RTE_FUNCTION:
|
|
WRITE_NODE_FIELD(functions);
|
|
WRITE_BOOL_FIELD(funcordinality);
|
|
break;
|
|
case RTE_TABLEFUNC:
|
|
WRITE_NODE_FIELD(tablefunc);
|
|
break;
|
|
case RTE_VALUES:
|
|
WRITE_NODE_FIELD(values_lists);
|
|
WRITE_NODE_FIELD(coltypes);
|
|
WRITE_NODE_FIELD(coltypmods);
|
|
WRITE_NODE_FIELD(colcollations);
|
|
break;
|
|
case RTE_CTE:
|
|
WRITE_STRING_FIELD(ctename);
|
|
WRITE_UINT_FIELD(ctelevelsup);
|
|
WRITE_BOOL_FIELD(self_reference);
|
|
WRITE_NODE_FIELD(coltypes);
|
|
WRITE_NODE_FIELD(coltypmods);
|
|
WRITE_NODE_FIELD(colcollations);
|
|
break;
|
|
case RTE_NAMEDTUPLESTORE:
|
|
WRITE_STRING_FIELD(enrname);
|
|
WRITE_FLOAT_FIELD(enrtuples, "%.0f");
|
|
WRITE_OID_FIELD(relid);
|
|
WRITE_NODE_FIELD(coltypes);
|
|
WRITE_NODE_FIELD(coltypmods);
|
|
WRITE_NODE_FIELD(colcollations);
|
|
break;
|
|
case RTE_RESULT:
|
|
/* no extra fields */
|
|
break;
|
|
default:
|
|
elog(ERROR, "unrecognized RTE kind: %d", (int) node->rtekind);
|
|
break;
|
|
}
|
|
|
|
WRITE_BOOL_FIELD(lateral);
|
|
WRITE_BOOL_FIELD(inh);
|
|
WRITE_BOOL_FIELD(inFromCl);
|
|
WRITE_UINT_FIELD(requiredPerms);
|
|
WRITE_OID_FIELD(checkAsUser);
|
|
WRITE_BITMAPSET_FIELD(selectedCols);
|
|
WRITE_BITMAPSET_FIELD(insertedCols);
|
|
WRITE_BITMAPSET_FIELD(updatedCols);
|
|
WRITE_BITMAPSET_FIELD(extraUpdatedCols);
|
|
WRITE_NODE_FIELD(securityQuals);
|
|
}
|
|
|
|
static void
|
|
_outA_Expr(StringInfo str, const A_Expr *node)
|
|
{
|
|
WRITE_NODE_TYPE("A_EXPR");
|
|
|
|
switch (node->kind)
|
|
{
|
|
case AEXPR_OP:
|
|
appendStringInfoChar(str, ' ');
|
|
WRITE_NODE_FIELD(name);
|
|
break;
|
|
case AEXPR_OP_ANY:
|
|
appendStringInfoChar(str, ' ');
|
|
WRITE_NODE_FIELD(name);
|
|
appendStringInfoString(str, " ANY ");
|
|
break;
|
|
case AEXPR_OP_ALL:
|
|
appendStringInfoChar(str, ' ');
|
|
WRITE_NODE_FIELD(name);
|
|
appendStringInfoString(str, " ALL ");
|
|
break;
|
|
case AEXPR_DISTINCT:
|
|
appendStringInfoString(str, " DISTINCT ");
|
|
WRITE_NODE_FIELD(name);
|
|
break;
|
|
case AEXPR_NOT_DISTINCT:
|
|
appendStringInfoString(str, " NOT_DISTINCT ");
|
|
WRITE_NODE_FIELD(name);
|
|
break;
|
|
case AEXPR_NULLIF:
|
|
appendStringInfoString(str, " NULLIF ");
|
|
WRITE_NODE_FIELD(name);
|
|
break;
|
|
case AEXPR_IN:
|
|
appendStringInfoString(str, " IN ");
|
|
WRITE_NODE_FIELD(name);
|
|
break;
|
|
case AEXPR_LIKE:
|
|
appendStringInfoString(str, " LIKE ");
|
|
WRITE_NODE_FIELD(name);
|
|
break;
|
|
case AEXPR_ILIKE:
|
|
appendStringInfoString(str, " ILIKE ");
|
|
WRITE_NODE_FIELD(name);
|
|
break;
|
|
case AEXPR_SIMILAR:
|
|
appendStringInfoString(str, " SIMILAR ");
|
|
WRITE_NODE_FIELD(name);
|
|
break;
|
|
case AEXPR_BETWEEN:
|
|
appendStringInfoString(str, " BETWEEN ");
|
|
WRITE_NODE_FIELD(name);
|
|
break;
|
|
case AEXPR_NOT_BETWEEN:
|
|
appendStringInfoString(str, " NOT_BETWEEN ");
|
|
WRITE_NODE_FIELD(name);
|
|
break;
|
|
case AEXPR_BETWEEN_SYM:
|
|
appendStringInfoString(str, " BETWEEN_SYM ");
|
|
WRITE_NODE_FIELD(name);
|
|
break;
|
|
case AEXPR_NOT_BETWEEN_SYM:
|
|
appendStringInfoString(str, " NOT_BETWEEN_SYM ");
|
|
WRITE_NODE_FIELD(name);
|
|
break;
|
|
default:
|
|
appendStringInfoString(str, " ??");
|
|
break;
|
|
}
|
|
|
|
WRITE_NODE_FIELD(lexpr);
|
|
WRITE_NODE_FIELD(rexpr);
|
|
WRITE_LOCATION_FIELD(location);
|
|
}
|
|
|
|
static void
|
|
_outInteger(StringInfo str, const Integer *node)
|
|
{
|
|
appendStringInfo(str, "%d", node->ival);
|
|
}
|
|
|
|
static void
|
|
_outFloat(StringInfo str, const Float *node)
|
|
{
|
|
/*
|
|
* We assume the value is a valid numeric literal and so does not need
|
|
* quoting.
|
|
*/
|
|
appendStringInfoString(str, node->fval);
|
|
}
|
|
|
|
static void
|
|
_outBoolean(StringInfo str, const Boolean *node)
|
|
{
|
|
appendStringInfoString(str, node->boolval ? "true" : "false");
|
|
}
|
|
|
|
static void
|
|
_outString(StringInfo str, const String *node)
|
|
{
|
|
/*
|
|
* We use outToken to provide escaping of the string's content, but we
|
|
* don't want it to do anything with an empty string.
|
|
*/
|
|
appendStringInfoChar(str, '"');
|
|
if (node->sval[0] != '\0')
|
|
outToken(str, node->sval);
|
|
appendStringInfoChar(str, '"');
|
|
}
|
|
|
|
static void
|
|
_outBitString(StringInfo str, const BitString *node)
|
|
{
|
|
/* internal representation already has leading 'b' */
|
|
appendStringInfoString(str, node->bsval);
|
|
}
|
|
|
|
static void
|
|
_outA_Const(StringInfo str, const A_Const *node)
|
|
{
|
|
WRITE_NODE_TYPE("A_CONST");
|
|
|
|
if (node->isnull)
|
|
appendStringInfoString(str, "NULL");
|
|
else
|
|
{
|
|
appendStringInfoString(str, " :val ");
|
|
outNode(str, &node->val);
|
|
}
|
|
WRITE_LOCATION_FIELD(location);
|
|
}
|
|
|
|
static void
|
|
_outConstraint(StringInfo str, const Constraint *node)
|
|
{
|
|
WRITE_NODE_TYPE("CONSTRAINT");
|
|
|
|
WRITE_STRING_FIELD(conname);
|
|
WRITE_BOOL_FIELD(deferrable);
|
|
WRITE_BOOL_FIELD(initdeferred);
|
|
WRITE_LOCATION_FIELD(location);
|
|
|
|
appendStringInfoString(str, " :contype ");
|
|
switch (node->contype)
|
|
{
|
|
case CONSTR_NULL:
|
|
appendStringInfoString(str, "NULL");
|
|
break;
|
|
|
|
case CONSTR_NOTNULL:
|
|
appendStringInfoString(str, "NOT_NULL");
|
|
break;
|
|
|
|
case CONSTR_DEFAULT:
|
|
appendStringInfoString(str, "DEFAULT");
|
|
WRITE_NODE_FIELD(raw_expr);
|
|
WRITE_STRING_FIELD(cooked_expr);
|
|
break;
|
|
|
|
case CONSTR_IDENTITY:
|
|
appendStringInfoString(str, "IDENTITY");
|
|
WRITE_NODE_FIELD(raw_expr);
|
|
WRITE_STRING_FIELD(cooked_expr);
|
|
WRITE_CHAR_FIELD(generated_when);
|
|
break;
|
|
|
|
case CONSTR_GENERATED:
|
|
appendStringInfoString(str, "GENERATED");
|
|
WRITE_NODE_FIELD(raw_expr);
|
|
WRITE_STRING_FIELD(cooked_expr);
|
|
WRITE_CHAR_FIELD(generated_when);
|
|
break;
|
|
|
|
case CONSTR_CHECK:
|
|
appendStringInfoString(str, "CHECK");
|
|
WRITE_BOOL_FIELD(is_no_inherit);
|
|
WRITE_NODE_FIELD(raw_expr);
|
|
WRITE_STRING_FIELD(cooked_expr);
|
|
break;
|
|
|
|
case CONSTR_PRIMARY:
|
|
appendStringInfoString(str, "PRIMARY_KEY");
|
|
WRITE_NODE_FIELD(keys);
|
|
WRITE_NODE_FIELD(including);
|
|
WRITE_NODE_FIELD(options);
|
|
WRITE_STRING_FIELD(indexname);
|
|
WRITE_STRING_FIELD(indexspace);
|
|
WRITE_BOOL_FIELD(reset_default_tblspc);
|
|
/* access_method and where_clause not currently used */
|
|
break;
|
|
|
|
case CONSTR_UNIQUE:
|
|
appendStringInfoString(str, "UNIQUE");
|
|
WRITE_BOOL_FIELD(nulls_not_distinct);
|
|
WRITE_NODE_FIELD(keys);
|
|
WRITE_NODE_FIELD(including);
|
|
WRITE_NODE_FIELD(options);
|
|
WRITE_STRING_FIELD(indexname);
|
|
WRITE_STRING_FIELD(indexspace);
|
|
WRITE_BOOL_FIELD(reset_default_tblspc);
|
|
/* access_method and where_clause not currently used */
|
|
break;
|
|
|
|
case CONSTR_EXCLUSION:
|
|
appendStringInfoString(str, "EXCLUSION");
|
|
WRITE_NODE_FIELD(exclusions);
|
|
WRITE_NODE_FIELD(including);
|
|
WRITE_NODE_FIELD(options);
|
|
WRITE_STRING_FIELD(indexname);
|
|
WRITE_STRING_FIELD(indexspace);
|
|
WRITE_BOOL_FIELD(reset_default_tblspc);
|
|
WRITE_STRING_FIELD(access_method);
|
|
WRITE_NODE_FIELD(where_clause);
|
|
break;
|
|
|
|
case CONSTR_FOREIGN:
|
|
appendStringInfoString(str, "FOREIGN_KEY");
|
|
WRITE_NODE_FIELD(pktable);
|
|
WRITE_NODE_FIELD(fk_attrs);
|
|
WRITE_NODE_FIELD(pk_attrs);
|
|
WRITE_CHAR_FIELD(fk_matchtype);
|
|
WRITE_CHAR_FIELD(fk_upd_action);
|
|
WRITE_CHAR_FIELD(fk_del_action);
|
|
WRITE_NODE_FIELD(fk_del_set_cols);
|
|
WRITE_NODE_FIELD(old_conpfeqop);
|
|
WRITE_OID_FIELD(old_pktable_oid);
|
|
WRITE_BOOL_FIELD(skip_validation);
|
|
WRITE_BOOL_FIELD(initially_valid);
|
|
break;
|
|
|
|
case CONSTR_ATTR_DEFERRABLE:
|
|
appendStringInfoString(str, "ATTR_DEFERRABLE");
|
|
break;
|
|
|
|
case CONSTR_ATTR_NOT_DEFERRABLE:
|
|
appendStringInfoString(str, "ATTR_NOT_DEFERRABLE");
|
|
break;
|
|
|
|
case CONSTR_ATTR_DEFERRED:
|
|
appendStringInfoString(str, "ATTR_DEFERRED");
|
|
break;
|
|
|
|
case CONSTR_ATTR_IMMEDIATE:
|
|
appendStringInfoString(str, "ATTR_IMMEDIATE");
|
|
break;
|
|
|
|
default:
|
|
appendStringInfo(str, "<unrecognized_constraint %d>",
|
|
(int) node->contype);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* outNode -
|
|
* converts a Node into ascii string and append it to 'str'
|
|
*/
|
|
void
|
|
outNode(StringInfo str, const void *obj)
|
|
{
|
|
/* Guard against stack overflow due to overly complex expressions */
|
|
check_stack_depth();
|
|
|
|
if (obj == NULL)
|
|
appendStringInfoString(str, "<>");
|
|
else if (IsA(obj, List) || IsA(obj, IntList) || IsA(obj, OidList) ||
|
|
IsA(obj, XidList))
|
|
_outList(str, obj);
|
|
/* nodeRead does not want to see { } around these! */
|
|
else if (IsA(obj, Integer))
|
|
_outInteger(str, (Integer *) obj);
|
|
else if (IsA(obj, Float))
|
|
_outFloat(str, (Float *) obj);
|
|
else if (IsA(obj, Boolean))
|
|
_outBoolean(str, (Boolean *) obj);
|
|
else if (IsA(obj, String))
|
|
_outString(str, (String *) obj);
|
|
else if (IsA(obj, BitString))
|
|
_outBitString(str, (BitString *) obj);
|
|
else
|
|
{
|
|
appendStringInfoChar(str, '{');
|
|
switch (nodeTag(obj))
|
|
{
|
|
#include "outfuncs.switch.c"
|
|
|
|
default:
|
|
|
|
/*
|
|
* This should be an ERROR, but it's too useful to be able to
|
|
* dump structures that outNode only understands part of.
|
|
*/
|
|
elog(WARNING, "could not dump unrecognized node type: %d",
|
|
(int) nodeTag(obj));
|
|
break;
|
|
}
|
|
appendStringInfoChar(str, '}');
|
|
}
|
|
}
|
|
|
|
/*
|
|
* nodeToString -
|
|
* returns the ascii representation of the Node as a palloc'd string
|
|
*/
|
|
char *
|
|
nodeToString(const void *obj)
|
|
{
|
|
StringInfoData str;
|
|
|
|
/* see stringinfo.h for an explanation of this maneuver */
|
|
initStringInfo(&str);
|
|
outNode(&str, obj);
|
|
return str.data;
|
|
}
|
|
|
|
/*
|
|
* bmsToString -
|
|
* returns the ascii representation of the Bitmapset as a palloc'd string
|
|
*/
|
|
char *
|
|
bmsToString(const Bitmapset *bms)
|
|
{
|
|
StringInfoData str;
|
|
|
|
/* see stringinfo.h for an explanation of this maneuver */
|
|
initStringInfo(&str);
|
|
outBitmapset(&str, bms);
|
|
return str.data;
|
|
}
|