/*-------------------------------------------------------------------------
 *
 * readfuncs.c
 *	  Reader functions for Postgres tree nodes.
 *
 * 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,
 * 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)
	{
		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)
	{
		Value	   *v = (Value *) lfirst(l);

		/*
		 * This is a bit tricky because OID is unsigned, and so nodeRead
		 * 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))
		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 */
	}

	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))
			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]",
			 (unsigned long) length);

	return res;
}
