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);
|
|
|
|
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_BOOL_FIELD(inh);
|
|
READ_CHAR_FIELD(relkind);
|
|
READ_INT_FIELD(rellockmode);
|
|
READ_UINT_FIELD(perminfoindex);
|
|
READ_NODE_FIELD(tablesample);
|
|
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_BOOL_FIELD(inh);
|
|
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(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)
|