mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-09-02 19:37:18 +02:00
The core of this patch is hash_array() and associated typcache infrastructure, which works just about exactly like the existing support for array comparison. In addition I did some work to ensure that the planner won't think that an array type is hashable unless its element type is hashable, and similarly for sorting. This includes adding a datatype parameter to op_hashjoinable and op_mergejoinable, and adding an explicit "hashable" flag to SortGroupClause. The lack of a cross-check on the element type was a pre-existing bug in mergejoin support --- but it didn't matter so much before, because if you couldn't sort the element type there wasn't any good alternative to failing anyhow. Now that we have the alternative of hashing the array type, there are cases where we can avoid a failure by being picky at the planner stage, so it's time to be picky. The issue of exactly how to combine the per-element hash values to produce an array hash is still open for discussion, but the rest of this is pretty solid, so I'll commit it as-is.
1376 lines
28 KiB
C
1376 lines
28 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* readfuncs.c
|
|
* Reader functions for Postgres tree nodes.
|
|
*
|
|
* Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
*
|
|
* IDENTIFICATION
|
|
* src/backend/nodes/readfuncs.c
|
|
*
|
|
* NOTES
|
|
* Path and Plan nodes do not have any readfuncs support, because we
|
|
* never have occasion to read them in. (There was once code here that
|
|
* claimed to read them, but it was broken as well as unused.) We
|
|
* never read executor state trees, either.
|
|
*
|
|
* Parse location fields are written out by outfuncs.c, but only for
|
|
* possible debugging use. When reading a location field, we 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.
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
#include "postgres.h"
|
|
|
|
#include <math.h>
|
|
|
|
#include "nodes/parsenodes.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() \
|
|
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 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 */ \
|
|
local_node->fldname = 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 throw away the value, per notes above) */
|
|
#define READ_LOCATION_FIELD(fldname) \
|
|
token = pg_strtok(&length); /* skip :fldname */ \
|
|
token = pg_strtok(&length); /* get field value */ \
|
|
local_node->fldname = -1 /* set field to "unknown" */
|
|
|
|
/* Read a Node field */
|
|
#define READ_NODE_FIELD(fldname) \
|
|
token = pg_strtok(&length); /* skip :fldname */ \
|
|
local_node->fldname = nodeRead(NULL, 0)
|
|
|
|
/* Read a bitmapset field */
|
|
#define READ_BITMAPSET_FIELD(fldname) \
|
|
token = pg_strtok(&length); /* skip :fldname */ \
|
|
local_node->fldname = _readBitmapset()
|
|
|
|
/* 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 atooid(x) ((Oid) strtoul((x), NULL, 10))
|
|
|
|
#define strtobool(x) ((*(x) == 't') ? true : false)
|
|
|
|
#define nullable_string(token,length) \
|
|
((length) == 0 ? NULL : debackslash(token, length))
|
|
|
|
|
|
static Datum readDatum(bool typbyval);
|
|
|
|
/*
|
|
* _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;
|
|
}
|
|
|
|
|
|
/*
|
|
* _readQuery
|
|
*/
|
|
static Query *
|
|
_readQuery(void)
|
|
{
|
|
READ_LOCALS(Query);
|
|
|
|
READ_ENUM_FIELD(commandType, CmdType);
|
|
READ_ENUM_FIELD(querySource, QuerySource);
|
|
READ_BOOL_FIELD(canSetTag);
|
|
READ_NODE_FIELD(utilityStmt);
|
|
READ_INT_FIELD(resultRelation);
|
|
READ_NODE_FIELD(intoClause);
|
|
READ_BOOL_FIELD(hasAggs);
|
|
READ_BOOL_FIELD(hasWindowFuncs);
|
|
READ_BOOL_FIELD(hasSubLinks);
|
|
READ_BOOL_FIELD(hasDistinctOn);
|
|
READ_BOOL_FIELD(hasRecursive);
|
|
READ_BOOL_FIELD(hasForUpdate);
|
|
READ_NODE_FIELD(cteList);
|
|
READ_NODE_FIELD(rtable);
|
|
READ_NODE_FIELD(jointree);
|
|
READ_NODE_FIELD(targetList);
|
|
READ_NODE_FIELD(returningList);
|
|
READ_NODE_FIELD(groupClause);
|
|
READ_NODE_FIELD(havingQual);
|
|
READ_NODE_FIELD(windowClause);
|
|
READ_NODE_FIELD(distinctClause);
|
|
READ_NODE_FIELD(sortClause);
|
|
READ_NODE_FIELD(limitOffset);
|
|
READ_NODE_FIELD(limitCount);
|
|
READ_NODE_FIELD(rowMarks);
|
|
READ_NODE_FIELD(setOperations);
|
|
READ_NODE_FIELD(constraintDeps);
|
|
|
|
READ_DONE();
|
|
}
|
|
|
|
/*
|
|
* _readNotifyStmt
|
|
*/
|
|
static NotifyStmt *
|
|
_readNotifyStmt(void)
|
|
{
|
|
READ_LOCALS(NotifyStmt);
|
|
|
|
READ_STRING_FIELD(conditionname);
|
|
READ_STRING_FIELD(payload);
|
|
|
|
READ_DONE();
|
|
}
|
|
|
|
/*
|
|
* _readDeclareCursorStmt
|
|
*/
|
|
static DeclareCursorStmt *
|
|
_readDeclareCursorStmt(void)
|
|
{
|
|
READ_LOCALS(DeclareCursorStmt);
|
|
|
|
READ_STRING_FIELD(portalname);
|
|
READ_INT_FIELD(options);
|
|
READ_NODE_FIELD(query);
|
|
|
|
READ_DONE();
|
|
}
|
|
|
|
/*
|
|
* _readSortGroupClause
|
|
*/
|
|
static SortGroupClause *
|
|
_readSortGroupClause(void)
|
|
{
|
|
READ_LOCALS(SortGroupClause);
|
|
|
|
READ_UINT_FIELD(tleSortGroupRef);
|
|
READ_OID_FIELD(eqop);
|
|
READ_OID_FIELD(sortop);
|
|
READ_BOOL_FIELD(nulls_first);
|
|
READ_BOOL_FIELD(hashable);
|
|
|
|
READ_DONE();
|
|
}
|
|
|
|
/*
|
|
* _readWindowClause
|
|
*/
|
|
static WindowClause *
|
|
_readWindowClause(void)
|
|
{
|
|
READ_LOCALS(WindowClause);
|
|
|
|
READ_STRING_FIELD(name);
|
|
READ_STRING_FIELD(refname);
|
|
READ_NODE_FIELD(partitionClause);
|
|
READ_NODE_FIELD(orderClause);
|
|
READ_INT_FIELD(frameOptions);
|
|
READ_NODE_FIELD(startOffset);
|
|
READ_NODE_FIELD(endOffset);
|
|
READ_UINT_FIELD(winref);
|
|
READ_BOOL_FIELD(copiedOrder);
|
|
|
|
READ_DONE();
|
|
}
|
|
|
|
/*
|
|
* _readRowMarkClause
|
|
*/
|
|
static RowMarkClause *
|
|
_readRowMarkClause(void)
|
|
{
|
|
READ_LOCALS(RowMarkClause);
|
|
|
|
READ_UINT_FIELD(rti);
|
|
READ_BOOL_FIELD(forUpdate);
|
|
READ_BOOL_FIELD(noWait);
|
|
READ_BOOL_FIELD(pushedDown);
|
|
|
|
READ_DONE();
|
|
}
|
|
|
|
/*
|
|
* _readCommonTableExpr
|
|
*/
|
|
static CommonTableExpr *
|
|
_readCommonTableExpr(void)
|
|
{
|
|
READ_LOCALS(CommonTableExpr);
|
|
|
|
READ_STRING_FIELD(ctename);
|
|
READ_NODE_FIELD(aliascolnames);
|
|
READ_NODE_FIELD(ctequery);
|
|
READ_LOCATION_FIELD(location);
|
|
READ_BOOL_FIELD(cterecursive);
|
|
READ_INT_FIELD(cterefcount);
|
|
READ_NODE_FIELD(ctecolnames);
|
|
READ_NODE_FIELD(ctecoltypes);
|
|
READ_NODE_FIELD(ctecoltypmods);
|
|
|
|
READ_DONE();
|
|
}
|
|
|
|
/*
|
|
* _readSetOperationStmt
|
|
*/
|
|
static SetOperationStmt *
|
|
_readSetOperationStmt(void)
|
|
{
|
|
READ_LOCALS(SetOperationStmt);
|
|
|
|
READ_ENUM_FIELD(op, SetOperation);
|
|
READ_BOOL_FIELD(all);
|
|
READ_NODE_FIELD(larg);
|
|
READ_NODE_FIELD(rarg);
|
|
READ_NODE_FIELD(colTypes);
|
|
READ_NODE_FIELD(colTypmods);
|
|
READ_NODE_FIELD(groupClauses);
|
|
|
|
READ_DONE();
|
|
}
|
|
|
|
|
|
/*
|
|
* Stuff from primnodes.h.
|
|
*/
|
|
|
|
static Alias *
|
|
_readAlias(void)
|
|
{
|
|
READ_LOCALS(Alias);
|
|
|
|
READ_STRING_FIELD(aliasname);
|
|
READ_NODE_FIELD(colnames);
|
|
|
|
READ_DONE();
|
|
}
|
|
|
|
static RangeVar *
|
|
_readRangeVar(void)
|
|
{
|
|
READ_LOCALS(RangeVar);
|
|
|
|
local_node->catalogname = NULL; /* not currently saved in output
|
|
* format */
|
|
|
|
READ_STRING_FIELD(schemaname);
|
|
READ_STRING_FIELD(relname);
|
|
READ_ENUM_FIELD(inhOpt, InhOption);
|
|
READ_BOOL_FIELD(istemp);
|
|
READ_NODE_FIELD(alias);
|
|
READ_LOCATION_FIELD(location);
|
|
|
|
READ_DONE();
|
|
}
|
|
|
|
static IntoClause *
|
|
_readIntoClause(void)
|
|
{
|
|
READ_LOCALS(IntoClause);
|
|
|
|
READ_NODE_FIELD(rel);
|
|
READ_NODE_FIELD(colNames);
|
|
READ_NODE_FIELD(options);
|
|
READ_ENUM_FIELD(onCommit, OnCommitAction);
|
|
READ_STRING_FIELD(tableSpaceName);
|
|
|
|
READ_DONE();
|
|
}
|
|
|
|
/*
|
|
* _readVar
|
|
*/
|
|
static Var *
|
|
_readVar(void)
|
|
{
|
|
READ_LOCALS(Var);
|
|
|
|
READ_UINT_FIELD(varno);
|
|
READ_INT_FIELD(varattno);
|
|
READ_OID_FIELD(vartype);
|
|
READ_INT_FIELD(vartypmod);
|
|
READ_UINT_FIELD(varlevelsup);
|
|
READ_UINT_FIELD(varnoold);
|
|
READ_INT_FIELD(varoattno);
|
|
READ_LOCATION_FIELD(location);
|
|
|
|
READ_DONE();
|
|
}
|
|
|
|
/*
|
|
* _readConst
|
|
*/
|
|
static Const *
|
|
_readConst(void)
|
|
{
|
|
READ_LOCALS(Const);
|
|
|
|
READ_OID_FIELD(consttype);
|
|
READ_INT_FIELD(consttypmod);
|
|
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();
|
|
}
|
|
|
|
/*
|
|
* _readParam
|
|
*/
|
|
static Param *
|
|
_readParam(void)
|
|
{
|
|
READ_LOCALS(Param);
|
|
|
|
READ_ENUM_FIELD(paramkind, ParamKind);
|
|
READ_INT_FIELD(paramid);
|
|
READ_OID_FIELD(paramtype);
|
|
READ_INT_FIELD(paramtypmod);
|
|
READ_LOCATION_FIELD(location);
|
|
|
|
READ_DONE();
|
|
}
|
|
|
|
/*
|
|
* _readAggref
|
|
*/
|
|
static Aggref *
|
|
_readAggref(void)
|
|
{
|
|
READ_LOCALS(Aggref);
|
|
|
|
READ_OID_FIELD(aggfnoid);
|
|
READ_OID_FIELD(aggtype);
|
|
READ_NODE_FIELD(args);
|
|
READ_NODE_FIELD(aggorder);
|
|
READ_NODE_FIELD(aggdistinct);
|
|
READ_BOOL_FIELD(aggstar);
|
|
READ_UINT_FIELD(agglevelsup);
|
|
READ_LOCATION_FIELD(location);
|
|
|
|
READ_DONE();
|
|
}
|
|
|
|
/*
|
|
* _readWindowFunc
|
|
*/
|
|
static WindowFunc *
|
|
_readWindowFunc(void)
|
|
{
|
|
READ_LOCALS(WindowFunc);
|
|
|
|
READ_OID_FIELD(winfnoid);
|
|
READ_OID_FIELD(wintype);
|
|
READ_NODE_FIELD(args);
|
|
READ_UINT_FIELD(winref);
|
|
READ_BOOL_FIELD(winstar);
|
|
READ_BOOL_FIELD(winagg);
|
|
READ_LOCATION_FIELD(location);
|
|
|
|
READ_DONE();
|
|
}
|
|
|
|
/*
|
|
* _readArrayRef
|
|
*/
|
|
static ArrayRef *
|
|
_readArrayRef(void)
|
|
{
|
|
READ_LOCALS(ArrayRef);
|
|
|
|
READ_OID_FIELD(refarraytype);
|
|
READ_OID_FIELD(refelemtype);
|
|
READ_INT_FIELD(reftypmod);
|
|
READ_NODE_FIELD(refupperindexpr);
|
|
READ_NODE_FIELD(reflowerindexpr);
|
|
READ_NODE_FIELD(refexpr);
|
|
READ_NODE_FIELD(refassgnexpr);
|
|
|
|
READ_DONE();
|
|
}
|
|
|
|
/*
|
|
* _readFuncExpr
|
|
*/
|
|
static FuncExpr *
|
|
_readFuncExpr(void)
|
|
{
|
|
READ_LOCALS(FuncExpr);
|
|
|
|
READ_OID_FIELD(funcid);
|
|
READ_OID_FIELD(funcresulttype);
|
|
READ_BOOL_FIELD(funcretset);
|
|
READ_ENUM_FIELD(funcformat, CoercionForm);
|
|
READ_NODE_FIELD(args);
|
|
READ_LOCATION_FIELD(location);
|
|
|
|
READ_DONE();
|
|
}
|
|
|
|
/*
|
|
* _readNamedArgExpr
|
|
*/
|
|
static NamedArgExpr *
|
|
_readNamedArgExpr(void)
|
|
{
|
|
READ_LOCALS(NamedArgExpr);
|
|
|
|
READ_NODE_FIELD(arg);
|
|
READ_STRING_FIELD(name);
|
|
READ_INT_FIELD(argnumber);
|
|
READ_LOCATION_FIELD(location);
|
|
|
|
READ_DONE();
|
|
}
|
|
|
|
/*
|
|
* _readOpExpr
|
|
*/
|
|
static OpExpr *
|
|
_readOpExpr(void)
|
|
{
|
|
READ_LOCALS(OpExpr);
|
|
|
|
READ_OID_FIELD(opno);
|
|
READ_OID_FIELD(opfuncid);
|
|
|
|
/*
|
|
* The opfuncid is stored in the textual format primarily for debugging
|
|
* and documentation reasons. We want to always read it as zero to force
|
|
* it to be re-looked-up in the pg_operator entry. This ensures that
|
|
* stored rules don't have hidden dependencies on operators' functions.
|
|
* (We don't currently support an ALTER OPERATOR command, but might
|
|
* someday.)
|
|
*/
|
|
local_node->opfuncid = InvalidOid;
|
|
|
|
READ_OID_FIELD(opresulttype);
|
|
READ_BOOL_FIELD(opretset);
|
|
READ_NODE_FIELD(args);
|
|
READ_LOCATION_FIELD(location);
|
|
|
|
READ_DONE();
|
|
}
|
|
|
|
/*
|
|
* _readDistinctExpr
|
|
*/
|
|
static DistinctExpr *
|
|
_readDistinctExpr(void)
|
|
{
|
|
READ_LOCALS(DistinctExpr);
|
|
|
|
READ_OID_FIELD(opno);
|
|
READ_OID_FIELD(opfuncid);
|
|
|
|
/*
|
|
* The opfuncid is stored in the textual format primarily for debugging
|
|
* and documentation reasons. We want to always read it as zero to force
|
|
* it to be re-looked-up in the pg_operator entry. This ensures that
|
|
* stored rules don't have hidden dependencies on operators' functions.
|
|
* (We don't currently support an ALTER OPERATOR command, but might
|
|
* someday.)
|
|
*/
|
|
local_node->opfuncid = InvalidOid;
|
|
|
|
READ_OID_FIELD(opresulttype);
|
|
READ_BOOL_FIELD(opretset);
|
|
READ_NODE_FIELD(args);
|
|
READ_LOCATION_FIELD(location);
|
|
|
|
READ_DONE();
|
|
}
|
|
|
|
/*
|
|
* _readScalarArrayOpExpr
|
|
*/
|
|
static ScalarArrayOpExpr *
|
|
_readScalarArrayOpExpr(void)
|
|
{
|
|
READ_LOCALS(ScalarArrayOpExpr);
|
|
|
|
READ_OID_FIELD(opno);
|
|
READ_OID_FIELD(opfuncid);
|
|
|
|
/*
|
|
* The opfuncid is stored in the textual format primarily for debugging
|
|
* and documentation reasons. We want to always read it as zero to force
|
|
* it to be re-looked-up in the pg_operator entry. This ensures that
|
|
* stored rules don't have hidden dependencies on operators' functions.
|
|
* (We don't currently support an ALTER OPERATOR command, but might
|
|
* someday.)
|
|
*/
|
|
local_node->opfuncid = InvalidOid;
|
|
|
|
READ_BOOL_FIELD(useOr);
|
|
READ_NODE_FIELD(args);
|
|
READ_LOCATION_FIELD(location);
|
|
|
|
READ_DONE();
|
|
}
|
|
|
|
/*
|
|
* _readBoolExpr
|
|
*/
|
|
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();
|
|
}
|
|
|
|
/*
|
|
* _readSubLink
|
|
*/
|
|
static SubLink *
|
|
_readSubLink(void)
|
|
{
|
|
READ_LOCALS(SubLink);
|
|
|
|
READ_ENUM_FIELD(subLinkType, SubLinkType);
|
|
READ_NODE_FIELD(testexpr);
|
|
READ_NODE_FIELD(operName);
|
|
READ_NODE_FIELD(subselect);
|
|
READ_LOCATION_FIELD(location);
|
|
|
|
READ_DONE();
|
|
}
|
|
|
|
/*
|
|
* _readSubPlan is not needed since it doesn't appear in stored rules.
|
|
*/
|
|
|
|
/*
|
|
* _readFieldSelect
|
|
*/
|
|
static FieldSelect *
|
|
_readFieldSelect(void)
|
|
{
|
|
READ_LOCALS(FieldSelect);
|
|
|
|
READ_NODE_FIELD(arg);
|
|
READ_INT_FIELD(fieldnum);
|
|
READ_OID_FIELD(resulttype);
|
|
READ_INT_FIELD(resulttypmod);
|
|
|
|
READ_DONE();
|
|
}
|
|
|
|
/*
|
|
* _readFieldStore
|
|
*/
|
|
static FieldStore *
|
|
_readFieldStore(void)
|
|
{
|
|
READ_LOCALS(FieldStore);
|
|
|
|
READ_NODE_FIELD(arg);
|
|
READ_NODE_FIELD(newvals);
|
|
READ_NODE_FIELD(fieldnums);
|
|
READ_OID_FIELD(resulttype);
|
|
|
|
READ_DONE();
|
|
}
|
|
|
|
/*
|
|
* _readRelabelType
|
|
*/
|
|
static RelabelType *
|
|
_readRelabelType(void)
|
|
{
|
|
READ_LOCALS(RelabelType);
|
|
|
|
READ_NODE_FIELD(arg);
|
|
READ_OID_FIELD(resulttype);
|
|
READ_INT_FIELD(resulttypmod);
|
|
READ_ENUM_FIELD(relabelformat, CoercionForm);
|
|
READ_LOCATION_FIELD(location);
|
|
|
|
READ_DONE();
|
|
}
|
|
|
|
/*
|
|
* _readCoerceViaIO
|
|
*/
|
|
static CoerceViaIO *
|
|
_readCoerceViaIO(void)
|
|
{
|
|
READ_LOCALS(CoerceViaIO);
|
|
|
|
READ_NODE_FIELD(arg);
|
|
READ_OID_FIELD(resulttype);
|
|
READ_ENUM_FIELD(coerceformat, CoercionForm);
|
|
READ_LOCATION_FIELD(location);
|
|
|
|
READ_DONE();
|
|
}
|
|
|
|
/*
|
|
* _readArrayCoerceExpr
|
|
*/
|
|
static ArrayCoerceExpr *
|
|
_readArrayCoerceExpr(void)
|
|
{
|
|
READ_LOCALS(ArrayCoerceExpr);
|
|
|
|
READ_NODE_FIELD(arg);
|
|
READ_OID_FIELD(elemfuncid);
|
|
READ_OID_FIELD(resulttype);
|
|
READ_INT_FIELD(resulttypmod);
|
|
READ_BOOL_FIELD(isExplicit);
|
|
READ_ENUM_FIELD(coerceformat, CoercionForm);
|
|
READ_LOCATION_FIELD(location);
|
|
|
|
READ_DONE();
|
|
}
|
|
|
|
/*
|
|
* _readConvertRowtypeExpr
|
|
*/
|
|
static ConvertRowtypeExpr *
|
|
_readConvertRowtypeExpr(void)
|
|
{
|
|
READ_LOCALS(ConvertRowtypeExpr);
|
|
|
|
READ_NODE_FIELD(arg);
|
|
READ_OID_FIELD(resulttype);
|
|
READ_ENUM_FIELD(convertformat, CoercionForm);
|
|
READ_LOCATION_FIELD(location);
|
|
|
|
READ_DONE();
|
|
}
|
|
|
|
/*
|
|
* _readCaseExpr
|
|
*/
|
|
static CaseExpr *
|
|
_readCaseExpr(void)
|
|
{
|
|
READ_LOCALS(CaseExpr);
|
|
|
|
READ_OID_FIELD(casetype);
|
|
READ_NODE_FIELD(arg);
|
|
READ_NODE_FIELD(args);
|
|
READ_NODE_FIELD(defresult);
|
|
READ_LOCATION_FIELD(location);
|
|
|
|
READ_DONE();
|
|
}
|
|
|
|
/*
|
|
* _readCaseWhen
|
|
*/
|
|
static CaseWhen *
|
|
_readCaseWhen(void)
|
|
{
|
|
READ_LOCALS(CaseWhen);
|
|
|
|
READ_NODE_FIELD(expr);
|
|
READ_NODE_FIELD(result);
|
|
READ_LOCATION_FIELD(location);
|
|
|
|
READ_DONE();
|
|
}
|
|
|
|
/*
|
|
* _readCaseTestExpr
|
|
*/
|
|
static CaseTestExpr *
|
|
_readCaseTestExpr(void)
|
|
{
|
|
READ_LOCALS(CaseTestExpr);
|
|
|
|
READ_OID_FIELD(typeId);
|
|
READ_INT_FIELD(typeMod);
|
|
|
|
READ_DONE();
|
|
}
|
|
|
|
/*
|
|
* _readArrayExpr
|
|
*/
|
|
static ArrayExpr *
|
|
_readArrayExpr(void)
|
|
{
|
|
READ_LOCALS(ArrayExpr);
|
|
|
|
READ_OID_FIELD(array_typeid);
|
|
READ_OID_FIELD(element_typeid);
|
|
READ_NODE_FIELD(elements);
|
|
READ_BOOL_FIELD(multidims);
|
|
READ_LOCATION_FIELD(location);
|
|
|
|
READ_DONE();
|
|
}
|
|
|
|
/*
|
|
* _readRowExpr
|
|
*/
|
|
static RowExpr *
|
|
_readRowExpr(void)
|
|
{
|
|
READ_LOCALS(RowExpr);
|
|
|
|
READ_NODE_FIELD(args);
|
|
READ_OID_FIELD(row_typeid);
|
|
READ_ENUM_FIELD(row_format, CoercionForm);
|
|
READ_NODE_FIELD(colnames);
|
|
READ_LOCATION_FIELD(location);
|
|
|
|
READ_DONE();
|
|
}
|
|
|
|
/*
|
|
* _readRowCompareExpr
|
|
*/
|
|
static RowCompareExpr *
|
|
_readRowCompareExpr(void)
|
|
{
|
|
READ_LOCALS(RowCompareExpr);
|
|
|
|
READ_ENUM_FIELD(rctype, RowCompareType);
|
|
READ_NODE_FIELD(opnos);
|
|
READ_NODE_FIELD(opfamilies);
|
|
READ_NODE_FIELD(largs);
|
|
READ_NODE_FIELD(rargs);
|
|
|
|
READ_DONE();
|
|
}
|
|
|
|
/*
|
|
* _readCoalesceExpr
|
|
*/
|
|
static CoalesceExpr *
|
|
_readCoalesceExpr(void)
|
|
{
|
|
READ_LOCALS(CoalesceExpr);
|
|
|
|
READ_OID_FIELD(coalescetype);
|
|
READ_NODE_FIELD(args);
|
|
READ_LOCATION_FIELD(location);
|
|
|
|
READ_DONE();
|
|
}
|
|
|
|
/*
|
|
* _readMinMaxExpr
|
|
*/
|
|
static MinMaxExpr *
|
|
_readMinMaxExpr(void)
|
|
{
|
|
READ_LOCALS(MinMaxExpr);
|
|
|
|
READ_OID_FIELD(minmaxtype);
|
|
READ_ENUM_FIELD(op, MinMaxOp);
|
|
READ_NODE_FIELD(args);
|
|
READ_LOCATION_FIELD(location);
|
|
|
|
READ_DONE();
|
|
}
|
|
|
|
/*
|
|
* _readXmlExpr
|
|
*/
|
|
static XmlExpr *
|
|
_readXmlExpr(void)
|
|
{
|
|
READ_LOCALS(XmlExpr);
|
|
|
|
READ_ENUM_FIELD(op, XmlExprOp);
|
|
READ_STRING_FIELD(name);
|
|
READ_NODE_FIELD(named_args);
|
|
READ_NODE_FIELD(arg_names);
|
|
READ_NODE_FIELD(args);
|
|
READ_ENUM_FIELD(xmloption, XmlOptionType);
|
|
READ_OID_FIELD(type);
|
|
READ_INT_FIELD(typmod);
|
|
READ_LOCATION_FIELD(location);
|
|
|
|
READ_DONE();
|
|
}
|
|
|
|
/*
|
|
* _readNullIfExpr
|
|
*/
|
|
static NullIfExpr *
|
|
_readNullIfExpr(void)
|
|
{
|
|
READ_LOCALS(NullIfExpr);
|
|
|
|
READ_OID_FIELD(opno);
|
|
READ_OID_FIELD(opfuncid);
|
|
|
|
/*
|
|
* The opfuncid is stored in the textual format primarily for debugging
|
|
* and documentation reasons. We want to always read it as zero to force
|
|
* it to be re-looked-up in the pg_operator entry. This ensures that
|
|
* stored rules don't have hidden dependencies on operators' functions.
|
|
* (We don't currently support an ALTER OPERATOR command, but might
|
|
* someday.)
|
|
*/
|
|
local_node->opfuncid = InvalidOid;
|
|
|
|
READ_OID_FIELD(opresulttype);
|
|
READ_BOOL_FIELD(opretset);
|
|
READ_NODE_FIELD(args);
|
|
READ_LOCATION_FIELD(location);
|
|
|
|
READ_DONE();
|
|
}
|
|
|
|
/*
|
|
* _readNullTest
|
|
*/
|
|
static NullTest *
|
|
_readNullTest(void)
|
|
{
|
|
READ_LOCALS(NullTest);
|
|
|
|
READ_NODE_FIELD(arg);
|
|
READ_ENUM_FIELD(nulltesttype, NullTestType);
|
|
READ_BOOL_FIELD(argisrow);
|
|
|
|
READ_DONE();
|
|
}
|
|
|
|
/*
|
|
* _readBooleanTest
|
|
*/
|
|
static BooleanTest *
|
|
_readBooleanTest(void)
|
|
{
|
|
READ_LOCALS(BooleanTest);
|
|
|
|
READ_NODE_FIELD(arg);
|
|
READ_ENUM_FIELD(booltesttype, BoolTestType);
|
|
|
|
READ_DONE();
|
|
}
|
|
|
|
/*
|
|
* _readCoerceToDomain
|
|
*/
|
|
static CoerceToDomain *
|
|
_readCoerceToDomain(void)
|
|
{
|
|
READ_LOCALS(CoerceToDomain);
|
|
|
|
READ_NODE_FIELD(arg);
|
|
READ_OID_FIELD(resulttype);
|
|
READ_INT_FIELD(resulttypmod);
|
|
READ_ENUM_FIELD(coercionformat, CoercionForm);
|
|
READ_LOCATION_FIELD(location);
|
|
|
|
READ_DONE();
|
|
}
|
|
|
|
/*
|
|
* _readCoerceToDomainValue
|
|
*/
|
|
static CoerceToDomainValue *
|
|
_readCoerceToDomainValue(void)
|
|
{
|
|
READ_LOCALS(CoerceToDomainValue);
|
|
|
|
READ_OID_FIELD(typeId);
|
|
READ_INT_FIELD(typeMod);
|
|
READ_LOCATION_FIELD(location);
|
|
|
|
READ_DONE();
|
|
}
|
|
|
|
/*
|
|
* _readSetToDefault
|
|
*/
|
|
static SetToDefault *
|
|
_readSetToDefault(void)
|
|
{
|
|
READ_LOCALS(SetToDefault);
|
|
|
|
READ_OID_FIELD(typeId);
|
|
READ_INT_FIELD(typeMod);
|
|
READ_LOCATION_FIELD(location);
|
|
|
|
READ_DONE();
|
|
}
|
|
|
|
/*
|
|
* _readCurrentOfExpr
|
|
*/
|
|
static CurrentOfExpr *
|
|
_readCurrentOfExpr(void)
|
|
{
|
|
READ_LOCALS(CurrentOfExpr);
|
|
|
|
READ_UINT_FIELD(cvarno);
|
|
READ_STRING_FIELD(cursor_name);
|
|
READ_INT_FIELD(cursor_param);
|
|
|
|
READ_DONE();
|
|
}
|
|
|
|
/*
|
|
* _readTargetEntry
|
|
*/
|
|
static TargetEntry *
|
|
_readTargetEntry(void)
|
|
{
|
|
READ_LOCALS(TargetEntry);
|
|
|
|
READ_NODE_FIELD(expr);
|
|
READ_INT_FIELD(resno);
|
|
READ_STRING_FIELD(resname);
|
|
READ_UINT_FIELD(ressortgroupref);
|
|
READ_OID_FIELD(resorigtbl);
|
|
READ_INT_FIELD(resorigcol);
|
|
READ_BOOL_FIELD(resjunk);
|
|
|
|
READ_DONE();
|
|
}
|
|
|
|
/*
|
|
* _readRangeTblRef
|
|
*/
|
|
static RangeTblRef *
|
|
_readRangeTblRef(void)
|
|
{
|
|
READ_LOCALS(RangeTblRef);
|
|
|
|
READ_INT_FIELD(rtindex);
|
|
|
|
READ_DONE();
|
|
}
|
|
|
|
/*
|
|
* _readJoinExpr
|
|
*/
|
|
static JoinExpr *
|
|
_readJoinExpr(void)
|
|
{
|
|
READ_LOCALS(JoinExpr);
|
|
|
|
READ_ENUM_FIELD(jointype, JoinType);
|
|
READ_BOOL_FIELD(isNatural);
|
|
READ_NODE_FIELD(larg);
|
|
READ_NODE_FIELD(rarg);
|
|
READ_NODE_FIELD(usingClause);
|
|
READ_NODE_FIELD(quals);
|
|
READ_NODE_FIELD(alias);
|
|
READ_INT_FIELD(rtindex);
|
|
|
|
READ_DONE();
|
|
}
|
|
|
|
/*
|
|
* _readFromExpr
|
|
*/
|
|
static FromExpr *
|
|
_readFromExpr(void)
|
|
{
|
|
READ_LOCALS(FromExpr);
|
|
|
|
READ_NODE_FIELD(fromlist);
|
|
READ_NODE_FIELD(quals);
|
|
|
|
READ_DONE();
|
|
}
|
|
|
|
|
|
/*
|
|
* Stuff from parsenodes.h.
|
|
*/
|
|
|
|
/*
|
|
* _readRangeTblEntry
|
|
*/
|
|
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:
|
|
case RTE_SPECIAL:
|
|
READ_OID_FIELD(relid);
|
|
break;
|
|
case RTE_SUBQUERY:
|
|
READ_NODE_FIELD(subquery);
|
|
break;
|
|
case RTE_JOIN:
|
|
READ_ENUM_FIELD(jointype, JoinType);
|
|
READ_NODE_FIELD(joinaliasvars);
|
|
break;
|
|
case RTE_FUNCTION:
|
|
READ_NODE_FIELD(funcexpr);
|
|
READ_NODE_FIELD(funccoltypes);
|
|
READ_NODE_FIELD(funccoltypmods);
|
|
break;
|
|
case RTE_VALUES:
|
|
READ_NODE_FIELD(values_lists);
|
|
break;
|
|
case RTE_CTE:
|
|
READ_STRING_FIELD(ctename);
|
|
READ_UINT_FIELD(ctelevelsup);
|
|
READ_BOOL_FIELD(self_reference);
|
|
READ_NODE_FIELD(ctecoltypes);
|
|
READ_NODE_FIELD(ctecoltypmods);
|
|
break;
|
|
default:
|
|
elog(ERROR, "unrecognized RTE kind: %d",
|
|
(int) local_node->rtekind);
|
|
break;
|
|
}
|
|
|
|
READ_BOOL_FIELD(inh);
|
|
READ_BOOL_FIELD(inFromCl);
|
|
READ_UINT_FIELD(requiredPerms);
|
|
READ_OID_FIELD(checkAsUser);
|
|
READ_BITMAPSET_FIELD(selectedCols);
|
|
READ_BITMAPSET_FIELD(modifiedCols);
|
|
|
|
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();
|
|
|
|
token = pg_strtok(&length);
|
|
|
|
#define MATCH(tokname, namelen) \
|
|
(length == namelen && strncmp(token, tokname, namelen) == 0)
|
|
|
|
if (MATCH("QUERY", 5))
|
|
return_value = _readQuery();
|
|
else if (MATCH("SORTGROUPCLAUSE", 15))
|
|
return_value = _readSortGroupClause();
|
|
else if (MATCH("WINDOWCLAUSE", 12))
|
|
return_value = _readWindowClause();
|
|
else if (MATCH("ROWMARKCLAUSE", 13))
|
|
return_value = _readRowMarkClause();
|
|
else if (MATCH("COMMONTABLEEXPR", 15))
|
|
return_value = _readCommonTableExpr();
|
|
else if (MATCH("SETOPERATIONSTMT", 16))
|
|
return_value = _readSetOperationStmt();
|
|
else if (MATCH("ALIAS", 5))
|
|
return_value = _readAlias();
|
|
else if (MATCH("RANGEVAR", 8))
|
|
return_value = _readRangeVar();
|
|
else if (MATCH("INTOCLAUSE", 10))
|
|
return_value = _readIntoClause();
|
|
else if (MATCH("VAR", 3))
|
|
return_value = _readVar();
|
|
else if (MATCH("CONST", 5))
|
|
return_value = _readConst();
|
|
else if (MATCH("PARAM", 5))
|
|
return_value = _readParam();
|
|
else if (MATCH("AGGREF", 6))
|
|
return_value = _readAggref();
|
|
else if (MATCH("WINDOWFUNC", 10))
|
|
return_value = _readWindowFunc();
|
|
else if (MATCH("ARRAYREF", 8))
|
|
return_value = _readArrayRef();
|
|
else if (MATCH("FUNCEXPR", 8))
|
|
return_value = _readFuncExpr();
|
|
else if (MATCH("NAMEDARGEXPR", 12))
|
|
return_value = _readNamedArgExpr();
|
|
else if (MATCH("OPEXPR", 6))
|
|
return_value = _readOpExpr();
|
|
else if (MATCH("DISTINCTEXPR", 12))
|
|
return_value = _readDistinctExpr();
|
|
else if (MATCH("SCALARARRAYOPEXPR", 17))
|
|
return_value = _readScalarArrayOpExpr();
|
|
else if (MATCH("BOOLEXPR", 8))
|
|
return_value = _readBoolExpr();
|
|
else if (MATCH("SUBLINK", 7))
|
|
return_value = _readSubLink();
|
|
else if (MATCH("FIELDSELECT", 11))
|
|
return_value = _readFieldSelect();
|
|
else if (MATCH("FIELDSTORE", 10))
|
|
return_value = _readFieldStore();
|
|
else if (MATCH("RELABELTYPE", 11))
|
|
return_value = _readRelabelType();
|
|
else if (MATCH("COERCEVIAIO", 11))
|
|
return_value = _readCoerceViaIO();
|
|
else if (MATCH("ARRAYCOERCEEXPR", 15))
|
|
return_value = _readArrayCoerceExpr();
|
|
else if (MATCH("CONVERTROWTYPEEXPR", 18))
|
|
return_value = _readConvertRowtypeExpr();
|
|
else if (MATCH("CASE", 4))
|
|
return_value = _readCaseExpr();
|
|
else if (MATCH("WHEN", 4))
|
|
return_value = _readCaseWhen();
|
|
else if (MATCH("CASETESTEXPR", 12))
|
|
return_value = _readCaseTestExpr();
|
|
else if (MATCH("ARRAY", 5))
|
|
return_value = _readArrayExpr();
|
|
else if (MATCH("ROW", 3))
|
|
return_value = _readRowExpr();
|
|
else if (MATCH("ROWCOMPARE", 10))
|
|
return_value = _readRowCompareExpr();
|
|
else if (MATCH("COALESCE", 8))
|
|
return_value = _readCoalesceExpr();
|
|
else if (MATCH("MINMAX", 6))
|
|
return_value = _readMinMaxExpr();
|
|
else if (MATCH("XMLEXPR", 7))
|
|
return_value = _readXmlExpr();
|
|
else if (MATCH("NULLIFEXPR", 10))
|
|
return_value = _readNullIfExpr();
|
|
else if (MATCH("NULLTEST", 8))
|
|
return_value = _readNullTest();
|
|
else if (MATCH("BOOLEANTEST", 11))
|
|
return_value = _readBooleanTest();
|
|
else if (MATCH("COERCETODOMAIN", 14))
|
|
return_value = _readCoerceToDomain();
|
|
else if (MATCH("COERCETODOMAINVALUE", 19))
|
|
return_value = _readCoerceToDomainValue();
|
|
else if (MATCH("SETTODEFAULT", 12))
|
|
return_value = _readSetToDefault();
|
|
else if (MATCH("CURRENTOFEXPR", 13))
|
|
return_value = _readCurrentOfExpr();
|
|
else if (MATCH("TARGETENTRY", 11))
|
|
return_value = _readTargetEntry();
|
|
else if (MATCH("RANGETBLREF", 11))
|
|
return_value = _readRangeTblRef();
|
|
else if (MATCH("JOINEXPR", 8))
|
|
return_value = _readJoinExpr();
|
|
else if (MATCH("FROMEXPR", 8))
|
|
return_value = _readFromExpr();
|
|
else if (MATCH("RTE", 3))
|
|
return_value = _readRangeTblEntry();
|
|
else if (MATCH("NOTIFY", 6))
|
|
return_value = _readNotifyStmt();
|
|
else if (MATCH("DECLARECURSOR", 13))
|
|
return_value = _readDeclareCursorStmt();
|
|
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.
|
|
*/
|
|
static Datum
|
|
readDatum(bool typbyval)
|
|
{
|
|
Size length,
|
|
i;
|
|
int tokenLength;
|
|
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 = %lu",
|
|
token ? (const char *) token : "[NULL]",
|
|
(unsigned long) length);
|
|
|
|
if (typbyval)
|
|
{
|
|
if (length > (Size) sizeof(Datum))
|
|
elog(ERROR, "byval datum but length = %lu",
|
|
(unsigned long) 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 = %lu",
|
|
token ? (const char *) token : "[NULL]",
|
|
(unsigned long) length);
|
|
|
|
return res;
|
|
}
|