PostgreSQL/src/backend/nodes/readfuncs.c
Tom Lane ff33a8c887 Remove artificial restrictions on which node types have out/read funcs.
The initial version of gen_node_support.pl manually excluded most
utility statement node types from having out/read support, and
also some raw-parse-tree-only node types.  That was mostly to keep
the output comparable to the old hand-maintained code.  We'd like
to have out/read support for utility statements, for debugging
purposes and so that they can be included in new-style SQL functions;
so it's time to lift that restriction.

Most if not all of the previously-excluded raw-parse-tree-only node
types can appear in expression subtrees of utility statements, so
they have to be handled too.

We don't quite have full read support yet; certain custom_read_write
node types need to have their handwritten read functions implemented
before that will work.

Doing this allows us to drop the previous hack in _outQuery to not
dump the utilityStmt field in most cases, which means we no longer
need manually-maintained out/read functions for Query, so get rid
of those in favor of auto-generating them.

Fix a couple of omissions in gen_node_support.pl that are exposed
through having to handle more node types.

catversion bump forced because somebody was sloppy about the field
order in the manually-maintained Query out/read functions.
(Committers should note that almost all changes in parsenodes.h
are now grounds for a catversion bump.)
2022-07-13 11:48:17 -04:00

599 lines
15 KiB
C

/*-------------------------------------------------------------------------
*
* readfuncs.c
* Reader 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/readfuncs.c
*
* NOTES
* Parse location fields are written out by outfuncs.c, but only for
* debugging use. When reading a location field, we normally discard
* the stored value and set the location field to -1 (ie, "unknown").
* This is because nodes coming from a stored rule should not be thought
* to have a known location in the current query's text.
*
* However, if restore_location_fields is true, we do restore location
* fields from the string. This is currently intended only for use by the
* WRITE_READ_PARSE_PLAN_TREES test code, which doesn't want to cause
* any change in the node contents.
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include <math.h>
#include "miscadmin.h"
#include "nodes/bitmapset.h"
#include "nodes/readfuncs.h"
/*
* Macros to simplify reading 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 a Read
* routine.
*/
/* Macros for declaring appropriate local variables */
/* A few guys need only local_node */
#define READ_LOCALS_NO_FIELDS(nodeTypeName) \
nodeTypeName *local_node = makeNode(nodeTypeName)
/* And a few guys need only the pg_strtok support fields */
#define READ_TEMP_LOCALS() \
const char *token; \
int length
/* ... but most need both */
#define READ_LOCALS(nodeTypeName) \
READ_LOCALS_NO_FIELDS(nodeTypeName); \
READ_TEMP_LOCALS()
/* Read an integer field (anything written as ":fldname %d") */
#define READ_INT_FIELD(fldname) \
token = pg_strtok(&length); /* skip :fldname */ \
token = pg_strtok(&length); /* get field value */ \
local_node->fldname = atoi(token)
/* Read an unsigned integer field (anything written as ":fldname %u") */
#define READ_UINT_FIELD(fldname) \
token = pg_strtok(&length); /* skip :fldname */ \
token = pg_strtok(&length); /* get field value */ \
local_node->fldname = atoui(token)
/* Read an unsigned integer field (anything written using UINT64_FORMAT) */
#define READ_UINT64_FIELD(fldname) \
token = pg_strtok(&length); /* skip :fldname */ \
token = pg_strtok(&length); /* get field value */ \
local_node->fldname = strtou64(token, NULL, 10)
/* Read a long integer field (anything written as ":fldname %ld") */
#define READ_LONG_FIELD(fldname) \
token = pg_strtok(&length); /* skip :fldname */ \
token = pg_strtok(&length); /* get field value */ \
local_node->fldname = atol(token)
/* Read an OID field (don't hard-wire assumption that OID is same as uint) */
#define READ_OID_FIELD(fldname) \
token = pg_strtok(&length); /* skip :fldname */ \
token = pg_strtok(&length); /* get field value */ \
local_node->fldname = atooid(token)
/* Read a char field (ie, one ascii character) */
#define READ_CHAR_FIELD(fldname) \
token = pg_strtok(&length); /* skip :fldname */ \
token = pg_strtok(&length); /* get field value */ \
/* avoid overhead of calling debackslash() for one char */ \
local_node->fldname = (length == 0) ? '\0' : (token[0] == '\\' ? token[1] : token[0])
/* Read an enumerated-type field that was written as an integer code */
#define READ_ENUM_FIELD(fldname, enumtype) \
token = pg_strtok(&length); /* skip :fldname */ \
token = pg_strtok(&length); /* get field value */ \
local_node->fldname = (enumtype) atoi(token)
/* Read a float field */
#define READ_FLOAT_FIELD(fldname) \
token = pg_strtok(&length); /* skip :fldname */ \
token = pg_strtok(&length); /* get field value */ \
local_node->fldname = atof(token)
/* Read a boolean field */
#define READ_BOOL_FIELD(fldname) \
token = pg_strtok(&length); /* skip :fldname */ \
token = pg_strtok(&length); /* get field value */ \
local_node->fldname = strtobool(token)
/* Read a character-string field */
#define READ_STRING_FIELD(fldname) \
token = pg_strtok(&length); /* skip :fldname */ \
token = pg_strtok(&length); /* get field value */ \
local_node->fldname = nullable_string(token, length)
/* Read a parse location field (and possibly throw away the value) */
#ifdef WRITE_READ_PARSE_PLAN_TREES
#define READ_LOCATION_FIELD(fldname) \
token = pg_strtok(&length); /* skip :fldname */ \
token = pg_strtok(&length); /* get field value */ \
local_node->fldname = restore_location_fields ? atoi(token) : -1
#else
#define READ_LOCATION_FIELD(fldname) \
token = pg_strtok(&length); /* skip :fldname */ \
token = pg_strtok(&length); /* get field value */ \
(void) token; /* in case not used elsewhere */ \
local_node->fldname = -1 /* set field to "unknown" */
#endif
/* Read a Node field */
#define READ_NODE_FIELD(fldname) \
token = pg_strtok(&length); /* skip :fldname */ \
(void) token; /* in case not used elsewhere */ \
local_node->fldname = nodeRead(NULL, 0)
/* Read a bitmapset field */
#define READ_BITMAPSET_FIELD(fldname) \
token = pg_strtok(&length); /* skip :fldname */ \
(void) token; /* in case not used elsewhere */ \
local_node->fldname = _readBitmapset()
/* Read an attribute number array */
#define READ_ATTRNUMBER_ARRAY(fldname, len) \
token = pg_strtok(&length); /* skip :fldname */ \
local_node->fldname = readAttrNumberCols(len)
/* Read an oid array */
#define READ_OID_ARRAY(fldname, len) \
token = pg_strtok(&length); /* skip :fldname */ \
local_node->fldname = readOidCols(len)
/* Read an int array */
#define READ_INT_ARRAY(fldname, len) \
token = pg_strtok(&length); /* skip :fldname */ \
local_node->fldname = readIntCols(len)
/* Read a bool array */
#define READ_BOOL_ARRAY(fldname, len) \
token = pg_strtok(&length); /* skip :fldname */ \
local_node->fldname = readBoolCols(len)
/* Routine exit */
#define READ_DONE() \
return local_node
/*
* NOTE: use atoi() to read values written with %d, or atoui() to read
* values written with %u in outfuncs.c. An exception is OID values,
* for which use atooid(). (As of 7.1, outfuncs.c writes OIDs as %u,
* but this will probably change in the future.)
*/
#define atoui(x) ((unsigned int) strtoul((x), NULL, 10))
#define strtobool(x) ((*(x) == 't') ? true : false)
#define nullable_string(token,length) \
((length) == 0 ? NULL : debackslash(token, length))
/*
* _readBitmapset
*/
static Bitmapset *
_readBitmapset(void)
{
Bitmapset *result = NULL;
READ_TEMP_LOCALS();
token = pg_strtok(&length);
if (token == NULL)
elog(ERROR, "incomplete Bitmapset structure");
if (length != 1 || token[0] != '(')
elog(ERROR, "unrecognized token: \"%.*s\"", length, token);
token = pg_strtok(&length);
if (token == NULL)
elog(ERROR, "incomplete Bitmapset structure");
if (length != 1 || token[0] != 'b')
elog(ERROR, "unrecognized token: \"%.*s\"", length, token);
for (;;)
{
int val;
char *endptr;
token = pg_strtok(&length);
if (token == NULL)
elog(ERROR, "unterminated Bitmapset structure");
if (length == 1 && token[0] == ')')
break;
val = (int) strtol(token, &endptr, 10);
if (endptr != token + length)
elog(ERROR, "unrecognized integer: \"%.*s\"", length, token);
result = bms_add_member(result, val);
}
return result;
}
/*
* for use by extensions which define extensible nodes
*/
Bitmapset *
readBitmapset(void)
{
return _readBitmapset();
}
#include "readfuncs.funcs.c"
/*
* Support functions for nodes with custom_read_write attribute or
* special_read_write attribute
*/
static Const *
_readConst(void)
{
READ_LOCALS(Const);
READ_OID_FIELD(consttype);
READ_INT_FIELD(consttypmod);
READ_OID_FIELD(constcollid);
READ_INT_FIELD(constlen);
READ_BOOL_FIELD(constbyval);
READ_BOOL_FIELD(constisnull);
READ_LOCATION_FIELD(location);
token = pg_strtok(&length); /* skip :constvalue */
if (local_node->constisnull)
token = pg_strtok(&length); /* skip "<>" */
else
local_node->constvalue = readDatum(local_node->constbyval);
READ_DONE();
}
static BoolExpr *
_readBoolExpr(void)
{
READ_LOCALS(BoolExpr);
/* do-it-yourself enum representation */
token = pg_strtok(&length); /* skip :boolop */
token = pg_strtok(&length); /* get field value */
if (strncmp(token, "and", 3) == 0)
local_node->boolop = AND_EXPR;
else if (strncmp(token, "or", 2) == 0)
local_node->boolop = OR_EXPR;
else if (strncmp(token, "not", 3) == 0)
local_node->boolop = NOT_EXPR;
else
elog(ERROR, "unrecognized boolop \"%.*s\"", length, token);
READ_NODE_FIELD(args);
READ_LOCATION_FIELD(location);
READ_DONE();
}
static RangeTblEntry *
_readRangeTblEntry(void)
{
READ_LOCALS(RangeTblEntry);
/* put alias + eref first to make dump more legible */
READ_NODE_FIELD(alias);
READ_NODE_FIELD(eref);
READ_ENUM_FIELD(rtekind, RTEKind);
switch (local_node->rtekind)
{
case RTE_RELATION:
READ_OID_FIELD(relid);
READ_CHAR_FIELD(relkind);
READ_INT_FIELD(rellockmode);
READ_NODE_FIELD(tablesample);
break;
case RTE_SUBQUERY:
READ_NODE_FIELD(subquery);
READ_BOOL_FIELD(security_barrier);
break;
case RTE_JOIN:
READ_ENUM_FIELD(jointype, JoinType);
READ_INT_FIELD(joinmergedcols);
READ_NODE_FIELD(joinaliasvars);
READ_NODE_FIELD(joinleftcols);
READ_NODE_FIELD(joinrightcols);
READ_NODE_FIELD(join_using_alias);
break;
case RTE_FUNCTION:
READ_NODE_FIELD(functions);
READ_BOOL_FIELD(funcordinality);
break;
case RTE_TABLEFUNC:
READ_NODE_FIELD(tablefunc);
/* The RTE must have a copy of the column type info, if any */
if (local_node->tablefunc)
{
TableFunc *tf = local_node->tablefunc;
local_node->coltypes = tf->coltypes;
local_node->coltypmods = tf->coltypmods;
local_node->colcollations = tf->colcollations;
}
break;
case RTE_VALUES:
READ_NODE_FIELD(values_lists);
READ_NODE_FIELD(coltypes);
READ_NODE_FIELD(coltypmods);
READ_NODE_FIELD(colcollations);
break;
case RTE_CTE:
READ_STRING_FIELD(ctename);
READ_UINT_FIELD(ctelevelsup);
READ_BOOL_FIELD(self_reference);
READ_NODE_FIELD(coltypes);
READ_NODE_FIELD(coltypmods);
READ_NODE_FIELD(colcollations);
break;
case RTE_NAMEDTUPLESTORE:
READ_STRING_FIELD(enrname);
READ_FLOAT_FIELD(enrtuples);
READ_OID_FIELD(relid);
READ_NODE_FIELD(coltypes);
READ_NODE_FIELD(coltypmods);
READ_NODE_FIELD(colcollations);
break;
case RTE_RESULT:
/* no extra fields */
break;
default:
elog(ERROR, "unrecognized RTE kind: %d",
(int) local_node->rtekind);
break;
}
READ_BOOL_FIELD(lateral);
READ_BOOL_FIELD(inh);
READ_BOOL_FIELD(inFromCl);
READ_UINT_FIELD(requiredPerms);
READ_OID_FIELD(checkAsUser);
READ_BITMAPSET_FIELD(selectedCols);
READ_BITMAPSET_FIELD(insertedCols);
READ_BITMAPSET_FIELD(updatedCols);
READ_BITMAPSET_FIELD(extraUpdatedCols);
READ_NODE_FIELD(securityQuals);
READ_DONE();
}
static ExtensibleNode *
_readExtensibleNode(void)
{
const ExtensibleNodeMethods *methods;
ExtensibleNode *local_node;
const char *extnodename;
READ_TEMP_LOCALS();
token = pg_strtok(&length); /* skip :extnodename */
token = pg_strtok(&length); /* get extnodename */
extnodename = nullable_string(token, length);
if (!extnodename)
elog(ERROR, "extnodename has to be supplied");
methods = GetExtensibleNodeMethods(extnodename, false);
local_node = (ExtensibleNode *) newNode(methods->node_size,
T_ExtensibleNode);
local_node->extnodename = extnodename;
/* deserialize the private fields */
methods->nodeRead(local_node);
READ_DONE();
}
/*
* parseNodeString
*
* Given a character string representing a node tree, parseNodeString creates
* the internal node structure.
*
* The string to be read must already have been loaded into pg_strtok().
*/
Node *
parseNodeString(void)
{
void *return_value;
READ_TEMP_LOCALS();
/* Guard against stack overflow due to overly complex expressions */
check_stack_depth();
token = pg_strtok(&length);
#define MATCH(tokname, namelen) \
(length == namelen && memcmp(token, tokname, namelen) == 0)
if (false)
;
#include "readfuncs.switch.c"
else
{
elog(ERROR, "badly formatted node string \"%.32s\"...", token);
return_value = NULL; /* keep compiler quiet */
}
return (Node *) return_value;
}
/*
* readDatum
*
* Given a string representation of a constant, recreate the appropriate
* Datum. The string representation embeds length info, but not byValue,
* so we must be told that.
*/
Datum
readDatum(bool typbyval)
{
Size length,
i;
int tokenLength;
const char *token;
Datum res;
char *s;
/*
* read the actual length of the value
*/
token = pg_strtok(&tokenLength);
length = atoui(token);
token = pg_strtok(&tokenLength); /* read the '[' */
if (token == NULL || token[0] != '[')
elog(ERROR, "expected \"[\" to start datum, but got \"%s\"; length = %zu",
token ? token : "[NULL]", length);
if (typbyval)
{
if (length > (Size) sizeof(Datum))
elog(ERROR, "byval datum but length = %zu", length);
res = (Datum) 0;
s = (char *) (&res);
for (i = 0; i < (Size) sizeof(Datum); i++)
{
token = pg_strtok(&tokenLength);
s[i] = (char) atoi(token);
}
}
else if (length <= 0)
res = (Datum) NULL;
else
{
s = (char *) palloc(length);
for (i = 0; i < length; i++)
{
token = pg_strtok(&tokenLength);
s[i] = (char) atoi(token);
}
res = PointerGetDatum(s);
}
token = pg_strtok(&tokenLength); /* read the ']' */
if (token == NULL || token[0] != ']')
elog(ERROR, "expected \"]\" to end datum, but got \"%s\"; length = %zu",
token ? token : "[NULL]", length);
return res;
}
/*
* readAttrNumberCols
*/
AttrNumber *
readAttrNumberCols(int numCols)
{
int tokenLength,
i;
const char *token;
AttrNumber *attr_vals;
if (numCols <= 0)
return NULL;
attr_vals = (AttrNumber *) palloc(numCols * sizeof(AttrNumber));
for (i = 0; i < numCols; i++)
{
token = pg_strtok(&tokenLength);
attr_vals[i] = atoi(token);
}
return attr_vals;
}
/*
* readOidCols
*/
Oid *
readOidCols(int numCols)
{
int tokenLength,
i;
const char *token;
Oid *oid_vals;
if (numCols <= 0)
return NULL;
oid_vals = (Oid *) palloc(numCols * sizeof(Oid));
for (i = 0; i < numCols; i++)
{
token = pg_strtok(&tokenLength);
oid_vals[i] = atooid(token);
}
return oid_vals;
}
/*
* readIntCols
*/
int *
readIntCols(int numCols)
{
int tokenLength,
i;
const char *token;
int *int_vals;
if (numCols <= 0)
return NULL;
int_vals = (int *) palloc(numCols * sizeof(int));
for (i = 0; i < numCols; i++)
{
token = pg_strtok(&tokenLength);
int_vals[i] = atoi(token);
}
return int_vals;
}
/*
* readBoolCols
*/
bool *
readBoolCols(int numCols)
{
int tokenLength,
i;
const char *token;
bool *bool_vals;
if (numCols <= 0)
return NULL;
bool_vals = (bool *) palloc(numCols * sizeof(bool));
for (i = 0; i < numCols; i++)
{
token = pg_strtok(&tokenLength);
bool_vals[i] = strtobool(token);
}
return bool_vals;
}