postgresql/src/backend/nodes/readfuncs.c

981 lines
20 KiB
C
Raw Normal View History

/*-------------------------------------------------------------------------
*
* readfuncs.c
* Reader functions for Postgres tree nodes.
*
2002-06-20 22:29:54 +02:00
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.140 2002/11/25 21:29:38 tgl Exp $
*
* 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.)
*
*-------------------------------------------------------------------------
*/
#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.
*/
/* Declare appropriate local variables */
#define READ_LOCALS(nodeTypeName) \
nodeTypeName *local_node = makeNode(nodeTypeName); \
char *token; \
int length
/* 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
/* 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 Node field */
#define READ_NODE_FIELD(fldname) \
token = pg_strtok(&length); /* skip :fldname */ \
local_node->fldname = nodeRead(true)
/* Read an integer-list field */
#define READ_INTLIST_FIELD(fldname) \
token = pg_strtok(&length); /* skip :fldname */ \
local_node->fldname = toIntList(nodeRead(true))
/* Read an OID-list field */
#define READ_OIDLIST_FIELD(fldname) \
token = pg_strtok(&length); /* skip :fldname */ \
local_node->fldname = toOidList(nodeRead(true))
/* 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,
2001-03-22 05:01:46 +01:00
* 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 ? (char *) NULL : debackslash(token, length))
static Datum readDatum(bool typbyval);
/* Convert Value list returned by nodeRead into list of integers */
static List *
toIntList(List *list)
{
List *l;
foreach(l, list)
{
2001-03-22 05:01:46 +01:00
Value *v = (Value *) lfirst(l);
if (!IsA(v, Integer))
elog(ERROR, "toIntList: unexpected datatype");
lfirsti(l) = intVal(v);
pfree(v);
}
return list;
}
/* Convert Value list returned by nodeRead into list of OIDs */
static List *
toOidList(List *list)
{
List *l;
foreach(l, list)
{
2001-03-22 05:01:46 +01:00
Value *v = (Value *) lfirst(l);
/*
* This is a bit tricky because OID is unsigned, and so nodeRead
2001-03-22 05:01:46 +01:00
* might have concluded the value doesn't fit in an integer. Must
* cope with T_Float as well.
*/
if (IsA(v, Integer))
{
lfirsti(l) = (Oid) intVal(v);
pfree(v);
}
else if (IsA(v, Float))
{
lfirsti(l) = atooid(strVal(v));
pfree(strVal(v));
pfree(v);
}
else
elog(ERROR, "toOidList: unexpected datatype");
}
return list;
}
/*
* _readQuery
*/
static Query *
_readQuery(void)
{
READ_LOCALS(Query);
READ_ENUM_FIELD(commandType, CmdType);
READ_ENUM_FIELD(querySource, QuerySource);
READ_NODE_FIELD(utilityStmt);
READ_INT_FIELD(resultRelation);
READ_NODE_FIELD(into);
READ_BOOL_FIELD(isPortal);
READ_BOOL_FIELD(isBinary);
READ_BOOL_FIELD(hasAggs);
READ_BOOL_FIELD(hasSubLinks);
READ_NODE_FIELD(rtable);
READ_NODE_FIELD(jointree);
READ_INTLIST_FIELD(rowMarks);
READ_NODE_FIELD(targetList);
READ_NODE_FIELD(groupClause);
READ_NODE_FIELD(havingQual);
READ_NODE_FIELD(distinctClause);
READ_NODE_FIELD(sortClause);
READ_NODE_FIELD(limitOffset);
READ_NODE_FIELD(limitCount);
READ_NODE_FIELD(setOperations);
READ_INTLIST_FIELD(resultRelations);
/* planner-internal fields are left zero */
READ_DONE();
}
/*
* _readNotifyStmt
*/
static NotifyStmt *
_readNotifyStmt(void)
{
READ_LOCALS(NotifyStmt);
READ_NODE_FIELD(relation);
READ_DONE();
}
/*
* _readSortClause
*/
static SortClause *
_readSortClause(void)
{
READ_LOCALS(SortClause);
READ_UINT_FIELD(tleSortGroupRef);
READ_OID_FIELD(sortop);
READ_DONE();
}
/*
* _readGroupClause
*/
static GroupClause *
_readGroupClause(void)
{
READ_LOCALS(GroupClause);
READ_UINT_FIELD(tleSortGroupRef);
READ_OID_FIELD(sortop);
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_OIDLIST_FIELD(colTypes);
READ_DONE();
}
/*
* Stuff from primnodes.h.
*/
/*
* _readResdom
*/
static Resdom *
_readResdom(void)
{
READ_LOCALS(Resdom);
READ_INT_FIELD(resno);
READ_OID_FIELD(restype);
READ_INT_FIELD(restypmod);
READ_STRING_FIELD(resname);
READ_UINT_FIELD(ressortgroupref);
READ_UINT_FIELD(reskey);
READ_OID_FIELD(reskeyop);
READ_BOOL_FIELD(resjunk);
READ_DONE();
}
/*
* _readExpr
*/
static Expr *
_readExpr(void)
{
READ_LOCALS(Expr);
READ_OID_FIELD(typeOid);
/* do-it-yourself enum representation */
token = pg_strtok(&length); /* skip :opType */
token = pg_strtok(&length); /* get field value */
if (strncmp(token, "op", 2) == 0)
local_node->opType = OP_EXPR;
else if (strncmp(token, "distinct", 8) == 0)
local_node->opType = DISTINCT_EXPR;
else if (strncmp(token, "func", 4) == 0)
local_node->opType = FUNC_EXPR;
else if (strncmp(token, "or", 2) == 0)
local_node->opType = OR_EXPR;
else if (strncmp(token, "and", 3) == 0)
local_node->opType = AND_EXPR;
else if (strncmp(token, "not", 3) == 0)
local_node->opType = NOT_EXPR;
else if (strncmp(token, "subp", 4) == 0)
local_node->opType = SUBPLAN_EXPR;
else
elog(ERROR, "_readExpr: unknown opType \"%.*s\"", length, token);
READ_NODE_FIELD(oper);
READ_NODE_FIELD(args);
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_DONE();
}
/*
* _readArrayRef
*/
static ArrayRef *
_readArrayRef(void)
{
READ_LOCALS(ArrayRef);
READ_OID_FIELD(refrestype);
READ_INT_FIELD(refattrlength);
READ_INT_FIELD(refelemlength);
READ_BOOL_FIELD(refelembyval);
READ_CHAR_FIELD(refelemalign);
READ_NODE_FIELD(refupperindexpr);
READ_NODE_FIELD(reflowerindexpr);
READ_NODE_FIELD(refexpr);
READ_NODE_FIELD(refassgnexpr);
READ_DONE();
}
/*
* _readConst
*/
static Const *
_readConst(void)
{
READ_LOCALS(Const);
READ_OID_FIELD(consttype);
READ_INT_FIELD(constlen);
READ_BOOL_FIELD(constbyval);
READ_BOOL_FIELD(constisnull);
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();
}
/*
* _readFunc
*/
static Func *
_readFunc(void)
{
READ_LOCALS(Func);
READ_OID_FIELD(funcid);
READ_OID_FIELD(funcresulttype);
READ_BOOL_FIELD(funcretset);
READ_ENUM_FIELD(funcformat, CoercionForm);
local_node->func_fcache = NULL;
READ_DONE();
}
/*
* _readOper
*/
static Oper *
_readOper(void)
{
READ_LOCALS(Oper);
READ_OID_FIELD(opno);
READ_OID_FIELD(opid);
READ_OID_FIELD(opresulttype);
READ_BOOL_FIELD(opretset);
local_node->op_fcache = NULL;
READ_DONE();
}
/*
* _readParam
*/
static Param *
_readParam(void)
{
READ_LOCALS(Param);
READ_INT_FIELD(paramkind);
READ_INT_FIELD(paramid);
READ_STRING_FIELD(paramname);
READ_OID_FIELD(paramtype);
READ_DONE();
}
/*
* _readAggref
*/
static Aggref *
_readAggref(void)
{
READ_LOCALS(Aggref);
READ_OID_FIELD(aggfnoid);
READ_OID_FIELD(aggtype);
READ_NODE_FIELD(target);
READ_BOOL_FIELD(aggstar);
READ_BOOL_FIELD(aggdistinct);
/* aggno is not saved since it is just executor state */
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_DONE();
}
/*
* _readSubLink
*/
static SubLink *
_readSubLink(void)
{
READ_LOCALS(SubLink);
READ_ENUM_FIELD(subLinkType, SubLinkType);
READ_BOOL_FIELD(useor);
READ_NODE_FIELD(lefthand);
READ_NODE_FIELD(oper);
READ_NODE_FIELD(subselect);
READ_DONE();
}
/*
* _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();
}
/*
* _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_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(using);
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.
*/
/*
* _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_DONE();
}
/*
* _readCaseWhen
*/
static CaseWhen *
_readCaseWhen(void)
{
READ_LOCALS(CaseWhen);
READ_NODE_FIELD(expr);
READ_NODE_FIELD(result);
READ_DONE();
}
/*
* _readNullTest
*/
static NullTest *
_readNullTest(void)
{
READ_LOCALS(NullTest);
READ_NODE_FIELD(arg);
READ_ENUM_FIELD(nulltesttype, NullTestType);
READ_DONE();
}
/*
* _readBooleanTest
*/
static BooleanTest *
_readBooleanTest(void)
{
READ_LOCALS(BooleanTest);
READ_NODE_FIELD(arg);
READ_ENUM_FIELD(booltesttype, BoolTestType);
READ_DONE();
}
/*
* _readConstraintTest
*/
static ConstraintTest *
_readConstraintTest(void)
{
READ_LOCALS(ConstraintTest);
READ_NODE_FIELD(arg);
READ_ENUM_FIELD(testtype, ConstraintTestType);
READ_STRING_FIELD(name);
READ_STRING_FIELD(domname);
READ_NODE_FIELD(check_expr);
READ_DONE();
}
/*
* _readDomainConstraintValue
*/
static DomainConstraintValue *
_readDomainConstraintValue(void)
{
READ_LOCALS_NO_FIELDS(DomainConstraintValue);
READ_DONE();
}
/*
* _readConstraintTestValue
*/
static ConstraintTestValue *
_readConstraintTestValue(void)
{
READ_LOCALS(ConstraintTestValue);
READ_OID_FIELD(typeId);
READ_INT_FIELD(typeMod);
READ_DONE();
}
/*
* _readTargetEntry
*/
static TargetEntry *
_readTargetEntry(void)
{
READ_LOCALS(TargetEntry);
READ_NODE_FIELD(resdom);
/* fjoin not supported ... */
READ_NODE_FIELD(expr);
READ_DONE();
}
static ColumnRef *
_readColumnRef(void)
{
READ_LOCALS(ColumnRef);
READ_NODE_FIELD(fields);
READ_NODE_FIELD(indirection);
READ_DONE();
}
static ColumnDef *
_readColumnDef(void)
{
READ_LOCALS(ColumnDef);
READ_STRING_FIELD(colname);
READ_NODE_FIELD(typename);
READ_INT_FIELD(inhcount);
READ_BOOL_FIELD(is_local);
READ_BOOL_FIELD(is_not_null);
READ_NODE_FIELD(raw_default);
READ_STRING_FIELD(cooked_default);
READ_NODE_FIELD(constraints);
READ_NODE_FIELD(support);
READ_DONE();
}
static TypeName *
_readTypeName(void)
{
READ_LOCALS(TypeName);
READ_NODE_FIELD(names);
READ_OID_FIELD(typeid);
READ_BOOL_FIELD(timezone);
READ_BOOL_FIELD(setof);
READ_BOOL_FIELD(pct_type);
READ_INT_FIELD(typmod);
READ_NODE_FIELD(arrayBounds);
READ_DONE();
}
static ExprFieldSelect *
_readExprFieldSelect(void)
{
READ_LOCALS(ExprFieldSelect);
READ_NODE_FIELD(arg);
READ_NODE_FIELD(fields);
READ_NODE_FIELD(indirection);
READ_DONE();
}
static Alias *
_readAlias(void)
{
READ_LOCALS(Alias);
READ_STRING_FIELD(aliasname);
READ_NODE_FIELD(colnames);
READ_DONE();
}
/*
* _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_FUNCTION:
READ_NODE_FIELD(funcexpr);
READ_NODE_FIELD(coldeflist);
break;
case RTE_JOIN:
READ_ENUM_FIELD(jointype, JoinType);
READ_NODE_FIELD(joinaliasvars);
break;
default:
elog(ERROR, "bogus rte kind %d", (int) local_node->rtekind);
break;
}
READ_BOOL_FIELD(inh);
READ_BOOL_FIELD(inFromCl);
READ_BOOL_FIELD(checkForRead);
READ_BOOL_FIELD(checkForWrite);
READ_OID_FIELD(checkAsUser);
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("AGGREF", 6))
1999-01-24 01:28:37 +01:00
return_value = _readAggref();
else if (MATCH("SUBLINK", 7))
return_value = _readSubLink();
else if (MATCH("FIELDSELECT", 11))
return_value = _readFieldSelect();
else if (MATCH("RELABELTYPE", 11))
return_value = _readRelabelType();
else if (MATCH("RANGETBLREF", 11))
return_value = _readRangeTblRef();
else if (MATCH("FROMEXPR", 8))
return_value = _readFromExpr();
else if (MATCH("JOINEXPR", 8))
return_value = _readJoinExpr();
else if (MATCH("RESDOM", 6))
return_value = _readResdom();
else if (MATCH("EXPR", 4))
return_value = _readExpr();
else if (MATCH("ARRAYREF", 8))
return_value = _readArrayRef();
else if (MATCH("VAR", 3))
return_value = _readVar();
else if (MATCH("CONST", 5))
return_value = _readConst();
else if (MATCH("FUNC", 4))
return_value = _readFunc();
else if (MATCH("OPER", 4))
return_value = _readOper();
else if (MATCH("PARAM", 5))
return_value = _readParam();
else if (MATCH("TARGETENTRY", 11))
return_value = _readTargetEntry();
else if (MATCH("RANGEVAR", 8))
return_value = _readRangeVar();
else if (MATCH("COLUMNREF", 9))
return_value = _readColumnRef();
else if (MATCH("COLUMNDEF", 9))
return_value = _readColumnDef();
else if (MATCH("TYPENAME", 8))
return_value = _readTypeName();
else if (MATCH("EXPRFIELDSELECT", 15))
return_value = _readExprFieldSelect();
else if (MATCH("ALIAS", 5))
return_value = _readAlias();
else if (MATCH("RTE", 3))
return_value = _readRangeTblEntry();
else if (MATCH("QUERY", 5))
return_value = _readQuery();
else if (MATCH("NOTIFY", 6))
return_value = _readNotifyStmt();
else if (MATCH("SORTCLAUSE", 10))
return_value = _readSortClause();
else if (MATCH("GROUPCLAUSE", 11))
return_value = _readGroupClause();
else if (MATCH("SETOPERATIONSTMT", 16))
return_value = _readSetOperationStmt();
else if (MATCH("CASE", 4))
return_value = _readCaseExpr();
else if (MATCH("WHEN", 4))
return_value = _readCaseWhen();
else if (MATCH("NULLTEST", 8))
return_value = _readNullTest();
else if (MATCH("BOOLEANTEST", 11))
return_value = _readBooleanTest();
else if (MATCH("CONSTRAINTTEST", 14))
return_value = _readConstraintTest();
else if (MATCH("DOMAINCONSTRAINTVALUE", 21))
return_value = _readDomainConstraintValue();
else if (MATCH("CONSTRAINTTESTVALUE", 19))
return_value = _readConstraintTestValue();
else
{
elog(ERROR, "badly formatted node string \"%.32s\"...", token);
return_value = NULL; /* keep compiler quiet */
}
1998-09-01 05:29:17 +02:00
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, "readDatum: expected '%s', got '%s'; length = %lu",
"[", token ? (const char *) token : "[NULL]",
(unsigned long) length);
if (typbyval)
{
if (length > (Size) sizeof(Datum))
2001-02-12 22:03:03 +01:00
elog(ERROR, "readDatum: byval & 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, "readDatum: expected '%s', got '%s'; length = %lu",
"]", token ? (const char *) token : "[NULL]",
2001-02-12 22:03:03 +01:00
(unsigned long) length);
1998-09-01 05:29:17 +02:00
return res;
}