mirror of
https://github.com/postgres/postgres.git
synced 2025-07-08 11:42:09 +03:00
This is part of an effort to reduce the number of special cases in the automatically generated node support functions. Allegedly, only certain fields of the Constraint node are valid based on contype. But this has historically not been kept up to date in the read/write functions. The Constraint node is only used for debugging DDL statements, so there are no strong requirements for its output, and there is no enforcement for its correctness. (There was no read support before a6bc3301925.) Commitse7a552f303
andabf46ad9c7
are examples of where omissions were fixed. This patch just removes the custom read/write implementations for the Constraint node type. Now we just output all the fields, which is a bit more than before, but at least we don't have to maintain these functions anymore. Also, we lose the string representation of the contype field, but for this marginal use case that seems tolerable. This patch also changes the documentation of the Constraint struct to put less emphasis on grouping fields by constraint type but rather document for each field how it's used. Reviewed-by: Paul Jungwirth <pj@illuminatedcomputing.com> Discussion: https://www.postgresql.org/message-id/flat/4b27fc50-8cd6-46f5-ab20-88dbaadca645@eisentraut.org
687 lines
19 KiB
C
687 lines
19 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* readfuncs.c
|
|
* Reader functions for Postgres tree nodes.
|
|
*
|
|
* Portions Copyright (c) 1996-2024, 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)
|
|
|
|
static char *
|
|
nullable_string(const char *token, int length)
|
|
{
|
|
/* outToken emits <> for NULL, and pg_strtok makes that an empty string */
|
|
if (length == 0)
|
|
return NULL;
|
|
/* outToken emits "" for empty string */
|
|
if (length == 2 && token[0] == '"' && token[1] == '"')
|
|
return pstrdup("");
|
|
/* otherwise, we must remove protective backslashes added by outToken */
|
|
return debackslash(token, length);
|
|
}
|
|
|
|
|
|
/*
|
|
* _readBitmapset
|
|
*
|
|
* Note: this code is used in contexts where we know that a Bitmapset
|
|
* is expected. There is equivalent code in nodeRead() that can read a
|
|
* Bitmapset when we come across one in other contexts.
|
|
*/
|
|
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;
|
|
}
|
|
|
|
/*
|
|
* We export this function for use by extensions that define extensible nodes.
|
|
* That's somewhat historical, though, because calling nodeRead() will work.
|
|
*/
|
|
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 (length == 3 && strncmp(token, "and", 3) == 0)
|
|
local_node->boolop = AND_EXPR;
|
|
else if (length == 2 && strncmp(token, "or", 2) == 0)
|
|
local_node->boolop = OR_EXPR;
|
|
else if (length == 3 && 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 A_Const *
|
|
_readA_Const(void)
|
|
{
|
|
READ_LOCALS(A_Const);
|
|
|
|
/* We expect either NULL or :val here */
|
|
token = pg_strtok(&length);
|
|
if (length == 4 && strncmp(token, "NULL", 4) == 0)
|
|
local_node->isnull = true;
|
|
else
|
|
{
|
|
union ValUnion *tmp = nodeRead(NULL, 0);
|
|
|
|
/* To forestall valgrind complaints, copy only the valid data */
|
|
switch (nodeTag(tmp))
|
|
{
|
|
case T_Integer:
|
|
memcpy(&local_node->val, tmp, sizeof(Integer));
|
|
break;
|
|
case T_Float:
|
|
memcpy(&local_node->val, tmp, sizeof(Float));
|
|
break;
|
|
case T_Boolean:
|
|
memcpy(&local_node->val, tmp, sizeof(Boolean));
|
|
break;
|
|
case T_String:
|
|
memcpy(&local_node->val, tmp, sizeof(String));
|
|
break;
|
|
case T_BitString:
|
|
memcpy(&local_node->val, tmp, sizeof(BitString));
|
|
break;
|
|
default:
|
|
elog(ERROR, "unrecognized node type: %d",
|
|
(int) nodeTag(tmp));
|
|
break;
|
|
}
|
|
}
|
|
|
|
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);
|
|
READ_UINT_FIELD(perminfoindex);
|
|
break;
|
|
case RTE_SUBQUERY:
|
|
READ_NODE_FIELD(subquery);
|
|
READ_BOOL_FIELD(security_barrier);
|
|
/* we re-use these RELATION fields, too: */
|
|
READ_OID_FIELD(relid);
|
|
READ_CHAR_FIELD(relkind);
|
|
READ_INT_FIELD(rellockmode);
|
|
READ_UINT_FIELD(perminfoindex);
|
|
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_NODE_FIELD(coltypes);
|
|
READ_NODE_FIELD(coltypmods);
|
|
READ_NODE_FIELD(colcollations);
|
|
/* we re-use these RELATION fields, too: */
|
|
READ_OID_FIELD(relid);
|
|
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_NODE_FIELD(securityQuals);
|
|
|
|
READ_DONE();
|
|
}
|
|
|
|
static A_Expr *
|
|
_readA_Expr(void)
|
|
{
|
|
READ_LOCALS(A_Expr);
|
|
|
|
token = pg_strtok(&length);
|
|
|
|
if (length == 3 && strncmp(token, "ANY", 3) == 0)
|
|
{
|
|
local_node->kind = AEXPR_OP_ANY;
|
|
READ_NODE_FIELD(name);
|
|
}
|
|
else if (length == 3 && strncmp(token, "ALL", 3) == 0)
|
|
{
|
|
local_node->kind = AEXPR_OP_ALL;
|
|
READ_NODE_FIELD(name);
|
|
}
|
|
else if (length == 8 && strncmp(token, "DISTINCT", 8) == 0)
|
|
{
|
|
local_node->kind = AEXPR_DISTINCT;
|
|
READ_NODE_FIELD(name);
|
|
}
|
|
else if (length == 12 && strncmp(token, "NOT_DISTINCT", 12) == 0)
|
|
{
|
|
local_node->kind = AEXPR_NOT_DISTINCT;
|
|
READ_NODE_FIELD(name);
|
|
}
|
|
else if (length == 6 && strncmp(token, "NULLIF", 6) == 0)
|
|
{
|
|
local_node->kind = AEXPR_NULLIF;
|
|
READ_NODE_FIELD(name);
|
|
}
|
|
else if (length == 2 && strncmp(token, "IN", 2) == 0)
|
|
{
|
|
local_node->kind = AEXPR_IN;
|
|
READ_NODE_FIELD(name);
|
|
}
|
|
else if (length == 4 && strncmp(token, "LIKE", 4) == 0)
|
|
{
|
|
local_node->kind = AEXPR_LIKE;
|
|
READ_NODE_FIELD(name);
|
|
}
|
|
else if (length == 5 && strncmp(token, "ILIKE", 5) == 0)
|
|
{
|
|
local_node->kind = AEXPR_ILIKE;
|
|
READ_NODE_FIELD(name);
|
|
}
|
|
else if (length == 7 && strncmp(token, "SIMILAR", 7) == 0)
|
|
{
|
|
local_node->kind = AEXPR_SIMILAR;
|
|
READ_NODE_FIELD(name);
|
|
}
|
|
else if (length == 7 && strncmp(token, "BETWEEN", 7) == 0)
|
|
{
|
|
local_node->kind = AEXPR_BETWEEN;
|
|
READ_NODE_FIELD(name);
|
|
}
|
|
else if (length == 11 && strncmp(token, "NOT_BETWEEN", 11) == 0)
|
|
{
|
|
local_node->kind = AEXPR_NOT_BETWEEN;
|
|
READ_NODE_FIELD(name);
|
|
}
|
|
else if (length == 11 && strncmp(token, "BETWEEN_SYM", 11) == 0)
|
|
{
|
|
local_node->kind = AEXPR_BETWEEN_SYM;
|
|
READ_NODE_FIELD(name);
|
|
}
|
|
else if (length == 15 && strncmp(token, "NOT_BETWEEN_SYM", 15) == 0)
|
|
{
|
|
local_node->kind = AEXPR_NOT_BETWEEN_SYM;
|
|
READ_NODE_FIELD(name);
|
|
}
|
|
else if (length == 5 && strncmp(token, ":name", 5) == 0)
|
|
{
|
|
local_node->kind = AEXPR_OP;
|
|
local_node->name = nodeRead(NULL, 0);
|
|
}
|
|
else
|
|
elog(ERROR, "unrecognized A_Expr kind: \"%.*s\"", length, token);
|
|
|
|
READ_NODE_FIELD(lexpr);
|
|
READ_NODE_FIELD(rexpr);
|
|
READ_LOCATION_FIELD(location);
|
|
|
|
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)
|
|
{
|
|
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)
|
|
|
|
#include "readfuncs.switch.c"
|
|
|
|
elog(ERROR, "badly formatted node string \"%.32s\"...", token);
|
|
return NULL; /* keep compiler quiet */
|
|
}
|
|
|
|
|
|
/*
|
|
* 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;
|
|
}
|
|
|
|
/*
|
|
* common implementation for scalar-array-reading functions
|
|
*
|
|
* The data format is either "<>" for a NULL pointer (in which case numCols
|
|
* is ignored) or "(item item item)" where the number of items must equal
|
|
* numCols. The convfunc must be okay with stopping at whitespace or a
|
|
* right parenthesis, since pg_strtok won't null-terminate the token.
|
|
*/
|
|
#define READ_SCALAR_ARRAY(fnname, datatype, convfunc) \
|
|
datatype * \
|
|
fnname(int numCols) \
|
|
{ \
|
|
datatype *vals; \
|
|
READ_TEMP_LOCALS(); \
|
|
token = pg_strtok(&length); \
|
|
if (token == NULL) \
|
|
elog(ERROR, "incomplete scalar array"); \
|
|
if (length == 0) \
|
|
return NULL; /* it was "<>", so return NULL pointer */ \
|
|
if (length != 1 || token[0] != '(') \
|
|
elog(ERROR, "unrecognized token: \"%.*s\"", length, token); \
|
|
vals = (datatype *) palloc(numCols * sizeof(datatype)); \
|
|
for (int i = 0; i < numCols; i++) \
|
|
{ \
|
|
token = pg_strtok(&length); \
|
|
if (token == NULL || token[0] == ')') \
|
|
elog(ERROR, "incomplete scalar array"); \
|
|
vals[i] = convfunc(token); \
|
|
} \
|
|
token = pg_strtok(&length); \
|
|
if (token == NULL || length != 1 || token[0] != ')') \
|
|
elog(ERROR, "incomplete scalar array"); \
|
|
return vals; \
|
|
}
|
|
|
|
/*
|
|
* Note: these functions are exported in nodes.h for possible use by
|
|
* extensions, so don't mess too much with their names or API.
|
|
*/
|
|
READ_SCALAR_ARRAY(readAttrNumberCols, int16, atoi)
|
|
READ_SCALAR_ARRAY(readOidCols, Oid, atooid)
|
|
/* outfuncs.c has writeIndexCols, but we don't yet need that here */
|
|
/* READ_SCALAR_ARRAY(readIndexCols, Index, atoui) */
|
|
READ_SCALAR_ARRAY(readIntCols, int, atoi)
|
|
READ_SCALAR_ARRAY(readBoolCols, bool, strtobool)
|