Hi all,

This thread is a follow-up of the recent discussion about query
jumbling with DDL statements, where the conclusion was that we'd want
to generate all this code automatically for all the nodes:
https://www.postgresql.org/message-id/36e5bffe-e989-194f-85c8-06e7bc88e...@amazon.com

What this patch allows to do it to compute the same query ID for
utility statements using their parsed Node state instead of their
string, meaning that things like "BEGIN", "bEGIN" or "begin" would be
treated the same, for example.  But the main idea is not only that.

I have implemented that as of the attached, where the following things
are done:
- queryjumble.c is moved to src/backend/nodes/, to stick with the
other things for node equal/read/write/copy, renamed to
jumblefuncs.c.
- gen_node_support.c is extended to generate the functions and the
switch for the jumbling.  There are a few exceptions, as of the Lists
and RangeTblEntry to do the jumbling consistently.
- Two pg_node_attr() are added in consistency with the existing ones:
no_jumble to discard completely a node from the the query jumbling
and jumble_ignore to discard one field from the jumble.

The patch is in a rather good shape, passes check-world and the CI,
but there are a few things that need to be discussed IMO.  Things
could be perhaps divided in more patches, now the areas touched are
quite different so it did not look like a big deal to me as the
changes touch different areas and are straight-forward.

The location of the Nodes is quite invasive because we only care about
that for T_Const now in the query jumbling, and this could be
compensated with a third pg_node_attr() that tracks for the "int 
location" of a Node whether it should participate in the jumbling or
not.  There is also an argument where we would want to not include by
default new fields added to a Node, but that would require added more
pg_node_attr() than what's done here.

Note that the plan is to extend the normalization to some other parts
of the Nodes, like CALL and SET as mentioned on the other thread.  I
have done nothing about that yet but doing so can be done in a few
lines with the facility presented here (aka just add a location
field).  Hence, the normalization is consistent with the existing
queryjumble.c for the fields and the nodes processed.

In this patch, things are done so as the query ID is not computed
anymore from the query string but from the Query.  I still need to
study the performance impact of that with short queries.  If things
prove to be noticeable in some cases, this stuff could be switched to
use a new GUC where we could have a code path for the computation of
utilityStmt using its string as a fallback.  I am not sure that I want
to enter in this level of complications, though, to keep things
simple, but that's yet to be done.

A bit more could be cut but pg_ident got in the way..  There are also
a few things for pg_stat_statements where a query ID of 0 can be
implied for utility statements in some cases.

Generating this code leads to an overall removal of code as what
 queryjumble.c is generated automatically:
 13 files changed, 901 insertions(+), 1113 deletions(-)

I am adding that to the next commit fest.

Thoughts?
--
Michael
From 55b6d9154a5524e67865299b9a73ef074bd12adf Mon Sep 17 00:00:00 2001
From: Michael Paquier <mich...@paquier.xyz>
Date: Wed, 7 Dec 2022 16:36:47 +0900
Subject: [PATCH] Support for automated query jumble with all Nodes

This applies query jumbling in a consistent way to all the Nodes,
including DDLs & friends.
---
 src/include/nodes/bitmapset.h         |   2 +-
 src/include/nodes/nodes.h             |   4 +
 src/include/nodes/parsenodes.h        | 267 +++++---
 src/include/nodes/plannodes.h         |   3 +-
 src/include/nodes/primnodes.h         | 419 ++++++++-----
 src/backend/nodes/Makefile            |   1 +
 src/backend/nodes/README              |   1 +
 src/backend/nodes/gen_node_support.pl |  95 ++-
 src/backend/nodes/jumblefuncs.c       | 358 +++++++++++
 src/backend/nodes/meson.build         |   1 +
 src/backend/utils/misc/Makefile       |   1 -
 src/backend/utils/misc/meson.build    |   1 -
 src/backend/utils/misc/queryjumble.c  | 861 --------------------------
 13 files changed, 901 insertions(+), 1113 deletions(-)
 create mode 100644 src/backend/nodes/jumblefuncs.c
 delete mode 100644 src/backend/utils/misc/queryjumble.c

diff --git a/src/include/nodes/bitmapset.h b/src/include/nodes/bitmapset.h
index 2792281658..29d531224c 100644
--- a/src/include/nodes/bitmapset.h
+++ b/src/include/nodes/bitmapset.h
@@ -50,7 +50,7 @@ typedef int32 signedbitmapword; /* must be the matching signed type */
 
 typedef struct Bitmapset
 {
-	pg_node_attr(custom_copy_equal, special_read_write)
+	pg_node_attr(custom_copy_equal, special_read_write, no_jumble)
 
 	NodeTag		type;
 	int			nwords;			/* number of words in array */
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 1f33902947..afaeeaf2c9 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -59,6 +59,8 @@ typedef enum NodeTag
  *
  * - no_copy_equal: Shorthand for both no_copy and no_equal.
  *
+ * - no_jumble: Does not support jumble() at all.
+ *
  * - no_read: Does not support nodeRead() at all.
  *
  * - nodetag_only: Does not support copyObject(), equal(), outNode(),
@@ -97,6 +99,8 @@ typedef enum NodeTag
  * - equal_ignore_if_zero: Ignore the field for equality if it is zero.
  *   (Otherwise, compare normally.)
  *
+ * - jumble_ignore: Ignore the field for the query jumbling.
+ *
  * - read_as(VALUE): In nodeRead(), replace the field's value with VALUE.
  *
  * - read_write_ignore: Ignore the field for read/write.  This is only allowed
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 6a6d3293e4..16b6591c00 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -124,47 +124,61 @@ typedef struct Query
 
 	CmdType		commandType;	/* select|insert|update|delete|merge|utility */
 
-	QuerySource querySource;	/* where did I come from? */
+	/* where did I come from? */
+	QuerySource querySource pg_node_attr(jumble_ignore);
 
 	/*
 	 * query identifier (can be set by plugins); ignored for equal, as it
 	 * might not be set; also not stored
 	 */
-	uint64		queryId pg_node_attr(equal_ignore, read_write_ignore, read_as(0));
+	uint64		queryId pg_node_attr(equal_ignore, jumble_ignore, read_write_ignore, read_as(0));
 
-	bool		canSetTag;		/* do I set the command result tag? */
+	/* do I set the command result tag? */
+	bool		canSetTag pg_node_attr(jumble_ignore);
 
 	Node	   *utilityStmt;	/* non-null if commandType == CMD_UTILITY */
 
-	int			resultRelation; /* rtable index of target relation for
-								 * INSERT/UPDATE/DELETE/MERGE; 0 for SELECT */
+	/*
+	 * rtable index of target relation for INSERT/UPDATE/DELETE/MERGE; 0 for
+	 * SELECT.
+	 */
+	int			resultRelation pg_node_attr(jumble_ignore);
 
-	bool		hasAggs;		/* has aggregates in tlist or havingQual */
-	bool		hasWindowFuncs; /* has window functions in tlist */
-	bool		hasTargetSRFs;	/* has set-returning functions in tlist */
-	bool		hasSubLinks;	/* has subquery SubLink */
-	bool		hasDistinctOn;	/* distinctClause is from DISTINCT ON */
-	bool		hasRecursive;	/* WITH RECURSIVE was specified */
-	bool		hasModifyingCTE;	/* has INSERT/UPDATE/DELETE in WITH */
-	bool		hasForUpdate;	/* FOR [KEY] UPDATE/SHARE was specified */
-	bool		hasRowSecurity; /* rewriter has applied some RLS policy */
+	/* has aggregates in tlist or havingQual */
+	bool		hasAggs pg_node_attr(jumble_ignore);
+	/* has window functions in tlist */
+	bool		hasWindowFuncs pg_node_attr(jumble_ignore);
+	/* has set-returning functions in tlist */
+	bool		hasTargetSRFs pg_node_attr(jumble_ignore);
+	bool		hasSubLinks pg_node_attr(jumble_ignore);	/* has subquery SubLink */
+	/* distinctClause is from DISTINCT ON */
+	bool		hasDistinctOn pg_node_attr(jumble_ignore);
+	/* WITH RECURSIVE was specified */
+	bool		hasRecursive pg_node_attr(jumble_ignore);
+	/* has INSERT/UPDATE/DELETE in WITH */
+	bool		hasModifyingCTE pg_node_attr(jumble_ignore);
+	/* FOR [KEY] UPDATE/SHARE was specified */
+	bool		hasForUpdate pg_node_attr(jumble_ignore);
+	/* rewriter has applied some RLS policy */
+	bool		hasRowSecurity pg_node_attr(jumble_ignore);
 
-	bool		isReturn;		/* is a RETURN statement */
+	bool		isReturn pg_node_attr(jumble_ignore);	/* is a RETURN statement */
 
 	List	   *cteList;		/* WITH list (of CommonTableExpr's) */
 
 	List	   *rtable;			/* list of range table entries */
-	List	   *rteperminfos;	/* list of RTEPermissionInfo nodes for the
-								 * rtable entries having perminfoindex > 0 */
+	/* list of RTEPermissionInfo nodes for the rtable entries having perminfoindex > 0 */
+	List	   *rteperminfos pg_node_attr(jumble_ignore);
 	FromExpr   *jointree;		/* table join tree (FROM and WHERE clauses);
 								 * also USING clause for MERGE */
 
 	List	   *mergeActionList;	/* list of actions for MERGE (only) */
-	bool		mergeUseOuterJoin;	/* whether to use outer join */
+	/* whether to use outer join */
+	bool		mergeUseOuterJoin pg_node_attr(jumble_ignore);
 
 	List	   *targetList;		/* target list (of TargetEntry) */
 
-	OverridingKind override;	/* OVERRIDING clause */
+	OverridingKind override pg_node_attr(jumble_ignore);	/* OVERRIDING clause */
 
 	OnConflictExpr *onConflict; /* ON CONFLICT DO [NOTHING | UPDATE] */
 
@@ -192,11 +206,14 @@ typedef struct Query
 	Node	   *setOperations;	/* set-operation tree if this is top level of
 								 * a UNION/INTERSECT/EXCEPT query */
 
-	List	   *constraintDeps; /* a list of pg_constraint OIDs that the query
-								 * depends on to be semantically valid */
+	/*
+	 * A list of pg_constraint OIDs that the query depends on to be
+	 * semantically valid
+	 */
+	List	   *constraintDeps pg_node_attr(jumble_ignore);
 
-	List	   *withCheckOptions;	/* a list of WithCheckOption's (added
-									 * during rewrite) */
+	/* a list of WithCheckOption's (added during rewrite) */
+	List	   *withCheckOptions pg_node_attr(jumble_ignore);
 
 	/*
 	 * The following two fields identify the portion of the source text string
@@ -204,8 +221,10 @@ typedef struct Query
 	 * Queries, not in sub-queries.  When not set, they might both be zero, or
 	 * both be -1 meaning "unknown".
 	 */
-	int			stmt_location;	/* start location, or -1 if unknown */
-	int			stmt_len;		/* length in bytes; 0 means "rest of string" */
+	/* start location, or -1 if unknown */
+	int			stmt_location pg_node_attr(jumble_ignore);
+	/* length in bytes; 0 means "rest of string" */
+	int			stmt_len pg_node_attr(jumble_ignore);
 } Query;
 
 
@@ -240,7 +259,8 @@ typedef struct TypeName
 	List	   *typmods;		/* type modifier expression(s) */
 	int32		typemod;		/* prespecified type modifier */
 	List	   *arrayBounds;	/* array bounds */
-	int			location;		/* token location, or -1 if unknown */
+	int			location pg_node_attr(jumble_ignore);	/* token location, or -1
+														 * if unknown */
 } TypeName;
 
 /*
@@ -260,7 +280,8 @@ typedef struct ColumnRef
 {
 	NodeTag		type;
 	List	   *fields;			/* field names (String nodes) or A_Star */
-	int			location;		/* token location, or -1 if unknown */
+	int			location pg_node_attr(jumble_ignore);	/* token location, or -1
+														 * if unknown */
 } ColumnRef;
 
 /*
@@ -270,7 +291,8 @@ typedef struct ParamRef
 {
 	NodeTag		type;
 	int			number;			/* the number of the parameter */
-	int			location;		/* token location, or -1 if unknown */
+	int			location pg_node_attr(jumble_ignore);	/* token location, or -1
+														 * if unknown */
 } ParamRef;
 
 /*
@@ -303,7 +325,8 @@ typedef struct A_Expr
 	List	   *name;			/* possibly-qualified name of operator */
 	Node	   *lexpr;			/* left argument, or NULL if none */
 	Node	   *rexpr;			/* right argument, or NULL if none */
-	int			location;		/* token location, or -1 if unknown */
+	int			location pg_node_attr(jumble_ignore);	/* token location, or -1
+														 * if unknown */
 } A_Expr;
 
 /*
@@ -329,7 +352,8 @@ typedef struct A_Const
 	NodeTag		type;
 	union ValUnion val;
 	bool		isnull;			/* SQL NULL constant */
-	int			location;		/* token location, or -1 if unknown */
+	int			location pg_node_attr(jumble_ignore);	/* token location, or -1
+														 * if unknown */
 } A_Const;
 
 /*
@@ -340,7 +364,8 @@ typedef struct TypeCast
 	NodeTag		type;
 	Node	   *arg;			/* the expression being casted */
 	TypeName   *typeName;		/* the target type */
-	int			location;		/* token location, or -1 if unknown */
+	int			location pg_node_attr(jumble_ignore);	/* token location, or -1
+														 * if unknown */
 } TypeCast;
 
 /*
@@ -351,7 +376,8 @@ typedef struct CollateClause
 	NodeTag		type;
 	Node	   *arg;			/* input expression */
 	List	   *collname;		/* possibly-qualified collation name */
-	int			location;		/* token location, or -1 if unknown */
+	int			location pg_node_attr(jumble_ignore);	/* token location, or -1
+														 * if unknown */
 } CollateClause;
 
 /*
@@ -371,7 +397,8 @@ typedef struct RoleSpec
 	NodeTag		type;
 	RoleSpecType roletype;		/* Type of this rolespec */
 	char	   *rolename;		/* filled only for ROLESPEC_CSTRING */
-	int			location;		/* token location, or -1 if unknown */
+	int			location pg_node_attr(jumble_ignore);	/* token location, or -1
+														 * if unknown */
 } RoleSpec;
 
 /*
@@ -401,7 +428,8 @@ typedef struct FuncCall
 	bool		agg_distinct;	/* arguments were labeled DISTINCT */
 	bool		func_variadic;	/* last argument was labeled VARIADIC */
 	CoercionForm funcformat;	/* how to display this node */
-	int			location;		/* token location, or -1 if unknown */
+	int			location pg_node_attr(jumble_ignore);	/* token location, or -1
+														 * if unknown */
 } FuncCall;
 
 /*
@@ -458,7 +486,8 @@ typedef struct A_ArrayExpr
 {
 	NodeTag		type;
 	List	   *elements;		/* array element expressions */
-	int			location;		/* token location, or -1 if unknown */
+	int			location pg_node_attr(jumble_ignore);	/* token location, or -1
+														 * if unknown */
 } A_ArrayExpr;
 
 /*
@@ -485,7 +514,8 @@ typedef struct ResTarget
 	char	   *name;			/* column name or NULL */
 	List	   *indirection;	/* subscripts, field names, and '*', or NIL */
 	Node	   *val;			/* the value expression to compute or assign */
-	int			location;		/* token location, or -1 if unknown */
+	int			location pg_node_attr(jumble_ignore);	/* token location, or -1
+														 * if unknown */
 } ResTarget;
 
 /*
@@ -515,7 +545,8 @@ typedef struct SortBy
 	SortByDir	sortby_dir;		/* ASC/DESC/USING/default */
 	SortByNulls sortby_nulls;	/* NULLS FIRST/LAST */
 	List	   *useOp;			/* name of op to use, if SORTBY_USING */
-	int			location;		/* operator location, or -1 if none/unknown */
+	int			location pg_node_attr(jumble_ignore);	/* operator location, or
+														 * -1 if none/unknown */
 } SortBy;
 
 /*
@@ -536,7 +567,8 @@ typedef struct WindowDef
 	int			frameOptions;	/* frame_clause options, see below */
 	Node	   *startOffset;	/* expression for starting bound, if any */
 	Node	   *endOffset;		/* expression for ending bound, if any */
-	int			location;		/* parse location, or -1 if none/unknown */
+	int			location pg_node_attr(jumble_ignore);	/* parse location, or -1
+														 * if none/unknown */
 } WindowDef;
 
 /*
@@ -626,7 +658,8 @@ typedef struct RangeTableFunc
 	List	   *namespaces;		/* list of namespaces as ResTarget */
 	List	   *columns;		/* list of RangeTableFuncCol */
 	Alias	   *alias;			/* table alias & optional column aliases */
-	int			location;		/* token location, or -1 if unknown */
+	int			location pg_node_attr(jumble_ignore);	/* token location, or -1
+														 * if unknown */
 } RangeTableFunc;
 
 /*
@@ -644,7 +677,8 @@ typedef struct RangeTableFuncCol
 	bool		is_not_null;	/* does it have NOT NULL? */
 	Node	   *colexpr;		/* column filter expression */
 	Node	   *coldefexpr;		/* column default value expression */
-	int			location;		/* token location, or -1 if unknown */
+	int			location pg_node_attr(jumble_ignore);	/* token location, or -1
+														 * if unknown */
 } RangeTableFuncCol;
 
 /*
@@ -664,7 +698,8 @@ typedef struct RangeTableSample
 	List	   *method;			/* sampling method name (possibly qualified) */
 	List	   *args;			/* argument(s) for sampling method */
 	Node	   *repeatable;		/* REPEATABLE expression, or NULL if none */
-	int			location;		/* method name location, or -1 if unknown */
+	int			location pg_node_attr(jumble_ignore);	/* method name location,
+														 * or -1 if unknown */
 } RangeTableSample;
 
 /*
@@ -707,7 +742,8 @@ typedef struct ColumnDef
 	Oid			collOid;		/* collation OID (InvalidOid if not set) */
 	List	   *constraints;	/* other constraints on column */
 	List	   *fdwoptions;		/* per-column FDW options */
-	int			location;		/* parse location, or -1 if none/unknown */
+	int			location pg_node_attr(jumble_ignore);	/* parse location, or -1
+														 * if none/unknown */
 } ColumnDef;
 
 /*
@@ -781,7 +817,8 @@ typedef struct DefElem
 	Node	   *arg;			/* typically Integer, Float, String, or
 								 * TypeName */
 	DefElemAction defaction;	/* unspecified action, or SET/ADD/DROP */
-	int			location;		/* token location, or -1 if unknown */
+	int			location pg_node_attr(jumble_ignore);	/* token location, or -1
+														 * if unknown */
 } DefElem;
 
 /*
@@ -810,7 +847,8 @@ typedef struct XmlSerialize
 	XmlOptionType xmloption;	/* DOCUMENT or CONTENT */
 	Node	   *expr;
 	TypeName   *typeName;
-	int			location;		/* token location, or -1 if unknown */
+	int			location pg_node_attr(jumble_ignore);	/* token location, or -1
+														 * if unknown */
 } XmlSerialize;
 
 /* Partitioning related definitions */
@@ -828,7 +866,8 @@ typedef struct PartitionElem
 	Node	   *expr;			/* expression to partition on, or NULL */
 	List	   *collation;		/* name of collation; NIL = default */
 	List	   *opclass;		/* name of desired opclass; NIL = default */
-	int			location;		/* token location, or -1 if unknown */
+	int			location pg_node_attr(jumble_ignore);	/* token location, or -1
+														 * if unknown */
 } PartitionElem;
 
 typedef enum PartitionStrategy
@@ -848,7 +887,8 @@ typedef struct PartitionSpec
 	NodeTag		type;
 	PartitionStrategy strategy;
 	List	   *partParams;		/* List of PartitionElems */
-	int			location;		/* token location, or -1 if unknown */
+	int			location pg_node_attr(jumble_ignore);	/* token location, or -1
+														 * if unknown */
 } PartitionSpec;
 
 /*
@@ -875,7 +915,8 @@ struct PartitionBoundSpec
 	List	   *lowerdatums;	/* List of PartitionRangeDatums */
 	List	   *upperdatums;	/* List of PartitionRangeDatums */
 
-	int			location;		/* token location, or -1 if unknown */
+	int			location pg_node_attr(jumble_ignore);	/* token location, or -1
+														 * if unknown */
 };
 
 /*
@@ -898,7 +939,8 @@ typedef struct PartitionRangeDatum
 	Node	   *value;			/* Const (or A_Const in raw tree), if kind is
 								 * PARTITION_RANGE_DATUM_VALUE, else NULL */
 
-	int			location;		/* token location, or -1 if unknown */
+	int			location pg_node_attr(jumble_ignore);	/* token location, or -1
+														 * if unknown */
 } PartitionRangeDatum;
 
 /*
@@ -995,7 +1037,7 @@ typedef enum RTEKind
 
 typedef struct RangeTblEntry
 {
-	pg_node_attr(custom_read_write)
+	pg_node_attr(custom_read_write, no_jumble)
 
 	NodeTag		type;
 
@@ -1234,14 +1276,20 @@ typedef struct RangeTblFunction
 	NodeTag		type;
 
 	Node	   *funcexpr;		/* expression tree for func call */
-	int			funccolcount;	/* number of columns it contributes to RTE */
+	int			funccolcount pg_node_attr(jumble_ignore);	/* number of columns it
+															 * contributes to RTE */
 	/* These fields record the contents of a column definition list, if any: */
-	List	   *funccolnames;	/* column names (list of String) */
-	List	   *funccoltypes;	/* OID list of column type OIDs */
-	List	   *funccoltypmods; /* integer list of column typmods */
-	List	   *funccolcollations;	/* OID list of column collation OIDs */
+	List	   *funccolnames pg_node_attr(jumble_ignore);	/* column names (list of
+															 * String) */
+	List	   *funccoltypes pg_node_attr(jumble_ignore);	/* OID list of column
+															 * type OIDs */
+	List	   *funccoltypmods pg_node_attr(jumble_ignore); /* integer list of
+															 * column typmods */
+	List	   *funccolcollations pg_node_attr(jumble_ignore);	/* OID list of column
+																 * collation OIDs */
 	/* This is set during planning for use by the executor: */
-	Bitmapset  *funcparams;		/* PARAM_EXEC Param IDs affecting this func */
+	Bitmapset  *funcparams pg_node_attr(jumble_ignore); /* PARAM_EXEC Param IDs
+														 * affecting this func */
 } RangeTblFunction;
 
 /*
@@ -1348,7 +1396,9 @@ typedef struct SortGroupClause
 	Oid			eqop;			/* the equality operator ('=' op) */
 	Oid			sortop;			/* the ordering operator ('<' op), or 0 */
 	bool		nulls_first;	/* do NULLs come before normal values? */
-	bool		hashable;		/* can eqop be implemented by hashing? */
+	bool		hashable pg_node_attr(jumble_ignore);	/* can eqop be
+														 * implemented by
+														 * hashing? */
 } SortGroupClause;
 
 /*
@@ -1413,9 +1463,9 @@ typedef enum GroupingSetKind
 typedef struct GroupingSet
 {
 	NodeTag		type;
-	GroupingSetKind kind;
+	GroupingSetKind kind pg_node_attr(jumble_ignore);
 	List	   *content;
-	int			location;
+	int			location pg_node_attr(jumble_ignore);
 } GroupingSet;
 
 /*
@@ -1438,21 +1488,30 @@ typedef struct GroupingSet
 typedef struct WindowClause
 {
 	NodeTag		type;
-	char	   *name;			/* window name (NULL in an OVER clause) */
-	char	   *refname;		/* referenced window name, if any */
+	char	   *name pg_node_attr(jumble_ignore);	/* window name (NULL in an
+													 * OVER clause) */
+	char	   *refname pg_node_attr(jumble_ignore);	/* referenced window
+														 * name, if any */
 	List	   *partitionClause;	/* PARTITION BY list */
-	List	   *orderClause;	/* ORDER BY list */
+	List	   *orderClause pg_node_attr(jumble_ignore);	/* ORDER BY list */
 	int			frameOptions;	/* frame_clause options, see WindowDef */
 	Node	   *startOffset;	/* expression for starting bound, if any */
 	Node	   *endOffset;		/* expression for ending bound, if any */
-	List	   *runCondition;	/* qual to help short-circuit execution */
-	Oid			startInRangeFunc;	/* in_range function for startOffset */
-	Oid			endInRangeFunc; /* in_range function for endOffset */
-	Oid			inRangeColl;	/* collation for in_range tests */
-	bool		inRangeAsc;		/* use ASC sort order for in_range tests? */
-	bool		inRangeNullsFirst;	/* nulls sort first for in_range tests? */
+	/* qual to help short-circuit execution */
+	List	   *runCondition pg_node_attr(jumble_ignore);
+	/* in_range function for startOffset */
+	Oid			startInRangeFunc pg_node_attr(jumble_ignore);
+	/* in_range function for endOffset */
+	Oid			endInRangeFunc pg_node_attr(jumble_ignore);
+	/* collation for in_range tests */
+	Oid			inRangeColl pg_node_attr(jumble_ignore);
+	/* use ASC sort order for in_range tests? */
+	bool		inRangeAsc pg_node_attr(jumble_ignore);
+	/* nulls sort first for in_range tests? */
+	bool		inRangeNullsFirst pg_node_attr(jumble_ignore);
 	Index		winref;			/* ID referenced by window functions */
-	bool		copiedOrder;	/* did we copy orderClause from refname? */
+	/* did we copy orderClause from refname? */
+	bool		copiedOrder pg_node_attr(jumble_ignore);
 } WindowClause;
 
 /*
@@ -1488,7 +1547,8 @@ typedef struct WithClause
 	NodeTag		type;
 	List	   *ctes;			/* list of CommonTableExprs */
 	bool		recursive;		/* true = WITH RECURSIVE */
-	int			location;		/* token location, or -1 if unknown */
+	int			location pg_node_attr(jumble_ignore);	/* token location, or -1
+														 * if unknown */
 } WithClause;
 
 /*
@@ -1503,7 +1563,8 @@ typedef struct InferClause
 	List	   *indexElems;		/* IndexElems to infer unique index */
 	Node	   *whereClause;	/* qualification (partial-index predicate) */
 	char	   *conname;		/* Constraint name, or NULL if unnamed */
-	int			location;		/* token location, or -1 if unknown */
+	int			location pg_node_attr(jumble_ignore);	/* token location, or -1
+														 * if unknown */
 } InferClause;
 
 /*
@@ -1519,7 +1580,8 @@ typedef struct OnConflictClause
 	InferClause *infer;			/* Optional index inference clause */
 	List	   *targetList;		/* the target list (of ResTarget) */
 	Node	   *whereClause;	/* qualifications */
-	int			location;		/* token location, or -1 if unknown */
+	int			location pg_node_attr(jumble_ignore);	/* token location, or -1
+														 * if unknown */
 } OnConflictClause;
 
 /*
@@ -1540,7 +1602,7 @@ typedef struct CTESearchClause
 	List	   *search_col_list;
 	bool		search_breadth_first;
 	char	   *search_seq_column;
-	int			location;
+	int			location pg_node_attr(jumble_ignore);
 } CTESearchClause;
 
 typedef struct CTECycleClause
@@ -1551,7 +1613,7 @@ typedef struct CTECycleClause
 	Node	   *cycle_mark_value;
 	Node	   *cycle_mark_default;
 	char	   *cycle_path_column;
-	int			location;
+	int			location pg_node_attr(jumble_ignore);
 	/* These fields are set during parse analysis: */
 	Oid			cycle_mark_type;	/* common type of _value and _default */
 	int			cycle_mark_typmod;
@@ -1567,17 +1629,26 @@ typedef struct CommonTableExpr
 	CTEMaterialize ctematerialized; /* is this an optimization fence? */
 	/* SelectStmt/InsertStmt/etc before parse analysis, Query afterwards: */
 	Node	   *ctequery;		/* the CTE's subquery */
-	CTESearchClause *search_clause;
-	CTECycleClause *cycle_clause;
-	int			location;		/* token location, or -1 if unknown */
+	CTESearchClause *search_clause pg_node_attr(jumble_ignore);
+	CTECycleClause *cycle_clause pg_node_attr(jumble_ignore);
+	int			location pg_node_attr(jumble_ignore);	/* token location, or -1
+														 * if unknown */
 	/* These fields are set during parse analysis: */
-	bool		cterecursive;	/* is this CTE actually recursive? */
-	int			cterefcount;	/* number of RTEs referencing this CTE
-								 * (excluding internal self-references) */
-	List	   *ctecolnames;	/* list of output column names */
-	List	   *ctecoltypes;	/* OID list of output column type OIDs */
-	List	   *ctecoltypmods;	/* integer list of output column typmods */
-	List	   *ctecolcollations;	/* OID list of column collation OIDs */
+	/* is this CTE actually recursive? */
+	bool		cterecursive pg_node_attr(jumble_ignore);
+	/*
+	 * Number of RTEs referencing this CTE (excluding internal
+	 * self-references)
+	 */
+	int			cterefcount pg_node_attr(jumble_ignore);
+	List	   *ctecolnames pg_node_attr(jumble_ignore);	/* list of output column
+															 * names */
+	List	   *ctecoltypes pg_node_attr(jumble_ignore);	/* OID list of output
+															 * column type OIDs */
+	List	   *ctecoltypmods pg_node_attr(jumble_ignore);	/* integer list of
+															 * output column typmods */
+	List	   *ctecolcollations pg_node_attr(jumble_ignore);	/* OID list of column
+																 * collation OIDs */
 } CommonTableExpr;
 
 /* Convenience macro to get the output tlist of a CTE's query */
@@ -1614,10 +1685,11 @@ typedef struct MergeAction
 	NodeTag		type;
 	bool		matched;		/* true=MATCHED, false=NOT MATCHED */
 	CmdType		commandType;	/* INSERT/UPDATE/DELETE/DO NOTHING */
-	OverridingKind override;	/* OVERRIDING clause */
+	OverridingKind override pg_node_attr(jumble_ignore);	/* OVERRIDING clause */
 	Node	   *qual;			/* transformed WHEN conditions */
 	List	   *targetList;		/* the target list (of TargetEntry) */
-	List	   *updateColnos;	/* target attribute numbers of an UPDATE */
+	List	   *updateColnos pg_node_attr(jumble_ignore);	/* target attribute
+															 * numbers of an UPDATE */
 } MergeAction;
 
 /*
@@ -1656,7 +1728,8 @@ typedef struct RawStmt
 {
 	NodeTag		type;
 	Node	   *stmt;			/* raw parse tree */
-	int			stmt_location;	/* start location, or -1 if unknown */
+	int			stmt_location pg_node_attr(jumble_ignore);	/* start location, or -1
+															 * if unknown */
 	int			stmt_len;		/* length in bytes; 0 means "rest of string" */
 } RawStmt;
 
@@ -1827,10 +1900,14 @@ typedef struct SetOperationStmt
 	/* Eventually add fields for CORRESPONDING spec here */
 
 	/* Fields derived during parse analysis: */
-	List	   *colTypes;		/* OID list of output column type OIDs */
-	List	   *colTypmods;		/* integer list of output column typmods */
-	List	   *colCollations;	/* OID list of output column collation OIDs */
-	List	   *groupClauses;	/* a list of SortGroupClause's */
+	List	   *colTypes pg_node_attr(jumble_ignore);	/* OID list of output
+														 * column type OIDs */
+	List	   *colTypmods pg_node_attr(jumble_ignore); /* integer list of
+														 * output column typmods */
+	List	   *colCollations pg_node_attr(jumble_ignore);	/* OID list of output
+															 * column collation OIDs */
+	List	   *groupClauses pg_node_attr(jumble_ignore);	/* a list of
+															 * SortGroupClause's */
 	/* groupClauses is NIL if UNION ALL, but must be set otherwise */
 } SetOperationStmt;
 
@@ -1860,7 +1937,9 @@ typedef struct PLAssignStmt
 	List	   *indirection;	/* subscripts and field names, if any */
 	int			nnames;			/* number of names to use in ColumnRef */
 	SelectStmt *val;			/* the PL/pgSQL expression to assign */
-	int			location;		/* name's token location, or -1 if unknown */
+	int			location pg_node_attr(jumble_ignore);	/* name's token
+														 * location, or -1 if
+														 * unknown */
 } PLAssignStmt;
 
 
@@ -2371,7 +2450,8 @@ typedef struct Constraint
 	char	   *conname;		/* Constraint name, or NULL if unnamed */
 	bool		deferrable;		/* DEFERRABLE? */
 	bool		initdeferred;	/* INITIALLY DEFERRED? */
-	int			location;		/* token location, or -1 if unknown */
+	int			location pg_node_attr(jumble_ignore);	/* token location, or -1
+														 * if unknown */
 
 	/* Fields used for constraints with expressions (CHECK and DEFAULT): */
 	bool		is_no_inherit;	/* is constraint non-inheritable? */
@@ -3780,7 +3860,8 @@ typedef struct PublicationObjSpec
 	PublicationObjSpecType pubobjtype;	/* type of this publication object */
 	char	   *name;
 	PublicationTable *pubtable;
-	int			location;		/* token location, or -1 if unknown */
+	int			location pg_node_attr(jumble_ignore);	/* token location, or -1
+														 * if unknown */
 } PublicationObjSpec;
 
 typedef struct CreatePublicationStmt
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index bddfe86191..3d7e2942b1 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -99,7 +99,8 @@ typedef struct PlannedStmt
 	Node	   *utilityStmt;	/* non-null if this is utility stmt */
 
 	/* statement location in source string (copied from Query) */
-	int			stmt_location;	/* start location, or -1 if unknown */
+	int			stmt_location pg_node_attr(jumble_ignore);	/* start location, or -1
+															 * if unknown */
 	int			stmt_len;		/* length in bytes; 0 means "rest of string" */
 } PlannedStmt;
 
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 74f228d959..8a0458c3d3 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -86,7 +86,7 @@ typedef struct RangeVar
 	Alias	   *alias;
 
 	/* token location, or -1 if unknown */
-	int			location;
+	int			location pg_node_attr(jumble_ignore);
 } RangeVar;
 
 /*
@@ -98,19 +98,29 @@ typedef struct RangeVar
 typedef struct TableFunc
 {
 	NodeTag		type;
-	List	   *ns_uris;		/* list of namespace URI expressions */
-	List	   *ns_names;		/* list of namespace names or NULL */
+	List	   *ns_uris pg_node_attr(jumble_ignore);	/* list of namespace URI
+														 * expressions */
+	List	   *ns_names pg_node_attr(jumble_ignore);	/* list of namespace
+														 * names or NULL */
 	Node	   *docexpr;		/* input document expression */
 	Node	   *rowexpr;		/* row filter expression */
-	List	   *colnames;		/* column names (list of String) */
-	List	   *coltypes;		/* OID list of column type OIDs */
-	List	   *coltypmods;		/* integer list of column typmods */
-	List	   *colcollations;	/* OID list of column collation OIDs */
+	List	   *colnames pg_node_attr(jumble_ignore);	/* column names (list of
+														 * String) */
+	List	   *coltypes pg_node_attr(jumble_ignore);	/* OID list of column
+														 * type OIDs */
+	List	   *coltypmods pg_node_attr(jumble_ignore); /* integer list of
+														 * column typmods */
+	List	   *colcollations pg_node_attr(jumble_ignore);	/* OID list of column
+															 * collation OIDs */
 	List	   *colexprs;		/* list of column filter expressions */
-	List	   *coldefexprs;	/* list of column default expressions */
-	Bitmapset  *notnulls;		/* nullability flag for each output column */
-	int			ordinalitycol;	/* counts from 0; -1 if none specified */
-	int			location;		/* token location, or -1 if unknown */
+	List	   *coldefexprs pg_node_attr(jumble_ignore);	/* list of column
+															 * default expressions */
+	Bitmapset  *notnulls pg_node_attr(jumble_ignore);	/* nullability flag for
+														 * each output column */
+	int			ordinalitycol pg_node_attr(jumble_ignore);	/* counts from 0; -1 if
+															 * none specified */
+	int			location pg_node_attr(jumble_ignore);	/* token location, or -1
+														 * if unknown */
 } TableFunc;
 
 /*
@@ -217,11 +227,11 @@ typedef struct Var
 	AttrNumber	varattno;
 
 	/* pg_type OID for the type of this var */
-	Oid			vartype;
+	Oid			vartype pg_node_attr(jumble_ignore);
 	/* pg_attribute typmod value */
-	int32		vartypmod;
+	int32		vartypmod pg_node_attr(jumble_ignore);
 	/* OID of collation, or InvalidOid if none */
-	Oid			varcollid;
+	Oid			varcollid pg_node_attr(jumble_ignore);
 
 	/*
 	 * for subquery variables referencing outer relations; 0 in a normal var,
@@ -235,12 +245,12 @@ typedef struct Var
 	 * their varno/varattno match.
 	 */
 	/* syntactic relation index (0 if unknown) */
-	Index		varnosyn pg_node_attr(equal_ignore);
+	Index		varnosyn pg_node_attr(equal_ignore, jumble_ignore);
 	/* syntactic attribute number */
-	AttrNumber	varattnosyn pg_node_attr(equal_ignore);
+	AttrNumber	varattnosyn pg_node_attr(equal_ignore, jumble_ignore);
 
 	/* token location, or -1 if unknown */
-	int			location;
+	int			location pg_node_attr(jumble_ignore);
 } Var;
 
 /*
@@ -257,16 +267,20 @@ typedef struct Const
 
 	Expr		xpr;
 	Oid			consttype;		/* pg_type OID of the constant's datatype */
-	int32		consttypmod;	/* typmod value, if any */
-	Oid			constcollid;	/* OID of collation, or InvalidOid if none */
-	int			constlen;		/* typlen of the constant's datatype */
-	Datum		constvalue;		/* the constant's value */
-	bool		constisnull;	/* whether the constant is null (if true,
-								 * constvalue is undefined) */
-	bool		constbyval;		/* whether this datatype is passed by value.
-								 * If true, then all the information is stored
-								 * in the Datum. If false, then the Datum
-								 * contains a pointer to the information. */
+	int32		consttypmod pg_node_attr(jumble_ignore);	/* typmod value, if any */
+	Oid			constcollid pg_node_attr(jumble_ignore);	/* OID of collation, or
+															 * InvalidOid if none */
+	int			constlen pg_node_attr(jumble_ignore);	/* typlen of the
+														 * constant's datatype */
+	Datum		constvalue pg_node_attr(jumble_ignore); /* the constant's value */
+	/* whether the constant is null (if true, constvalue is undefined) */
+	bool		constisnull pg_node_attr(jumble_ignore);
+	/*
+	 * Whether this datatype is passed by value.  If true, then all the
+	 * information is stored in the Datum.  If false, then the Datum contains
+	 * a pointer to the information.
+	 */
+	bool		constbyval pg_node_attr(jumble_ignore);
 	int			location;		/* token location, or -1 if unknown */
 } Const;
 
@@ -311,9 +325,12 @@ typedef struct Param
 	ParamKind	paramkind;		/* kind of parameter. See above */
 	int			paramid;		/* numeric ID for parameter */
 	Oid			paramtype;		/* pg_type OID of parameter's datatype */
-	int32		paramtypmod;	/* typmod value, if known */
-	Oid			paramcollid;	/* OID of collation, or InvalidOid if none */
-	int			location;		/* token location, or -1 if unknown */
+	int32		paramtypmod pg_node_attr(jumble_ignore);	/* typmod value, if
+															 * known */
+	Oid			paramcollid pg_node_attr(jumble_ignore);	/* OID of collation, or
+															 * InvalidOid if none */
+	int			location pg_node_attr(jumble_ignore);	/* token location, or -1
+														 * if unknown */
 } Param;
 
 /*
@@ -373,22 +390,22 @@ typedef struct Aggref
 	Oid			aggfnoid;
 
 	/* type Oid of result of the aggregate */
-	Oid			aggtype;
+	Oid			aggtype pg_node_attr(jumble_ignore);
 
 	/* OID of collation of result */
-	Oid			aggcollid;
+	Oid			aggcollid pg_node_attr(jumble_ignore);
 
 	/* OID of collation that function should use */
-	Oid			inputcollid;
+	Oid			inputcollid pg_node_attr(jumble_ignore);
 
 	/*
 	 * type Oid of aggregate's transition value; ignored for equal since it
 	 * might not be set yet
 	 */
-	Oid			aggtranstype pg_node_attr(equal_ignore);
+	Oid			aggtranstype pg_node_attr(equal_ignore, jumble_ignore);
 
 	/* type Oids of direct and aggregated args */
-	List	   *aggargtypes;
+	List	   *aggargtypes pg_node_attr(jumble_ignore);
 
 	/* direct arguments, if an ordered-set agg */
 	List	   *aggdirectargs;
@@ -406,34 +423,34 @@ typedef struct Aggref
 	Expr	   *aggfilter;
 
 	/* true if argument list was really '*' */
-	bool		aggstar;
+	bool		aggstar pg_node_attr(jumble_ignore);
 
 	/*
 	 * true if variadic arguments have been combined into an array last
 	 * argument
 	 */
-	bool		aggvariadic;
+	bool		aggvariadic pg_node_attr(jumble_ignore);
 
 	/* aggregate kind (see pg_aggregate.h) */
-	char		aggkind;
+	char		aggkind pg_node_attr(jumble_ignore);
 
 	/* aggregate input already sorted */
-	bool		aggpresorted pg_node_attr(equal_ignore);
+	bool		aggpresorted pg_node_attr(equal_ignore, jumble_ignore);
 
 	/* > 0 if agg belongs to outer query */
-	Index		agglevelsup;
+	Index		agglevelsup pg_node_attr(jumble_ignore);
 
 	/* expected agg-splitting mode of parent Agg */
-	AggSplit	aggsplit;
+	AggSplit	aggsplit pg_node_attr(jumble_ignore);
 
 	/* unique ID within the Agg node */
-	int			aggno;
+	int			aggno pg_node_attr(jumble_ignore);
 
 	/* unique ID of transition state in the Agg */
-	int			aggtransno;
+	int			aggtransno pg_node_attr(jumble_ignore);
 
 	/* token location, or -1 if unknown */
-	int			location;
+	int			location pg_node_attr(jumble_ignore);
 } Aggref;
 
 /*
@@ -465,19 +482,19 @@ typedef struct GroupingFunc
 	Expr		xpr;
 
 	/* arguments, not evaluated but kept for benefit of EXPLAIN etc. */
-	List	   *args;
+	List	   *args pg_node_attr(jumble_ignore);
 
 	/* ressortgrouprefs of arguments */
 	List	   *refs pg_node_attr(equal_ignore);
 
 	/* actual column positions set by planner */
-	List	   *cols pg_node_attr(equal_ignore);
+	List	   *cols pg_node_attr(equal_ignore, jumble_ignore);
 
 	/* same as Aggref.agglevelsup */
 	Index		agglevelsup;
 
 	/* token location */
-	int			location;
+	int			location pg_node_attr(jumble_ignore);
 } GroupingFunc;
 
 /*
@@ -487,15 +504,21 @@ typedef struct WindowFunc
 {
 	Expr		xpr;
 	Oid			winfnoid;		/* pg_proc Oid of the function */
-	Oid			wintype;		/* type Oid of result of the window function */
-	Oid			wincollid;		/* OID of collation of result */
-	Oid			inputcollid;	/* OID of collation that function should use */
+	Oid			wintype pg_node_attr(jumble_ignore);	/* type Oid of result of
+														 * the window function */
+	Oid			wincollid pg_node_attr(jumble_ignore);	/* OID of collation of
+														 * result */
+	Oid			inputcollid pg_node_attr(jumble_ignore);	/* OID of collation that
+															 * function should use */
 	List	   *args;			/* arguments to the window function */
 	Expr	   *aggfilter;		/* FILTER expression, if any */
 	Index		winref;			/* index of associated WindowClause */
-	bool		winstar;		/* true if argument list was really '*' */
-	bool		winagg;			/* is function a simple aggregate? */
-	int			location;		/* token location, or -1 if unknown */
+	bool		winstar pg_node_attr(jumble_ignore);	/* true if argument list
+														 * was really '*' */
+	bool		winagg pg_node_attr(jumble_ignore); /* is function a simple
+													 * aggregate? */
+	int			location pg_node_attr(jumble_ignore);	/* token location, or -1
+														 * if unknown */
 } WindowFunc;
 
 /*
@@ -539,11 +562,16 @@ typedef struct WindowFunc
 typedef struct SubscriptingRef
 {
 	Expr		xpr;
-	Oid			refcontainertype;	/* type of the container proper */
-	Oid			refelemtype;	/* the container type's pg_type.typelem */
-	Oid			refrestype;		/* type of the SubscriptingRef's result */
-	int32		reftypmod;		/* typmod of the result */
-	Oid			refcollid;		/* collation of result, or InvalidOid if none */
+	Oid			refcontainertype pg_node_attr(jumble_ignore);	/* type of the container
+																 * proper */
+	Oid			refelemtype pg_node_attr(jumble_ignore);	/* the container type's
+															 * pg_type.typelem */
+	Oid			refrestype pg_node_attr(jumble_ignore); /* type of the
+														 * SubscriptingRef's
+														 * result */
+	int32		reftypmod pg_node_attr(jumble_ignore);	/* typmod of the result */
+	Oid			refcollid pg_node_attr(jumble_ignore);	/* collation of result,
+														 * or InvalidOid if none */
 	List	   *refupperindexpr;	/* expressions that evaluate to upper
 									 * container indexes */
 	List	   *reflowerindexpr;	/* expressions that evaluate to lower
@@ -596,15 +624,23 @@ typedef struct FuncExpr
 {
 	Expr		xpr;
 	Oid			funcid;			/* PG_PROC OID of the function */
-	Oid			funcresulttype; /* PG_TYPE OID of result value */
-	bool		funcretset;		/* true if function returns set */
-	bool		funcvariadic;	/* true if variadic arguments have been
-								 * combined into an array last argument */
-	CoercionForm funcformat;	/* how to display this function call */
-	Oid			funccollid;		/* OID of collation of result */
-	Oid			inputcollid;	/* OID of collation that function should use */
+	Oid			funcresulttype pg_node_attr(jumble_ignore); /* PG_TYPE OID of result
+															 * value */
+	bool		funcretset pg_node_attr(jumble_ignore); /* true if function
+														 * returns set */
+	bool		funcvariadic pg_node_attr(jumble_ignore);	/* true if variadic
+															 * arguments have been
+															 * combined into an
+															 * array last argument */
+	CoercionForm funcformat pg_node_attr(jumble_ignore);	/* how to display this
+															 * function call */
+	Oid			funccollid pg_node_attr(jumble_ignore); /* OID of collation of
+														 * result */
+	Oid			inputcollid pg_node_attr(jumble_ignore);	/* OID of collation that
+															 * function should use */
 	List	   *args;			/* arguments to the function */
-	int			location;		/* token location, or -1 if unknown */
+	int			location pg_node_attr(jumble_ignore);	/* token location, or -1
+														 * if unknown */
 } FuncExpr;
 
 /*
@@ -625,9 +661,11 @@ typedef struct NamedArgExpr
 {
 	Expr		xpr;
 	Expr	   *arg;			/* the argument expression */
-	char	   *name;			/* the name */
+	char	   *name pg_node_attr(jumble_ignore);	/* the name */
 	int			argnumber;		/* argument's number in positional notation */
-	int			location;		/* argument name location, or -1 if unknown */
+	int			location pg_node_attr(jumble_ignore);	/* argument name
+														 * location, or -1 if
+														 * unknown */
 } NamedArgExpr;
 
 /*
@@ -648,25 +686,25 @@ typedef struct OpExpr
 	Oid			opno;
 
 	/* PG_PROC OID of underlying function */
-	Oid			opfuncid pg_node_attr(equal_ignore_if_zero);
+	Oid			opfuncid pg_node_attr(equal_ignore_if_zero, jumble_ignore);
 
 	/* PG_TYPE OID of result value */
-	Oid			opresulttype;
+	Oid			opresulttype pg_node_attr(jumble_ignore);
 
 	/* true if operator returns set */
-	bool		opretset;
+	bool		opretset pg_node_attr(jumble_ignore);
 
 	/* OID of collation of result */
-	Oid			opcollid;
+	Oid			opcollid pg_node_attr(jumble_ignore);
 
 	/* OID of collation that operator should use */
-	Oid			inputcollid;
+	Oid			inputcollid pg_node_attr(jumble_ignore);
 
 	/* arguments to the operator (1 or 2) */
 	List	   *args;
 
 	/* token location, or -1 if unknown */
-	int			location;
+	int			location pg_node_attr(jumble_ignore);
 } OpExpr;
 
 /*
@@ -725,25 +763,25 @@ typedef struct ScalarArrayOpExpr
 	Oid			opno;
 
 	/* PG_PROC OID of comparison function */
-	Oid			opfuncid pg_node_attr(equal_ignore_if_zero);
+	Oid			opfuncid pg_node_attr(equal_ignore_if_zero, jumble_ignore);
 
 	/* PG_PROC OID of hash func or InvalidOid */
-	Oid			hashfuncid pg_node_attr(equal_ignore_if_zero);
+	Oid			hashfuncid pg_node_attr(equal_ignore_if_zero, jumble_ignore);
 
 	/* PG_PROC OID of negator of opfuncid function or InvalidOid.  See above */
-	Oid			negfuncid pg_node_attr(equal_ignore_if_zero);
+	Oid			negfuncid pg_node_attr(equal_ignore_if_zero, jumble_ignore);
 
 	/* true for ANY, false for ALL */
 	bool		useOr;
 
 	/* OID of collation that operator should use */
-	Oid			inputcollid;
+	Oid			inputcollid pg_node_attr(jumble_ignore);
 
 	/* the scalar and array operands */
 	List	   *args;
 
 	/* token location, or -1 if unknown */
-	int			location;
+	int			location pg_node_attr(jumble_ignore);
 } ScalarArrayOpExpr;
 
 /*
@@ -765,7 +803,8 @@ typedef struct BoolExpr
 	Expr		xpr;
 	BoolExprType boolop;
 	List	   *args;			/* arguments to this expression */
-	int			location;		/* token location, or -1 if unknown */
+	int			location pg_node_attr(jumble_ignore);	/* token location, or -1
+														 * if unknown */
 } BoolExpr;
 
 /*
@@ -838,9 +877,11 @@ typedef struct SubLink
 	SubLinkType subLinkType;	/* see above */
 	int			subLinkId;		/* ID (1..n); 0 if not MULTIEXPR */
 	Node	   *testexpr;		/* outer-query test for ALL/ANY/ROWCOMPARE */
-	List	   *operName;		/* originally specified operator name */
+	List	   *operName pg_node_attr(jumble_ignore);	/* originally specified
+														 * operator name */
 	Node	   *subselect;		/* subselect as Query* or raw parsetree */
-	int			location;		/* token location, or -1 if unknown */
+	int			location pg_node_attr(jumble_ignore);	/* token location, or -1
+														 * if unknown */
 } SubLink;
 
 /*
@@ -948,10 +989,13 @@ typedef struct FieldSelect
 	Expr		xpr;
 	Expr	   *arg;			/* input expression */
 	AttrNumber	fieldnum;		/* attribute number of field to extract */
-	Oid			resulttype;		/* type of the field (result type of this
-								 * node) */
-	int32		resulttypmod;	/* output typmod (usually -1) */
-	Oid			resultcollid;	/* OID of collation of the field */
+	Oid			resulttype pg_node_attr(jumble_ignore); /* type of the field
+														 * (result type of this
+														 * node) */
+	int32		resulttypmod pg_node_attr(jumble_ignore);	/* output typmod
+															 * (usually -1) */
+	Oid			resultcollid pg_node_attr(jumble_ignore);	/* OID of collation of
+															 * the field */
 } FieldSelect;
 
 /* ----------------
@@ -977,8 +1021,10 @@ typedef struct FieldStore
 	Expr		xpr;
 	Expr	   *arg;			/* input tuple value */
 	List	   *newvals;		/* new value(s) for field(s) */
-	List	   *fieldnums;		/* integer list of field attnums */
-	Oid			resulttype;		/* type of result (same as type of arg) */
+	List	   *fieldnums pg_node_attr(jumble_ignore);	/* integer list of field
+														 * attnums */
+	Oid			resulttype pg_node_attr(jumble_ignore); /* type of result (same
+														 * as type of arg) */
 	/* Like RowExpr, we deliberately omit a typmod and collation here */
 } FieldStore;
 
@@ -1000,10 +1046,14 @@ typedef struct RelabelType
 	Expr		xpr;
 	Expr	   *arg;			/* input expression */
 	Oid			resulttype;		/* output type of coercion expression */
-	int32		resulttypmod;	/* output typmod (usually -1) */
-	Oid			resultcollid;	/* OID of collation, or InvalidOid if none */
-	CoercionForm relabelformat; /* how to display this node */
-	int			location;		/* token location, or -1 if unknown */
+	int32		resulttypmod pg_node_attr(jumble_ignore);	/* output typmod
+															 * (usually -1) */
+	Oid			resultcollid pg_node_attr(jumble_ignore);	/* OID of collation, or
+															 * InvalidOid if none */
+	CoercionForm relabelformat pg_node_attr(jumble_ignore); /* how to display this
+															 * node */
+	int			location pg_node_attr(jumble_ignore);	/* token location, or -1
+														 * if unknown */
 } RelabelType;
 
 /* ----------------
@@ -1021,9 +1071,12 @@ typedef struct CoerceViaIO
 	Expr	   *arg;			/* input expression */
 	Oid			resulttype;		/* output type of coercion */
 	/* output typmod is not stored, but is presumed -1 */
-	Oid			resultcollid;	/* OID of collation, or InvalidOid if none */
-	CoercionForm coerceformat;	/* how to display this node */
-	int			location;		/* token location, or -1 if unknown */
+	Oid			resultcollid pg_node_attr(jumble_ignore);	/* OID of collation, or
+															 * InvalidOid if none */
+	CoercionForm coerceformat pg_node_attr(jumble_ignore);	/* how to display this
+															 * node */
+	int			location pg_node_attr(jumble_ignore);	/* token location, or -1
+														 * if unknown */
 } CoerceViaIO;
 
 /* ----------------
@@ -1045,10 +1098,14 @@ typedef struct ArrayCoerceExpr
 	Expr	   *arg;			/* input expression (yields an array) */
 	Expr	   *elemexpr;		/* expression representing per-element work */
 	Oid			resulttype;		/* output type of coercion (an array type) */
-	int32		resulttypmod;	/* output typmod (also element typmod) */
-	Oid			resultcollid;	/* OID of collation, or InvalidOid if none */
-	CoercionForm coerceformat;	/* how to display this node */
-	int			location;		/* token location, or -1 if unknown */
+	int32		resulttypmod pg_node_attr(jumble_ignore);	/* output typmod (also
+															 * element typmod) */
+	Oid			resultcollid pg_node_attr(jumble_ignore);	/* OID of collation, or
+															 * InvalidOid if none */
+	CoercionForm coerceformat pg_node_attr(jumble_ignore);	/* how to display this
+															 * node */
+	int			location pg_node_attr(jumble_ignore);	/* token location, or -1
+														 * if unknown */
 } ArrayCoerceExpr;
 
 /* ----------------
@@ -1070,8 +1127,10 @@ typedef struct ConvertRowtypeExpr
 	Expr	   *arg;			/* input expression */
 	Oid			resulttype;		/* output type (always a composite type) */
 	/* Like RowExpr, we deliberately omit a typmod and collation here */
-	CoercionForm convertformat; /* how to display this node */
-	int			location;		/* token location, or -1 if unknown */
+	CoercionForm convertformat pg_node_attr(jumble_ignore); /* how to display this
+															 * node */
+	int			location pg_node_attr(jumble_ignore);	/* token location, or -1
+														 * if unknown */
 } ConvertRowtypeExpr;
 
 /*----------
@@ -1086,7 +1145,8 @@ typedef struct CollateExpr
 	Expr		xpr;
 	Expr	   *arg;			/* input expression */
 	Oid			collOid;		/* collation's OID */
-	int			location;		/* token location, or -1 if unknown */
+	int			location pg_node_attr(jumble_ignore);	/* token location, or -1
+														 * if unknown */
 } CollateExpr;
 
 /*----------
@@ -1114,12 +1174,15 @@ typedef struct CollateExpr
 typedef struct CaseExpr
 {
 	Expr		xpr;
-	Oid			casetype;		/* type of expression result */
-	Oid			casecollid;		/* OID of collation, or InvalidOid if none */
+	Oid			casetype pg_node_attr(jumble_ignore);	/* type of expression
+														 * result */
+	Oid			casecollid pg_node_attr(jumble_ignore); /* OID of collation, or
+														 * InvalidOid if none */
 	Expr	   *arg;			/* implicit equality comparison argument */
 	List	   *args;			/* the arguments (list of WHEN clauses) */
 	Expr	   *defresult;		/* the default result (ELSE clause) */
-	int			location;		/* token location, or -1 if unknown */
+	int			location pg_node_attr(jumble_ignore);	/* token location, or -1
+														 * if unknown */
 } CaseExpr;
 
 /*
@@ -1130,7 +1193,8 @@ typedef struct CaseWhen
 	Expr		xpr;
 	Expr	   *expr;			/* condition expression */
 	Expr	   *result;			/* substitution result */
-	int			location;		/* token location, or -1 if unknown */
+	int			location pg_node_attr(jumble_ignore);	/* token location, or -1
+														 * if unknown */
 } CaseWhen;
 
 /*
@@ -1157,8 +1221,10 @@ typedef struct CaseTestExpr
 {
 	Expr		xpr;
 	Oid			typeId;			/* type for substituted value */
-	int32		typeMod;		/* typemod for substituted value */
-	Oid			collation;		/* collation for the substituted value */
+	int32		typeMod pg_node_attr(jumble_ignore);	/* typemod for
+														 * substituted value */
+	Oid			collation pg_node_attr(jumble_ignore);	/* collation for the
+														 * substituted value */
 } CaseTestExpr;
 
 /*
@@ -1172,12 +1238,17 @@ typedef struct CaseTestExpr
 typedef struct ArrayExpr
 {
 	Expr		xpr;
-	Oid			array_typeid;	/* type of expression result */
-	Oid			array_collid;	/* OID of collation, or InvalidOid if none */
-	Oid			element_typeid; /* common type of array elements */
+	Oid			array_typeid pg_node_attr(jumble_ignore);	/* type of expression
+															 * result */
+	Oid			array_collid pg_node_attr(jumble_ignore);	/* OID of collation, or
+															 * InvalidOid if none */
+	Oid			element_typeid pg_node_attr(jumble_ignore); /* common type of array
+															 * elements */
 	List	   *elements;		/* the array elements or sub-arrays */
-	bool		multidims;		/* true if elements are sub-arrays */
-	int			location;		/* token location, or -1 if unknown */
+	bool		multidims pg_node_attr(jumble_ignore);	/* true if elements are
+														 * sub-arrays */
+	int			location pg_node_attr(jumble_ignore);	/* token location, or -1
+														 * if unknown */
 } ArrayExpr;
 
 /*
@@ -1205,7 +1276,8 @@ typedef struct RowExpr
 {
 	Expr		xpr;
 	List	   *args;			/* the fields */
-	Oid			row_typeid;		/* RECORDOID or a composite type's ID */
+	Oid			row_typeid pg_node_attr(jumble_ignore); /* RECORDOID or a
+														 * composite type's ID */
 
 	/*
 	 * row_typeid cannot be a domain over composite, only plain composite.  To
@@ -1219,9 +1291,12 @@ typedef struct RowExpr
 	 * We don't need to store a collation either.  The result type is
 	 * necessarily composite, and composite types never have a collation.
 	 */
-	CoercionForm row_format;	/* how to display this node */
-	List	   *colnames;		/* list of String, or NIL */
-	int			location;		/* token location, or -1 if unknown */
+	CoercionForm row_format pg_node_attr(jumble_ignore);	/* how to display this
+															 * node */
+	List	   *colnames pg_node_attr(jumble_ignore);	/* list of String, or
+														 * NIL */
+	int			location pg_node_attr(jumble_ignore);	/* token location, or -1
+														 * if unknown */
 } RowExpr;
 
 /*
@@ -1253,9 +1328,14 @@ typedef struct RowCompareExpr
 {
 	Expr		xpr;
 	RowCompareType rctype;		/* LT LE GE or GT, never EQ or NE */
-	List	   *opnos;			/* OID list of pairwise comparison ops */
-	List	   *opfamilies;		/* OID list of containing operator families */
-	List	   *inputcollids;	/* OID list of collations for comparisons */
+	List	   *opnos pg_node_attr(jumble_ignore);	/* OID list of pairwise
+													 * comparison ops */
+	List	   *opfamilies pg_node_attr(jumble_ignore); /* OID list of
+														 * containing operator
+														 * families */
+	List	   *inputcollids pg_node_attr(jumble_ignore);	/* OID list of
+															 * collations for
+															 * comparisons */
 	List	   *largs;			/* the left-hand input arguments */
 	List	   *rargs;			/* the right-hand input arguments */
 } RowCompareExpr;
@@ -1266,10 +1346,13 @@ typedef struct RowCompareExpr
 typedef struct CoalesceExpr
 {
 	Expr		xpr;
-	Oid			coalescetype;	/* type of expression result */
-	Oid			coalescecollid; /* OID of collation, or InvalidOid if none */
+	Oid			coalescetype pg_node_attr(jumble_ignore);	/* type of expression
+															 * result */
+	Oid			coalescecollid pg_node_attr(jumble_ignore); /* OID of collation, or
+															 * InvalidOid if none */
 	List	   *args;			/* the arguments */
-	int			location;		/* token location, or -1 if unknown */
+	int			location pg_node_attr(jumble_ignore);	/* token location, or -1
+														 * if unknown */
 } CoalesceExpr;
 
 /*
@@ -1284,12 +1367,16 @@ typedef enum MinMaxOp
 typedef struct MinMaxExpr
 {
 	Expr		xpr;
-	Oid			minmaxtype;		/* common type of arguments and result */
-	Oid			minmaxcollid;	/* OID of collation of result */
-	Oid			inputcollid;	/* OID of collation that function should use */
+	Oid			minmaxtype pg_node_attr(jumble_ignore); /* common type of
+														 * arguments and result */
+	Oid			minmaxcollid pg_node_attr(jumble_ignore);	/* OID of collation of
+															 * result */
+	Oid			inputcollid pg_node_attr(jumble_ignore);	/* OID of collation that
+															 * function should use */
 	MinMaxOp	op;				/* function to execute */
 	List	   *args;			/* the arguments */
-	int			location;		/* token location, or -1 if unknown */
+	int			location pg_node_attr(jumble_ignore);	/* token location, or -1
+														 * if unknown */
 } MinMaxExpr;
 
 /*
@@ -1325,14 +1412,18 @@ typedef struct XmlExpr
 {
 	Expr		xpr;
 	XmlExprOp	op;				/* xml function ID */
-	char	   *name;			/* name in xml(NAME foo ...) syntaxes */
+	char	   *name pg_node_attr(jumble_ignore);	/* name in xml(NAME foo
+													 * ...) syntaxes */
 	List	   *named_args;		/* non-XML expressions for xml_attributes */
-	List	   *arg_names;		/* parallel list of String values */
+	List	   *arg_names pg_node_attr(jumble_ignore);	/* parallel list of
+														 * String values */
 	List	   *args;			/* list of expressions */
-	XmlOptionType xmloption;	/* DOCUMENT or CONTENT */
-	Oid			type;			/* target type/typmod for XMLSERIALIZE */
-	int32		typmod;
-	int			location;		/* token location, or -1 if unknown */
+	XmlOptionType xmloption pg_node_attr(jumble_ignore);	/* DOCUMENT or CONTENT */
+	Oid			type pg_node_attr(jumble_ignore);	/* target type/typmod for
+													 * XMLSERIALIZE */
+	int32		typmod pg_node_attr(jumble_ignore);
+	int			location pg_node_attr(jumble_ignore);	/* token location, or -1
+														 * if unknown */
 } XmlExpr;
 
 /* ----------------
@@ -1364,8 +1455,11 @@ typedef struct NullTest
 	Expr		xpr;
 	Expr	   *arg;			/* input expression */
 	NullTestType nulltesttype;	/* IS NULL, IS NOT NULL */
-	bool		argisrow;		/* T to perform field-by-field null checks */
-	int			location;		/* token location, or -1 if unknown */
+	bool		argisrow pg_node_attr(jumble_ignore);	/* T to perform
+														 * field-by-field null
+														 * checks */
+	int			location pg_node_attr(jumble_ignore);	/* token location, or -1
+														 * if unknown */
 } NullTest;
 
 /*
@@ -1387,7 +1481,8 @@ typedef struct BooleanTest
 	Expr		xpr;
 	Expr	   *arg;			/* input expression */
 	BoolTestType booltesttype;	/* test type */
-	int			location;		/* token location, or -1 if unknown */
+	int			location pg_node_attr(jumble_ignore);	/* token location, or -1
+														 * if unknown */
 } BooleanTest;
 
 /*
@@ -1404,10 +1499,14 @@ typedef struct CoerceToDomain
 	Expr		xpr;
 	Expr	   *arg;			/* input expression */
 	Oid			resulttype;		/* domain type ID (result type) */
-	int32		resulttypmod;	/* output typmod (currently always -1) */
-	Oid			resultcollid;	/* OID of collation, or InvalidOid if none */
-	CoercionForm coercionformat;	/* how to display this node */
-	int			location;		/* token location, or -1 if unknown */
+	int32		resulttypmod pg_node_attr(jumble_ignore);	/* output typmod
+															 * (currently always -1) */
+	Oid			resultcollid pg_node_attr(jumble_ignore);	/* OID of collation, or
+															 * InvalidOid if none */
+	CoercionForm coercionformat pg_node_attr(jumble_ignore);	/* how to display this
+																 * node */
+	int			location pg_node_attr(jumble_ignore);	/* token location, or -1
+														 * if unknown */
 } CoerceToDomain;
 
 /*
@@ -1423,9 +1522,12 @@ typedef struct CoerceToDomainValue
 {
 	Expr		xpr;
 	Oid			typeId;			/* type for substituted value */
-	int32		typeMod;		/* typemod for substituted value */
-	Oid			collation;		/* collation for the substituted value */
-	int			location;		/* token location, or -1 if unknown */
+	int32		typeMod pg_node_attr(jumble_ignore);	/* typemod for
+														 * substituted value */
+	Oid			collation pg_node_attr(jumble_ignore);	/* collation for the
+														 * substituted value */
+	int			location pg_node_attr(jumble_ignore);	/* token location, or -1
+														 * if unknown */
 } CoerceToDomainValue;
 
 /*
@@ -1439,9 +1541,12 @@ typedef struct SetToDefault
 {
 	Expr		xpr;
 	Oid			typeId;			/* type for substituted value */
-	int32		typeMod;		/* typemod for substituted value */
-	Oid			collation;		/* collation for the substituted value */
-	int			location;		/* token location, or -1 if unknown */
+	int32		typeMod pg_node_attr(jumble_ignore);	/* typemod for
+														 * substituted value */
+	Oid			collation pg_node_attr(jumble_ignore);	/* collation for the
+														 * substituted value */
+	int			location pg_node_attr(jumble_ignore);	/* token location, or -1
+														 * if unknown */
 } SetToDefault;
 
 /*
@@ -1554,13 +1659,16 @@ typedef struct TargetEntry
 	Expr		xpr;
 	Expr	   *expr;			/* expression to evaluate */
 	AttrNumber	resno;			/* attribute number (see notes above) */
-	char	   *resname;		/* name of the column (could be NULL) */
+	char	   *resname pg_node_attr(jumble_ignore);	/* name of the column
+														 * (could be NULL) */
 	Index		ressortgroupref;	/* nonzero if referenced by a sort/group
 									 * clause */
-	Oid			resorigtbl;		/* OID of column's source table */
-	AttrNumber	resorigcol;		/* column's number in source table */
-	bool		resjunk;		/* set to true to eliminate the attribute from
-								 * final target list */
+	Oid			resorigtbl pg_node_attr(jumble_ignore); /* OID of column's
+														 * source table */
+	AttrNumber	resorigcol pg_node_attr(jumble_ignore); /* column's number in
+														 * source table */
+	/* set to true to eliminate the attribute from final target list */
+	bool		resjunk pg_node_attr(jumble_ignore);
 } TargetEntry;
 
 
@@ -1642,10 +1750,13 @@ typedef struct JoinExpr
 	bool		isNatural;		/* Natural join? Will need to shape table */
 	Node	   *larg;			/* left subtree */
 	Node	   *rarg;			/* right subtree */
-	List	   *usingClause;	/* USING clause, if any (list of String) */
-	Alias	   *join_using_alias;	/* alias attached to USING clause, if any */
+	List	   *usingClause pg_node_attr(jumble_ignore);	/* USING clause, if any
+															 * (list of String) */
+	Alias	   *join_using_alias pg_node_attr(jumble_ignore);	/* alias attached to
+																 * USING clause, if any */
 	Node	   *quals;			/* qualifiers on join, if any */
-	Alias	   *alias;			/* user-written alias clause, if any */
+	Alias	   *alias pg_node_attr(jumble_ignore);	/* user-written alias
+													 * clause, if any */
 	int			rtindex;		/* RT index assigned for join, or 0 */
 } JoinExpr;
 
diff --git a/src/backend/nodes/Makefile b/src/backend/nodes/Makefile
index 4368c30fdb..aac5f59feb 100644
--- a/src/backend/nodes/Makefile
+++ b/src/backend/nodes/Makefile
@@ -19,6 +19,7 @@ OBJS = \
 	copyfuncs.o \
 	equalfuncs.o \
 	extensible.o \
+	jumblefuncs.o \
 	list.o \
 	makefuncs.o \
 	multibitmapset.o \
diff --git a/src/backend/nodes/README b/src/backend/nodes/README
index 489a67eb89..e97a918e95 100644
--- a/src/backend/nodes/README
+++ b/src/backend/nodes/README
@@ -47,6 +47,7 @@ FILES IN THIS DIRECTORY (src/backend/nodes/)
     General-purpose node manipulation functions:
 	copyfuncs.c	- copy a node tree (*)
 	equalfuncs.c	- compare two node trees (*)
+	jumblefuncs.c   - compute a node tree for query jumbling (*)
 	outfuncs.c	- convert a node tree to text representation (*)
 	readfuncs.c	- convert text representation back to a node tree (*)
 	makefuncs.c	- creator functions for some common node types
diff --git a/src/backend/nodes/gen_node_support.pl b/src/backend/nodes/gen_node_support.pl
index 7212bc486f..4d9cf83230 100644
--- a/src/backend/nodes/gen_node_support.pl
+++ b/src/backend/nodes/gen_node_support.pl
@@ -119,6 +119,8 @@ my %node_type_info;
 my @no_copy;
 # node types we don't want equal support for
 my @no_equal;
+# node types we don't want jumble support for
+my @no_jumble;
 # node types we don't want read support for
 my @no_read;
 # node types we don't want read/write support for
@@ -153,12 +155,13 @@ my @extra_tags = qw(
 # This is a regular node, but we skip parsing it from its header file
 # since we won't use its internal structure here anyway.
 push @node_types, qw(List);
-# Lists are specially treated in all four support files, too.
+# Lists are specially treated in all five support files, too.
 # (Ideally we'd mark List as "special copy/equal" not "no copy/equal".
 # But until there's other use-cases for that, just hot-wire the tests
 # that would need to distinguish.)
 push @no_copy,            qw(List);
 push @no_equal,           qw(List);
+push @no_jumble,          qw(List);
 push @special_read_write, qw(List);
 
 # Nodes with custom copy/equal implementations are skipped from
@@ -330,6 +333,10 @@ foreach my $infile (@ARGV)
 							push @no_copy,  $in_struct;
 							push @no_equal, $in_struct;
 						}
+						elsif ($attr eq 'no_jumble')
+						{
+							push @no_jumble, $in_struct;
+						}
 						elsif ($attr eq 'no_read')
 						{
 							push @no_read, $in_struct;
@@ -455,6 +462,7 @@ foreach my $infile (@ARGV)
 								equal_as_scalar
 								equal_ignore
 								equal_ignore_if_zero
+								jumble_ignore
 								read_write_ignore
 								write_only_relids
 								write_only_nondefault_pathtarget
@@ -1223,6 +1231,91 @@ close $ofs;
 close $rfs;
 
 
+# jumblefuncs.c
+
+push @output_files, 'jumblefuncs.funcs.c';
+open my $jff, '>', "$output_path/jumblefuncs.funcs.c$tmpext" or die $!;
+push @output_files, 'jumblefuncs.switch.c';
+open my $jfs, '>', "$output_path/jumblefuncs.switch.c$tmpext" or die $!;
+
+printf $jff $header_comment, 'jumblefuncs.funcs.c';
+printf $jfs $header_comment, 'jumblefuncs.switch.c';
+
+print $jff $node_includes;
+
+foreach my $n (@node_types)
+{
+	next if elem $n, @abstract_types;
+	next if elem $n, @nodetag_only;
+	my $struct_no_jumble = (elem $n, @no_jumble);
+
+	print $jfs "\t\t\tcase T_${n}:\n"
+	  . "\t\t\t\t_jumble${n}(jstate, expr);\n"
+	  . "\t\t\t\tbreak;\n"
+	  unless $struct_no_jumble;
+
+	print $jff "
+static void
+_jumble${n}(JumbleState *jstate, Node *node)
+{
+\t${n} *expr = (${n} *) node;\n
+" unless $struct_no_jumble;
+
+	# print instructions for each field
+	foreach my $f (@{ $node_type_info{$n}->{fields} })
+	{
+		my $t             = $node_type_info{$n}->{field_types}{$f};
+		my @a             = @{ $node_type_info{$n}->{field_attrs}{$f} };
+		my $jumble_ignore = $struct_no_jumble;
+
+		# extract per-field attributes
+		foreach my $a (@a)
+		{
+			if ($a eq 'jumble_ignore')
+			{
+				$jumble_ignore = 1;
+			}
+		}
+
+		# node type
+		if (($t =~ /^(\w+)\*$/ or $t =~ /^struct\s+(\w+)\*$/)
+			and elem $1, @node_types)
+		{
+			print $jff "\tJUMBLE_NODE($f);\n"
+			  unless $jumble_ignore;
+		}
+		elsif ($t eq 'int' && $f =~ 'location$')
+		{
+			print $jff "\tJUMBLE_LOCATION($f);\n"
+			  unless $jumble_ignore;
+		}
+		elsif ($t eq 'char*')
+		{
+			print $jff "\tJUMBLE_STRING($f);\n"
+			  unless $jumble_ignore;
+		}
+		else
+		{
+			print $jff "\tJUMBLE_FIELD($f);\n"
+			  unless $jumble_ignore;
+		}
+	}
+
+	# Some nodes have no attributes like CheckPointStmt,
+	# so tweak things for empty contents.
+	if (scalar(@{ $node_type_info{$n}->{fields} }) == 0)
+	{
+		print $jff "\t(void) expr;\n"
+		  unless $struct_no_jumble;
+	}
+
+	print $jff "}
+" unless $struct_no_jumble;
+}
+
+close $jff;
+close $jfs;
+
 # now rename the temporary files to their final names
 foreach my $file (@output_files)
 {
diff --git a/src/backend/nodes/jumblefuncs.c b/src/backend/nodes/jumblefuncs.c
new file mode 100644
index 0000000000..b65e022d17
--- /dev/null
+++ b/src/backend/nodes/jumblefuncs.c
@@ -0,0 +1,358 @@
+/*-------------------------------------------------------------------------
+ *
+ * jumblefuncs.c
+ *	 Query normalization and fingerprinting.
+ *
+ * Normalization is a process whereby similar queries, typically differing only
+ * in their constants (though the exact rules are somewhat more subtle than
+ * that) are recognized as equivalent, and are tracked as a single entry.  This
+ * is particularly useful for non-prepared queries.
+ *
+ * Normalization is implemented by fingerprinting queries, selectively
+ * serializing those fields of each query tree's nodes that are judged to be
+ * essential to the query.  This is referred to as a query jumble.  This is
+ * distinct from a regular serialization in that various extraneous
+ * information is ignored as irrelevant or not essential to the query, such
+ * as the collations of Vars and, most notably, the values of constants.
+ *
+ * This jumble is acquired at the end of parse analysis of each query, and
+ * a 64-bit hash of it is stored into the query's Query.queryId field.
+ * The server then copies this value around, making it available in plan
+ * tree(s) generated from the query.  The executor can then use this value
+ * to blame query costs on the proper queryId.
+ *
+ * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/nodes/jumblefuncs.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "common/hashfn.h"
+#include "miscadmin.h"
+#include "parser/scansup.h"
+#include "utils/queryjumble.h"
+
+#define JUMBLE_SIZE				1024	/* query serialization buffer size */
+
+/* GUC parameters */
+int			compute_query_id = COMPUTE_QUERY_ID_AUTO;
+
+/* True when compute_query_id is ON, or AUTO and a module requests them */
+bool		query_id_enabled = false;
+
+static void AppendJumble(JumbleState *jstate,
+						 const unsigned char *item, Size size);
+static void RecordConstLocation(JumbleState *jstate, int location);
+static void _jumbleNode(JumbleState *jstate, Node *node);
+static void _jumbleList(JumbleState *jstate, Node *node);
+static void _jumbleRangeTblEntry(JumbleState *jstate, Node *node);
+
+/*
+ * Given a possibly multi-statement source string, confine our attention to the
+ * relevant part of the string.
+ */
+const char *
+CleanQuerytext(const char *query, int *location, int *len)
+{
+	int			query_location = *location;
+	int			query_len = *len;
+
+	/* First apply starting offset, unless it's -1 (unknown). */
+	if (query_location >= 0)
+	{
+		Assert(query_location <= strlen(query));
+		query += query_location;
+		/* Length of 0 (or -1) means "rest of string" */
+		if (query_len <= 0)
+			query_len = strlen(query);
+		else
+			Assert(query_len <= strlen(query));
+	}
+	else
+	{
+		/* If query location is unknown, distrust query_len as well */
+		query_location = 0;
+		query_len = strlen(query);
+	}
+
+	/*
+	 * Discard leading and trailing whitespace, too.  Use scanner_isspace()
+	 * not libc's isspace(), because we want to match the lexer's behavior.
+	 */
+	while (query_len > 0 && scanner_isspace(query[0]))
+		query++, query_location++, query_len--;
+	while (query_len > 0 && scanner_isspace(query[query_len - 1]))
+		query_len--;
+
+	*location = query_location;
+	*len = query_len;
+
+	return query;
+}
+
+JumbleState *
+JumbleQuery(Query *query, const char *querytext)
+{
+	JumbleState *jstate = NULL;
+
+	Assert(IsQueryIdEnabled());
+
+	jstate = (JumbleState *) palloc(sizeof(JumbleState));
+
+	/* Set up workspace for query jumbling */
+	jstate->jumble = (unsigned char *) palloc(JUMBLE_SIZE);
+	jstate->jumble_len = 0;
+	jstate->clocations_buf_size = 32;
+	jstate->clocations = (LocationLen *)
+		palloc(jstate->clocations_buf_size * sizeof(LocationLen));
+	jstate->clocations_count = 0;
+	jstate->highest_extern_param_id = 0;
+
+	/* Compute query ID and mark the Query node with it */
+	_jumbleNode(jstate, (Node *) query);
+	query->queryId = DatumGetUInt64(hash_any_extended(jstate->jumble,
+													  jstate->jumble_len,
+													  0));
+
+	/*
+	 * If we are unlucky enough to get a hash of zero, use 1 instead, to
+	 * prevent confusion with the utility-statement case.
+	 */
+	if (query->queryId == UINT64CONST(0))
+		query->queryId = UINT64CONST(1);
+
+	return jstate;
+}
+
+/*
+ * Enables query identifier computation.
+ *
+ * Third-party plugins can use this function to inform core that they require
+ * a query identifier to be computed.
+ */
+void
+EnableQueryId(void)
+{
+	if (compute_query_id != COMPUTE_QUERY_ID_OFF)
+		query_id_enabled = true;
+}
+
+/*
+ * AppendJumble: Append a value that is substantive in a given query to
+ * the current jumble.
+ */
+static void
+AppendJumble(JumbleState *jstate, const unsigned char *item, Size size)
+{
+	unsigned char *jumble = jstate->jumble;
+	Size		jumble_len = jstate->jumble_len;
+
+	/*
+	 * Whenever the jumble buffer is full, we hash the current contents and
+	 * reset the buffer to contain just that hash value, thus relying on the
+	 * hash to summarize everything so far.
+	 */
+	while (size > 0)
+	{
+		Size		part_size;
+
+		if (jumble_len >= JUMBLE_SIZE)
+		{
+			uint64		start_hash;
+
+			start_hash = DatumGetUInt64(hash_any_extended(jumble,
+														  JUMBLE_SIZE, 0));
+			memcpy(jumble, &start_hash, sizeof(start_hash));
+			jumble_len = sizeof(start_hash);
+		}
+		part_size = Min(size, JUMBLE_SIZE - jumble_len);
+		memcpy(jumble + jumble_len, item, part_size);
+		jumble_len += part_size;
+		item += part_size;
+		size -= part_size;
+	}
+	jstate->jumble_len = jumble_len;
+}
+
+/*
+ * Record location of constant within query string of query tree
+ * that is currently being walked.
+ */
+static void
+RecordConstLocation(JumbleState *jstate, int location)
+{
+	/* -1 indicates unknown or undefined location */
+	if (location >= 0)
+	{
+		/* enlarge array if needed */
+		if (jstate->clocations_count >= jstate->clocations_buf_size)
+		{
+			jstate->clocations_buf_size *= 2;
+			jstate->clocations = (LocationLen *)
+				repalloc(jstate->clocations,
+						 jstate->clocations_buf_size *
+						 sizeof(LocationLen));
+		}
+		jstate->clocations[jstate->clocations_count].location = location;
+		/* initialize lengths to -1 to simplify third-party module usage */
+		jstate->clocations[jstate->clocations_count].length = -1;
+		jstate->clocations_count++;
+	}
+}
+
+#define JUMBLE_NODE(item) \
+	_jumbleNode(jstate, (Node *) expr->item)
+#define JUMBLE_LOCATION(location) \
+	RecordConstLocation(jstate, expr->location)
+#define JUMBLE_FIELD(item) \
+	AppendJumble(jstate, (const unsigned char *) &(expr->item), sizeof(expr->item))
+#define JUMBLE_FIELD_SINGLE(item) \
+	AppendJumble(jstate, (const unsigned char *) &(item), sizeof(item))
+#define JUMBLE_STRING(str) \
+do { \
+	if (expr->str) \
+		AppendJumble(jstate, (const unsigned char *) (expr->str), strlen(expr->str) + 1); \
+} while(0)
+
+#include "jumblefuncs.funcs.c"
+
+static void
+_jumbleNode(JumbleState *jstate, Node *node)
+{
+	Node	   *expr = node;
+
+	if (expr == NULL)
+		return;
+
+	/* Guard against stack overflow due to overly complex expressions */
+	check_stack_depth();
+
+	/*
+	 * We always emit the node's NodeTag, then any additional fields that are
+	 * considered significant, and then we recurse to any child nodes.
+	 */
+	JUMBLE_FIELD(type);
+
+	switch (nodeTag(expr))
+	{
+#include "jumblefuncs.switch.c"
+
+		case T_List:
+		case T_IntList:
+		case T_OidList:
+		case T_XidList:
+			_jumbleList(jstate, expr);
+			break;
+
+		case T_RangeTblEntry:
+			_jumbleRangeTblEntry(jstate, expr);
+			break;
+
+		default:
+			/* Only a warning, since we can stumble along anyway */
+			elog(WARNING, "unrecognized node type: %d",
+				 (int) nodeTag(expr));
+			break;
+	}
+
+	/* Special cases */
+	switch (nodeTag(expr))
+	{
+		case T_Param:
+			{
+				Param	   *p = (Param *) node;
+
+				/* Also, track the highest external Param id */
+				if (p->paramkind == PARAM_EXTERN &&
+					p->paramid > jstate->highest_extern_param_id)
+					jstate->highest_extern_param_id = p->paramid;
+			}
+			break;
+		default:
+			break;
+	}
+}
+
+static void
+_jumbleList(JumbleState *jstate, Node *node)
+{
+	List	   *expr = (List *) node;
+	ListCell   *l;
+
+	switch (expr->type)
+	{
+		case T_List:
+			foreach(l, expr)
+				_jumbleNode(jstate, lfirst(l));
+			break;
+		case T_IntList:
+			foreach(l, expr)
+				JUMBLE_FIELD_SINGLE(lfirst_int(l));
+			break;
+		case T_OidList:
+			foreach(l, expr)
+				JUMBLE_FIELD_SINGLE(lfirst_oid(l));
+			break;
+		case T_XidList:
+			foreach(l, expr)
+				JUMBLE_FIELD_SINGLE(lfirst_xid(l));
+			break;
+		default:
+			elog(ERROR, "unrecognized list node type: %d",
+				 (int) expr->type);
+			return;
+	}
+}
+
+static void
+_jumbleRangeTblEntry(JumbleState *jstate, Node *node)
+{
+	RangeTblEntry *expr = (RangeTblEntry *) node;
+
+	JUMBLE_FIELD(rtekind);
+	switch (expr->rtekind)
+	{
+		case RTE_RELATION:
+			JUMBLE_FIELD(relid);
+			JUMBLE_NODE(tablesample);
+			JUMBLE_FIELD(inh);
+			break;
+		case RTE_SUBQUERY:
+			JUMBLE_NODE(subquery);
+			break;
+		case RTE_JOIN:
+			JUMBLE_FIELD(jointype);
+			break;
+		case RTE_FUNCTION:
+			JUMBLE_NODE(functions);
+			break;
+		case RTE_TABLEFUNC:
+			JUMBLE_NODE(tablefunc);
+			break;
+		case RTE_VALUES:
+			JUMBLE_NODE(values_lists);
+			break;
+		case RTE_CTE:
+
+			/*
+			 * Depending on the CTE name here isn't ideal, but it's the only
+			 * info we have to identify the referenced WITH item.
+			 */
+			JUMBLE_STRING(ctename);
+			JUMBLE_FIELD(ctelevelsup);
+			break;
+		case RTE_NAMEDTUPLESTORE:
+			JUMBLE_STRING(enrname);
+			break;
+		case RTE_RESULT:
+			break;
+		default:
+			elog(ERROR, "unrecognized RTE kind: %d", (int) expr->rtekind);
+			break;
+	}
+}
diff --git a/src/backend/nodes/meson.build b/src/backend/nodes/meson.build
index c4f3897ef2..323a1f07d0 100644
--- a/src/backend/nodes/meson.build
+++ b/src/backend/nodes/meson.build
@@ -18,6 +18,7 @@ backend_sources += files(
 nodefunc_sources = files(
   'copyfuncs.c',
   'equalfuncs.c',
+  'jumblefuncs.c',
   'outfuncs.c',
   'readfuncs.c',
 )
diff --git a/src/backend/utils/misc/Makefile b/src/backend/utils/misc/Makefile
index b9ee4eb48a..2910032930 100644
--- a/src/backend/utils/misc/Makefile
+++ b/src/backend/utils/misc/Makefile
@@ -26,7 +26,6 @@ OBJS = \
 	pg_rusage.o \
 	ps_status.o \
 	queryenvironment.o \
-	queryjumble.o \
 	rls.o \
 	sampling.o \
 	superuser.o \
diff --git a/src/backend/utils/misc/meson.build b/src/backend/utils/misc/meson.build
index e7a9730229..6caa2f5a2c 100644
--- a/src/backend/utils/misc/meson.build
+++ b/src/backend/utils/misc/meson.build
@@ -9,7 +9,6 @@ backend_sources += files(
   'pg_rusage.c',
   'ps_status.c',
   'queryenvironment.c',
-  'queryjumble.c',
   'rls.c',
   'sampling.c',
   'superuser.c',
diff --git a/src/backend/utils/misc/queryjumble.c b/src/backend/utils/misc/queryjumble.c
deleted file mode 100644
index 0ace74de78..0000000000
--- a/src/backend/utils/misc/queryjumble.c
+++ /dev/null
@@ -1,861 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * queryjumble.c
- *	 Query normalization and fingerprinting.
- *
- * Normalization is a process whereby similar queries, typically differing only
- * in their constants (though the exact rules are somewhat more subtle than
- * that) are recognized as equivalent, and are tracked as a single entry.  This
- * is particularly useful for non-prepared queries.
- *
- * Normalization is implemented by fingerprinting queries, selectively
- * serializing those fields of each query tree's nodes that are judged to be
- * essential to the query.  This is referred to as a query jumble.  This is
- * distinct from a regular serialization in that various extraneous
- * information is ignored as irrelevant or not essential to the query, such
- * as the collations of Vars and, most notably, the values of constants.
- *
- * This jumble is acquired at the end of parse analysis of each query, and
- * a 64-bit hash of it is stored into the query's Query.queryId field.
- * The server then copies this value around, making it available in plan
- * tree(s) generated from the query.  The executor can then use this value
- * to blame query costs on the proper queryId.
- *
- * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- *
- * IDENTIFICATION
- *	  src/backend/utils/misc/queryjumble.c
- *
- *-------------------------------------------------------------------------
- */
-#include "postgres.h"
-
-#include "common/hashfn.h"
-#include "miscadmin.h"
-#include "parser/scansup.h"
-#include "utils/queryjumble.h"
-
-#define JUMBLE_SIZE				1024	/* query serialization buffer size */
-
-/* GUC parameters */
-int			compute_query_id = COMPUTE_QUERY_ID_AUTO;
-
-/* True when compute_query_id is ON, or AUTO and a module requests them */
-bool		query_id_enabled = false;
-
-static uint64 compute_utility_query_id(const char *query_text,
-									   int query_location, int query_len);
-static void AppendJumble(JumbleState *jstate,
-						 const unsigned char *item, Size size);
-static void JumbleQueryInternal(JumbleState *jstate, Query *query);
-static void JumbleRangeTable(JumbleState *jstate, List *rtable);
-static void JumbleRowMarks(JumbleState *jstate, List *rowMarks);
-static void JumbleExpr(JumbleState *jstate, Node *node);
-static void RecordConstLocation(JumbleState *jstate, int location);
-
-/*
- * Given a possibly multi-statement source string, confine our attention to the
- * relevant part of the string.
- */
-const char *
-CleanQuerytext(const char *query, int *location, int *len)
-{
-	int			query_location = *location;
-	int			query_len = *len;
-
-	/* First apply starting offset, unless it's -1 (unknown). */
-	if (query_location >= 0)
-	{
-		Assert(query_location <= strlen(query));
-		query += query_location;
-		/* Length of 0 (or -1) means "rest of string" */
-		if (query_len <= 0)
-			query_len = strlen(query);
-		else
-			Assert(query_len <= strlen(query));
-	}
-	else
-	{
-		/* If query location is unknown, distrust query_len as well */
-		query_location = 0;
-		query_len = strlen(query);
-	}
-
-	/*
-	 * Discard leading and trailing whitespace, too.  Use scanner_isspace()
-	 * not libc's isspace(), because we want to match the lexer's behavior.
-	 */
-	while (query_len > 0 && scanner_isspace(query[0]))
-		query++, query_location++, query_len--;
-	while (query_len > 0 && scanner_isspace(query[query_len - 1]))
-		query_len--;
-
-	*location = query_location;
-	*len = query_len;
-
-	return query;
-}
-
-JumbleState *
-JumbleQuery(Query *query, const char *querytext)
-{
-	JumbleState *jstate = NULL;
-
-	Assert(IsQueryIdEnabled());
-
-	if (query->utilityStmt)
-	{
-		query->queryId = compute_utility_query_id(querytext,
-												  query->stmt_location,
-												  query->stmt_len);
-	}
-	else
-	{
-		jstate = (JumbleState *) palloc(sizeof(JumbleState));
-
-		/* Set up workspace for query jumbling */
-		jstate->jumble = (unsigned char *) palloc(JUMBLE_SIZE);
-		jstate->jumble_len = 0;
-		jstate->clocations_buf_size = 32;
-		jstate->clocations = (LocationLen *)
-			palloc(jstate->clocations_buf_size * sizeof(LocationLen));
-		jstate->clocations_count = 0;
-		jstate->highest_extern_param_id = 0;
-
-		/* Compute query ID and mark the Query node with it */
-		JumbleQueryInternal(jstate, query);
-		query->queryId = DatumGetUInt64(hash_any_extended(jstate->jumble,
-														  jstate->jumble_len,
-														  0));
-
-		/*
-		 * If we are unlucky enough to get a hash of zero, use 1 instead, to
-		 * prevent confusion with the utility-statement case.
-		 */
-		if (query->queryId == UINT64CONST(0))
-			query->queryId = UINT64CONST(1);
-	}
-
-	return jstate;
-}
-
-/*
- * Enables query identifier computation.
- *
- * Third-party plugins can use this function to inform core that they require
- * a query identifier to be computed.
- */
-void
-EnableQueryId(void)
-{
-	if (compute_query_id != COMPUTE_QUERY_ID_OFF)
-		query_id_enabled = true;
-}
-
-/*
- * Compute a query identifier for the given utility query string.
- */
-static uint64
-compute_utility_query_id(const char *query_text, int query_location, int query_len)
-{
-	uint64		queryId;
-	const char *sql;
-
-	/*
-	 * Confine our attention to the relevant part of the string, if the query
-	 * is a portion of a multi-statement source string.
-	 */
-	sql = CleanQuerytext(query_text, &query_location, &query_len);
-
-	queryId = DatumGetUInt64(hash_any_extended((const unsigned char *) sql,
-											   query_len, 0));
-
-	/*
-	 * If we are unlucky enough to get a hash of zero(invalid), use queryID as
-	 * 2 instead, queryID 1 is already in use for normal statements.
-	 */
-	if (queryId == UINT64CONST(0))
-		queryId = UINT64CONST(2);
-
-	return queryId;
-}
-
-/*
- * AppendJumble: Append a value that is substantive in a given query to
- * the current jumble.
- */
-static void
-AppendJumble(JumbleState *jstate, const unsigned char *item, Size size)
-{
-	unsigned char *jumble = jstate->jumble;
-	Size		jumble_len = jstate->jumble_len;
-
-	/*
-	 * Whenever the jumble buffer is full, we hash the current contents and
-	 * reset the buffer to contain just that hash value, thus relying on the
-	 * hash to summarize everything so far.
-	 */
-	while (size > 0)
-	{
-		Size		part_size;
-
-		if (jumble_len >= JUMBLE_SIZE)
-		{
-			uint64		start_hash;
-
-			start_hash = DatumGetUInt64(hash_any_extended(jumble,
-														  JUMBLE_SIZE, 0));
-			memcpy(jumble, &start_hash, sizeof(start_hash));
-			jumble_len = sizeof(start_hash);
-		}
-		part_size = Min(size, JUMBLE_SIZE - jumble_len);
-		memcpy(jumble + jumble_len, item, part_size);
-		jumble_len += part_size;
-		item += part_size;
-		size -= part_size;
-	}
-	jstate->jumble_len = jumble_len;
-}
-
-/*
- * Wrappers around AppendJumble to encapsulate details of serialization
- * of individual local variable elements.
- */
-#define APP_JUMB(item) \
-	AppendJumble(jstate, (const unsigned char *) &(item), sizeof(item))
-#define APP_JUMB_STRING(str) \
-	AppendJumble(jstate, (const unsigned char *) (str), strlen(str) + 1)
-
-/*
- * JumbleQueryInternal: Selectively serialize the query tree, appending
- * significant data to the "query jumble" while ignoring nonsignificant data.
- *
- * Rule of thumb for what to include is that we should ignore anything not
- * semantically significant (such as alias names) as well as anything that can
- * be deduced from child nodes (else we'd just be double-hashing that piece
- * of information).
- */
-static void
-JumbleQueryInternal(JumbleState *jstate, Query *query)
-{
-	Assert(IsA(query, Query));
-	Assert(query->utilityStmt == NULL);
-
-	APP_JUMB(query->commandType);
-	/* resultRelation is usually predictable from commandType */
-	JumbleExpr(jstate, (Node *) query->cteList);
-	JumbleRangeTable(jstate, query->rtable);
-	JumbleExpr(jstate, (Node *) query->jointree);
-	JumbleExpr(jstate, (Node *) query->mergeActionList);
-	JumbleExpr(jstate, (Node *) query->targetList);
-	JumbleExpr(jstate, (Node *) query->onConflict);
-	JumbleExpr(jstate, (Node *) query->returningList);
-	JumbleExpr(jstate, (Node *) query->groupClause);
-	APP_JUMB(query->groupDistinct);
-	JumbleExpr(jstate, (Node *) query->groupingSets);
-	JumbleExpr(jstate, query->havingQual);
-	JumbleExpr(jstate, (Node *) query->windowClause);
-	JumbleExpr(jstate, (Node *) query->distinctClause);
-	JumbleExpr(jstate, (Node *) query->sortClause);
-	JumbleExpr(jstate, query->limitOffset);
-	JumbleExpr(jstate, query->limitCount);
-	APP_JUMB(query->limitOption);
-	JumbleRowMarks(jstate, query->rowMarks);
-	JumbleExpr(jstate, query->setOperations);
-}
-
-/*
- * Jumble a range table
- */
-static void
-JumbleRangeTable(JumbleState *jstate, List *rtable)
-{
-	ListCell   *lc;
-
-	foreach(lc, rtable)
-	{
-		RangeTblEntry *rte = lfirst_node(RangeTblEntry, lc);
-
-		APP_JUMB(rte->rtekind);
-		switch (rte->rtekind)
-		{
-			case RTE_RELATION:
-				APP_JUMB(rte->relid);
-				JumbleExpr(jstate, (Node *) rte->tablesample);
-				APP_JUMB(rte->inh);
-				break;
-			case RTE_SUBQUERY:
-				JumbleQueryInternal(jstate, rte->subquery);
-				break;
-			case RTE_JOIN:
-				APP_JUMB(rte->jointype);
-				break;
-			case RTE_FUNCTION:
-				JumbleExpr(jstate, (Node *) rte->functions);
-				break;
-			case RTE_TABLEFUNC:
-				JumbleExpr(jstate, (Node *) rte->tablefunc);
-				break;
-			case RTE_VALUES:
-				JumbleExpr(jstate, (Node *) rte->values_lists);
-				break;
-			case RTE_CTE:
-
-				/*
-				 * Depending on the CTE name here isn't ideal, but it's the
-				 * only info we have to identify the referenced WITH item.
-				 */
-				APP_JUMB_STRING(rte->ctename);
-				APP_JUMB(rte->ctelevelsup);
-				break;
-			case RTE_NAMEDTUPLESTORE:
-				APP_JUMB_STRING(rte->enrname);
-				break;
-			case RTE_RESULT:
-				break;
-			default:
-				elog(ERROR, "unrecognized RTE kind: %d", (int) rte->rtekind);
-				break;
-		}
-	}
-}
-
-/*
- * Jumble a rowMarks list
- */
-static void
-JumbleRowMarks(JumbleState *jstate, List *rowMarks)
-{
-	ListCell   *lc;
-
-	foreach(lc, rowMarks)
-	{
-		RowMarkClause *rowmark = lfirst_node(RowMarkClause, lc);
-
-		if (!rowmark->pushedDown)
-		{
-			APP_JUMB(rowmark->rti);
-			APP_JUMB(rowmark->strength);
-			APP_JUMB(rowmark->waitPolicy);
-		}
-	}
-}
-
-/*
- * Jumble an expression tree
- *
- * In general this function should handle all the same node types that
- * expression_tree_walker() does, and therefore it's coded to be as parallel
- * to that function as possible.  However, since we are only invoked on
- * queries immediately post-parse-analysis, we need not handle node types
- * that only appear in planning.
- *
- * Note: the reason we don't simply use expression_tree_walker() is that the
- * point of that function is to support tree walkers that don't care about
- * most tree node types, but here we care about all types.  We should complain
- * about any unrecognized node type.
- */
-static void
-JumbleExpr(JumbleState *jstate, Node *node)
-{
-	ListCell   *temp;
-
-	if (node == NULL)
-		return;
-
-	/* Guard against stack overflow due to overly complex expressions */
-	check_stack_depth();
-
-	/*
-	 * We always emit the node's NodeTag, then any additional fields that are
-	 * considered significant, and then we recurse to any child nodes.
-	 */
-	APP_JUMB(node->type);
-
-	switch (nodeTag(node))
-	{
-		case T_Var:
-			{
-				Var		   *var = (Var *) node;
-
-				APP_JUMB(var->varno);
-				APP_JUMB(var->varattno);
-				APP_JUMB(var->varlevelsup);
-			}
-			break;
-		case T_Const:
-			{
-				Const	   *c = (Const *) node;
-
-				/* We jumble only the constant's type, not its value */
-				APP_JUMB(c->consttype);
-				/* Also, record its parse location for query normalization */
-				RecordConstLocation(jstate, c->location);
-			}
-			break;
-		case T_Param:
-			{
-				Param	   *p = (Param *) node;
-
-				APP_JUMB(p->paramkind);
-				APP_JUMB(p->paramid);
-				APP_JUMB(p->paramtype);
-				/* Also, track the highest external Param id */
-				if (p->paramkind == PARAM_EXTERN &&
-					p->paramid > jstate->highest_extern_param_id)
-					jstate->highest_extern_param_id = p->paramid;
-			}
-			break;
-		case T_Aggref:
-			{
-				Aggref	   *expr = (Aggref *) node;
-
-				APP_JUMB(expr->aggfnoid);
-				JumbleExpr(jstate, (Node *) expr->aggdirectargs);
-				JumbleExpr(jstate, (Node *) expr->args);
-				JumbleExpr(jstate, (Node *) expr->aggorder);
-				JumbleExpr(jstate, (Node *) expr->aggdistinct);
-				JumbleExpr(jstate, (Node *) expr->aggfilter);
-			}
-			break;
-		case T_GroupingFunc:
-			{
-				GroupingFunc *grpnode = (GroupingFunc *) node;
-
-				JumbleExpr(jstate, (Node *) grpnode->refs);
-				APP_JUMB(grpnode->agglevelsup);
-			}
-			break;
-		case T_WindowFunc:
-			{
-				WindowFunc *expr = (WindowFunc *) node;
-
-				APP_JUMB(expr->winfnoid);
-				APP_JUMB(expr->winref);
-				JumbleExpr(jstate, (Node *) expr->args);
-				JumbleExpr(jstate, (Node *) expr->aggfilter);
-			}
-			break;
-		case T_SubscriptingRef:
-			{
-				SubscriptingRef *sbsref = (SubscriptingRef *) node;
-
-				JumbleExpr(jstate, (Node *) sbsref->refupperindexpr);
-				JumbleExpr(jstate, (Node *) sbsref->reflowerindexpr);
-				JumbleExpr(jstate, (Node *) sbsref->refexpr);
-				JumbleExpr(jstate, (Node *) sbsref->refassgnexpr);
-			}
-			break;
-		case T_FuncExpr:
-			{
-				FuncExpr   *expr = (FuncExpr *) node;
-
-				APP_JUMB(expr->funcid);
-				JumbleExpr(jstate, (Node *) expr->args);
-			}
-			break;
-		case T_NamedArgExpr:
-			{
-				NamedArgExpr *nae = (NamedArgExpr *) node;
-
-				APP_JUMB(nae->argnumber);
-				JumbleExpr(jstate, (Node *) nae->arg);
-			}
-			break;
-		case T_OpExpr:
-		case T_DistinctExpr:	/* struct-equivalent to OpExpr */
-		case T_NullIfExpr:		/* struct-equivalent to OpExpr */
-			{
-				OpExpr	   *expr = (OpExpr *) node;
-
-				APP_JUMB(expr->opno);
-				JumbleExpr(jstate, (Node *) expr->args);
-			}
-			break;
-		case T_ScalarArrayOpExpr:
-			{
-				ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
-
-				APP_JUMB(expr->opno);
-				APP_JUMB(expr->useOr);
-				JumbleExpr(jstate, (Node *) expr->args);
-			}
-			break;
-		case T_BoolExpr:
-			{
-				BoolExpr   *expr = (BoolExpr *) node;
-
-				APP_JUMB(expr->boolop);
-				JumbleExpr(jstate, (Node *) expr->args);
-			}
-			break;
-		case T_SubLink:
-			{
-				SubLink    *sublink = (SubLink *) node;
-
-				APP_JUMB(sublink->subLinkType);
-				APP_JUMB(sublink->subLinkId);
-				JumbleExpr(jstate, (Node *) sublink->testexpr);
-				JumbleQueryInternal(jstate, castNode(Query, sublink->subselect));
-			}
-			break;
-		case T_FieldSelect:
-			{
-				FieldSelect *fs = (FieldSelect *) node;
-
-				APP_JUMB(fs->fieldnum);
-				JumbleExpr(jstate, (Node *) fs->arg);
-			}
-			break;
-		case T_FieldStore:
-			{
-				FieldStore *fstore = (FieldStore *) node;
-
-				JumbleExpr(jstate, (Node *) fstore->arg);
-				JumbleExpr(jstate, (Node *) fstore->newvals);
-			}
-			break;
-		case T_RelabelType:
-			{
-				RelabelType *rt = (RelabelType *) node;
-
-				APP_JUMB(rt->resulttype);
-				JumbleExpr(jstate, (Node *) rt->arg);
-			}
-			break;
-		case T_CoerceViaIO:
-			{
-				CoerceViaIO *cio = (CoerceViaIO *) node;
-
-				APP_JUMB(cio->resulttype);
-				JumbleExpr(jstate, (Node *) cio->arg);
-			}
-			break;
-		case T_ArrayCoerceExpr:
-			{
-				ArrayCoerceExpr *acexpr = (ArrayCoerceExpr *) node;
-
-				APP_JUMB(acexpr->resulttype);
-				JumbleExpr(jstate, (Node *) acexpr->arg);
-				JumbleExpr(jstate, (Node *) acexpr->elemexpr);
-			}
-			break;
-		case T_ConvertRowtypeExpr:
-			{
-				ConvertRowtypeExpr *crexpr = (ConvertRowtypeExpr *) node;
-
-				APP_JUMB(crexpr->resulttype);
-				JumbleExpr(jstate, (Node *) crexpr->arg);
-			}
-			break;
-		case T_CollateExpr:
-			{
-				CollateExpr *ce = (CollateExpr *) node;
-
-				APP_JUMB(ce->collOid);
-				JumbleExpr(jstate, (Node *) ce->arg);
-			}
-			break;
-		case T_CaseExpr:
-			{
-				CaseExpr   *caseexpr = (CaseExpr *) node;
-
-				JumbleExpr(jstate, (Node *) caseexpr->arg);
-				foreach(temp, caseexpr->args)
-				{
-					CaseWhen   *when = lfirst_node(CaseWhen, temp);
-
-					JumbleExpr(jstate, (Node *) when->expr);
-					JumbleExpr(jstate, (Node *) when->result);
-				}
-				JumbleExpr(jstate, (Node *) caseexpr->defresult);
-			}
-			break;
-		case T_CaseTestExpr:
-			{
-				CaseTestExpr *ct = (CaseTestExpr *) node;
-
-				APP_JUMB(ct->typeId);
-			}
-			break;
-		case T_ArrayExpr:
-			JumbleExpr(jstate, (Node *) ((ArrayExpr *) node)->elements);
-			break;
-		case T_RowExpr:
-			JumbleExpr(jstate, (Node *) ((RowExpr *) node)->args);
-			break;
-		case T_RowCompareExpr:
-			{
-				RowCompareExpr *rcexpr = (RowCompareExpr *) node;
-
-				APP_JUMB(rcexpr->rctype);
-				JumbleExpr(jstate, (Node *) rcexpr->largs);
-				JumbleExpr(jstate, (Node *) rcexpr->rargs);
-			}
-			break;
-		case T_CoalesceExpr:
-			JumbleExpr(jstate, (Node *) ((CoalesceExpr *) node)->args);
-			break;
-		case T_MinMaxExpr:
-			{
-				MinMaxExpr *mmexpr = (MinMaxExpr *) node;
-
-				APP_JUMB(mmexpr->op);
-				JumbleExpr(jstate, (Node *) mmexpr->args);
-			}
-			break;
-		case T_XmlExpr:
-			{
-				XmlExpr    *xexpr = (XmlExpr *) node;
-
-				APP_JUMB(xexpr->op);
-				JumbleExpr(jstate, (Node *) xexpr->named_args);
-				JumbleExpr(jstate, (Node *) xexpr->args);
-			}
-			break;
-		case T_NullTest:
-			{
-				NullTest   *nt = (NullTest *) node;
-
-				APP_JUMB(nt->nulltesttype);
-				JumbleExpr(jstate, (Node *) nt->arg);
-			}
-			break;
-		case T_BooleanTest:
-			{
-				BooleanTest *bt = (BooleanTest *) node;
-
-				APP_JUMB(bt->booltesttype);
-				JumbleExpr(jstate, (Node *) bt->arg);
-			}
-			break;
-		case T_CoerceToDomain:
-			{
-				CoerceToDomain *cd = (CoerceToDomain *) node;
-
-				APP_JUMB(cd->resulttype);
-				JumbleExpr(jstate, (Node *) cd->arg);
-			}
-			break;
-		case T_CoerceToDomainValue:
-			{
-				CoerceToDomainValue *cdv = (CoerceToDomainValue *) node;
-
-				APP_JUMB(cdv->typeId);
-			}
-			break;
-		case T_SetToDefault:
-			{
-				SetToDefault *sd = (SetToDefault *) node;
-
-				APP_JUMB(sd->typeId);
-			}
-			break;
-		case T_CurrentOfExpr:
-			{
-				CurrentOfExpr *ce = (CurrentOfExpr *) node;
-
-				APP_JUMB(ce->cvarno);
-				if (ce->cursor_name)
-					APP_JUMB_STRING(ce->cursor_name);
-				APP_JUMB(ce->cursor_param);
-			}
-			break;
-		case T_NextValueExpr:
-			{
-				NextValueExpr *nve = (NextValueExpr *) node;
-
-				APP_JUMB(nve->seqid);
-				APP_JUMB(nve->typeId);
-			}
-			break;
-		case T_InferenceElem:
-			{
-				InferenceElem *ie = (InferenceElem *) node;
-
-				APP_JUMB(ie->infercollid);
-				APP_JUMB(ie->inferopclass);
-				JumbleExpr(jstate, ie->expr);
-			}
-			break;
-		case T_TargetEntry:
-			{
-				TargetEntry *tle = (TargetEntry *) node;
-
-				APP_JUMB(tle->resno);
-				APP_JUMB(tle->ressortgroupref);
-				JumbleExpr(jstate, (Node *) tle->expr);
-			}
-			break;
-		case T_RangeTblRef:
-			{
-				RangeTblRef *rtr = (RangeTblRef *) node;
-
-				APP_JUMB(rtr->rtindex);
-			}
-			break;
-		case T_JoinExpr:
-			{
-				JoinExpr   *join = (JoinExpr *) node;
-
-				APP_JUMB(join->jointype);
-				APP_JUMB(join->isNatural);
-				APP_JUMB(join->rtindex);
-				JumbleExpr(jstate, join->larg);
-				JumbleExpr(jstate, join->rarg);
-				JumbleExpr(jstate, join->quals);
-			}
-			break;
-		case T_FromExpr:
-			{
-				FromExpr   *from = (FromExpr *) node;
-
-				JumbleExpr(jstate, (Node *) from->fromlist);
-				JumbleExpr(jstate, from->quals);
-			}
-			break;
-		case T_OnConflictExpr:
-			{
-				OnConflictExpr *conf = (OnConflictExpr *) node;
-
-				APP_JUMB(conf->action);
-				JumbleExpr(jstate, (Node *) conf->arbiterElems);
-				JumbleExpr(jstate, conf->arbiterWhere);
-				JumbleExpr(jstate, (Node *) conf->onConflictSet);
-				JumbleExpr(jstate, conf->onConflictWhere);
-				APP_JUMB(conf->constraint);
-				APP_JUMB(conf->exclRelIndex);
-				JumbleExpr(jstate, (Node *) conf->exclRelTlist);
-			}
-			break;
-		case T_MergeAction:
-			{
-				MergeAction *mergeaction = (MergeAction *) node;
-
-				APP_JUMB(mergeaction->matched);
-				APP_JUMB(mergeaction->commandType);
-				JumbleExpr(jstate, mergeaction->qual);
-				JumbleExpr(jstate, (Node *) mergeaction->targetList);
-			}
-			break;
-		case T_List:
-			foreach(temp, (List *) node)
-			{
-				JumbleExpr(jstate, (Node *) lfirst(temp));
-			}
-			break;
-		case T_IntList:
-			foreach(temp, (List *) node)
-			{
-				APP_JUMB(lfirst_int(temp));
-			}
-			break;
-		case T_SortGroupClause:
-			{
-				SortGroupClause *sgc = (SortGroupClause *) node;
-
-				APP_JUMB(sgc->tleSortGroupRef);
-				APP_JUMB(sgc->eqop);
-				APP_JUMB(sgc->sortop);
-				APP_JUMB(sgc->nulls_first);
-			}
-			break;
-		case T_GroupingSet:
-			{
-				GroupingSet *gsnode = (GroupingSet *) node;
-
-				JumbleExpr(jstate, (Node *) gsnode->content);
-			}
-			break;
-		case T_WindowClause:
-			{
-				WindowClause *wc = (WindowClause *) node;
-
-				APP_JUMB(wc->winref);
-				APP_JUMB(wc->frameOptions);
-				JumbleExpr(jstate, (Node *) wc->partitionClause);
-				JumbleExpr(jstate, (Node *) wc->orderClause);
-				JumbleExpr(jstate, wc->startOffset);
-				JumbleExpr(jstate, wc->endOffset);
-			}
-			break;
-		case T_CommonTableExpr:
-			{
-				CommonTableExpr *cte = (CommonTableExpr *) node;
-
-				/* we store the string name because RTE_CTE RTEs need it */
-				APP_JUMB_STRING(cte->ctename);
-				APP_JUMB(cte->ctematerialized);
-				JumbleQueryInternal(jstate, castNode(Query, cte->ctequery));
-			}
-			break;
-		case T_SetOperationStmt:
-			{
-				SetOperationStmt *setop = (SetOperationStmt *) node;
-
-				APP_JUMB(setop->op);
-				APP_JUMB(setop->all);
-				JumbleExpr(jstate, setop->larg);
-				JumbleExpr(jstate, setop->rarg);
-			}
-			break;
-		case T_RangeTblFunction:
-			{
-				RangeTblFunction *rtfunc = (RangeTblFunction *) node;
-
-				JumbleExpr(jstate, rtfunc->funcexpr);
-			}
-			break;
-		case T_TableFunc:
-			{
-				TableFunc  *tablefunc = (TableFunc *) node;
-
-				JumbleExpr(jstate, tablefunc->docexpr);
-				JumbleExpr(jstate, tablefunc->rowexpr);
-				JumbleExpr(jstate, (Node *) tablefunc->colexprs);
-			}
-			break;
-		case T_TableSampleClause:
-			{
-				TableSampleClause *tsc = (TableSampleClause *) node;
-
-				APP_JUMB(tsc->tsmhandler);
-				JumbleExpr(jstate, (Node *) tsc->args);
-				JumbleExpr(jstate, (Node *) tsc->repeatable);
-			}
-			break;
-		default:
-			/* Only a warning, since we can stumble along anyway */
-			elog(WARNING, "unrecognized node type: %d",
-				 (int) nodeTag(node));
-			break;
-	}
-}
-
-/*
- * Record location of constant within query string of query tree
- * that is currently being walked.
- */
-static void
-RecordConstLocation(JumbleState *jstate, int location)
-{
-	/* -1 indicates unknown or undefined location */
-	if (location >= 0)
-	{
-		/* enlarge array if needed */
-		if (jstate->clocations_count >= jstate->clocations_buf_size)
-		{
-			jstate->clocations_buf_size *= 2;
-			jstate->clocations = (LocationLen *)
-				repalloc(jstate->clocations,
-						 jstate->clocations_buf_size *
-						 sizeof(LocationLen));
-		}
-		jstate->clocations[jstate->clocations_count].location = location;
-		/* initialize lengths to -1 to simplify third-party module usage */
-		jstate->clocations[jstate->clocations_count].length = -1;
-		jstate->clocations_count++;
-	}
-}
-- 
2.38.1

Attachment: signature.asc
Description: PGP signature

Reply via email to