From 55b6d9154a5524e67865299b9a73ef074bd12adf Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@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

