mirror of
				https://github.com/postgres/postgres.git
				synced 2025-11-03 09:13:20 +03:00 
			
		
		
		
	Between v15 and now, this function's "else if" chain grew from 252 lines to 592 lines, exceeding a compiler limit that manifests as "fatal error C1026: parser stack overflow, program too complex (compiling source file src/backend/nodes/readfuncs.c)". Use "if (...) return ...;" instead. Reviewed by Tom Lane, Peter Eisentraut and Michael Paquier. Not all reviewers endorse this. Discussion: https://postgr.es/m/20230607185458.GA1334487@rfd.leadboat.com
		
			
				
	
	
		
			823 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			823 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*-------------------------------------------------------------------------
 | 
						|
 *
 | 
						|
 * readfuncs.c
 | 
						|
 *	  Reader functions for Postgres tree nodes.
 | 
						|
 *
 | 
						|
 * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
 | 
						|
 * Portions Copyright (c) 1994, Regents of the University of California
 | 
						|
 *
 | 
						|
 *
 | 
						|
 * IDENTIFICATION
 | 
						|
 *	  src/backend/nodes/readfuncs.c
 | 
						|
 *
 | 
						|
 * NOTES
 | 
						|
 *	  Parse location fields are written out by outfuncs.c, but only for
 | 
						|
 *	  debugging use.  When reading a location field, we normally discard
 | 
						|
 *	  the stored value and set the location field to -1 (ie, "unknown").
 | 
						|
 *	  This is because nodes coming from a stored rule should not be thought
 | 
						|
 *	  to have a known location in the current query's text.
 | 
						|
 *
 | 
						|
 *	  However, if restore_location_fields is true, we do restore location
 | 
						|
 *	  fields from the string.  This is currently intended only for use by the
 | 
						|
 *	  WRITE_READ_PARSE_PLAN_TREES test code, which doesn't want to cause
 | 
						|
 *	  any change in the node contents.
 | 
						|
 *
 | 
						|
 *-------------------------------------------------------------------------
 | 
						|
 */
 | 
						|
#include "postgres.h"
 | 
						|
 | 
						|
#include <math.h>
 | 
						|
 | 
						|
#include "miscadmin.h"
 | 
						|
#include "nodes/bitmapset.h"
 | 
						|
#include "nodes/readfuncs.h"
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * Macros to simplify reading of different kinds of fields.  Use these
 | 
						|
 * wherever possible to reduce the chance for silly typos.  Note that these
 | 
						|
 * hard-wire conventions about the names of the local variables in a Read
 | 
						|
 * routine.
 | 
						|
 */
 | 
						|
 | 
						|
/* Macros for declaring appropriate local variables */
 | 
						|
 | 
						|
/* A few guys need only local_node */
 | 
						|
#define READ_LOCALS_NO_FIELDS(nodeTypeName) \
 | 
						|
	nodeTypeName *local_node = makeNode(nodeTypeName)
 | 
						|
 | 
						|
/* And a few guys need only the pg_strtok support fields */
 | 
						|
#define READ_TEMP_LOCALS()	\
 | 
						|
	const char *token;		\
 | 
						|
	int			length
 | 
						|
 | 
						|
/* ... but most need both */
 | 
						|
#define READ_LOCALS(nodeTypeName)			\
 | 
						|
	READ_LOCALS_NO_FIELDS(nodeTypeName);	\
 | 
						|
	READ_TEMP_LOCALS()
 | 
						|
 | 
						|
/* Read an integer field (anything written as ":fldname %d") */
 | 
						|
#define READ_INT_FIELD(fldname) \
 | 
						|
	token = pg_strtok(&length);		/* skip :fldname */ \
 | 
						|
	token = pg_strtok(&length);		/* get field value */ \
 | 
						|
	local_node->fldname = atoi(token)
 | 
						|
 | 
						|
/* Read an unsigned integer field (anything written as ":fldname %u") */
 | 
						|
#define READ_UINT_FIELD(fldname) \
 | 
						|
	token = pg_strtok(&length);		/* skip :fldname */ \
 | 
						|
	token = pg_strtok(&length);		/* get field value */ \
 | 
						|
	local_node->fldname = atoui(token)
 | 
						|
 | 
						|
/* Read an unsigned integer field (anything written using UINT64_FORMAT) */
 | 
						|
#define READ_UINT64_FIELD(fldname) \
 | 
						|
	token = pg_strtok(&length);		/* skip :fldname */ \
 | 
						|
	token = pg_strtok(&length);		/* get field value */ \
 | 
						|
	local_node->fldname = strtou64(token, NULL, 10)
 | 
						|
 | 
						|
/* Read a long integer field (anything written as ":fldname %ld") */
 | 
						|
#define READ_LONG_FIELD(fldname) \
 | 
						|
	token = pg_strtok(&length);		/* skip :fldname */ \
 | 
						|
	token = pg_strtok(&length);		/* get field value */ \
 | 
						|
	local_node->fldname = atol(token)
 | 
						|
 | 
						|
/* Read an OID field (don't hard-wire assumption that OID is same as uint) */
 | 
						|
#define READ_OID_FIELD(fldname) \
 | 
						|
	token = pg_strtok(&length);		/* skip :fldname */ \
 | 
						|
	token = pg_strtok(&length);		/* get field value */ \
 | 
						|
	local_node->fldname = atooid(token)
 | 
						|
 | 
						|
/* Read a char field (ie, one ascii character) */
 | 
						|
#define READ_CHAR_FIELD(fldname) \
 | 
						|
	token = pg_strtok(&length);		/* skip :fldname */ \
 | 
						|
	token = pg_strtok(&length);		/* get field value */ \
 | 
						|
	/* avoid overhead of calling debackslash() for one char */ \
 | 
						|
	local_node->fldname = (length == 0) ? '\0' : (token[0] == '\\' ? token[1] : token[0])
 | 
						|
 | 
						|
/* Read an enumerated-type field that was written as an integer code */
 | 
						|
#define READ_ENUM_FIELD(fldname, enumtype) \
 | 
						|
	token = pg_strtok(&length);		/* skip :fldname */ \
 | 
						|
	token = pg_strtok(&length);		/* get field value */ \
 | 
						|
	local_node->fldname = (enumtype) atoi(token)
 | 
						|
 | 
						|
/* Read a float field */
 | 
						|
#define READ_FLOAT_FIELD(fldname) \
 | 
						|
	token = pg_strtok(&length);		/* skip :fldname */ \
 | 
						|
	token = pg_strtok(&length);		/* get field value */ \
 | 
						|
	local_node->fldname = atof(token)
 | 
						|
 | 
						|
/* Read a boolean field */
 | 
						|
#define READ_BOOL_FIELD(fldname) \
 | 
						|
	token = pg_strtok(&length);		/* skip :fldname */ \
 | 
						|
	token = pg_strtok(&length);		/* get field value */ \
 | 
						|
	local_node->fldname = strtobool(token)
 | 
						|
 | 
						|
/* Read a character-string field */
 | 
						|
#define READ_STRING_FIELD(fldname) \
 | 
						|
	token = pg_strtok(&length);		/* skip :fldname */ \
 | 
						|
	token = pg_strtok(&length);		/* get field value */ \
 | 
						|
	local_node->fldname = nullable_string(token, length)
 | 
						|
 | 
						|
/* Read a parse location field (and possibly throw away the value) */
 | 
						|
#ifdef WRITE_READ_PARSE_PLAN_TREES
 | 
						|
#define READ_LOCATION_FIELD(fldname) \
 | 
						|
	token = pg_strtok(&length);		/* skip :fldname */ \
 | 
						|
	token = pg_strtok(&length);		/* get field value */ \
 | 
						|
	local_node->fldname = restore_location_fields ? atoi(token) : -1
 | 
						|
#else
 | 
						|
#define READ_LOCATION_FIELD(fldname) \
 | 
						|
	token = pg_strtok(&length);		/* skip :fldname */ \
 | 
						|
	token = pg_strtok(&length);		/* get field value */ \
 | 
						|
	(void) token;				/* in case not used elsewhere */ \
 | 
						|
	local_node->fldname = -1	/* set field to "unknown" */
 | 
						|
#endif
 | 
						|
 | 
						|
/* Read a Node field */
 | 
						|
#define READ_NODE_FIELD(fldname) \
 | 
						|
	token = pg_strtok(&length);		/* skip :fldname */ \
 | 
						|
	(void) token;				/* in case not used elsewhere */ \
 | 
						|
	local_node->fldname = nodeRead(NULL, 0)
 | 
						|
 | 
						|
/* Read a bitmapset field */
 | 
						|
#define READ_BITMAPSET_FIELD(fldname) \
 | 
						|
	token = pg_strtok(&length);		/* skip :fldname */ \
 | 
						|
	(void) token;				/* in case not used elsewhere */ \
 | 
						|
	local_node->fldname = _readBitmapset()
 | 
						|
 | 
						|
/* Read an attribute number array */
 | 
						|
#define READ_ATTRNUMBER_ARRAY(fldname, len) \
 | 
						|
	token = pg_strtok(&length);		/* skip :fldname */ \
 | 
						|
	local_node->fldname = readAttrNumberCols(len)
 | 
						|
 | 
						|
/* Read an oid array */
 | 
						|
#define READ_OID_ARRAY(fldname, len) \
 | 
						|
	token = pg_strtok(&length);		/* skip :fldname */ \
 | 
						|
	local_node->fldname = readOidCols(len)
 | 
						|
 | 
						|
/* Read an int array */
 | 
						|
#define READ_INT_ARRAY(fldname, len) \
 | 
						|
	token = pg_strtok(&length);		/* skip :fldname */ \
 | 
						|
	local_node->fldname = readIntCols(len)
 | 
						|
 | 
						|
/* Read a bool array */
 | 
						|
#define READ_BOOL_ARRAY(fldname, len) \
 | 
						|
	token = pg_strtok(&length);		/* skip :fldname */ \
 | 
						|
	local_node->fldname = readBoolCols(len)
 | 
						|
 | 
						|
/* Routine exit */
 | 
						|
#define READ_DONE() \
 | 
						|
	return local_node
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * NOTE: use atoi() to read values written with %d, or atoui() to read
 | 
						|
 * values written with %u in outfuncs.c.  An exception is OID values,
 | 
						|
 * for which use atooid().  (As of 7.1, outfuncs.c writes OIDs as %u,
 | 
						|
 * but this will probably change in the future.)
 | 
						|
 */
 | 
						|
#define atoui(x)  ((unsigned int) strtoul((x), NULL, 10))
 | 
						|
 | 
						|
#define strtobool(x)  ((*(x) == 't') ? true : false)
 | 
						|
 | 
						|
static char *
 | 
						|
nullable_string(const char *token, int length)
 | 
						|
{
 | 
						|
	/* outToken emits <> for NULL, and pg_strtok makes that an empty string */
 | 
						|
	if (length == 0)
 | 
						|
		return NULL;
 | 
						|
	/* outToken emits "" for empty string */
 | 
						|
	if (length == 2 && token[0] == '"' && token[1] == '"')
 | 
						|
		return pstrdup("");
 | 
						|
	/* otherwise, we must remove protective backslashes added by outToken */
 | 
						|
	return debackslash(token, length);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * _readBitmapset
 | 
						|
 *
 | 
						|
 * Note: this code is used in contexts where we know that a Bitmapset
 | 
						|
 * is expected.  There is equivalent code in nodeRead() that can read a
 | 
						|
 * Bitmapset when we come across one in other contexts.
 | 
						|
 */
 | 
						|
static Bitmapset *
 | 
						|
_readBitmapset(void)
 | 
						|
{
 | 
						|
	Bitmapset  *result = NULL;
 | 
						|
 | 
						|
	READ_TEMP_LOCALS();
 | 
						|
 | 
						|
	token = pg_strtok(&length);
 | 
						|
	if (token == NULL)
 | 
						|
		elog(ERROR, "incomplete Bitmapset structure");
 | 
						|
	if (length != 1 || token[0] != '(')
 | 
						|
		elog(ERROR, "unrecognized token: \"%.*s\"", length, token);
 | 
						|
 | 
						|
	token = pg_strtok(&length);
 | 
						|
	if (token == NULL)
 | 
						|
		elog(ERROR, "incomplete Bitmapset structure");
 | 
						|
	if (length != 1 || token[0] != 'b')
 | 
						|
		elog(ERROR, "unrecognized token: \"%.*s\"", length, token);
 | 
						|
 | 
						|
	for (;;)
 | 
						|
	{
 | 
						|
		int			val;
 | 
						|
		char	   *endptr;
 | 
						|
 | 
						|
		token = pg_strtok(&length);
 | 
						|
		if (token == NULL)
 | 
						|
			elog(ERROR, "unterminated Bitmapset structure");
 | 
						|
		if (length == 1 && token[0] == ')')
 | 
						|
			break;
 | 
						|
		val = (int) strtol(token, &endptr, 10);
 | 
						|
		if (endptr != token + length)
 | 
						|
			elog(ERROR, "unrecognized integer: \"%.*s\"", length, token);
 | 
						|
		result = bms_add_member(result, val);
 | 
						|
	}
 | 
						|
 | 
						|
	return result;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * We export this function for use by extensions that define extensible nodes.
 | 
						|
 * That's somewhat historical, though, because calling nodeRead() will work.
 | 
						|
 */
 | 
						|
Bitmapset *
 | 
						|
readBitmapset(void)
 | 
						|
{
 | 
						|
	return _readBitmapset();
 | 
						|
}
 | 
						|
 | 
						|
#include "readfuncs.funcs.c"
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * Support functions for nodes with custom_read_write attribute or
 | 
						|
 * special_read_write attribute
 | 
						|
 */
 | 
						|
 | 
						|
static Const *
 | 
						|
_readConst(void)
 | 
						|
{
 | 
						|
	READ_LOCALS(Const);
 | 
						|
 | 
						|
	READ_OID_FIELD(consttype);
 | 
						|
	READ_INT_FIELD(consttypmod);
 | 
						|
	READ_OID_FIELD(constcollid);
 | 
						|
	READ_INT_FIELD(constlen);
 | 
						|
	READ_BOOL_FIELD(constbyval);
 | 
						|
	READ_BOOL_FIELD(constisnull);
 | 
						|
	READ_LOCATION_FIELD(location);
 | 
						|
 | 
						|
	token = pg_strtok(&length); /* skip :constvalue */
 | 
						|
	if (local_node->constisnull)
 | 
						|
		token = pg_strtok(&length); /* skip "<>" */
 | 
						|
	else
 | 
						|
		local_node->constvalue = readDatum(local_node->constbyval);
 | 
						|
 | 
						|
	READ_DONE();
 | 
						|
}
 | 
						|
 | 
						|
static BoolExpr *
 | 
						|
_readBoolExpr(void)
 | 
						|
{
 | 
						|
	READ_LOCALS(BoolExpr);
 | 
						|
 | 
						|
	/* do-it-yourself enum representation */
 | 
						|
	token = pg_strtok(&length); /* skip :boolop */
 | 
						|
	token = pg_strtok(&length); /* get field value */
 | 
						|
	if (length == 3 && strncmp(token, "and", 3) == 0)
 | 
						|
		local_node->boolop = AND_EXPR;
 | 
						|
	else if (length == 2 && strncmp(token, "or", 2) == 0)
 | 
						|
		local_node->boolop = OR_EXPR;
 | 
						|
	else if (length == 3 && strncmp(token, "not", 3) == 0)
 | 
						|
		local_node->boolop = NOT_EXPR;
 | 
						|
	else
 | 
						|
		elog(ERROR, "unrecognized boolop \"%.*s\"", length, token);
 | 
						|
 | 
						|
	READ_NODE_FIELD(args);
 | 
						|
	READ_LOCATION_FIELD(location);
 | 
						|
 | 
						|
	READ_DONE();
 | 
						|
}
 | 
						|
 | 
						|
static A_Const *
 | 
						|
_readA_Const(void)
 | 
						|
{
 | 
						|
	READ_LOCALS(A_Const);
 | 
						|
 | 
						|
	/* We expect either NULL or :val here */
 | 
						|
	token = pg_strtok(&length);
 | 
						|
	if (length == 4 && strncmp(token, "NULL", 4) == 0)
 | 
						|
		local_node->isnull = true;
 | 
						|
	else
 | 
						|
	{
 | 
						|
		union ValUnion *tmp = nodeRead(NULL, 0);
 | 
						|
 | 
						|
		/* To forestall valgrind complaints, copy only the valid data */
 | 
						|
		switch (nodeTag(tmp))
 | 
						|
		{
 | 
						|
			case T_Integer:
 | 
						|
				memcpy(&local_node->val, tmp, sizeof(Integer));
 | 
						|
				break;
 | 
						|
			case T_Float:
 | 
						|
				memcpy(&local_node->val, tmp, sizeof(Float));
 | 
						|
				break;
 | 
						|
			case T_Boolean:
 | 
						|
				memcpy(&local_node->val, tmp, sizeof(Boolean));
 | 
						|
				break;
 | 
						|
			case T_String:
 | 
						|
				memcpy(&local_node->val, tmp, sizeof(String));
 | 
						|
				break;
 | 
						|
			case T_BitString:
 | 
						|
				memcpy(&local_node->val, tmp, sizeof(BitString));
 | 
						|
				break;
 | 
						|
			default:
 | 
						|
				elog(ERROR, "unrecognized node type: %d",
 | 
						|
					 (int) nodeTag(tmp));
 | 
						|
				break;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	READ_LOCATION_FIELD(location);
 | 
						|
 | 
						|
	READ_DONE();
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * _readConstraint
 | 
						|
 */
 | 
						|
static Constraint *
 | 
						|
_readConstraint(void)
 | 
						|
{
 | 
						|
	READ_LOCALS(Constraint);
 | 
						|
 | 
						|
	READ_STRING_FIELD(conname);
 | 
						|
	READ_BOOL_FIELD(deferrable);
 | 
						|
	READ_BOOL_FIELD(initdeferred);
 | 
						|
	READ_LOCATION_FIELD(location);
 | 
						|
 | 
						|
	token = pg_strtok(&length); /* skip :contype */
 | 
						|
	token = pg_strtok(&length); /* get field value */
 | 
						|
	if (length == 4 && strncmp(token, "NULL", 4) == 0)
 | 
						|
		local_node->contype = CONSTR_NULL;
 | 
						|
	else if (length == 8 && strncmp(token, "NOT_NULL", 8) == 0)
 | 
						|
		local_node->contype = CONSTR_NOTNULL;
 | 
						|
	else if (length == 7 && strncmp(token, "DEFAULT", 7) == 0)
 | 
						|
		local_node->contype = CONSTR_DEFAULT;
 | 
						|
	else if (length == 8 && strncmp(token, "IDENTITY", 8) == 0)
 | 
						|
		local_node->contype = CONSTR_IDENTITY;
 | 
						|
	else if (length == 9 && strncmp(token, "GENERATED", 9) == 0)
 | 
						|
		local_node->contype = CONSTR_GENERATED;
 | 
						|
	else if (length == 5 && strncmp(token, "CHECK", 5) == 0)
 | 
						|
		local_node->contype = CONSTR_CHECK;
 | 
						|
	else if (length == 11 && strncmp(token, "PRIMARY_KEY", 11) == 0)
 | 
						|
		local_node->contype = CONSTR_PRIMARY;
 | 
						|
	else if (length == 6 && strncmp(token, "UNIQUE", 6) == 0)
 | 
						|
		local_node->contype = CONSTR_UNIQUE;
 | 
						|
	else if (length == 9 && strncmp(token, "EXCLUSION", 9) == 0)
 | 
						|
		local_node->contype = CONSTR_EXCLUSION;
 | 
						|
	else if (length == 11 && strncmp(token, "FOREIGN_KEY", 11) == 0)
 | 
						|
		local_node->contype = CONSTR_FOREIGN;
 | 
						|
	else if (length == 15 && strncmp(token, "ATTR_DEFERRABLE", 15) == 0)
 | 
						|
		local_node->contype = CONSTR_ATTR_DEFERRABLE;
 | 
						|
	else if (length == 19 && strncmp(token, "ATTR_NOT_DEFERRABLE", 19) == 0)
 | 
						|
		local_node->contype = CONSTR_ATTR_NOT_DEFERRABLE;
 | 
						|
	else if (length == 13 && strncmp(token, "ATTR_DEFERRED", 13) == 0)
 | 
						|
		local_node->contype = CONSTR_ATTR_DEFERRED;
 | 
						|
	else if (length == 14 && strncmp(token, "ATTR_IMMEDIATE", 14) == 0)
 | 
						|
		local_node->contype = CONSTR_ATTR_IMMEDIATE;
 | 
						|
 | 
						|
	switch (local_node->contype)
 | 
						|
	{
 | 
						|
		case CONSTR_NULL:
 | 
						|
		case CONSTR_NOTNULL:
 | 
						|
			/* no extra fields */
 | 
						|
			break;
 | 
						|
 | 
						|
		case CONSTR_DEFAULT:
 | 
						|
			READ_NODE_FIELD(raw_expr);
 | 
						|
			READ_STRING_FIELD(cooked_expr);
 | 
						|
			break;
 | 
						|
 | 
						|
		case CONSTR_IDENTITY:
 | 
						|
			READ_NODE_FIELD(options);
 | 
						|
			READ_CHAR_FIELD(generated_when);
 | 
						|
			break;
 | 
						|
 | 
						|
		case CONSTR_GENERATED:
 | 
						|
			READ_NODE_FIELD(raw_expr);
 | 
						|
			READ_STRING_FIELD(cooked_expr);
 | 
						|
			READ_CHAR_FIELD(generated_when);
 | 
						|
			break;
 | 
						|
 | 
						|
		case CONSTR_CHECK:
 | 
						|
			READ_BOOL_FIELD(is_no_inherit);
 | 
						|
			READ_NODE_FIELD(raw_expr);
 | 
						|
			READ_STRING_FIELD(cooked_expr);
 | 
						|
			READ_BOOL_FIELD(skip_validation);
 | 
						|
			READ_BOOL_FIELD(initially_valid);
 | 
						|
			break;
 | 
						|
 | 
						|
		case CONSTR_PRIMARY:
 | 
						|
			READ_NODE_FIELD(keys);
 | 
						|
			READ_NODE_FIELD(including);
 | 
						|
			READ_NODE_FIELD(options);
 | 
						|
			READ_STRING_FIELD(indexname);
 | 
						|
			READ_STRING_FIELD(indexspace);
 | 
						|
			READ_BOOL_FIELD(reset_default_tblspc);
 | 
						|
			/* access_method and where_clause not currently used */
 | 
						|
			break;
 | 
						|
 | 
						|
		case CONSTR_UNIQUE:
 | 
						|
			READ_BOOL_FIELD(nulls_not_distinct);
 | 
						|
			READ_NODE_FIELD(keys);
 | 
						|
			READ_NODE_FIELD(including);
 | 
						|
			READ_NODE_FIELD(options);
 | 
						|
			READ_STRING_FIELD(indexname);
 | 
						|
			READ_STRING_FIELD(indexspace);
 | 
						|
			READ_BOOL_FIELD(reset_default_tblspc);
 | 
						|
			/* access_method and where_clause not currently used */
 | 
						|
			break;
 | 
						|
 | 
						|
		case CONSTR_EXCLUSION:
 | 
						|
			READ_NODE_FIELD(exclusions);
 | 
						|
			READ_NODE_FIELD(including);
 | 
						|
			READ_NODE_FIELD(options);
 | 
						|
			READ_STRING_FIELD(indexname);
 | 
						|
			READ_STRING_FIELD(indexspace);
 | 
						|
			READ_BOOL_FIELD(reset_default_tblspc);
 | 
						|
			READ_STRING_FIELD(access_method);
 | 
						|
			READ_NODE_FIELD(where_clause);
 | 
						|
			break;
 | 
						|
 | 
						|
		case CONSTR_FOREIGN:
 | 
						|
			READ_NODE_FIELD(pktable);
 | 
						|
			READ_NODE_FIELD(fk_attrs);
 | 
						|
			READ_NODE_FIELD(pk_attrs);
 | 
						|
			READ_CHAR_FIELD(fk_matchtype);
 | 
						|
			READ_CHAR_FIELD(fk_upd_action);
 | 
						|
			READ_CHAR_FIELD(fk_del_action);
 | 
						|
			READ_NODE_FIELD(fk_del_set_cols);
 | 
						|
			READ_NODE_FIELD(old_conpfeqop);
 | 
						|
			READ_OID_FIELD(old_pktable_oid);
 | 
						|
			READ_BOOL_FIELD(skip_validation);
 | 
						|
			READ_BOOL_FIELD(initially_valid);
 | 
						|
			break;
 | 
						|
 | 
						|
		case CONSTR_ATTR_DEFERRABLE:
 | 
						|
		case CONSTR_ATTR_NOT_DEFERRABLE:
 | 
						|
		case CONSTR_ATTR_DEFERRED:
 | 
						|
		case CONSTR_ATTR_IMMEDIATE:
 | 
						|
			/* no extra fields */
 | 
						|
			break;
 | 
						|
 | 
						|
		default:
 | 
						|
			elog(ERROR, "unrecognized ConstrType: %d", (int) local_node->contype);
 | 
						|
			break;
 | 
						|
	}
 | 
						|
 | 
						|
	READ_DONE();
 | 
						|
}
 | 
						|
 | 
						|
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:
 | 
						|
			READ_OID_FIELD(relid);
 | 
						|
			READ_CHAR_FIELD(relkind);
 | 
						|
			READ_INT_FIELD(rellockmode);
 | 
						|
			READ_NODE_FIELD(tablesample);
 | 
						|
			READ_UINT_FIELD(perminfoindex);
 | 
						|
			break;
 | 
						|
		case RTE_SUBQUERY:
 | 
						|
			READ_NODE_FIELD(subquery);
 | 
						|
			READ_BOOL_FIELD(security_barrier);
 | 
						|
			/* we re-use these RELATION fields, too: */
 | 
						|
			READ_OID_FIELD(relid);
 | 
						|
			READ_CHAR_FIELD(relkind);
 | 
						|
			READ_INT_FIELD(rellockmode);
 | 
						|
			READ_UINT_FIELD(perminfoindex);
 | 
						|
			break;
 | 
						|
		case RTE_JOIN:
 | 
						|
			READ_ENUM_FIELD(jointype, JoinType);
 | 
						|
			READ_INT_FIELD(joinmergedcols);
 | 
						|
			READ_NODE_FIELD(joinaliasvars);
 | 
						|
			READ_NODE_FIELD(joinleftcols);
 | 
						|
			READ_NODE_FIELD(joinrightcols);
 | 
						|
			READ_NODE_FIELD(join_using_alias);
 | 
						|
			break;
 | 
						|
		case RTE_FUNCTION:
 | 
						|
			READ_NODE_FIELD(functions);
 | 
						|
			READ_BOOL_FIELD(funcordinality);
 | 
						|
			break;
 | 
						|
		case RTE_TABLEFUNC:
 | 
						|
			READ_NODE_FIELD(tablefunc);
 | 
						|
			/* The RTE must have a copy of the column type info, if any */
 | 
						|
			if (local_node->tablefunc)
 | 
						|
			{
 | 
						|
				TableFunc  *tf = local_node->tablefunc;
 | 
						|
 | 
						|
				local_node->coltypes = tf->coltypes;
 | 
						|
				local_node->coltypmods = tf->coltypmods;
 | 
						|
				local_node->colcollations = tf->colcollations;
 | 
						|
			}
 | 
						|
			break;
 | 
						|
		case RTE_VALUES:
 | 
						|
			READ_NODE_FIELD(values_lists);
 | 
						|
			READ_NODE_FIELD(coltypes);
 | 
						|
			READ_NODE_FIELD(coltypmods);
 | 
						|
			READ_NODE_FIELD(colcollations);
 | 
						|
			break;
 | 
						|
		case RTE_CTE:
 | 
						|
			READ_STRING_FIELD(ctename);
 | 
						|
			READ_UINT_FIELD(ctelevelsup);
 | 
						|
			READ_BOOL_FIELD(self_reference);
 | 
						|
			READ_NODE_FIELD(coltypes);
 | 
						|
			READ_NODE_FIELD(coltypmods);
 | 
						|
			READ_NODE_FIELD(colcollations);
 | 
						|
			break;
 | 
						|
		case RTE_NAMEDTUPLESTORE:
 | 
						|
			READ_STRING_FIELD(enrname);
 | 
						|
			READ_FLOAT_FIELD(enrtuples);
 | 
						|
			READ_NODE_FIELD(coltypes);
 | 
						|
			READ_NODE_FIELD(coltypmods);
 | 
						|
			READ_NODE_FIELD(colcollations);
 | 
						|
			/* we re-use these RELATION fields, too: */
 | 
						|
			READ_OID_FIELD(relid);
 | 
						|
			break;
 | 
						|
		case RTE_RESULT:
 | 
						|
			/* no extra fields */
 | 
						|
			break;
 | 
						|
		default:
 | 
						|
			elog(ERROR, "unrecognized RTE kind: %d",
 | 
						|
				 (int) local_node->rtekind);
 | 
						|
			break;
 | 
						|
	}
 | 
						|
 | 
						|
	READ_BOOL_FIELD(lateral);
 | 
						|
	READ_BOOL_FIELD(inh);
 | 
						|
	READ_BOOL_FIELD(inFromCl);
 | 
						|
	READ_NODE_FIELD(securityQuals);
 | 
						|
 | 
						|
	READ_DONE();
 | 
						|
}
 | 
						|
 | 
						|
static A_Expr *
 | 
						|
_readA_Expr(void)
 | 
						|
{
 | 
						|
	READ_LOCALS(A_Expr);
 | 
						|
 | 
						|
	token = pg_strtok(&length);
 | 
						|
 | 
						|
	if (length == 3 && strncmp(token, "ANY", 3) == 0)
 | 
						|
	{
 | 
						|
		local_node->kind = AEXPR_OP_ANY;
 | 
						|
		READ_NODE_FIELD(name);
 | 
						|
	}
 | 
						|
	else if (length == 3 && strncmp(token, "ALL", 3) == 0)
 | 
						|
	{
 | 
						|
		local_node->kind = AEXPR_OP_ALL;
 | 
						|
		READ_NODE_FIELD(name);
 | 
						|
	}
 | 
						|
	else if (length == 8 && strncmp(token, "DISTINCT", 8) == 0)
 | 
						|
	{
 | 
						|
		local_node->kind = AEXPR_DISTINCT;
 | 
						|
		READ_NODE_FIELD(name);
 | 
						|
	}
 | 
						|
	else if (length == 12 && strncmp(token, "NOT_DISTINCT", 12) == 0)
 | 
						|
	{
 | 
						|
		local_node->kind = AEXPR_NOT_DISTINCT;
 | 
						|
		READ_NODE_FIELD(name);
 | 
						|
	}
 | 
						|
	else if (length == 6 && strncmp(token, "NULLIF", 6) == 0)
 | 
						|
	{
 | 
						|
		local_node->kind = AEXPR_NULLIF;
 | 
						|
		READ_NODE_FIELD(name);
 | 
						|
	}
 | 
						|
	else if (length == 2 && strncmp(token, "IN", 2) == 0)
 | 
						|
	{
 | 
						|
		local_node->kind = AEXPR_IN;
 | 
						|
		READ_NODE_FIELD(name);
 | 
						|
	}
 | 
						|
	else if (length == 4 && strncmp(token, "LIKE", 4) == 0)
 | 
						|
	{
 | 
						|
		local_node->kind = AEXPR_LIKE;
 | 
						|
		READ_NODE_FIELD(name);
 | 
						|
	}
 | 
						|
	else if (length == 5 && strncmp(token, "ILIKE", 5) == 0)
 | 
						|
	{
 | 
						|
		local_node->kind = AEXPR_ILIKE;
 | 
						|
		READ_NODE_FIELD(name);
 | 
						|
	}
 | 
						|
	else if (length == 7 && strncmp(token, "SIMILAR", 7) == 0)
 | 
						|
	{
 | 
						|
		local_node->kind = AEXPR_SIMILAR;
 | 
						|
		READ_NODE_FIELD(name);
 | 
						|
	}
 | 
						|
	else if (length == 7 && strncmp(token, "BETWEEN", 7) == 0)
 | 
						|
	{
 | 
						|
		local_node->kind = AEXPR_BETWEEN;
 | 
						|
		READ_NODE_FIELD(name);
 | 
						|
	}
 | 
						|
	else if (length == 11 && strncmp(token, "NOT_BETWEEN", 11) == 0)
 | 
						|
	{
 | 
						|
		local_node->kind = AEXPR_NOT_BETWEEN;
 | 
						|
		READ_NODE_FIELD(name);
 | 
						|
	}
 | 
						|
	else if (length == 11 && strncmp(token, "BETWEEN_SYM", 11) == 0)
 | 
						|
	{
 | 
						|
		local_node->kind = AEXPR_BETWEEN_SYM;
 | 
						|
		READ_NODE_FIELD(name);
 | 
						|
	}
 | 
						|
	else if (length == 15 && strncmp(token, "NOT_BETWEEN_SYM", 15) == 0)
 | 
						|
	{
 | 
						|
		local_node->kind = AEXPR_NOT_BETWEEN_SYM;
 | 
						|
		READ_NODE_FIELD(name);
 | 
						|
	}
 | 
						|
	else if (length == 5 && strncmp(token, ":name", 5) == 0)
 | 
						|
	{
 | 
						|
		local_node->kind = AEXPR_OP;
 | 
						|
		local_node->name = nodeRead(NULL, 0);
 | 
						|
	}
 | 
						|
	else
 | 
						|
		elog(ERROR, "unrecognized A_Expr kind: \"%.*s\"", length, token);
 | 
						|
 | 
						|
	READ_NODE_FIELD(lexpr);
 | 
						|
	READ_NODE_FIELD(rexpr);
 | 
						|
	READ_LOCATION_FIELD(location);
 | 
						|
 | 
						|
	READ_DONE();
 | 
						|
}
 | 
						|
 | 
						|
static ExtensibleNode *
 | 
						|
_readExtensibleNode(void)
 | 
						|
{
 | 
						|
	const ExtensibleNodeMethods *methods;
 | 
						|
	ExtensibleNode *local_node;
 | 
						|
	const char *extnodename;
 | 
						|
 | 
						|
	READ_TEMP_LOCALS();
 | 
						|
 | 
						|
	token = pg_strtok(&length); /* skip :extnodename */
 | 
						|
	token = pg_strtok(&length); /* get extnodename */
 | 
						|
 | 
						|
	extnodename = nullable_string(token, length);
 | 
						|
	if (!extnodename)
 | 
						|
		elog(ERROR, "extnodename has to be supplied");
 | 
						|
	methods = GetExtensibleNodeMethods(extnodename, false);
 | 
						|
 | 
						|
	local_node = (ExtensibleNode *) newNode(methods->node_size,
 | 
						|
											T_ExtensibleNode);
 | 
						|
	local_node->extnodename = extnodename;
 | 
						|
 | 
						|
	/* deserialize the private fields */
 | 
						|
	methods->nodeRead(local_node);
 | 
						|
 | 
						|
	READ_DONE();
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * parseNodeString
 | 
						|
 *
 | 
						|
 * Given a character string representing a node tree, parseNodeString creates
 | 
						|
 * the internal node structure.
 | 
						|
 *
 | 
						|
 * The string to be read must already have been loaded into pg_strtok().
 | 
						|
 */
 | 
						|
Node *
 | 
						|
parseNodeString(void)
 | 
						|
{
 | 
						|
	READ_TEMP_LOCALS();
 | 
						|
 | 
						|
	/* Guard against stack overflow due to overly complex expressions */
 | 
						|
	check_stack_depth();
 | 
						|
 | 
						|
	token = pg_strtok(&length);
 | 
						|
 | 
						|
#define MATCH(tokname, namelen) \
 | 
						|
	(length == namelen && memcmp(token, tokname, namelen) == 0)
 | 
						|
 | 
						|
#include "readfuncs.switch.c"
 | 
						|
 | 
						|
	elog(ERROR, "badly formatted node string \"%.32s\"...", token);
 | 
						|
	return NULL;				/* keep compiler quiet */
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * readDatum
 | 
						|
 *
 | 
						|
 * Given a string representation of a constant, recreate the appropriate
 | 
						|
 * Datum.  The string representation embeds length info, but not byValue,
 | 
						|
 * so we must be told that.
 | 
						|
 */
 | 
						|
Datum
 | 
						|
readDatum(bool typbyval)
 | 
						|
{
 | 
						|
	Size		length,
 | 
						|
				i;
 | 
						|
	int			tokenLength;
 | 
						|
	const char *token;
 | 
						|
	Datum		res;
 | 
						|
	char	   *s;
 | 
						|
 | 
						|
	/*
 | 
						|
	 * read the actual length of the value
 | 
						|
	 */
 | 
						|
	token = pg_strtok(&tokenLength);
 | 
						|
	length = atoui(token);
 | 
						|
 | 
						|
	token = pg_strtok(&tokenLength);	/* read the '[' */
 | 
						|
	if (token == NULL || token[0] != '[')
 | 
						|
		elog(ERROR, "expected \"[\" to start datum, but got \"%s\"; length = %zu",
 | 
						|
			 token ? token : "[NULL]", length);
 | 
						|
 | 
						|
	if (typbyval)
 | 
						|
	{
 | 
						|
		if (length > (Size) sizeof(Datum))
 | 
						|
			elog(ERROR, "byval datum but length = %zu", length);
 | 
						|
		res = (Datum) 0;
 | 
						|
		s = (char *) (&res);
 | 
						|
		for (i = 0; i < (Size) sizeof(Datum); i++)
 | 
						|
		{
 | 
						|
			token = pg_strtok(&tokenLength);
 | 
						|
			s[i] = (char) atoi(token);
 | 
						|
		}
 | 
						|
	}
 | 
						|
	else if (length <= 0)
 | 
						|
		res = (Datum) NULL;
 | 
						|
	else
 | 
						|
	{
 | 
						|
		s = (char *) palloc(length);
 | 
						|
		for (i = 0; i < length; i++)
 | 
						|
		{
 | 
						|
			token = pg_strtok(&tokenLength);
 | 
						|
			s[i] = (char) atoi(token);
 | 
						|
		}
 | 
						|
		res = PointerGetDatum(s);
 | 
						|
	}
 | 
						|
 | 
						|
	token = pg_strtok(&tokenLength);	/* read the ']' */
 | 
						|
	if (token == NULL || token[0] != ']')
 | 
						|
		elog(ERROR, "expected \"]\" to end datum, but got \"%s\"; length = %zu",
 | 
						|
			 token ? token : "[NULL]", length);
 | 
						|
 | 
						|
	return res;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * common implementation for scalar-array-reading functions
 | 
						|
 *
 | 
						|
 * The data format is either "<>" for a NULL pointer (in which case numCols
 | 
						|
 * is ignored) or "(item item item)" where the number of items must equal
 | 
						|
 * numCols.  The convfunc must be okay with stopping at whitespace or a
 | 
						|
 * right parenthesis, since pg_strtok won't null-terminate the token.
 | 
						|
 */
 | 
						|
#define READ_SCALAR_ARRAY(fnname, datatype, convfunc) \
 | 
						|
datatype * \
 | 
						|
fnname(int numCols) \
 | 
						|
{ \
 | 
						|
	datatype   *vals; \
 | 
						|
	READ_TEMP_LOCALS(); \
 | 
						|
	token = pg_strtok(&length); \
 | 
						|
	if (token == NULL) \
 | 
						|
		elog(ERROR, "incomplete scalar array"); \
 | 
						|
	if (length == 0) \
 | 
						|
		return NULL;			/* it was "<>", so return NULL pointer */ \
 | 
						|
	if (length != 1 || token[0] != '(') \
 | 
						|
		elog(ERROR, "unrecognized token: \"%.*s\"", length, token); \
 | 
						|
	vals = (datatype *) palloc(numCols * sizeof(datatype)); \
 | 
						|
	for (int i = 0; i < numCols; i++) \
 | 
						|
	{ \
 | 
						|
		token = pg_strtok(&length); \
 | 
						|
		if (token == NULL || token[0] == ')') \
 | 
						|
			elog(ERROR, "incomplete scalar array"); \
 | 
						|
		vals[i] = convfunc(token); \
 | 
						|
	} \
 | 
						|
	token = pg_strtok(&length); \
 | 
						|
	if (token == NULL || length != 1 || token[0] != ')') \
 | 
						|
		elog(ERROR, "incomplete scalar array"); \
 | 
						|
	return vals; \
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Note: these functions are exported in nodes.h for possible use by
 | 
						|
 * extensions, so don't mess too much with their names or API.
 | 
						|
 */
 | 
						|
READ_SCALAR_ARRAY(readAttrNumberCols, int16, atoi)
 | 
						|
READ_SCALAR_ARRAY(readOidCols, Oid, atooid)
 | 
						|
/* outfuncs.c has writeIndexCols, but we don't yet need that here */
 | 
						|
/* READ_SCALAR_ARRAY(readIndexCols, Index, atoui) */
 | 
						|
READ_SCALAR_ARRAY(readIntCols, int, atoi)
 | 
						|
READ_SCALAR_ARRAY(readBoolCols, bool, strtobool)
 |