(moving to pgsql-hackers)

On 03/06/10 10:37, Heikki Linnakangas wrote:
However, I'm afraid we're lacking in input validation of read-funcs in
general. ...
>
Does anyone have an idea on how
to validate the input in a more wholesale fashion, so that we don't need
to plug these holes one by one?

Apparently not :-(.

We have two options:

1. Make pg_get_expr() handle arbitrary (possibly even malicious) input gracefully.

2. Restrict pg_get_expr() to superusers only.

Does anyone want to argue for option 2? We could create views using pg_get_expr() over the internal catalogs that store trees, so that regular users could access those without being able to pass arbitrary input to pg_get_expr(). However, it would break existing applications, at least pgAdmin uses pg_get_expr().

Assuming we want to make pg_get_expr() check its input, we need to:

* plug the hole Rushabh reported, and not crash on premature end of string
* check all Node types, so that you e.g. can't pass an Integer in a field that's supposed to hold a CaseWhenExpr
* similarly, check that all Lists contain elements of the right type.

This can all be done in a straightforward way in readfuncs.c, we just need a bit more decoration to all the READ_* macros. However, that's still not enough; the functions in ruleutils.c make a number of other assumptions, like that an OpExpr always has exactly one or two arguments. That kind of assumptions will need to be explicitly checked. Many of them already have Asserts, they need to be turned into elogs.

Thoughts? Attached is a patch for the readfuncs.c changes. Unless someone has a better idea, I'll start going through ruleutils.c and add explicit checks for any unsafe assumptions. It's going to be a lot of work, as there's a lot of code in ruleutils.c and the changes need to be backpatched as well.

--
  Heikki Linnakangas
  EnterpriseDB   http://www.enterprisedb.com
diff --git a/src/backend/nodes/read.c b/src/backend/nodes/read.c
index 5af15c2..d0157d0 100644
--- a/src/backend/nodes/read.c
+++ b/src/backend/nodes/read.c
@@ -50,7 +50,7 @@ stringToNode(char *str)
 
 	pg_strtok_ptr = str;		/* point pg_strtok at the string to read */
 
-	retval = nodeRead(NULL, 0); /* do the reading */
+	retval = nodeRead(NULL, 0, 0); /* do the reading */
 
 	pg_strtok_ptr = save_strtok;
 
@@ -266,15 +266,18 @@ nodeTokenType(char *token, int length)
  * The return value is declared void *, not Node *, to avoid having to
  * cast it explicitly in callers that assign to fields of different types.
  *
- * External callers should always pass NULL/0 for the arguments.  Internally
+ * External callers should always pass NULL/0 for the token/tok_lenarguments.  Internally
  * a non-NULL token may be passed when the upper recursion level has already
  * scanned the first token of a node's representation.
  *
+ * If expectedType is non-zero, the node must of the given type, or an error
+ * is thrown.
+ *
  * We assume pg_strtok is already initialized with a string to read (hence
  * this should only be invoked from within a stringToNode operation).
  */
 void *
-nodeRead(char *token, int tok_len)
+nodeRead(char *token, int tok_len, int expectedType)
 {
 	Node	   *result;
 	NodeTag		type;
@@ -358,7 +361,7 @@ nodeRead(char *token, int tok_len)
 						/* We have already scanned next token... */
 						if (token[0] == ')')
 							break;
-						l = lappend(l, nodeRead(token, tok_len));
+						l = lappend(l, nodeRead(token, tok_len, 0));
 						token = pg_strtok(&tok_len);
 						if (token == NULL)
 							elog(ERROR, "unterminated List structure");
@@ -419,5 +422,9 @@ nodeRead(char *token, int tok_len)
 			break;
 	}
 
+	if (expectedType != 0 && nodeTag(result) != expectedType)
+		elog(ERROR, "node type %d found, expected %d",
+			 nodeTag(result), expectedType);
+
 	return (void *) result;
 }
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index bc6e2a6..cb814fd 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -57,66 +57,90 @@
 
 /* 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 */ \
+	token = pg_strtok_e(&length);		/* skip :fldname */ \
+	token = pg_strtok_e(&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 */ \
+	token = pg_strtok_e(&length);		/* skip :fldname */ \
+	token = pg_strtok_e(&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 */ \
+	token = pg_strtok_e(&length);		/* skip :fldname */ \
+	token = pg_strtok_e(&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 */ \
+	token = pg_strtok_e(&length);		/* skip :fldname */ \
+	token = pg_strtok_e(&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 */ \
+	token = pg_strtok_e(&length);		/* skip :fldname */ \
+	token = pg_strtok_e(&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 */ \
+	token = pg_strtok_e(&length);		/* skip :fldname */ \
+	token = pg_strtok_e(&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 */ \
+	token = pg_strtok_e(&length);		/* skip :fldname */ \
+	token = pg_strtok_e(&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 */ \
+	token = pg_strtok_e(&length);		/* skip :fldname */ \
+	token = pg_strtok_e(&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 */ \
+	token = pg_strtok_e(&length);		/* skip :fldname */ \
+	token = pg_strtok_e(&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)
+#define READ_NODE_FIELD(fldname, type) \
+	token = pg_strtok_e(&length);		/* skip :fldname */ \
+	local_node->fldname = (type *) nodeRead(NULL, 0, T_##type)
+
+/* Read a Node field */
+#define READ_ANY_NODE_FIELD(fldname) \
+	token = pg_strtok_e(&length);		/* skip :fldname */ \
+	local_node->fldname = nodeRead(NULL, 0, 0)
+
+/* Read a List field */
+#define READ_LIST_FIELD(fldname, type)							\
+	token = pg_strtok_e(&length);		/* skip :fldname */ \
+	local_node->fldname = readList(T_##type)
+
+#define READ_ANY_LIST_FIELD(fldname)							\
+	token = pg_strtok_e(&length);		/* skip :fldname */ \
+	local_node->fldname = (List *) nodeRead(NULL, 0, T_List);
+
+/* Read a IntList field */
+#define READ_INT_LIST_FIELD(fldname) \
+	token = pg_strtok_e(&length);		/* skip :fldname */ \
+	local_node->fldname = nodeRead(NULL, 0, T_IntList)
+
+/* Read a OidList field */
+#define READ_OID_LIST_FIELD(fldname)						\
+	token = pg_strtok_e(&length);		/* skip :fldname */ \
+	local_node->fldname = nodeRead(NULL, 0, T_OidList)
 
 /* Read a bitmapset field */
 #define READ_BITMAPSET_FIELD(fldname) \
-	token = pg_strtok(&length);		/* skip :fldname */ \
+	token = pg_strtok_e(&length);		/* skip :fldname */ \
 	local_node->fldname = _readBitmapset()
 
 /* Routine exit */
@@ -139,8 +163,21 @@
 #define nullable_string(token,length)  \
 	((length) == 0 ? NULL : debackslash(token, length))
 
-
 static Datum readDatum(bool typbyval);
+static List *readList(int elemtype);
+static char *pg_strtok_e(int *length);
+
+/*
+ * Like pg_strtok(), but throws an error on end of string
+ */
+static char *
+pg_strtok_e(int *length)
+{
+	char *token = pg_strtok(length);
+	if (token == NULL)
+		elog(ERROR, "unexpected end of string while reading tree node");
+	return token;
+}
 
 /*
  * _readBitmapset
@@ -195,29 +232,29 @@ _readQuery(void)
 	READ_ENUM_FIELD(commandType, CmdType);
 	READ_ENUM_FIELD(querySource, QuerySource);
 	READ_BOOL_FIELD(canSetTag);
-	READ_NODE_FIELD(utilityStmt);
+	READ_ANY_NODE_FIELD(utilityStmt);
 	READ_INT_FIELD(resultRelation);
-	READ_NODE_FIELD(intoClause);
+	READ_NODE_FIELD(intoClause, 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_LIST_FIELD(cteList, CommonTableExpr);
+	READ_LIST_FIELD(rtable, RangeTblEntry);
+	READ_NODE_FIELD(jointree, FromExpr);
+	READ_LIST_FIELD(targetList, TargetEntry);
+	READ_LIST_FIELD(returningList, TargetEntry);
+	READ_LIST_FIELD(groupClause, SortGroupClause);
+	READ_ANY_NODE_FIELD(havingQual);
+	READ_LIST_FIELD(windowClause, WindowClause);
+	READ_LIST_FIELD(distinctClause, SortGroupClause);
+	READ_LIST_FIELD(sortClause, SortGroupClause);
+	READ_ANY_NODE_FIELD(limitOffset);
+	READ_ANY_NODE_FIELD(limitCount);
+	READ_LIST_FIELD(rowMarks, RowMarkClause);
+	READ_ANY_NODE_FIELD(setOperations);
 
 	READ_DONE();
 }
@@ -246,7 +283,7 @@ _readDeclareCursorStmt(void)
 
 	READ_STRING_FIELD(portalname);
 	READ_INT_FIELD(options);
-	READ_NODE_FIELD(query);
+	READ_ANY_NODE_FIELD(query);
 
 	READ_DONE();
 }
@@ -277,11 +314,11 @@ _readWindowClause(void)
 
 	READ_STRING_FIELD(name);
 	READ_STRING_FIELD(refname);
-	READ_NODE_FIELD(partitionClause);
-	READ_NODE_FIELD(orderClause);
+	READ_LIST_FIELD(partitionClause, SortGroupClause);
+	READ_LIST_FIELD(orderClause, SortGroupClause);
 	READ_INT_FIELD(frameOptions);
-	READ_NODE_FIELD(startOffset);
-	READ_NODE_FIELD(endOffset);
+	READ_ANY_NODE_FIELD(startOffset);
+	READ_ANY_NODE_FIELD(endOffset);
 	READ_UINT_FIELD(winref);
 	READ_BOOL_FIELD(copiedOrder);
 
@@ -313,14 +350,14 @@ _readCommonTableExpr(void)
 	READ_LOCALS(CommonTableExpr);
 
 	READ_STRING_FIELD(ctename);
-	READ_NODE_FIELD(aliascolnames);
-	READ_NODE_FIELD(ctequery);
+	READ_LIST_FIELD(aliascolnames, String);
+	READ_ANY_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_LIST_FIELD(ctecolnames, String);
+	READ_OID_LIST_FIELD(ctecoltypes);
+	READ_INT_LIST_FIELD(ctecoltypmods);
 
 	READ_DONE();
 }
@@ -335,11 +372,11 @@ _readSetOperationStmt(void)
 
 	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_ANY_NODE_FIELD(larg);
+	READ_ANY_NODE_FIELD(rarg);
+	READ_OID_LIST_FIELD(colTypes);
+	READ_INT_LIST_FIELD(colTypmods);
+	READ_LIST_FIELD(groupClauses, SortGroupClause);
 
 	READ_DONE();
 }
@@ -355,7 +392,7 @@ _readAlias(void)
 	READ_LOCALS(Alias);
 
 	READ_STRING_FIELD(aliasname);
-	READ_NODE_FIELD(colnames);
+	READ_LIST_FIELD(colnames, String);
 
 	READ_DONE();
 }
@@ -372,7 +409,7 @@ _readRangeVar(void)
 	READ_STRING_FIELD(relname);
 	READ_ENUM_FIELD(inhOpt, InhOption);
 	READ_BOOL_FIELD(istemp);
-	READ_NODE_FIELD(alias);
+	READ_NODE_FIELD(alias, Alias);
 	READ_LOCATION_FIELD(location);
 
 	READ_DONE();
@@ -383,9 +420,9 @@ _readIntoClause(void)
 {
 	READ_LOCALS(IntoClause);
 
-	READ_NODE_FIELD(rel);
-	READ_NODE_FIELD(colNames);
-	READ_NODE_FIELD(options);
+	READ_NODE_FIELD(rel, RangeVar);
+	READ_LIST_FIELD(colNames, ColumnDef);
+	READ_LIST_FIELD(options, DefElem);
 	READ_ENUM_FIELD(onCommit, OnCommitAction);
 	READ_STRING_FIELD(tableSpaceName);
 
@@ -463,9 +500,9 @@ _readAggref(void)
 
 	READ_OID_FIELD(aggfnoid);
 	READ_OID_FIELD(aggtype);
-	READ_NODE_FIELD(args);
-	READ_NODE_FIELD(aggorder);
-	READ_NODE_FIELD(aggdistinct);
+	READ_LIST_FIELD(args, TargetEntry);
+	READ_LIST_FIELD(aggorder, SortGroupClause);
+	READ_LIST_FIELD(aggdistinct, SortGroupClause);
 	READ_BOOL_FIELD(aggstar);
 	READ_UINT_FIELD(agglevelsup);
 	READ_LOCATION_FIELD(location);
@@ -483,7 +520,7 @@ _readWindowFunc(void)
 
 	READ_OID_FIELD(winfnoid);
 	READ_OID_FIELD(wintype);
-	READ_NODE_FIELD(args);
+	READ_ANY_LIST_FIELD(args);
 	READ_UINT_FIELD(winref);
 	READ_BOOL_FIELD(winstar);
 	READ_BOOL_FIELD(winagg);
@@ -503,10 +540,10 @@ _readArrayRef(void)
 	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_ANY_LIST_FIELD(refupperindexpr);
+	READ_ANY_LIST_FIELD(reflowerindexpr);
+	READ_ANY_NODE_FIELD(refexpr);
+	READ_ANY_NODE_FIELD(refassgnexpr);
 
 	READ_DONE();
 }
@@ -523,7 +560,7 @@ _readFuncExpr(void)
 	READ_OID_FIELD(funcresulttype);
 	READ_BOOL_FIELD(funcretset);
 	READ_ENUM_FIELD(funcformat, CoercionForm);
-	READ_NODE_FIELD(args);
+	READ_ANY_LIST_FIELD(args);
 	READ_LOCATION_FIELD(location);
 
 	READ_DONE();
@@ -537,7 +574,7 @@ _readNamedArgExpr(void)
 {
 	READ_LOCALS(NamedArgExpr);
 
-	READ_NODE_FIELD(arg);
+	READ_ANY_NODE_FIELD(arg);
 	READ_STRING_FIELD(name);
 	READ_INT_FIELD(argnumber);
 	READ_LOCATION_FIELD(location);
@@ -568,7 +605,7 @@ _readOpExpr(void)
 
 	READ_OID_FIELD(opresulttype);
 	READ_BOOL_FIELD(opretset);
-	READ_NODE_FIELD(args);
+	READ_ANY_LIST_FIELD(args);
 	READ_LOCATION_FIELD(location);
 
 	READ_DONE();
@@ -597,7 +634,7 @@ _readDistinctExpr(void)
 
 	READ_OID_FIELD(opresulttype);
 	READ_BOOL_FIELD(opretset);
-	READ_NODE_FIELD(args);
+	READ_ANY_LIST_FIELD(args);
 	READ_LOCATION_FIELD(location);
 
 	READ_DONE();
@@ -625,7 +662,7 @@ _readScalarArrayOpExpr(void)
 	local_node->opfuncid = InvalidOid;
 
 	READ_BOOL_FIELD(useOr);
-	READ_NODE_FIELD(args);
+	READ_ANY_LIST_FIELD(args);
 	READ_LOCATION_FIELD(location);
 
 	READ_DONE();
@@ -640,8 +677,8 @@ _readBoolExpr(void)
 	READ_LOCALS(BoolExpr);
 
 	/* do-it-yourself enum representation */
-	token = pg_strtok(&length); /* skip :boolop */
-	token = pg_strtok(&length); /* get field value */
+	token = pg_strtok_e(&length); /* skip :boolop */
+	token = pg_strtok_e(&length); /* get field value */
 	if (strncmp(token, "and", 3) == 0)
 		local_node->boolop = AND_EXPR;
 	else if (strncmp(token, "or", 2) == 0)
@@ -651,7 +688,7 @@ _readBoolExpr(void)
 	else
 		elog(ERROR, "unrecognized boolop \"%.*s\"", length, token);
 
-	READ_NODE_FIELD(args);
+	READ_ANY_LIST_FIELD(args);
 	READ_LOCATION_FIELD(location);
 
 	READ_DONE();
@@ -666,9 +703,9 @@ _readSubLink(void)
 	READ_LOCALS(SubLink);
 
 	READ_ENUM_FIELD(subLinkType, SubLinkType);
-	READ_NODE_FIELD(testexpr);
-	READ_NODE_FIELD(operName);
-	READ_NODE_FIELD(subselect);
+	READ_ANY_NODE_FIELD(testexpr);
+	READ_LIST_FIELD(operName, String);
+	READ_ANY_NODE_FIELD(subselect);
 	READ_LOCATION_FIELD(location);
 
 	READ_DONE();
@@ -686,7 +723,7 @@ _readFieldSelect(void)
 {
 	READ_LOCALS(FieldSelect);
 
-	READ_NODE_FIELD(arg);
+	READ_ANY_NODE_FIELD(arg);
 	READ_INT_FIELD(fieldnum);
 	READ_OID_FIELD(resulttype);
 	READ_INT_FIELD(resulttypmod);
@@ -702,9 +739,9 @@ _readFieldStore(void)
 {
 	READ_LOCALS(FieldStore);
 
-	READ_NODE_FIELD(arg);
-	READ_NODE_FIELD(newvals);
-	READ_NODE_FIELD(fieldnums);
+	READ_ANY_NODE_FIELD(arg);
+	READ_ANY_LIST_FIELD(newvals);
+	READ_INT_LIST_FIELD(fieldnums);
 	READ_OID_FIELD(resulttype);
 
 	READ_DONE();
@@ -718,7 +755,7 @@ _readRelabelType(void)
 {
 	READ_LOCALS(RelabelType);
 
-	READ_NODE_FIELD(arg);
+	READ_ANY_NODE_FIELD(arg);
 	READ_OID_FIELD(resulttype);
 	READ_INT_FIELD(resulttypmod);
 	READ_ENUM_FIELD(relabelformat, CoercionForm);
@@ -735,7 +772,7 @@ _readCoerceViaIO(void)
 {
 	READ_LOCALS(CoerceViaIO);
 
-	READ_NODE_FIELD(arg);
+	READ_ANY_NODE_FIELD(arg);
 	READ_OID_FIELD(resulttype);
 	READ_ENUM_FIELD(coerceformat, CoercionForm);
 	READ_LOCATION_FIELD(location);
@@ -751,7 +788,7 @@ _readArrayCoerceExpr(void)
 {
 	READ_LOCALS(ArrayCoerceExpr);
 
-	READ_NODE_FIELD(arg);
+	READ_ANY_NODE_FIELD(arg);
 	READ_OID_FIELD(elemfuncid);
 	READ_OID_FIELD(resulttype);
 	READ_INT_FIELD(resulttypmod);
@@ -770,7 +807,7 @@ _readConvertRowtypeExpr(void)
 {
 	READ_LOCALS(ConvertRowtypeExpr);
 
-	READ_NODE_FIELD(arg);
+	READ_ANY_NODE_FIELD(arg);
 	READ_OID_FIELD(resulttype);
 	READ_ENUM_FIELD(convertformat, CoercionForm);
 	READ_LOCATION_FIELD(location);
@@ -787,9 +824,9 @@ _readCaseExpr(void)
 	READ_LOCALS(CaseExpr);
 
 	READ_OID_FIELD(casetype);
-	READ_NODE_FIELD(arg);
-	READ_NODE_FIELD(args);
-	READ_NODE_FIELD(defresult);
+	READ_ANY_NODE_FIELD(arg);
+	READ_LIST_FIELD(args, CaseWhen);
+	READ_ANY_NODE_FIELD(defresult);
 	READ_LOCATION_FIELD(location);
 
 	READ_DONE();
@@ -803,8 +840,8 @@ _readCaseWhen(void)
 {
 	READ_LOCALS(CaseWhen);
 
-	READ_NODE_FIELD(expr);
-	READ_NODE_FIELD(result);
+	READ_ANY_NODE_FIELD(expr);
+	READ_ANY_NODE_FIELD(result);
 	READ_LOCATION_FIELD(location);
 
 	READ_DONE();
@@ -834,7 +871,7 @@ _readArrayExpr(void)
 
 	READ_OID_FIELD(array_typeid);
 	READ_OID_FIELD(element_typeid);
-	READ_NODE_FIELD(elements);
+	READ_ANY_LIST_FIELD(elements);
 	READ_BOOL_FIELD(multidims);
 	READ_LOCATION_FIELD(location);
 
@@ -849,10 +886,10 @@ _readRowExpr(void)
 {
 	READ_LOCALS(RowExpr);
 
-	READ_NODE_FIELD(args);
+	READ_ANY_LIST_FIELD(args);
 	READ_OID_FIELD(row_typeid);
 	READ_ENUM_FIELD(row_format, CoercionForm);
-	READ_NODE_FIELD(colnames);
+	READ_LIST_FIELD(colnames, String);
 	READ_LOCATION_FIELD(location);
 
 	READ_DONE();
@@ -867,10 +904,10 @@ _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_OID_LIST_FIELD(opnos);
+	READ_OID_LIST_FIELD(opfamilies);
+	READ_ANY_LIST_FIELD(largs);
+	READ_ANY_LIST_FIELD(rargs);
 
 	READ_DONE();
 }
@@ -884,7 +921,7 @@ _readCoalesceExpr(void)
 	READ_LOCALS(CoalesceExpr);
 
 	READ_OID_FIELD(coalescetype);
-	READ_NODE_FIELD(args);
+	READ_ANY_LIST_FIELD(args);
 	READ_LOCATION_FIELD(location);
 
 	READ_DONE();
@@ -900,7 +937,7 @@ _readMinMaxExpr(void)
 
 	READ_OID_FIELD(minmaxtype);
 	READ_ENUM_FIELD(op, MinMaxOp);
-	READ_NODE_FIELD(args);
+	READ_ANY_LIST_FIELD(args);
 	READ_LOCATION_FIELD(location);
 
 	READ_DONE();
@@ -916,9 +953,9 @@ _readXmlExpr(void)
 
 	READ_ENUM_FIELD(op, XmlExprOp);
 	READ_STRING_FIELD(name);
-	READ_NODE_FIELD(named_args);
-	READ_NODE_FIELD(arg_names);
-	READ_NODE_FIELD(args);
+	READ_ANY_LIST_FIELD(named_args);
+	READ_LIST_FIELD(arg_names, String);
+	READ_ANY_LIST_FIELD(args);
 	READ_ENUM_FIELD(xmloption, XmlOptionType);
 	READ_OID_FIELD(type);
 	READ_INT_FIELD(typmod);
@@ -950,7 +987,7 @@ _readNullIfExpr(void)
 
 	READ_OID_FIELD(opresulttype);
 	READ_BOOL_FIELD(opretset);
-	READ_NODE_FIELD(args);
+	READ_ANY_LIST_FIELD(args);
 	READ_LOCATION_FIELD(location);
 
 	READ_DONE();
@@ -964,7 +1001,7 @@ _readNullTest(void)
 {
 	READ_LOCALS(NullTest);
 
-	READ_NODE_FIELD(arg);
+	READ_ANY_NODE_FIELD(arg);
 	READ_ENUM_FIELD(nulltesttype, NullTestType);
 	READ_BOOL_FIELD(argisrow);
 
@@ -979,7 +1016,7 @@ _readBooleanTest(void)
 {
 	READ_LOCALS(BooleanTest);
 
-	READ_NODE_FIELD(arg);
+	READ_ANY_NODE_FIELD(arg);
 	READ_ENUM_FIELD(booltesttype, BoolTestType);
 
 	READ_DONE();
@@ -993,7 +1030,7 @@ _readCoerceToDomain(void)
 {
 	READ_LOCALS(CoerceToDomain);
 
-	READ_NODE_FIELD(arg);
+	READ_ANY_NODE_FIELD(arg);
 	READ_OID_FIELD(resulttype);
 	READ_INT_FIELD(resulttypmod);
 	READ_ENUM_FIELD(coercionformat, CoercionForm);
@@ -1055,7 +1092,7 @@ _readTargetEntry(void)
 {
 	READ_LOCALS(TargetEntry);
 
-	READ_NODE_FIELD(expr);
+	READ_ANY_NODE_FIELD(expr);
 	READ_INT_FIELD(resno);
 	READ_STRING_FIELD(resname);
 	READ_UINT_FIELD(ressortgroupref);
@@ -1089,11 +1126,11 @@ _readJoinExpr(void)
 
 	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_ANY_NODE_FIELD(larg);
+	READ_ANY_NODE_FIELD(rarg);
+	READ_LIST_FIELD(usingClause, String);
+	READ_ANY_NODE_FIELD(quals);
+	READ_NODE_FIELD(alias, Alias);
 	READ_INT_FIELD(rtindex);
 
 	READ_DONE();
@@ -1107,8 +1144,8 @@ _readFromExpr(void)
 {
 	READ_LOCALS(FromExpr);
 
-	READ_NODE_FIELD(fromlist);
-	READ_NODE_FIELD(quals);
+	READ_ANY_LIST_FIELD(fromlist);
+	READ_ANY_NODE_FIELD(quals);
 
 	READ_DONE();
 }
@@ -1127,8 +1164,8 @@ _readRangeTblEntry(void)
 	READ_LOCALS(RangeTblEntry);
 
 	/* put alias + eref first to make dump more legible */
-	READ_NODE_FIELD(alias);
-	READ_NODE_FIELD(eref);
+	READ_NODE_FIELD(alias, Alias);
+	READ_NODE_FIELD(eref, Alias);
 	READ_ENUM_FIELD(rtekind, RTEKind);
 
 	switch (local_node->rtekind)
@@ -1138,26 +1175,26 @@ _readRangeTblEntry(void)
 			READ_OID_FIELD(relid);
 			break;
 		case RTE_SUBQUERY:
-			READ_NODE_FIELD(subquery);
+			READ_NODE_FIELD(subquery, Query);
 			break;
 		case RTE_JOIN:
 			READ_ENUM_FIELD(jointype, JoinType);
-			READ_NODE_FIELD(joinaliasvars);
+			READ_ANY_LIST_FIELD(joinaliasvars);
 			break;
 		case RTE_FUNCTION:
-			READ_NODE_FIELD(funcexpr);
-			READ_NODE_FIELD(funccoltypes);
-			READ_NODE_FIELD(funccoltypmods);
+			READ_ANY_NODE_FIELD(funcexpr);
+			READ_OID_LIST_FIELD(funccoltypes);
+			READ_INT_LIST_FIELD(funccoltypmods);
 			break;
 		case RTE_VALUES:
-			READ_NODE_FIELD(values_lists);
+			READ_LIST_FIELD(values_lists, List);
 			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);
+			READ_OID_LIST_FIELD(ctecoltypes);
+			READ_INT_LIST_FIELD(ctecoltypmods);
 			break;
 		default:
 			elog(ERROR, "unrecognized RTE kind: %d",
@@ -1191,7 +1228,7 @@ parseNodeString(void)
 
 	READ_TEMP_LOCALS();
 
-	token = pg_strtok(&length);
+	token = pg_strtok_e(&length);
 
 #define MATCH(tokname, namelen) \
 	(length == namelen && strncmp(token, tokname, namelen) == 0)
@@ -1328,7 +1365,7 @@ readDatum(bool typbyval)
 	/*
 	 * read the actual length of the value
 	 */
-	token = pg_strtok(&tokenLength);
+	token = pg_strtok_e(&tokenLength);
 	length = atoui(token);
 
 	token = pg_strtok(&tokenLength);	/* read the '[' */
@@ -1346,7 +1383,7 @@ readDatum(bool typbyval)
 		s = (char *) (&res);
 		for (i = 0; i < (Size) sizeof(Datum); i++)
 		{
-			token = pg_strtok(&tokenLength);
+			token = pg_strtok_e(&tokenLength);
 			s[i] = (char) atoi(token);
 		}
 	}
@@ -1357,7 +1394,7 @@ readDatum(bool typbyval)
 		s = (char *) palloc(length);
 		for (i = 0; i < length; i++)
 		{
-			token = pg_strtok(&tokenLength);
+			token = pg_strtok_e(&tokenLength);
 			s[i] = (char) atoi(token);
 		}
 		res = PointerGetDatum(s);
@@ -1371,3 +1408,21 @@ readDatum(bool typbyval)
 
 	return res;
 }
+
+/*
+ * Reads a node list, and checks that all elements are of the given type.
+ */
+static List *
+readList(int elemtype)
+{
+	List *l = (List *) nodeRead(NULL, 0, T_List);
+	ListCell *lc;
+
+	foreach(lc, l)
+	{
+		if (nodeTag(lfirst(lc)) != elemtype)
+			elog(ERROR, "element type %d found in List, expected %d",
+				 nodeTag(lfirst(lc)), elemtype);
+	}
+	return l;
+}
diff --git a/src/include/nodes/readfuncs.h b/src/include/nodes/readfuncs.h
index f373b32..03f75aa 100644
--- a/src/include/nodes/readfuncs.h
+++ b/src/include/nodes/readfuncs.h
@@ -21,7 +21,7 @@
  */
 extern char *pg_strtok(int *length);
 extern char *debackslash(char *token, int length);
-extern void *nodeRead(char *token, int tok_len);
+extern void *nodeRead(char *token, int tok_len, int expectedType);
 
 /*
  * prototypes for functions in readfuncs.c
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to