public inbox for [email protected]  
help / color / mirror / Atom feed
From: Peter Eisentraut <[email protected]>
To: pgsql-hackers <[email protected]>
Subject: automatically generating node support functions
Date: Mon, 7 Jun 2021 22:27:52 +0200
Message-ID: <[email protected]> (raw)

I wrote a script to automatically generate the node support functions 
(copy, equal, out, and read, as well as the node tags enum) from the 
struct definitions.

The first eight patches are to clean up various inconsistencies to make 
parsing or generation easier.

The interesting stuff is in patch 0009.

For each of the four node support files, it creates two include files, 
e.g., copyfuncs.inc1.c and copyfuncs.inc2.c to include in the main file. 
  All the scaffolding of the main file stays in place.

In this patch, I have only ifdef'ed out the code to could be removed, 
mainly so that it won't constantly have merge conflicts.  Eventually, 
that should all be changed to delete the code.  When we do that, some 
code comments should probably be preserved elsewhere, so that will need 
another pass of consideration.

I have tried to mostly make the coverage of the output match what is 
currently there.  For example, one could do out/read coverage of utility 
statement nodes easily with this, but I have manually excluded those for 
now.  The reason is mainly that it's easier to diff the before and 
after, and adding a bunch of stuff like this might require a separate 
analysis and review.

Subtyping (TidScan -> Scan) is supported.

For the hard cases, you can just write a manual function and exclude 
generating one.

For the not so hard cases, there is a way of annotating struct fields to 
get special behaviors.  For example, pg_node_attr(equal_ignore) has the 
field ignored in equal functions.

There are a couple of additional minor issues mentioned in the script 
source.  But basically, it all seems to work.

From c782871d6cc59e2fed232c78c307d63e72cbb3d5 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <[email protected]>
Date: Mon, 7 Jun 2021 15:45:14 +0200
Subject: [PATCH v1 01/10] Rename NodeTag of ExprState

Rename from tag to type, for consistency with all other node structs.
---
 src/backend/executor/execExpr.c | 4 ++--
 src/include/nodes/execnodes.h   | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index 8c9f8a6aeb..c6ba11d035 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -363,7 +363,7 @@ ExecBuildProjectionInfo(List *targetList,
 
 	projInfo->pi_exprContext = econtext;
 	/* We embed ExprState into ProjectionInfo instead of doing extra palloc */
-	projInfo->pi_state.tag = T_ExprState;
+	projInfo->pi_state.type = T_ExprState;
 	state = &projInfo->pi_state;
 	state->expr = (Expr *) targetList;
 	state->parent = parent;
@@ -531,7 +531,7 @@ ExecBuildUpdateProjection(List *targetList,
 
 	projInfo->pi_exprContext = econtext;
 	/* We embed ExprState into ProjectionInfo instead of doing extra palloc */
-	projInfo->pi_state.tag = T_ExprState;
+	projInfo->pi_state.type = T_ExprState;
 	state = &projInfo->pi_state;
 	if (evalTargetList)
 		state->expr = (Expr *) targetList;
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 7795a69490..8fa9c8aff6 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -60,7 +60,7 @@ typedef Datum (*ExprStateEvalFunc) (struct ExprState *expression,
 
 typedef struct ExprState
 {
-	NodeTag		tag;
+	NodeTag		type;
 
 	uint8		flags;			/* bitmask of EEO_FLAG_* bits, see above */
 
-- 
2.31.1


From 7746ddd4f2a9534322b2b9226007638d3142c0c7 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <[email protected]>
Date: Mon, 7 Jun 2021 15:47:56 +0200
Subject: [PATCH v1 02/10] Rename argument of _outValue()

Rename from value to node, for consistency with similar functions.
---
 src/backend/nodes/outfuncs.c | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 04696f613c..b54c57d09f 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -3402,12 +3402,12 @@ _outAExpr(StringInfo str, const A_Expr *node)
 }
 
 static void
-_outValue(StringInfo str, const Value *value)
+_outValue(StringInfo str, const Value *node)
 {
-	switch (value->type)
+	switch (node->type)
 	{
 		case T_Integer:
-			appendStringInfo(str, "%d", value->val.ival);
+			appendStringInfo(str, "%d", node->val.ival);
 			break;
 		case T_Float:
 
@@ -3415,7 +3415,7 @@ _outValue(StringInfo str, const Value *value)
 			 * We assume the value is a valid numeric literal and so does not
 			 * need quoting.
 			 */
-			appendStringInfoString(str, value->val.str);
+			appendStringInfoString(str, node->val.str);
 			break;
 		case T_String:
 
@@ -3424,20 +3424,20 @@ _outValue(StringInfo str, const Value *value)
 			 * but we don't want it to do anything with an empty string.
 			 */
 			appendStringInfoChar(str, '"');
-			if (value->val.str[0] != '\0')
-				outToken(str, value->val.str);
+			if (node->val.str[0] != '\0')
+				outToken(str, node->val.str);
 			appendStringInfoChar(str, '"');
 			break;
 		case T_BitString:
 			/* internal representation already has leading 'b' */
-			appendStringInfoString(str, value->val.str);
+			appendStringInfoString(str, node->val.str);
 			break;
 		case T_Null:
 			/* this is seen only within A_Const, not in transformed trees */
 			appendStringInfoString(str, "NULL");
 			break;
 		default:
-			elog(ERROR, "unrecognized node type: %d", (int) value->type);
+			elog(ERROR, "unrecognized node type: %d", (int) node->type);
 			break;
 	}
 }
-- 
2.31.1


From 7282e926a6492046e3189f8ddc0ccbbbab8a470b Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <[email protected]>
Date: Mon, 7 Jun 2021 15:51:37 +0200
Subject: [PATCH v1 03/10] Rename some node support functions for consistency

Some node function names didn't match their node type names exactly.
Fix those for consistency.
---
 src/backend/nodes/copyfuncs.c  | 16 ++++++++--------
 src/backend/nodes/equalfuncs.c | 16 ++++++++--------
 src/backend/nodes/outfuncs.c   |  8 ++++----
 3 files changed, 20 insertions(+), 20 deletions(-)

diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 621f7ce068..35a85df442 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2700,7 +2700,7 @@ _copyCommonTableExpr(const CommonTableExpr *from)
 }
 
 static A_Expr *
-_copyAExpr(const A_Expr *from)
+_copyA_Expr(const A_Expr *from)
 {
 	A_Expr	   *newnode = makeNode(A_Expr);
 
@@ -2736,7 +2736,7 @@ _copyParamRef(const ParamRef *from)
 }
 
 static A_Const *
-_copyAConst(const A_Const *from)
+_copyA_Const(const A_Const *from)
 {
 	A_Const    *newnode = makeNode(A_Const);
 
@@ -2787,7 +2787,7 @@ _copyFuncCall(const FuncCall *from)
 }
 
 static A_Star *
-_copyAStar(const A_Star *from)
+_copyA_Star(const A_Star *from)
 {
 	A_Star	   *newnode = makeNode(A_Star);
 
@@ -2795,7 +2795,7 @@ _copyAStar(const A_Star *from)
 }
 
 static A_Indices *
-_copyAIndices(const A_Indices *from)
+_copyA_Indices(const A_Indices *from)
 {
 	A_Indices  *newnode = makeNode(A_Indices);
 
@@ -5711,7 +5711,7 @@ copyObjectImpl(const void *from)
 			retval = _copyDropSubscriptionStmt(from);
 			break;
 		case T_A_Expr:
-			retval = _copyAExpr(from);
+			retval = _copyA_Expr(from);
 			break;
 		case T_ColumnRef:
 			retval = _copyColumnRef(from);
@@ -5720,16 +5720,16 @@ copyObjectImpl(const void *from)
 			retval = _copyParamRef(from);
 			break;
 		case T_A_Const:
-			retval = _copyAConst(from);
+			retval = _copyA_Const(from);
 			break;
 		case T_FuncCall:
 			retval = _copyFuncCall(from);
 			break;
 		case T_A_Star:
-			retval = _copyAStar(from);
+			retval = _copyA_Star(from);
 			break;
 		case T_A_Indices:
-			retval = _copyAIndices(from);
+			retval = _copyA_Indices(from);
 			break;
 		case T_A_Indirection:
 			retval = _copyA_Indirection(from);
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 3033c1934c..e04ec41904 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -2369,7 +2369,7 @@ _equalAlterPolicyStmt(const AlterPolicyStmt *a, const AlterPolicyStmt *b)
 }
 
 static bool
-_equalAExpr(const A_Expr *a, const A_Expr *b)
+_equalA_Expr(const A_Expr *a, const A_Expr *b)
 {
 	COMPARE_SCALAR_FIELD(kind);
 	COMPARE_NODE_FIELD(name);
@@ -2399,7 +2399,7 @@ _equalParamRef(const ParamRef *a, const ParamRef *b)
 }
 
 static bool
-_equalAConst(const A_Const *a, const A_Const *b)
+_equalA_Const(const A_Const *a, const A_Const *b)
 {
 	if (!equal(&a->val, &b->val))	/* hack for in-line Value field */
 		return false;
@@ -2427,13 +2427,13 @@ _equalFuncCall(const FuncCall *a, const FuncCall *b)
 }
 
 static bool
-_equalAStar(const A_Star *a, const A_Star *b)
+_equalA_Star(const A_Star *a, const A_Star *b)
 {
 	return true;
 }
 
 static bool
-_equalAIndices(const A_Indices *a, const A_Indices *b)
+_equalA_Indices(const A_Indices *a, const A_Indices *b)
 {
 	COMPARE_SCALAR_FIELD(is_slice);
 	COMPARE_NODE_FIELD(lidx);
@@ -3702,7 +3702,7 @@ equal(const void *a, const void *b)
 			retval = _equalDropSubscriptionStmt(a, b);
 			break;
 		case T_A_Expr:
-			retval = _equalAExpr(a, b);
+			retval = _equalA_Expr(a, b);
 			break;
 		case T_ColumnRef:
 			retval = _equalColumnRef(a, b);
@@ -3711,16 +3711,16 @@ equal(const void *a, const void *b)
 			retval = _equalParamRef(a, b);
 			break;
 		case T_A_Const:
-			retval = _equalAConst(a, b);
+			retval = _equalA_Const(a, b);
 			break;
 		case T_FuncCall:
 			retval = _equalFuncCall(a, b);
 			break;
 		case T_A_Star:
-			retval = _equalAStar(a, b);
+			retval = _equalA_Star(a, b);
 			break;
 		case T_A_Indices:
-			retval = _equalAIndices(a, b);
+			retval = _equalA_Indices(a, b);
 			break;
 		case T_A_Indirection:
 			retval = _equalA_Indirection(a, b);
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index b54c57d09f..ed08f5acf4 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -3327,7 +3327,7 @@ _outTableSampleClause(StringInfo str, const TableSampleClause *node)
 }
 
 static void
-_outAExpr(StringInfo str, const A_Expr *node)
+_outA_Expr(StringInfo str, const A_Expr *node)
 {
 	WRITE_NODE_TYPE("AEXPR");
 
@@ -3475,7 +3475,7 @@ _outRawStmt(StringInfo str, const RawStmt *node)
 }
 
 static void
-_outAConst(StringInfo str, const A_Const *node)
+_outA_Const(StringInfo str, const A_Const *node)
 {
 	WRITE_NODE_TYPE("A_CONST");
 
@@ -4416,7 +4416,7 @@ outNode(StringInfo str, const void *obj)
 				_outTableSampleClause(str, obj);
 				break;
 			case T_A_Expr:
-				_outAExpr(str, obj);
+				_outA_Expr(str, obj);
 				break;
 			case T_ColumnRef:
 				_outColumnRef(str, obj);
@@ -4428,7 +4428,7 @@ outNode(StringInfo str, const void *obj)
 				_outRawStmt(str, obj);
 				break;
 			case T_A_Const:
-				_outAConst(str, obj);
+				_outA_Const(str, obj);
 				break;
 			case T_A_Star:
 				_outA_Star(str, obj);
-- 
2.31.1


From 06d24333eabc3c9c5ef4df8ef100dbaaacd3dc16 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <[email protected]>
Date: Mon, 7 Jun 2021 15:55:40 +0200
Subject: [PATCH v1 04/10] Change SeqScan node to contain Scan node

This makes the structure of all Scan-derived nodes the same,
independent of whether they have additional fields.
---
 src/backend/executor/nodeSeqscan.c      |  4 ++--
 src/backend/nodes/readfuncs.c           |  2 +-
 src/backend/optimizer/plan/createplan.c |  6 +++---
 src/backend/optimizer/plan/setrefs.c    | 10 +++++-----
 src/include/nodes/plannodes.h           |  5 ++++-
 5 files changed, 15 insertions(+), 12 deletions(-)

diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c
index 066f9ae37e..4d2bf16a6f 100644
--- a/src/backend/executor/nodeSeqscan.c
+++ b/src/backend/executor/nodeSeqscan.c
@@ -151,7 +151,7 @@ ExecInitSeqScan(SeqScan *node, EState *estate, int eflags)
 	 */
 	scanstate->ss.ss_currentRelation =
 		ExecOpenScanRelation(estate,
-							 node->scanrelid,
+							 node->scan.scanrelid,
 							 eflags);
 
 	/* and create slot with the appropriate rowtype */
@@ -169,7 +169,7 @@ ExecInitSeqScan(SeqScan *node, EState *estate, int eflags)
 	 * initialize child expressions
 	 */
 	scanstate->ss.ps.qual =
-		ExecInitQual(node->plan.qual, (PlanState *) scanstate);
+		ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
 
 	return scanstate;
 }
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index f0b34ecfac..fef4de94e6 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -1832,7 +1832,7 @@ _readSeqScan(void)
 {
 	READ_LOCALS_NO_FIELDS(SeqScan);
 
-	ReadCommonScan(local_node);
+	ReadCommonScan(&local_node->scan);
 
 	READ_DONE();
 }
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 439e6b6426..769b5a0cf7 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -2858,7 +2858,7 @@ create_seqscan_plan(PlannerInfo *root, Path *best_path,
 							 scan_clauses,
 							 scan_relid);
 
-	copy_generic_path_info(&scan_plan->plan, best_path);
+	copy_generic_path_info(&scan_plan->scan.plan, best_path);
 
 	return scan_plan;
 }
@@ -5372,13 +5372,13 @@ make_seqscan(List *qptlist,
 			 Index scanrelid)
 {
 	SeqScan    *node = makeNode(SeqScan);
-	Plan	   *plan = &node->plan;
+	Plan	   *plan = &node->scan.plan;
 
 	plan->targetlist = qptlist;
 	plan->qual = qpqual;
 	plan->lefttree = NULL;
 	plan->righttree = NULL;
-	node->scanrelid = scanrelid;
+	node->scan.scanrelid = scanrelid;
 
 	return node;
 }
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 61ccfd300b..6cb2d731c8 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -516,12 +516,12 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
 			{
 				SeqScan    *splan = (SeqScan *) plan;
 
-				splan->scanrelid += rtoffset;
-				splan->plan.targetlist =
-					fix_scan_list(root, splan->plan.targetlist,
+				splan->scan.scanrelid += rtoffset;
+				splan->scan.plan.targetlist =
+					fix_scan_list(root, splan->scan.plan.targetlist,
 								  rtoffset, NUM_EXEC_TLIST(plan));
-				splan->plan.qual =
-					fix_scan_list(root, splan->plan.qual,
+				splan->scan.plan.qual =
+					fix_scan_list(root, splan->scan.plan.qual,
 								  rtoffset, NUM_EXEC_QUAL(plan));
 			}
 			break;
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index aaa3b65d04..35ff94fbb6 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -348,7 +348,10 @@ typedef struct Scan
  *		sequential scan node
  * ----------------
  */
-typedef Scan SeqScan;
+typedef struct SeqScan
+{
+	Scan		scan;
+} SeqScan;
 
 /* ----------------
  *		table sample scan node
-- 
2.31.1


From d3dc7a83e23fe89a2767716a51c3d182f0719148 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <[email protected]>
Date: Mon, 7 Jun 2021 16:00:31 +0200
Subject: [PATCH v1 05/10] Change NestPath node to contain JoinPath node

This makes the structure of all JoinPath-derived nodes the same,
independent of whether they have additional fields.
---
 src/backend/optimizer/path/costsize.c   | 35 +++++++++++++------------
 src/backend/optimizer/plan/createplan.c | 24 ++++++++---------
 src/backend/optimizer/util/pathnode.c   | 32 +++++++++++-----------
 src/include/nodes/pathnodes.h           |  5 +++-
 4 files changed, 51 insertions(+), 45 deletions(-)

diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index 8577c7b138..64891342fc 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -167,7 +167,7 @@ static bool cost_qual_eval_walker(Node *node, cost_qual_eval_context *context);
 static void get_restriction_qual_cost(PlannerInfo *root, RelOptInfo *baserel,
 									  ParamPathInfo *param_info,
 									  QualCost *qpqual_cost);
-static bool has_indexed_join_quals(NestPath *joinpath);
+static bool has_indexed_join_quals(NestPath *path);
 static double approx_tuple_count(PlannerInfo *root, JoinPath *path,
 								 List *quals);
 static double calc_joinrel_size_estimate(PlannerInfo *root,
@@ -2978,8 +2978,8 @@ final_cost_nestloop(PlannerInfo *root, NestPath *path,
 					JoinCostWorkspace *workspace,
 					JoinPathExtraData *extra)
 {
-	Path	   *outer_path = path->outerjoinpath;
-	Path	   *inner_path = path->innerjoinpath;
+	Path	   *outer_path = path->jpath.outerjoinpath;
+	Path	   *inner_path = path->jpath.innerjoinpath;
 	double		outer_path_rows = outer_path->rows;
 	double		inner_path_rows = inner_path->rows;
 	Cost		startup_cost = workspace->startup_cost;
@@ -2994,18 +2994,18 @@ final_cost_nestloop(PlannerInfo *root, NestPath *path,
 	if (inner_path_rows <= 0)
 		inner_path_rows = 1;
 	/* Mark the path with the correct row estimate */
-	if (path->path.param_info)
-		path->path.rows = path->path.param_info->ppi_rows;
+	if (path->jpath.path.param_info)
+		path->jpath.path.rows = path->jpath.path.param_info->ppi_rows;
 	else
-		path->path.rows = path->path.parent->rows;
+		path->jpath.path.rows = path->jpath.path.parent->rows;
 
 	/* For partial paths, scale row estimate. */
-	if (path->path.parallel_workers > 0)
+	if (path->jpath.path.parallel_workers > 0)
 	{
-		double		parallel_divisor = get_parallel_divisor(&path->path);
+		double		parallel_divisor = get_parallel_divisor(&path->jpath.path);
 
-		path->path.rows =
-			clamp_row_est(path->path.rows / parallel_divisor);
+		path->jpath.path.rows =
+			clamp_row_est(path->jpath.path.rows / parallel_divisor);
 	}
 
 	/*
@@ -3018,7 +3018,7 @@ final_cost_nestloop(PlannerInfo *root, NestPath *path,
 
 	/* cost of inner-relation source data (we already dealt with outer rel) */
 
-	if (path->jointype == JOIN_SEMI || path->jointype == JOIN_ANTI ||
+	if (path->jpath.jointype == JOIN_SEMI || path->jpath.jointype == JOIN_ANTI ||
 		extra->inner_unique)
 	{
 		/*
@@ -3136,17 +3136,17 @@ final_cost_nestloop(PlannerInfo *root, NestPath *path,
 	}
 
 	/* CPU costs */
-	cost_qual_eval(&restrict_qual_cost, path->joinrestrictinfo, root);
+	cost_qual_eval(&restrict_qual_cost, path->jpath.joinrestrictinfo, root);
 	startup_cost += restrict_qual_cost.startup;
 	cpu_per_tuple = cpu_tuple_cost + restrict_qual_cost.per_tuple;
 	run_cost += cpu_per_tuple * ntuples;
 
 	/* tlist eval costs are paid per output row, not per tuple scanned */
-	startup_cost += path->path.pathtarget->cost.startup;
-	run_cost += path->path.pathtarget->cost.per_tuple * path->path.rows;
+	startup_cost += path->jpath.path.pathtarget->cost.startup;
+	run_cost += path->jpath.path.pathtarget->cost.per_tuple * path->jpath.path.rows;
 
-	path->path.startup_cost = startup_cost;
-	path->path.total_cost = startup_cost + run_cost;
+	path->jpath.path.startup_cost = startup_cost;
+	path->jpath.path.total_cost = startup_cost + run_cost;
 }
 
 /*
@@ -4774,8 +4774,9 @@ compute_semi_anti_join_factors(PlannerInfo *root,
  * expensive.
  */
 static bool
-has_indexed_join_quals(NestPath *joinpath)
+has_indexed_join_quals(NestPath *path)
 {
+	JoinPath   *joinpath = &path->jpath;
 	Relids		joinrelids = joinpath->path.parent->relids;
 	Path	   *innerpath = joinpath->innerjoinpath;
 	List	   *indexclauses;
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 769b5a0cf7..06a8588f4e 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -4224,8 +4224,8 @@ create_nestloop_plan(PlannerInfo *root,
 	NestLoop   *join_plan;
 	Plan	   *outer_plan;
 	Plan	   *inner_plan;
-	List	   *tlist = build_path_tlist(root, &best_path->path);
-	List	   *joinrestrictclauses = best_path->joinrestrictinfo;
+	List	   *tlist = build_path_tlist(root, &best_path->jpath.path);
+	List	   *joinrestrictclauses = best_path->jpath.joinrestrictinfo;
 	List	   *joinclauses;
 	List	   *otherclauses;
 	Relids		outerrelids;
@@ -4233,13 +4233,13 @@ create_nestloop_plan(PlannerInfo *root,
 	Relids		saveOuterRels = root->curOuterRels;
 
 	/* NestLoop can project, so no need to be picky about child tlists */
-	outer_plan = create_plan_recurse(root, best_path->outerjoinpath, 0);
+	outer_plan = create_plan_recurse(root, best_path->jpath.outerjoinpath, 0);
 
 	/* For a nestloop, include outer relids in curOuterRels for inner side */
 	root->curOuterRels = bms_union(root->curOuterRels,
-								   best_path->outerjoinpath->parent->relids);
+								   best_path->jpath.outerjoinpath->parent->relids);
 
-	inner_plan = create_plan_recurse(root, best_path->innerjoinpath, 0);
+	inner_plan = create_plan_recurse(root, best_path->jpath.innerjoinpath, 0);
 
 	/* Restore curOuterRels */
 	bms_free(root->curOuterRels);
@@ -4250,10 +4250,10 @@ create_nestloop_plan(PlannerInfo *root,
 
 	/* Get the join qual clauses (in plain expression form) */
 	/* Any pseudoconstant clauses are ignored here */
-	if (IS_OUTER_JOIN(best_path->jointype))
+	if (IS_OUTER_JOIN(best_path->jpath.jointype))
 	{
 		extract_actual_join_clauses(joinrestrictclauses,
-									best_path->path.parent->relids,
+									best_path->jpath.path.parent->relids,
 									&joinclauses, &otherclauses);
 	}
 	else
@@ -4264,7 +4264,7 @@ create_nestloop_plan(PlannerInfo *root,
 	}
 
 	/* Replace any outer-relation variables with nestloop params */
-	if (best_path->path.param_info)
+	if (best_path->jpath.path.param_info)
 	{
 		joinclauses = (List *)
 			replace_nestloop_params(root, (Node *) joinclauses);
@@ -4276,7 +4276,7 @@ create_nestloop_plan(PlannerInfo *root,
 	 * Identify any nestloop parameters that should be supplied by this join
 	 * node, and remove them from root->curOuterParams.
 	 */
-	outerrelids = best_path->outerjoinpath->parent->relids;
+	outerrelids = best_path->jpath.outerjoinpath->parent->relids;
 	nestParams = identify_current_nestloop_params(root, outerrelids);
 
 	join_plan = make_nestloop(tlist,
@@ -4285,10 +4285,10 @@ create_nestloop_plan(PlannerInfo *root,
 							  nestParams,
 							  outer_plan,
 							  inner_plan,
-							  best_path->jointype,
-							  best_path->inner_unique);
+							  best_path->jpath.jointype,
+							  best_path->jpath.inner_unique);
 
-	copy_generic_path_info(&join_plan->join.plan, &best_path->path);
+	copy_generic_path_info(&join_plan->join.plan, &best_path->jpath.path);
 
 	return join_plan;
 }
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index 9ce5f95e3b..cd6ef1bb32 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -2446,10 +2446,10 @@ create_nestloop_path(PlannerInfo *root,
 		restrict_clauses = jclauses;
 	}
 
-	pathnode->path.pathtype = T_NestLoop;
-	pathnode->path.parent = joinrel;
-	pathnode->path.pathtarget = joinrel->reltarget;
-	pathnode->path.param_info =
+	pathnode->jpath.path.pathtype = T_NestLoop;
+	pathnode->jpath.path.parent = joinrel;
+	pathnode->jpath.path.pathtarget = joinrel->reltarget;
+	pathnode->jpath.path.param_info =
 		get_joinrel_parampathinfo(root,
 								  joinrel,
 								  outer_path,
@@ -2457,17 +2457,17 @@ create_nestloop_path(PlannerInfo *root,
 								  extra->sjinfo,
 								  required_outer,
 								  &restrict_clauses);
-	pathnode->path.parallel_aware = false;
-	pathnode->path.parallel_safe = joinrel->consider_parallel &&
+	pathnode->jpath.path.parallel_aware = false;
+	pathnode->jpath.path.parallel_safe = joinrel->consider_parallel &&
 		outer_path->parallel_safe && inner_path->parallel_safe;
 	/* This is a foolish way to estimate parallel_workers, but for now... */
-	pathnode->path.parallel_workers = outer_path->parallel_workers;
-	pathnode->path.pathkeys = pathkeys;
-	pathnode->jointype = jointype;
-	pathnode->inner_unique = extra->inner_unique;
-	pathnode->outerjoinpath = outer_path;
-	pathnode->innerjoinpath = inner_path;
-	pathnode->joinrestrictinfo = restrict_clauses;
+	pathnode->jpath.path.parallel_workers = outer_path->parallel_workers;
+	pathnode->jpath.path.pathkeys = pathkeys;
+	pathnode->jpath.jointype = jointype;
+	pathnode->jpath.inner_unique = extra->inner_unique;
+	pathnode->jpath.outerjoinpath = outer_path;
+	pathnode->jpath.innerjoinpath = inner_path;
+	pathnode->jpath.joinrestrictinfo = restrict_clauses;
 
 	final_cost_nestloop(root, pathnode, workspace, extra);
 
@@ -4113,13 +4113,15 @@ do { \
 		case T_NestPath:
 			{
 				JoinPath   *jpath;
+				NestPath   *npath;
 
-				FLAT_COPY_PATH(jpath, path, NestPath);
+				FLAT_COPY_PATH(npath, path, NestPath);
 
+				jpath = (JoinPath *) npath;
 				REPARAMETERIZE_CHILD_PATH(jpath->outerjoinpath);
 				REPARAMETERIZE_CHILD_PATH(jpath->innerjoinpath);
 				ADJUST_CHILD_ATTRS(jpath->joinrestrictinfo);
-				new_path = (Path *) jpath;
+				new_path = (Path *) npath;
 			}
 			break;
 
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index b7b2817a5d..f8ddb9e910 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -1595,7 +1595,10 @@ typedef struct JoinPath
  * A nested-loop path needs no special fields.
  */
 
-typedef JoinPath NestPath;
+typedef struct NestPath
+{
+	JoinPath	jpath;
+} NestPath;
 
 /*
  * A mergejoin path has these fields.
-- 
2.31.1


From 3d66bc6725594d5d187fbd9a023a0ed91bf22eaa Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <[email protected]>
Date: Mon, 7 Jun 2021 16:03:07 +0200
Subject: [PATCH v1 06/10] Add missing enum tags in enums used in nodes

---
 src/include/nodes/parsenodes.h | 4 ++--
 src/include/nodes/pathnodes.h  | 2 +-
 src/include/nodes/primnodes.h  | 2 +-
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index ef73342019..cd9115dbb5 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1337,7 +1337,7 @@ typedef struct SortGroupClause
  *
  * SETS( SIMPLE(1,2), CUBE( SIMPLE(3), SIMPLE(4,5) ) )
  */
-typedef enum
+typedef enum GroupingSetKind
 {
 	GROUPING_SET_EMPTY,
 	GROUPING_SET_SIMPLE,
@@ -2113,7 +2113,7 @@ typedef struct CopyStmt
  * preserve the distinction in VariableSetKind for CreateCommandTag().
  * ----------------------
  */
-typedef enum
+typedef enum VariableSetKind
 {
 	VAR_SET_VALUE,				/* SET var = value */
 	VAR_SET_DEFAULT,			/* SET var TO DEFAULT */
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index f8ddb9e910..03c80c1620 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -1525,7 +1525,7 @@ typedef struct ResultCachePath
  * it's convenient to have a UniquePath in the path tree to signal upper-level
  * routines that the input is known distinct.)
  */
-typedef enum
+typedef enum UniquePathMethod
 {
 	UNIQUE_PATH_NOOP,			/* input is known unique already */
 	UNIQUE_PATH_HASH,			/* use hashing */
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 9ae851d847..3418e23873 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -1205,7 +1205,7 @@ typedef enum XmlExprOp
 	IS_DOCUMENT					/* xmlval IS DOCUMENT */
 } XmlExprOp;
 
-typedef enum
+typedef enum XmlOptionType
 {
 	XMLOPTION_DOCUMENT,
 	XMLOPTION_CONTENT
-- 
2.31.1


From bccacca061746724ae7687be8169b9d74b09acf1 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <[email protected]>
Date: Mon, 7 Jun 2021 16:04:12 +0200
Subject: [PATCH v1 07/10] Check the size in COPY_POINTER_FIELD

instead of making each caller do it.
---
 src/backend/nodes/copyfuncs.c | 54 ++++++++++++++---------------------
 1 file changed, 21 insertions(+), 33 deletions(-)

diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 35a85df442..dde2d47338 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -57,8 +57,11 @@
 #define COPY_POINTER_FIELD(fldname, sz) \
 	do { \
 		Size	_size = (sz); \
-		newnode->fldname = palloc(_size); \
-		memcpy(newnode->fldname, from->fldname, _size); \
+		if (_size > 0) \
+		{ \
+			newnode->fldname = palloc(_size); \
+			memcpy(newnode->fldname, from->fldname, _size); \
+		} \
 	} while (0)
 
 /* Copy a parse location field (for Copy, this is same as scalar case) */
@@ -296,12 +299,9 @@ _copyRecursiveUnion(const RecursiveUnion *from)
 	 */
 	COPY_SCALAR_FIELD(wtParam);
 	COPY_SCALAR_FIELD(numCols);
-	if (from->numCols > 0)
-	{
-		COPY_POINTER_FIELD(dupColIdx, from->numCols * sizeof(AttrNumber));
-		COPY_POINTER_FIELD(dupOperators, from->numCols * sizeof(Oid));
-		COPY_POINTER_FIELD(dupCollations, from->numCols * sizeof(Oid));
-	}
+	COPY_POINTER_FIELD(dupColIdx, from->numCols * sizeof(AttrNumber));
+	COPY_POINTER_FIELD(dupOperators, from->numCols * sizeof(Oid));
+	COPY_POINTER_FIELD(dupCollations, from->numCols * sizeof(Oid));
 	COPY_SCALAR_FIELD(numGroups);
 
 	return newnode;
@@ -896,13 +896,10 @@ _copyMergeJoin(const MergeJoin *from)
 	COPY_SCALAR_FIELD(skip_mark_restore);
 	COPY_NODE_FIELD(mergeclauses);
 	numCols = list_length(from->mergeclauses);
-	if (numCols > 0)
-	{
-		COPY_POINTER_FIELD(mergeFamilies, numCols * sizeof(Oid));
-		COPY_POINTER_FIELD(mergeCollations, numCols * sizeof(Oid));
-		COPY_POINTER_FIELD(mergeStrategies, numCols * sizeof(int));
-		COPY_POINTER_FIELD(mergeNullsFirst, numCols * sizeof(bool));
-	}
+	COPY_POINTER_FIELD(mergeFamilies, numCols * sizeof(Oid));
+	COPY_POINTER_FIELD(mergeCollations, numCols * sizeof(Oid));
+	COPY_POINTER_FIELD(mergeStrategies, numCols * sizeof(int));
+	COPY_POINTER_FIELD(mergeNullsFirst, numCols * sizeof(bool));
 
 	return newnode;
 }
@@ -1064,12 +1061,9 @@ _copyAgg(const Agg *from)
 	COPY_SCALAR_FIELD(aggstrategy);
 	COPY_SCALAR_FIELD(aggsplit);
 	COPY_SCALAR_FIELD(numCols);
-	if (from->numCols > 0)
-	{
-		COPY_POINTER_FIELD(grpColIdx, from->numCols * sizeof(AttrNumber));
-		COPY_POINTER_FIELD(grpOperators, from->numCols * sizeof(Oid));
-		COPY_POINTER_FIELD(grpCollations, from->numCols * sizeof(Oid));
-	}
+	COPY_POINTER_FIELD(grpColIdx, from->numCols * sizeof(AttrNumber));
+	COPY_POINTER_FIELD(grpOperators, from->numCols * sizeof(Oid));
+	COPY_POINTER_FIELD(grpCollations, from->numCols * sizeof(Oid));
 	COPY_SCALAR_FIELD(numGroups);
 	COPY_SCALAR_FIELD(transitionSpace);
 	COPY_BITMAPSET_FIELD(aggParams);
@@ -1091,19 +1085,13 @@ _copyWindowAgg(const WindowAgg *from)
 
 	COPY_SCALAR_FIELD(winref);
 	COPY_SCALAR_FIELD(partNumCols);
-	if (from->partNumCols > 0)
-	{
-		COPY_POINTER_FIELD(partColIdx, from->partNumCols * sizeof(AttrNumber));
-		COPY_POINTER_FIELD(partOperators, from->partNumCols * sizeof(Oid));
-		COPY_POINTER_FIELD(partCollations, from->partNumCols * sizeof(Oid));
-	}
+	COPY_POINTER_FIELD(partColIdx, from->partNumCols * sizeof(AttrNumber));
+	COPY_POINTER_FIELD(partOperators, from->partNumCols * sizeof(Oid));
+	COPY_POINTER_FIELD(partCollations, from->partNumCols * sizeof(Oid));
 	COPY_SCALAR_FIELD(ordNumCols);
-	if (from->ordNumCols > 0)
-	{
-		COPY_POINTER_FIELD(ordColIdx, from->ordNumCols * sizeof(AttrNumber));
-		COPY_POINTER_FIELD(ordOperators, from->ordNumCols * sizeof(Oid));
-		COPY_POINTER_FIELD(ordCollations, from->ordNumCols * sizeof(Oid));
-	}
+	COPY_POINTER_FIELD(ordColIdx, from->ordNumCols * sizeof(AttrNumber));
+	COPY_POINTER_FIELD(ordOperators, from->ordNumCols * sizeof(Oid));
+	COPY_POINTER_FIELD(ordCollations, from->ordNumCols * sizeof(Oid));
 	COPY_SCALAR_FIELD(frameOptions);
 	COPY_NODE_FIELD(startOffset);
 	COPY_NODE_FIELD(endOffset);
-- 
2.31.1


From 00fa8e67a141c90e38fcbec45e1cc2135c4fda5a Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <[email protected]>
Date: Mon, 7 Jun 2021 16:05:16 +0200
Subject: [PATCH v1 08/10] Remove T_MemoryContext

This is an abstract node that shouldn't have a node tag defined.
---
 src/include/nodes/nodes.h | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index d9e417bcd7..5e049a67e1 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -284,7 +284,6 @@ typedef enum NodeTag
 	/*
 	 * TAGS FOR MEMORY NODES (memnodes.h)
 	 */
-	T_MemoryContext,
 	T_AllocSetContext,
 	T_SlabContext,
 	T_GenerationContext,
-- 
2.31.1


From 79da68dba05e3ea3b506c4ebf6ea1a1c1e2de6f3 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <[email protected]>
Date: Mon, 7 Jun 2021 16:08:54 +0200
Subject: [PATCH v1 09/10] Add script to generate node support functions

---
 src/backend/Makefile                |   8 +-
 src/backend/nodes/.gitignore        |   3 +
 src/backend/nodes/Makefile          |  55 +++
 src/backend/nodes/copyfuncs.c       |  15 +
 src/backend/nodes/equalfuncs.c      |  19 +-
 src/backend/nodes/gen_node_stuff.pl | 641 ++++++++++++++++++++++++++++
 src/backend/nodes/outfuncs.c        |  25 ++
 src/backend/nodes/readfuncs.c       |  19 +-
 src/include/nodes/.gitignore        |   2 +
 src/include/nodes/nodes.h           |   8 +
 src/include/nodes/parsenodes.h      |   2 +-
 src/include/nodes/pathnodes.h       | 116 ++---
 src/include/nodes/primnodes.h       |  18 +-
 13 files changed, 857 insertions(+), 74 deletions(-)
 create mode 100644 src/backend/nodes/.gitignore
 create mode 100644 src/backend/nodes/gen_node_stuff.pl
 create mode 100644 src/include/nodes/.gitignore

diff --git a/src/backend/Makefile b/src/backend/Makefile
index 0da848b1fd..a33db1ae01 100644
--- a/src/backend/Makefile
+++ b/src/backend/Makefile
@@ -143,11 +143,15 @@ storage/lmgr/lwlocknames.h: storage/lmgr/generate-lwlocknames.pl storage/lmgr/lw
 submake-catalog-headers:
 	$(MAKE) -C catalog distprep generated-header-symlinks
 
+# run this unconditionally to avoid needing to know its dependencies here:
+submake-nodes-headers:
+	$(MAKE) -C nodes distprep generated-header-symlinks
+
 # run this unconditionally to avoid needing to know its dependencies here:
 submake-utils-headers:
 	$(MAKE) -C utils distprep generated-header-symlinks
 
-.PHONY: submake-catalog-headers submake-utils-headers
+.PHONY: submake-catalog-headers submake-nodes-headers submake-utils-headers
 
 # Make symlinks for these headers in the include directory. That way
 # we can cut down on the -I options. Also, a symlink is automatically
@@ -162,7 +166,7 @@ submake-utils-headers:
 
 .PHONY: generated-headers
 
-generated-headers: $(top_builddir)/src/include/parser/gram.h $(top_builddir)/src/include/storage/lwlocknames.h submake-catalog-headers submake-utils-headers
+generated-headers: $(top_builddir)/src/include/parser/gram.h $(top_builddir)/src/include/storage/lwlocknames.h submake-catalog-headers submake-nodes-headers submake-utils-headers
 
 $(top_builddir)/src/include/parser/gram.h: parser/gram.h
 	prereqdir=`cd '$(dir $<)' >/dev/null && pwd` && \
diff --git a/src/backend/nodes/.gitignore b/src/backend/nodes/.gitignore
new file mode 100644
index 0000000000..232c6e1817
--- /dev/null
+++ b/src/backend/nodes/.gitignore
@@ -0,0 +1,3 @@
+/node-stuff-stamp
+/nodetags.h
+/*funcs.inc?.c
diff --git a/src/backend/nodes/Makefile b/src/backend/nodes/Makefile
index 5d2b12a993..eb93166e9e 100644
--- a/src/backend/nodes/Makefile
+++ b/src/backend/nodes/Makefile
@@ -30,3 +30,58 @@ OBJS = \
 	value.o
 
 include $(top_srcdir)/src/backend/common.mk
+
+node_headers = \
+	nodes/nodes.h \
+	nodes/execnodes.h \
+	nodes/plannodes.h \
+	nodes/primnodes.h \
+	nodes/pathnodes.h \
+	nodes/memnodes.h \
+	nodes/extensible.h \
+	nodes/parsenodes.h \
+	nodes/replnodes.h \
+	commands/trigger.h \
+	commands/event_trigger.h \
+	foreign/fdwapi.h \
+	access/amapi.h \
+	access/tableam.h \
+	access/tsmapi.h \
+	utils/rel.h \
+	nodes/supportnodes.h \
+	executor/tuptable.h \
+	nodes/lockoptions.h \
+	access/sdir.h
+
+node_sources = \
+	executor/nodeWindowAgg.c \
+	nodes/tidbitmap.c \
+	utils/mmgr/aset.c \
+	utils/mmgr/generation.c \
+	utils/mmgr/slab.c
+
+node_files = $(addprefix $(top_srcdir)/src/include/,$(node_headers)) $(addprefix $(top_srcdir)/src/backend/,$(node_sources))
+
+# see also catalog/Makefile for an explanation of these make rules
+
+all: distprep generated-header-symlinks
+
+distprep: node-stuff-stamp
+
+.PHONY: generated-header-symlinks
+
+generated-header-symlinks: $(top_builddir)/src/include/nodes/header-stamp
+
+node-stuff-stamp: gen_node_stuff.pl
+	$(PERL) $< $(node_files)
+	touch $@
+
+$(top_builddir)/src/include/nodes/header-stamp: node-stuff-stamp
+	prereqdir=`cd '$(dir $<)' >/dev/null && pwd` && \
+	cd '$(dir $@)' && for file in nodetags.h; do \
+	  rm -f $$file && $(LN_S) "$$prereqdir/$$file" . ; \
+	done
+	touch $@
+
+maintainer-clean: clean
+	rm -f node-stuff-stamp *funcs.inc?.c nodetags.h
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index dde2d47338..4f57923e9e 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -69,6 +69,9 @@
 	(newnode->fldname = from->fldname)
 
 
+#include "copyfuncs.inc1.c"
+
+#ifdef OBSOLETE
 /* ****************************************************************
  *					 plannodes.h copy functions
  * ****************************************************************
@@ -1450,6 +1453,7 @@ _copyVar(const Var *from)
 
 	return newnode;
 }
+#endif /*OBSOLETE*/
 
 /*
  * _copyConst
@@ -1489,6 +1493,7 @@ _copyConst(const Const *from)
 	return newnode;
 }
 
+#ifdef OBSOLETE
 /*
  * _copyParam
  */
@@ -2722,6 +2727,7 @@ _copyParamRef(const ParamRef *from)
 
 	return newnode;
 }
+#endif /*OBSOLETE*/
 
 static A_Const *
 _copyA_Const(const A_Const *from)
@@ -2754,6 +2760,7 @@ _copyA_Const(const A_Const *from)
 	return newnode;
 }
 
+#ifdef OBSOLETE
 static FuncCall *
 _copyFuncCall(const FuncCall *from)
 {
@@ -4863,6 +4870,7 @@ _copyDropSubscriptionStmt(const DropSubscriptionStmt *from)
 
 	return newnode;
 }
+#endif /*OBSOLETE*/
 
 /* ****************************************************************
  *					extensible.h copy functions
@@ -4919,6 +4927,7 @@ _copyValue(const Value *from)
 }
 
 
+#ifdef OBSOLETE
 static ForeignKeyCacheInfo *
 _copyForeignKeyCacheInfo(const ForeignKeyCacheInfo *from)
 {
@@ -4935,6 +4944,7 @@ _copyForeignKeyCacheInfo(const ForeignKeyCacheInfo *from)
 
 	return newnode;
 }
+#endif /*OBSOLETE*/
 
 
 /*
@@ -4956,6 +4966,8 @@ copyObjectImpl(const void *from)
 
 	switch (nodeTag(from))
 	{
+#include "copyfuncs.inc2.c"
+#ifdef OBSOLETE
 			/*
 			 * PLAN NODES
 			 */
@@ -5297,6 +5309,7 @@ copyObjectImpl(const void *from)
 		case T_PlaceHolderInfo:
 			retval = _copyPlaceHolderInfo(from);
 			break;
+#endif /*OBSOLETE*/
 
 			/*
 			 * VALUE NODES
@@ -5325,6 +5338,7 @@ copyObjectImpl(const void *from)
 			retval = list_copy(from);
 			break;
 
+#ifdef OBSOLETE
 			/*
 			 * EXTENSIBLE NODES
 			 */
@@ -5858,6 +5872,7 @@ copyObjectImpl(const void *from)
 		case T_ForeignKeyCacheInfo:
 			retval = _copyForeignKeyCacheInfo(from);
 			break;
+#endif /*OBSOLETE*/
 
 		default:
 			elog(ERROR, "unrecognized node type: %d", (int) nodeTag(from));
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index e04ec41904..6757337062 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -10,9 +10,6 @@
  * because the circular linkages between RelOptInfo and Path nodes can't
  * be handled easily in a simple depth-first traversal.
  *
- * Currently, in fact, equal() doesn't know how to compare Plan trees
- * either.  This might need to be fixed someday.
- *
  * NOTE: it is intentional that parse location fields (in nodes that have
  * one) are not compared.  This is because we want, for example, a variable
  * "x" to be considered equal() to another reference to "x" in the query.
@@ -33,6 +30,7 @@
 #include "nodes/extensible.h"
 #include "nodes/pathnodes.h"
 #include "utils/datum.h"
+#include "utils/rel.h"
 
 
 /*
@@ -90,6 +88,9 @@
 	((void) 0)
 
 
+#include "equalfuncs.inc1.c"
+
+#ifdef OBSOLETE
 /*
  *	Stuff from primnodes.h
  */
@@ -178,6 +179,7 @@ _equalVar(const Var *a, const Var *b)
 
 	return true;
 }
+#endif /*OBSOLETE*/
 
 static bool
 _equalConst(const Const *a, const Const *b)
@@ -200,6 +202,7 @@ _equalConst(const Const *a, const Const *b)
 						a->constbyval, a->constlen);
 }
 
+#ifdef OBSOLETE
 static bool
 _equalParam(const Param *a, const Param *b)
 {
@@ -933,6 +936,7 @@ _equalPlaceHolderInfo(const PlaceHolderInfo *a, const PlaceHolderInfo *b)
 
 	return true;
 }
+#endif /*OBSOLETE*/
 
 /*
  * Stuff from extensible.h
@@ -954,6 +958,7 @@ _equalExtensibleNode(const ExtensibleNode *a, const ExtensibleNode *b)
 	return true;
 }
 
+#ifdef OBSOLETE
 /*
  * Stuff from parsenodes.h
  */
@@ -2397,6 +2402,7 @@ _equalParamRef(const ParamRef *a, const ParamRef *b)
 
 	return true;
 }
+#endif /*OBSOLETE*/
 
 static bool
 _equalA_Const(const A_Const *a, const A_Const *b)
@@ -2408,6 +2414,7 @@ _equalA_Const(const A_Const *a, const A_Const *b)
 	return true;
 }
 
+#ifdef OBSOLETE
 static bool
 _equalFuncCall(const FuncCall *a, const FuncCall *b)
 {
@@ -3016,6 +3023,7 @@ _equalPartitionCmd(const PartitionCmd *a, const PartitionCmd *b)
 
 	return true;
 }
+#endif /*OBSOLETE*/
 
 /*
  * Stuff from pg_list.h
@@ -3135,6 +3143,8 @@ equal(const void *a, const void *b)
 
 	switch (nodeTag(a))
 	{
+#include "equalfuncs.inc2.c"
+#ifdef OBSOLETE
 			/*
 			 * PRIMITIVE NODES
 			 */
@@ -3313,6 +3323,7 @@ equal(const void *a, const void *b)
 		case T_PlaceHolderInfo:
 			retval = _equalPlaceHolderInfo(a, b);
 			break;
+#endif /*OBSOLETE*/
 
 		case T_List:
 		case T_IntList:
@@ -3328,6 +3339,7 @@ equal(const void *a, const void *b)
 			retval = _equalValue(a, b);
 			break;
 
+#ifdef OBSOLETE
 			/*
 			 * EXTENSIBLE NODES
 			 */
@@ -3854,6 +3866,7 @@ equal(const void *a, const void *b)
 		case T_PartitionCmd:
 			retval = _equalPartitionCmd(a, b);
 			break;
+#endif /*OBSOLETE*/
 
 		default:
 			elog(ERROR, "unrecognized node type: %d",
diff --git a/src/backend/nodes/gen_node_stuff.pl b/src/backend/nodes/gen_node_stuff.pl
new file mode 100644
index 0000000000..aafe320e0a
--- /dev/null
+++ b/src/backend/nodes/gen_node_stuff.pl
@@ -0,0 +1,641 @@
+#!/usr/bin/perl
+#----------------------------------------------------------------------
+#
+# Generate node support files:
+# - nodetags.h
+# - copyfuncs
+# - equalfuncs
+# - readfuncs
+# - outfuncs
+#
+# src/backend/nodes/gen_node_stuff.pl
+#
+#----------------------------------------------------------------------
+
+use strict;
+use warnings;
+
+use experimental 'smartmatch';
+
+use File::Basename;
+
+use FindBin;
+use lib "$FindBin::RealBin/../catalog";
+
+use Catalog;  # for RenameTempFile
+
+
+my @node_types = qw(Node);
+my %node_type_info;
+
+my @no_copy;
+my @no_read_write;
+
+my @scalar_types = qw(
+	bits32 bool char double int int8 int16 int32 int64 long uint8 uint16 uint32 uint64
+	AclMode AttrNumber Cost Index Oid Selectivity Size StrategyNumber SubTransactionId TimeLineID XLogRecPtr
+);
+
+my @enum_types;
+
+# For abstract types we track their fields, so that subtypes can use
+# them, but we don't emit a node tag, so you can't instantiate them.
+my @abstract_types = qw(
+	Node
+	BufferHeapTupleTableSlot HeapTupleTableSlot MinimalTupleTableSlot VirtualTupleTableSlot
+	JoinPath
+	MemoryContextData
+	PartitionPruneStep
+);
+
+# These are additional node tags that don't have their own struct.
+my @extra_tags = qw(IntList OidList Integer Float String BitString Null);
+
+# These are regular nodes, but we skip parsing them from their header
+# files since we won't use their internal structure here anyway.
+push @node_types, qw(List Value);
+
+# XXX maybe this should be abstract?
+push @no_copy, qw(Expr);
+push @no_read_write, qw(Expr);
+
+# pathnodes.h exceptions
+push @no_copy, qw(
+	RelOptInfo IndexOptInfo Path PlannerGlobal EquivalenceClass EquivalenceMember ForeignKeyOptInfo
+	GroupingSetData IncrementalSortPath IndexClause MinMaxAggInfo PathTarget PlannerInfo PlannerParamItem
+	ParamPathInfo RollupData RowIdentityVarInfo StatisticExtInfo
+);
+push @scalar_types, qw(EquivalenceClass* EquivalenceMember* QualCost);
+
+# XXX various things we are not publishing right now to stay level
+# with the manual system
+push @no_copy, qw(CallContext InlineCodeBlock);
+push @no_read_write, qw(AccessPriv AlterTableCmd CallContext CreateOpClassItem FunctionParameter InferClause InlineCodeBlock ObjectWithArgs OnConflictClause PartitionCmd RoleSpec VacuumRelation);
+
+
+## read input
+
+foreach my $infile (@ARGV)
+{
+	my $in_struct;
+	my $subline;
+	my $is_node_struct;
+	my $supertype;
+	my $supertype_field;
+
+	my @my_fields;
+	my %my_field_types;
+	my %my_field_attrs;
+
+	open my $ifh, '<', $infile or die "could not open \"$infile\": $!";
+
+	while (my $line = <$ifh>)
+	{
+		chomp $line;
+		$line =~ s!/\*.*$!!;
+		$line =~ s/\s*$//;
+		next if $line eq '';
+		next if $line =~ m!^\s*\*.*$!;  # line starts with *, probably comment continuation
+		next if $line =~ /^#(define|ifdef|endif)/;
+
+		if ($in_struct)
+		{
+			$subline++;
+
+			# first line should have opening brace
+			if ($subline == 1)
+			{
+				$is_node_struct = 0;
+				$supertype = undef;
+				next if $line eq '{';
+				die;
+			}
+			# second line should have node tag or supertype
+			elsif ($subline == 2)
+			{
+				if ($line =~ /^\s*NodeTag\s+type;/)
+				{
+					$is_node_struct = 1;
+					next;
+				}
+				elsif ($line =~ /\s*(\w+)\s+(\w+);/ && $1 ~~ @node_types)
+				{
+					$is_node_struct = 1;
+					$supertype = $1;
+					$supertype_field = $2;
+					next;
+				}
+			}
+
+			# end of struct
+			if ($line =~ /^\}\s*$in_struct;$/ || $line =~ /^\};$/)
+			{
+				if ($is_node_struct)
+				{
+					push @node_types, $in_struct;
+					my @f = @my_fields;
+					my %ft = %my_field_types;
+					my %fa = %my_field_attrs;
+					if ($supertype)
+					{
+						my @superfields;
+						foreach my $sf (@{$node_type_info{$supertype}->{fields}})
+						{
+							my $fn = "${supertype_field}.$sf";
+							push @superfields, $fn;
+							$ft{$fn} = $node_type_info{$supertype}->{field_types}{$sf};
+							$fa{$fn} = $node_type_info{$supertype}->{field_attrs}{$sf};
+						}
+						unshift @f, @superfields;
+					}
+					$node_type_info{$in_struct}->{fields} = \@f;
+					$node_type_info{$in_struct}->{field_types} = \%ft;
+					$node_type_info{$in_struct}->{field_attrs} = \%fa;
+
+					if (basename($infile) eq 'execnodes.h' ||
+						basename($infile) eq 'trigger.h' ||
+						basename($infile) eq 'event_trigger.h' ||
+						basename($infile) eq 'amapi.h' ||
+						basename($infile) eq 'tableam.h' ||
+						basename($infile) eq 'tsmapi.h' ||
+						basename($infile) eq 'fdwapi.h' ||
+						basename($infile) eq 'tuptable.h' ||
+						basename($infile) eq 'replnodes.h' ||
+						basename($infile) eq 'supportnodes.h' ||
+						$infile =~ /\.c$/
+					)
+					{
+						push @no_copy, $in_struct;
+						push @no_read_write, $in_struct;
+					}
+
+					if ($supertype && ($supertype eq 'Path' || $supertype eq 'JoinPath'))
+					{
+						push @no_copy, $in_struct;
+					}
+				}
+
+				# start new cycle
+				$in_struct = undef;
+				@my_fields = ();
+				%my_field_types = ();
+				%my_field_attrs = ();
+			}
+			# normal struct field
+			elsif ($line =~ /^\s*(.+)\s*\b(\w+)(\[\w+\])?\s*(?:pg_node_attr\(([\w ]*)\))?;/)
+			{
+				if ($is_node_struct)
+				{
+					my $type = $1;
+					my $name = $2;
+					my $array_size = $3;
+					my $attr = $4;
+
+					$type =~ s/^const\s*//;
+					$type =~ s/\s*$//;
+					$type =~ s/\s+\*$/*/;
+					die if $type eq '';
+					$type = $type . $array_size if $array_size;
+					push @my_fields, $name;
+					$my_field_types{$name} = $type;
+					$my_field_attrs{$name} = $attr;
+				}
+			}
+			else
+			{
+				if ($is_node_struct)
+				{
+					#warn "$infile:$.: could not parse \"$line\"\n";
+				}
+			}
+		}
+		# not in a struct
+		else
+		{
+			# start of a struct?
+			if ($line =~ /^(?:typedef )?struct (\w+)(\s*\/\*.*)?$/ && $1 ne 'Node')
+			{
+				$in_struct = $1;
+				$subline = 0;
+			}
+			# one node type typedef'ed directly from another
+			elsif ($line =~ /^typedef (\w+) (\w+);$/ && $1 ~~ @node_types)
+			{
+				my $alias_of = $1;
+				my $n = $2;
+
+				push @node_types, $n;
+				my @f = @{$node_type_info{$alias_of}->{fields}};
+				my %ft = %{$node_type_info{$alias_of}->{field_types}};
+				my %fa = %{$node_type_info{$alias_of}->{field_attrs}};
+				$node_type_info{$n}->{fields} = \@f;
+				$node_type_info{$n}->{field_types} = \%ft;
+				$node_type_info{$n}->{field_attrs} = \%fa;
+			}
+			# collect enum names
+			elsif ($line =~ /^typedef enum (\w+)(\s*\/\*.*)?$/)
+			{
+				push @enum_types, $1;
+			}
+		}
+	}
+
+	if ($in_struct)
+	{
+		die "runaway \"$in_struct\" in file \"$infile\"\n";
+	}
+
+	close $ifh;
+} # for each file
+
+
+## write output
+
+my $tmpext  = ".tmp$$";
+
+# nodetags.h
+
+open my $nt, '>', 'nodetags.h' . $tmpext or die $!;
+
+my $i = 1;
+foreach my $n (@node_types,@extra_tags)
+{
+	next if $n ~~ @abstract_types;
+	print $nt "\tT_${n} = $i,\n";
+	$i++;
+}
+
+close $nt;
+
+
+# copyfuncs.c, equalfuncs.c
+
+open my $cf, '>', 'copyfuncs.inc1.c' . $tmpext or die $!;
+open my $ef, '>', 'equalfuncs.inc1.c' . $tmpext or die $!;
+open my $cf2, '>', 'copyfuncs.inc2.c' . $tmpext or die $!;
+open my $ef2, '>', 'equalfuncs.inc2.c' . $tmpext or die $!;
+
+my @custom_copy = qw(A_Const Const ExtensibleNode);
+
+foreach my $n (@node_types)
+{
+	next if $n ~~ @abstract_types;
+	next if $n ~~ @no_copy;
+	next if $n eq 'List';
+	next if $n eq 'Value';
+
+	print $cf2 "
+\t\tcase T_${n}:
+\t\t\tretval = _copy${n}(from);
+\t\t\tbreak;";
+
+	print $ef2 "
+\t\tcase T_${n}:
+\t\t\tretval = _equal${n}(a, b);
+\t\t\tbreak;";
+
+	next if $n ~~ @custom_copy;
+
+	print $cf "
+static $n *
+_copy${n}(const $n *from)
+{
+\t${n} *newnode = makeNode($n);
+
+";
+
+	print $ef "
+static bool
+_equal${n}(const $n *a, const $n *b)
+{
+";
+
+	my $last_array_size_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 $copy_ignore = ($a =~ /\bcopy_ignore\b/);
+		my $equal_ignore = ($a =~ /\bequal_ignore\b/);
+		if ($t eq 'char*')
+		{
+			print $cf "\tCOPY_STRING_FIELD($f);\n" unless $copy_ignore;
+			print $ef "\tCOMPARE_STRING_FIELD($f);\n" unless $equal_ignore;
+		}
+		elsif ($t eq 'Bitmapset*' || $t eq 'Relids')
+		{
+			print $cf "\tCOPY_BITMAPSET_FIELD($f);\n" unless $copy_ignore;
+			print $ef "\tCOMPARE_BITMAPSET_FIELD($f);\n" unless $equal_ignore;
+		}
+		elsif ($t eq 'int' && $f =~ 'location$')
+		{
+			print $cf "\tCOPY_LOCATION_FIELD($f);\n" unless $copy_ignore;
+			print $ef "\tCOMPARE_LOCATION_FIELD($f);\n" unless $equal_ignore;
+		}
+		elsif ($t ~~ @scalar_types || $t ~~ @enum_types)
+		{
+			print $cf "\tCOPY_SCALAR_FIELD($f);\n" unless $copy_ignore;
+			if ($a =~ /\bequal_ignore_if_zero\b/)
+			{
+				print $ef "\tif (a->$f != b->$f && a->$f != 0 && b->$f != 0)\n\t\treturn false;\n";
+			}
+			else
+			{
+				print $ef "\tCOMPARE_SCALAR_FIELD($f);\n" unless $equal_ignore || $t eq 'CoercionForm';
+			}
+			$last_array_size_field = "from->$f";
+		}
+		elsif ($t =~ /(\w+)\*/ && $1 ~~ @scalar_types)
+		{
+			my $tt = $1;
+			print $cf "\tCOPY_POINTER_FIELD($f, $last_array_size_field * sizeof($tt));\n" unless $copy_ignore;
+			(my $l2 = $last_array_size_field) =~ s/from/a/;
+			print $ef "\tCOMPARE_POINTER_FIELD($f, $l2 * sizeof($tt));\n" unless $equal_ignore;
+		}
+		elsif ($t =~ /(\w+)\*/ && $1 ~~ @node_types)
+		{
+			print $cf "\tCOPY_NODE_FIELD($f);\n" unless $copy_ignore;
+			print $ef "\tCOMPARE_NODE_FIELD($f);\n" unless $equal_ignore;
+			$last_array_size_field = "list_length(from->$f)" if $t eq 'List*';
+		}
+		elsif ($t =~ /\w+\[/)
+		{
+			# COPY_SCALAR_FIELD might work for these, but let's not assume that
+			print $cf "\tmemcpy(newnode->$f, from->$f, sizeof(newnode->$f));\n" unless $copy_ignore;
+			print $ef "\tCOMPARE_POINTER_FIELD($f, sizeof(a->$f));\n" unless $equal_ignore;
+		}
+		elsif ($t eq 'struct CustomPathMethods*' ||	$t eq 'struct CustomScanMethods*')
+		{
+			print $cf "\tCOPY_SCALAR_FIELD($f);\n" unless $copy_ignore;
+			print $ef "\tCOMPARE_SCALAR_FIELD($f);\n" unless $equal_ignore;
+		}
+		else
+		{
+			die "could not handle type \"$t\" in struct \"$n\" field \"$f\"";
+		}
+	}
+
+	print $cf "
+\treturn newnode;
+}
+";
+	print $ef "
+\treturn true;
+}
+";
+}
+
+close $cf;
+close $ef;
+close $cf2;
+close $ef2;
+
+
+# outfuncs.c, readfuncs.c
+
+open my $of, '>', 'outfuncs.inc1.c' . $tmpext or die $!;
+open my $rf, '>', 'readfuncs.inc1.c' . $tmpext or die $!;
+open my $of2, '>', 'outfuncs.inc2.c' . $tmpext or die $!;
+open my $rf2, '>', 'readfuncs.inc2.c' . $tmpext or die $!;
+
+my %name_map = (
+	'ARRAYEXPR' => 'ARRAY',
+	'CASEEXPR' => 'CASE',
+	'CASEWHEN' => 'WHEN',
+	'COALESCEEXPR' => 'COALESCE',
+	'COLLATEEXPR' => 'COLLATE',
+	'DECLARECURSORSTMT' => 'DECLARECURSOR',
+	'MINMAXEXPR' => 'MINMAX',
+	'NOTIFYSTMT' => 'NOTIFY',
+	'RANGETBLENTRY' => 'RTE',
+	'ROWCOMPAREEXPR' => 'ROWCOMPARE',
+	'ROWEXPR' => 'ROW',
+);
+
+my @custom_readwrite = qw(A_Const A_Expr BoolExpr Const Constraint ExtensibleNode Query RangeTblEntry);
+
+foreach my $n (@node_types)
+{
+	next if $n ~~ @abstract_types;
+	next if $n ~~ @no_read_write;
+	next if $n eq 'List';
+	next if $n eq 'Value';
+
+	# XXX For now, skip all "Stmt"s except that ones that were there before.
+	if ($n =~ /Stmt$/)
+	{
+		my @keep = qw(AlterStatsStmt CreateForeignTableStmt CreateStatsStmt CreateStmt DeclareCursorStmt ImportForeignSchemaStmt IndexStmt NotifyStmt PlannedStmt PLAssignStmt RawStmt ReturnStmt SelectStmt SetOperationStmt);
+		next unless $n ~~ @keep;
+	}
+
+	# XXX Also skip read support for those that didn't have it before.
+	my $no_read = ($n eq 'A_Star' || $n eq 'A_Const' || $n eq 'A_Expr' || $n eq 'Constraint' || $n =~ /Path$/ || $n eq 'ForeignKeyCacheInfo' || $n eq 'ForeignKeyOptInfo' || $n eq 'PathTarget');
+
+	my $N = uc $n;
+	$N =~ s/_//g;
+	$N = $name_map{$N} if $name_map{$N};
+
+	print $of2 "\t\t\tcase T_${n}:\n".
+	  "\t\t\t\t_out${n}(str, obj);\n".
+	  "\t\t\t\tbreak;\n";
+
+	print $rf2 "\telse if (MATCH(\"$N\", " . length($N) . "))\n".
+	  "\t\treturn_value = _read${n}();\n" unless $no_read;
+
+	next if $n ~~ @custom_readwrite;
+
+	print $of "
+static void
+_out${n}(StringInfo str, const $n *node)
+{
+\tWRITE_NODE_TYPE(\"$N\");
+
+";
+
+	print $rf "
+static $n *
+_read${n}(void)
+{
+\tREAD_LOCALS($n);
+
+" unless $no_read;
+
+	my $last_array_size_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 $readwrite_ignore = ($a =~ /\breadwrite_ignore\b/);
+		next if $readwrite_ignore;
+
+		# XXX Previously, for subtyping, only the leaf field name is
+		# used. Ponder whether we want to keep it that way.
+
+		if ($t eq 'bool')
+		{
+			print $of "\tWRITE_BOOL_FIELD($f);\n";
+			print $rf "\tREAD_BOOL_FIELD($f);\n" unless $no_read;
+		}
+		elsif ($t eq 'int' && $f =~ 'location$')
+		{
+			print $of "\tWRITE_LOCATION_FIELD($f);\n";
+			print $rf "\tREAD_LOCATION_FIELD($f);\n" unless $no_read;
+		}
+		elsif ($t eq 'int' || $t eq 'int32' || $t eq 'AttrNumber' || $t eq 'StrategyNumber')
+		{
+			print $of "\tWRITE_INT_FIELD($f);\n";
+			print $rf "\tREAD_INT_FIELD($f);\n" unless $no_read;
+			$last_array_size_field = "node->$f" if $t eq 'int';
+		}
+		elsif ($t eq 'uint32' || $t eq 'bits32' || $t eq 'AclMode' || $t eq 'BlockNumber' || $t eq 'Index' || $t eq 'SubTransactionId')
+		{
+			print $of "\tWRITE_UINT_FIELD($f);\n";
+			print $rf "\tREAD_UINT_FIELD($f);\n" unless $no_read;
+		}
+		elsif ($t eq 'uint64')
+		{
+			print $of "\tWRITE_UINT64_FIELD($f);\n";
+			print $rf "\tREAD_UINT64_FIELD($f);\n" unless $no_read;
+		}
+		elsif ($t eq 'Oid')
+		{
+			print $of "\tWRITE_OID_FIELD($f);\n";
+			print $rf "\tREAD_OID_FIELD($f);\n" unless $no_read;
+		}
+		elsif ($t eq 'long')
+		{
+			print $of "\tWRITE_LONG_FIELD($f);\n";
+			print $rf "\tREAD_LONG_FIELD($f);\n" unless $no_read;
+		}
+		elsif ($t eq 'char')
+		{
+			print $of "\tWRITE_CHAR_FIELD($f);\n";
+			print $rf "\tREAD_CHAR_FIELD($f);\n" unless $no_read;
+		}
+		elsif ($t eq 'double')
+		{
+			# XXX We out to split these into separate types, like Cost
+			# etc.
+			if ($f eq 'allvisfrac')
+			{
+				print $of "\tWRITE_FLOAT_FIELD($f, \"%.6f\");\n";
+			}
+			else
+			{
+				print $of "\tWRITE_FLOAT_FIELD($f, \"%.0f\");\n";
+			}
+			print $rf "\tREAD_FLOAT_FIELD($f);\n" unless $no_read;
+		}
+		elsif ($t eq 'Cost')
+		{
+			print $of "\tWRITE_FLOAT_FIELD($f, \"%.2f\");\n";
+			print $rf "\tREAD_FLOAT_FIELD($f);\n" unless $no_read;
+		}
+		elsif ($t eq 'QualCost')
+		{
+			print $of "\tWRITE_FLOAT_FIELD($f.startup, \"%.2f\");\n";
+			print $of "\tWRITE_FLOAT_FIELD($f.per_tuple, \"%.2f\");\n";
+			print $rf "\tREAD_FLOAT_FIELD($f.startup);\n" unless $no_read;
+			print $rf "\tREAD_FLOAT_FIELD($f.per_tuple);\n" unless $no_read;
+		}
+		elsif ($t eq 'Selectivity')
+		{
+			print $of "\tWRITE_FLOAT_FIELD($f, \"%.4f\");\n";
+			print $rf "\tREAD_FLOAT_FIELD($f);\n" unless $no_read;
+		}
+		elsif ($t eq 'char*')
+		{
+			print $of "\tWRITE_STRING_FIELD($f);\n";
+			print $rf "\tREAD_STRING_FIELD($f);\n" unless $no_read;
+		}
+		elsif ($t eq 'Bitmapset*' || $t eq 'Relids')
+		{
+			print $of "\tWRITE_BITMAPSET_FIELD($f);\n";
+			print $rf "\tREAD_BITMAPSET_FIELD($f);\n" unless $no_read;
+		}
+		elsif ($t ~~ @enum_types)
+		{
+			print $of "\tWRITE_ENUM_FIELD($f, $t);\n";
+			print $rf "\tREAD_ENUM_FIELD($f, $t);\n" unless $no_read;
+		}
+		elsif ($t =~ /(\w+)(\*|\[)/ && $1 ~~ @scalar_types)
+		{
+			warn "$t $n.$f" unless $last_array_size_field;
+			my $tt = uc $1;
+			print $of "\tWRITE_${tt}_ARRAY($f, $last_array_size_field);\n";
+			(my $l2 = $last_array_size_field) =~ s/node/local_node/;
+			print $rf "\tREAD_${tt}_ARRAY($f, $l2);\n" unless $no_read;
+		}
+		elsif ($t eq 'RelOptInfo*' && $a eq 'path_hack1')
+		{
+			print $of "\tappendStringInfoString(str, \" :parent_relids \");\n".
+			  "\toutBitmapset(str, node->$f->relids);\n";
+		}
+		elsif ($t eq 'PathTarget*' && $a eq 'path_hack2')
+		{
+			(my $f2 = $f) =~ s/pathtarget/parent/;
+			print $of "\tif (node->$f != node->$f2->reltarget)\n".
+			  "\t\tWRITE_NODE_FIELD($f);\n";
+		}
+		elsif ($t eq 'ParamPathInfo*' && $a eq 'path_hack3')
+		{
+			print $of "\tif (node->$f)\n".
+			  "\t\toutBitmapset(str, node->$f->ppi_req_outer);\n".
+			  "\telse\n".
+			  "\t\toutBitmapset(str, NULL);\n";
+		}
+		elsif ($t =~ /(\w+)\*/ && $1 ~~ @node_types)
+		{
+			print $of "\tWRITE_NODE_FIELD($f);\n";
+			print $rf "\tREAD_NODE_FIELD($f);\n" unless $no_read;
+			$last_array_size_field = "list_length(node->$f)" if $t eq 'List*';
+		}
+		elsif ($t eq 'struct CustomPathMethods*' ||	$t eq 'struct CustomScanMethods*')
+		{
+			print $of q{
+	appendStringInfoString(str, " :methods ");
+	outToken(str, node->methods->CustomName);
+};
+			print $rf q!
+	{
+		/* Lookup CustomScanMethods by CustomName */
+		char	   *custom_name;
+		const CustomScanMethods *methods;
+		token = pg_strtok(&length); /* skip methods: */
+		token = pg_strtok(&length); /* CustomName */
+		custom_name = nullable_string(token, length);
+		methods = GetCustomScanMethods(custom_name, false);
+		local_node->methods = methods;
+	}
+! unless $no_read;
+		}
+		elsif ($t eq 'ParamListInfo' || $t =~ /PartitionBoundInfoData/ || $t eq 'PartitionDirectory' || $t eq 'PartitionScheme' || $t eq 'void*' || $t =~ /\*\*$/)
+		{
+			# ignore
+		}
+		else
+		{
+			die "could not handle type \"$t\" in struct \"$n\" field \"$f\"";
+		}
+	}
+
+	print $of "}
+";
+	print $rf "
+\tREAD_DONE();
+}
+" unless $no_read;
+}
+
+close $of;
+close $rf;
+close $of2;
+close $rf2;
+
+
+foreach my $file (qw(nodetags.h copyfuncs.inc1.c copyfuncs.inc2.c equalfuncs.inc1.c equalfuncs.inc2.c outfuncs.inc1.c outfuncs.inc2.c readfuncs.inc1.c readfuncs.inc2.c))
+{
+	Catalog::RenameTempFile($file, $tmpext);
+}
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index ed08f5acf4..0978bc4e81 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -124,6 +124,8 @@ static void outChar(StringInfo str, char c);
 			appendStringInfo(str, " %u", node->fldname[i]); \
 	} while(0)
 
+#define WRITE_INDEX_ARRAY(fldname, len) WRITE_OID_ARRAY(fldname, len)
+
 #define WRITE_INT_ARRAY(fldname, len) \
 	do { \
 		appendStringInfoString(str, " :" CppAsString(fldname) " "); \
@@ -288,6 +290,9 @@ outDatum(StringInfo str, Datum value, int typlen, bool typbyval)
 }
 
 
+#include "outfuncs.inc1.c"
+
+#ifdef OBSOLETE
 /*
  *	Stuff from plannodes.h
  */
@@ -1126,6 +1131,7 @@ _outVar(StringInfo str, const Var *node)
 	WRITE_INT_FIELD(varattnosyn);
 	WRITE_LOCATION_FIELD(location);
 }
+#endif /*OBSOLETE*/
 
 static void
 _outConst(StringInfo str, const Const *node)
@@ -1147,6 +1153,7 @@ _outConst(StringInfo str, const Const *node)
 		outDatum(str, node->constvalue, node->constlen, node->constbyval);
 }
 
+#ifdef OBSOLETE
 static void
 _outParam(StringInfo str, const Param *node)
 {
@@ -1316,6 +1323,7 @@ _outScalarArrayOpExpr(StringInfo str, const ScalarArrayOpExpr *node)
 	WRITE_NODE_FIELD(args);
 	WRITE_LOCATION_FIELD(location);
 }
+#endif /*OBSOLETE*/
 
 static void
 _outBoolExpr(StringInfo str, const BoolExpr *node)
@@ -1344,6 +1352,7 @@ _outBoolExpr(StringInfo str, const BoolExpr *node)
 	WRITE_LOCATION_FIELD(location);
 }
 
+#ifdef OBSOLETE
 static void
 _outSubLink(StringInfo str, const SubLink *node)
 {
@@ -2658,6 +2667,7 @@ _outPlannerParamItem(StringInfo str, const PlannerParamItem *node)
 	WRITE_NODE_FIELD(item);
 	WRITE_INT_FIELD(paramId);
 }
+#endif /*OBSOLETE*/
 
 /*****************************************************************************
  *
@@ -2680,6 +2690,7 @@ _outExtensibleNode(StringInfo str, const ExtensibleNode *node)
 	methods->nodeOut(str, node);
 }
 
+#ifdef OBSOLETE
 /*****************************************************************************
  *
  *	Stuff from parsenodes.h.
@@ -3012,6 +3023,7 @@ _outStatsElem(StringInfo str, const StatsElem *node)
 	WRITE_STRING_FIELD(name);
 	WRITE_NODE_FIELD(expr);
 }
+#endif /*OBSOLETE*/
 
 static void
 _outQuery(StringInfo str, const Query *node)
@@ -3084,6 +3096,7 @@ _outQuery(StringInfo str, const Query *node)
 	WRITE_INT_FIELD(stmt_len);
 }
 
+#ifdef OBSOLETE
 static void
 _outWithCheckOption(StringInfo str, const WithCheckOption *node)
 {
@@ -3222,6 +3235,7 @@ _outSetOperationStmt(StringInfo str, const SetOperationStmt *node)
 	WRITE_NODE_FIELD(colCollations);
 	WRITE_NODE_FIELD(groupClauses);
 }
+#endif /*OBSOLETE*/
 
 static void
 _outRangeTblEntry(StringInfo str, const RangeTblEntry *node)
@@ -3302,6 +3316,7 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node)
 	WRITE_NODE_FIELD(securityQuals);
 }
 
+#ifdef OBSOLETE
 static void
 _outRangeTblFunction(StringInfo str, const RangeTblFunction *node)
 {
@@ -3325,6 +3340,7 @@ _outTableSampleClause(StringInfo str, const TableSampleClause *node)
 	WRITE_NODE_FIELD(args);
 	WRITE_NODE_FIELD(repeatable);
 }
+#endif /*OBSOLETE*/
 
 static void
 _outA_Expr(StringInfo str, const A_Expr *node)
@@ -3442,6 +3458,7 @@ _outValue(StringInfo str, const Value *node)
 	}
 }
 
+#ifdef OBSOLETE
 static void
 _outColumnRef(StringInfo str, const ColumnRef *node)
 {
@@ -3473,6 +3490,7 @@ _outRawStmt(StringInfo str, const RawStmt *node)
 	WRITE_LOCATION_FIELD(stmt_location);
 	WRITE_INT_FIELD(stmt_len);
 }
+#endif /*OBSOLETE*/
 
 static void
 _outA_Const(StringInfo str, const A_Const *node)
@@ -3484,6 +3502,7 @@ _outA_Const(StringInfo str, const A_Const *node)
 	WRITE_LOCATION_FIELD(location);
 }
 
+#ifdef OBSOLETE
 static void
 _outA_Star(StringInfo str, const A_Star *node)
 {
@@ -3628,6 +3647,7 @@ _outRangeTableFuncCol(StringInfo str, const RangeTableFuncCol *node)
 	WRITE_NODE_FIELD(coldefexpr);
 	WRITE_LOCATION_FIELD(location);
 }
+#endif /*OBSOLETE*/
 
 static void
 _outConstraint(StringInfo str, const Constraint *node)
@@ -3748,6 +3768,7 @@ _outConstraint(StringInfo str, const Constraint *node)
 	}
 }
 
+#ifdef OBSOLETE
 static void
 _outForeignKeyCacheInfo(StringInfo str, const ForeignKeyCacheInfo *node)
 {
@@ -3808,6 +3829,7 @@ _outPartitionRangeDatum(StringInfo str, const PartitionRangeDatum *node)
 	WRITE_NODE_FIELD(value);
 	WRITE_LOCATION_FIELD(location);
 }
+#endif /*OBSOLETE*/
 
 /*
  * outNode -
@@ -3836,6 +3858,8 @@ outNode(StringInfo str, const void *obj)
 		appendStringInfoChar(str, '{');
 		switch (nodeTag(obj))
 		{
+#include "outfuncs.inc2.c"
+#ifdef OBSOLETE
 			case T_PlannedStmt:
 				_outPlannedStmt(str, obj);
 				break;
@@ -4505,6 +4529,7 @@ outNode(StringInfo str, const void *obj)
 			case T_PartitionRangeDatum:
 				_outPartitionRangeDatum(str, obj);
 				break;
+#endif /*OBSOLETE*/
 
 			default:
 
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index fef4de94e6..b4b9379d9d 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -240,6 +240,8 @@ readBitmapset(void)
 	return _readBitmapset();
 }
 
+#include "readfuncs.inc1.c"
+
 /*
  * _readQuery
  */
@@ -291,6 +293,7 @@ _readQuery(void)
 	READ_DONE();
 }
 
+#ifdef OBSOLETE
 /*
  * _readNotifyStmt
  */
@@ -589,6 +592,7 @@ _readVar(void)
 
 	READ_DONE();
 }
+#endif /*OBSOLETE*/
 
 /*
  * _readConst
@@ -615,6 +619,7 @@ _readConst(void)
 	READ_DONE();
 }
 
+#ifdef OBSOLETE
 /*
  * _readParam
  */
@@ -839,6 +844,7 @@ _readScalarArrayOpExpr(void)
 
 	READ_DONE();
 }
+#endif /*OBSOLETE*/
 
 /*
  * _readBoolExpr
@@ -866,6 +872,7 @@ _readBoolExpr(void)
 	READ_DONE();
 }
 
+#ifdef OBSOLETE
 /*
  * _readSubLink
  */
@@ -1420,6 +1427,7 @@ _readAppendRelInfo(void)
 /*
  *	Stuff from parsenodes.h.
  */
+#endif /*OBSOLETE*/
 
 /*
  * _readRangeTblEntry
@@ -1515,6 +1523,7 @@ _readRangeTblEntry(void)
 	READ_DONE();
 }
 
+#ifdef OBSOLETE
 /*
  * _readRangeTblFunction
  */
@@ -2635,6 +2644,7 @@ _readAlternativeSubPlan(void)
 
 	READ_DONE();
 }
+#endif /*OBSOLETE*/
 
 /*
  * _readExtensibleNode
@@ -2666,6 +2676,7 @@ _readExtensibleNode(void)
 	READ_DONE();
 }
 
+#ifdef OBSOLETE
 /*
  * _readPartitionBoundSpec
  */
@@ -2700,6 +2711,7 @@ _readPartitionRangeDatum(void)
 
 	READ_DONE();
 }
+#endif /*OBSOLETE*/
 
 /*
  * parseNodeString
@@ -2724,7 +2736,11 @@ parseNodeString(void)
 #define MATCH(tokname, namelen) \
 	(length == namelen && memcmp(token, tokname, namelen) == 0)
 
-	if (MATCH("QUERY", 5))
+	if (false)
+		;
+#include "readfuncs.inc2.c"
+#ifdef OBSOLETE
+	else if (MATCH("QUERY", 5))
 		return_value = _readQuery();
 	else if (MATCH("WITHCHECKOPTION", 15))
 		return_value = _readWithCheckOption();
@@ -2972,6 +2988,7 @@ parseNodeString(void)
 		return_value = _readPartitionBoundSpec();
 	else if (MATCH("PARTITIONRANGEDATUM", 19))
 		return_value = _readPartitionRangeDatum();
+#endif /*OBSOLETE*/
 	else
 	{
 		elog(ERROR, "badly formatted node string \"%.32s\"...", token);
diff --git a/src/include/nodes/.gitignore b/src/include/nodes/.gitignore
new file mode 100644
index 0000000000..99fb1d3787
--- /dev/null
+++ b/src/include/nodes/.gitignore
@@ -0,0 +1,2 @@
+/nodetags.h
+/header-stamp
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 5e049a67e1..cb140a3b93 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -27,6 +27,8 @@ typedef enum NodeTag
 {
 	T_Invalid = 0,
 
+#include "nodes/nodetags.h"
+#ifdef OBSOLETE
 	/*
 	 * TAGS FOR EXECUTOR NODES (execnodes.h)
 	 */
@@ -527,8 +529,14 @@ typedef enum NodeTag
 	T_SupportRequestCost,		/* in nodes/supportnodes.h */
 	T_SupportRequestRows,		/* in nodes/supportnodes.h */
 	T_SupportRequestIndexCondition	/* in nodes/supportnodes.h */
+#endif /*OBSOLETE*/
 } NodeTag;
 
+/*
+ * used in node definitions to set extra information for gen_node_stuff.pl
+ */
+#define pg_node_attr(x)
+
 /*
  * The first field of a node of any type is guaranteed to be the NodeTag.
  * Hence the type of any node can be gotten by casting it to Node. Declaring
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index cd9115dbb5..b35d1414de 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -121,7 +121,7 @@ typedef struct Query
 
 	QuerySource querySource;	/* where did I come from? */
 
-	uint64		queryId;		/* query identifier (can be set by plugins) */
+	uint64		queryId pg_node_attr(equal_ignore);		/* query identifier (can be set by plugins) */
 
 	bool		canSetTag;		/* do I set the command result tag? */
 
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 03c80c1620..b98d1958fc 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -226,7 +226,7 @@ struct PlannerInfo
 	 * GEQO.
 	 */
 	List	   *join_rel_list;	/* list of join-relation RelOptInfos */
-	struct HTAB *join_rel_hash; /* optional hashtable for join relations */
+	struct HTAB *join_rel_hash pg_node_attr(readwrite_ignore); /* optional hashtable for join relations */
 
 	/*
 	 * When doing a dynamic-programming-style join search, join_rel_level[k]
@@ -331,7 +331,7 @@ struct PlannerInfo
 	AttrNumber *grouping_map;	/* for GroupingFunc fixup */
 	List	   *minmax_aggs;	/* List of MinMaxAggInfos */
 
-	MemoryContext planner_cxt;	/* context holding PlannerInfo */
+	MemoryContext planner_cxt pg_node_attr(readwrite_ignore);	/* context holding PlannerInfo */
 
 	double		total_table_pages;	/* # of pages in all non-dummy tables of
 									 * query */
@@ -706,8 +706,8 @@ typedef struct RelOptInfo
 	RTEKind		rtekind;		/* RELATION, SUBQUERY, FUNCTION, etc */
 	AttrNumber	min_attr;		/* smallest attrno of rel (often <0) */
 	AttrNumber	max_attr;		/* largest attrno of rel */
-	Relids	   *attr_needed;	/* array indexed [min_attr .. max_attr] */
-	int32	   *attr_widths;	/* array indexed [min_attr .. max_attr] */
+	Relids	   *attr_needed pg_node_attr(readwrite_ignore);	/* array indexed [min_attr .. max_attr] */
+	int32	   *attr_widths pg_node_attr(readwrite_ignore);	/* array indexed [min_attr .. max_attr] */
 	List	   *lateral_vars;	/* LATERAL Vars and PHVs referenced by rel */
 	Relids		lateral_referencers;	/* rels that reference me laterally */
 	List	   *indexlist;		/* list of IndexOptInfo */
@@ -728,13 +728,13 @@ typedef struct RelOptInfo
 	Oid			userid;			/* identifies user to check access as */
 	bool		useridiscurrent;	/* join is only valid for current user */
 	/* use "struct FdwRoutine" to avoid including fdwapi.h here */
-	struct FdwRoutine *fdwroutine;
+	struct FdwRoutine *fdwroutine pg_node_attr(readwrite_ignore);
 	void	   *fdw_private;
 
 	/* cache space for remembering if we have proven this relation unique */
-	List	   *unique_for_rels;	/* known unique for these other relid
+	List	   *unique_for_rels pg_node_attr(readwrite_ignore);	/* known unique for these other relid
 									 * set(s) */
-	List	   *non_unique_for_rels;	/* known not unique for these set(s) */
+	List	   *non_unique_for_rels pg_node_attr(readwrite_ignore);	/* known not unique for these set(s) */
 
 	/* used by various scans and joins: */
 	List	   *baserestrictinfo;	/* RestrictInfo structures (if base rel) */
@@ -829,7 +829,7 @@ struct IndexOptInfo
 
 	Oid			indexoid;		/* OID of the index relation */
 	Oid			reltablespace;	/* tablespace of index (not table) */
-	RelOptInfo *rel;			/* back-link to index's table */
+	RelOptInfo *rel pg_node_attr(readwrite_ignore);			/* back-link to index's table */
 
 	/* index-size statistics (from pg_class and elsewhere) */
 	BlockNumber pages;			/* number of disk pages in index */
@@ -839,20 +839,20 @@ struct IndexOptInfo
 	/* index descriptor information */
 	int			ncolumns;		/* number of columns in index */
 	int			nkeycolumns;	/* number of key columns in index */
-	int		   *indexkeys;		/* column numbers of index's attributes both
+	int		   *indexkeys pg_node_attr(readwrite_ignore);		/* column numbers of index's attributes both
 								 * key and included columns, or 0 */
-	Oid		   *indexcollations;	/* OIDs of collations of index columns */
-	Oid		   *opfamily;		/* OIDs of operator families for columns */
-	Oid		   *opcintype;		/* OIDs of opclass declared input data types */
-	Oid		   *sortopfamily;	/* OIDs of btree opfamilies, if orderable */
-	bool	   *reverse_sort;	/* is sort order descending? */
-	bool	   *nulls_first;	/* do NULLs come first in the sort order? */
-	bytea	  **opclassoptions; /* opclass-specific options for columns */
-	bool	   *canreturn;		/* which index cols can be returned in an
+	Oid		   *indexcollations pg_node_attr(readwrite_ignore);	/* OIDs of collations of index columns */
+	Oid		   *opfamily pg_node_attr(readwrite_ignore);		/* OIDs of operator families for columns */
+	Oid		   *opcintype pg_node_attr(readwrite_ignore);		/* OIDs of opclass declared input data types */
+	Oid		   *sortopfamily pg_node_attr(readwrite_ignore);	/* OIDs of btree opfamilies, if orderable */
+	bool	   *reverse_sort pg_node_attr(readwrite_ignore);	/* is sort order descending? */
+	bool	   *nulls_first pg_node_attr(readwrite_ignore);	/* do NULLs come first in the sort order? */
+	bytea	  **opclassoptions pg_node_attr(readwrite_ignore); /* opclass-specific options for columns */
+	bool	   *canreturn pg_node_attr(readwrite_ignore);		/* which index cols can be returned in an
 								 * index-only scan? */
 	Oid			relam;			/* OID of the access method (in pg_am) */
 
-	List	   *indexprs;		/* expressions for non-simple index columns */
+	List	   *indexprs pg_node_attr(readwrite_ignore);		/* expressions for non-simple index columns */
 	List	   *indpred;		/* predicate if a partial index, else NIL */
 
 	List	   *indextlist;		/* targetlist representing index columns */
@@ -869,14 +869,14 @@ struct IndexOptInfo
 	bool		hypothetical;	/* true if index doesn't really exist */
 
 	/* Remaining fields are copied from the index AM's API struct: */
-	bool		amcanorderbyop; /* does AM support order by operator result? */
-	bool		amoptionalkey;	/* can query omit key for the first column? */
-	bool		amsearcharray;	/* can AM handle ScalarArrayOpExpr quals? */
-	bool		amsearchnulls;	/* can AM search for NULL/NOT NULL entries? */
-	bool		amhasgettuple;	/* does AM have amgettuple interface? */
-	bool		amhasgetbitmap; /* does AM have amgetbitmap interface? */
-	bool		amcanparallel;	/* does AM support parallel scan? */
-	bool		amcanmarkpos;	/* does AM support mark/restore? */
+	bool		amcanorderbyop pg_node_attr(readwrite_ignore); /* does AM support order by operator result? */
+	bool		amoptionalkey pg_node_attr(readwrite_ignore);	/* can query omit key for the first column? */
+	bool		amsearcharray pg_node_attr(readwrite_ignore);	/* can AM handle ScalarArrayOpExpr quals? */
+	bool		amsearchnulls pg_node_attr(readwrite_ignore);	/* can AM search for NULL/NOT NULL entries? */
+	bool		amhasgettuple pg_node_attr(readwrite_ignore);	/* does AM have amgettuple interface? */
+	bool		amhasgetbitmap pg_node_attr(readwrite_ignore); /* does AM have amgetbitmap interface? */
+	bool		amcanparallel pg_node_attr(readwrite_ignore);	/* does AM support parallel scan? */
+	bool		amcanmarkpos pg_node_attr(readwrite_ignore);	/* does AM support mark/restore? */
 	/* Rather than include amapi.h here, we declare amcostestimate like this */
 	void		(*amcostestimate) ();	/* AM's cost estimator */
 };
@@ -926,7 +926,7 @@ typedef struct StatisticExtInfo
 	NodeTag		type;
 
 	Oid			statOid;		/* OID of the statistics row */
-	RelOptInfo *rel;			/* back-link to statistic's table */
+	RelOptInfo *rel pg_node_attr(readwrite_ignore);			/* back-link to statistic's table */
 	char		kind;			/* statistics kind of this entry */
 	Bitmapset  *keys;			/* attnums of the columns covered */
 	List	   *exprs;			/* expressions */
@@ -1171,10 +1171,10 @@ typedef struct Path
 
 	NodeTag		pathtype;		/* tag identifying scan/join method */
 
-	RelOptInfo *parent;			/* the relation this path can build */
-	PathTarget *pathtarget;		/* list of Vars/Exprs, cost, width */
+	RelOptInfo *parent pg_node_attr(path_hack1);			/* the relation this path can build */
+	PathTarget *pathtarget pg_node_attr(path_hack2);		/* list of Vars/Exprs, cost, width */
 
-	ParamPathInfo *param_info;	/* parameterization info, or NULL if none */
+	ParamPathInfo *param_info pg_node_attr(path_hack3);	/* parameterization info, or NULL if none */
 
 	bool		parallel_aware; /* engage parallel-aware logic? */
 	bool		parallel_safe;	/* OK to use as part of parallel plan? */
@@ -2051,19 +2051,19 @@ typedef struct RestrictInfo
 
 	bool		outerjoin_delayed;	/* true if delayed by lower outer join */
 
-	bool		can_join;		/* see comment above */
+	bool		can_join pg_node_attr(equal_ignore);		/* see comment above */
 
-	bool		pseudoconstant; /* see comment above */
+	bool		pseudoconstant pg_node_attr(equal_ignore); /* see comment above */
 
-	bool		leakproof;		/* true if known to contain no leaked Vars */
+	bool		leakproof pg_node_attr(equal_ignore);		/* true if known to contain no leaked Vars */
 
-	VolatileFunctionStatus has_volatile;	/* to indicate if clause contains
+	VolatileFunctionStatus has_volatile pg_node_attr(equal_ignore);	/* to indicate if clause contains
 											 * any volatile functions. */
 
 	Index		security_level; /* see comment above */
 
 	/* The set of relids (varnos) actually referenced in the clause: */
-	Relids		clause_relids;
+	Relids		clause_relids pg_node_attr(equal_ignore);
 
 	/* The set of relids required to evaluate the clause: */
 	Relids		required_relids;
@@ -2075,47 +2075,47 @@ typedef struct RestrictInfo
 	Relids		nullable_relids;
 
 	/* These fields are set for any binary opclause: */
-	Relids		left_relids;	/* relids in left side of clause */
-	Relids		right_relids;	/* relids in right side of clause */
+	Relids		left_relids pg_node_attr(equal_ignore);	/* relids in left side of clause */
+	Relids		right_relids pg_node_attr(equal_ignore);	/* relids in right side of clause */
 
 	/* This field is NULL unless clause is an OR clause: */
-	Expr	   *orclause;		/* modified clause with RestrictInfos */
+	Expr	   *orclause pg_node_attr(equal_ignore);		/* modified clause with RestrictInfos */
 
 	/* This field is NULL unless clause is potentially redundant: */
-	EquivalenceClass *parent_ec;	/* generating EquivalenceClass */
+	EquivalenceClass *parent_ec pg_node_attr(equal_ignore readwrite_ignore);	/* generating EquivalenceClass */
 
 	/* cache space for cost and selectivity */
-	QualCost	eval_cost;		/* eval cost of clause; -1 if not yet set */
-	Selectivity norm_selec;		/* selectivity for "normal" (JOIN_INNER)
+	QualCost	eval_cost pg_node_attr(equal_ignore);		/* eval cost of clause; -1 if not yet set */
+	Selectivity norm_selec pg_node_attr(equal_ignore);		/* selectivity for "normal" (JOIN_INNER)
 								 * semantics; -1 if not yet set; >1 means a
 								 * redundant clause */
-	Selectivity outer_selec;	/* selectivity for outer join semantics; -1 if
+	Selectivity outer_selec pg_node_attr(equal_ignore);	/* selectivity for outer join semantics; -1 if
 								 * not yet set */
 
 	/* valid if clause is mergejoinable, else NIL */
-	List	   *mergeopfamilies;	/* opfamilies containing clause operator */
+	List	   *mergeopfamilies pg_node_attr(equal_ignore);	/* opfamilies containing clause operator */
 
 	/* cache space for mergeclause processing; NULL if not yet set */
-	EquivalenceClass *left_ec;	/* EquivalenceClass containing lefthand */
-	EquivalenceClass *right_ec; /* EquivalenceClass containing righthand */
-	EquivalenceMember *left_em; /* EquivalenceMember for lefthand */
-	EquivalenceMember *right_em;	/* EquivalenceMember for righthand */
-	List	   *scansel_cache;	/* list of MergeScanSelCache structs */
+	EquivalenceClass *left_ec pg_node_attr(equal_ignore readwrite_ignore);	/* EquivalenceClass containing lefthand */
+	EquivalenceClass *right_ec pg_node_attr(equal_ignore readwrite_ignore); /* EquivalenceClass containing righthand */
+	EquivalenceMember *left_em pg_node_attr(equal_ignore); /* EquivalenceMember for lefthand */
+	EquivalenceMember *right_em pg_node_attr(equal_ignore);	/* EquivalenceMember for righthand */
+	List	   *scansel_cache pg_node_attr(copy_ignore equal_ignore);	/* list of MergeScanSelCache structs */
 
 	/* transient workspace for use while considering a specific join path */
-	bool		outer_is_left;	/* T = outer var on left, F = on right */
+	bool		outer_is_left pg_node_attr(equal_ignore);	/* T = outer var on left, F = on right */
 
 	/* valid if clause is hashjoinable, else InvalidOid: */
-	Oid			hashjoinoperator;	/* copy of clause operator */
+	Oid			hashjoinoperator pg_node_attr(equal_ignore);	/* copy of clause operator */
 
 	/* cache space for hashclause processing; -1 if not yet set */
-	Selectivity left_bucketsize;	/* avg bucketsize of left side */
-	Selectivity right_bucketsize;	/* avg bucketsize of right side */
-	Selectivity left_mcvfreq;	/* left side's most common val's freq */
-	Selectivity right_mcvfreq;	/* right side's most common val's freq */
+	Selectivity left_bucketsize pg_node_attr(equal_ignore);	/* avg bucketsize of left side */
+	Selectivity right_bucketsize pg_node_attr(equal_ignore);	/* avg bucketsize of right side */
+	Selectivity left_mcvfreq pg_node_attr(equal_ignore);	/* left side's most common val's freq */
+	Selectivity right_mcvfreq pg_node_attr(equal_ignore);	/* right side's most common val's freq */
 
 	/* hash equality operator used for result cache, else InvalidOid */
-	Oid			hasheqoperator;
+	Oid			hasheqoperator pg_node_attr(equal_ignore);
 } RestrictInfo;
 
 /*
@@ -2170,8 +2170,8 @@ typedef struct MergeScanSelCache
 typedef struct PlaceHolderVar
 {
 	Expr		xpr;
-	Expr	   *phexpr;			/* the represented expression */
-	Relids		phrels;			/* base relids syntactically within expr src */
+	Expr	   *phexpr pg_node_attr(equal_ignore);			/* the represented expression */
+	Relids		phrels pg_node_attr(equal_ignore);			/* base relids syntactically within expr src */
 	Index		phid;			/* ID for PHV (unique within planner run) */
 	Index		phlevelsup;		/* > 0 if PHV belongs to outer query */
 } PlaceHolderVar;
@@ -2420,7 +2420,7 @@ typedef struct MinMaxAggInfo
 	Oid			aggfnoid;		/* pg_proc Oid of the aggregate */
 	Oid			aggsortop;		/* Oid of its sort operator */
 	Expr	   *target;			/* expression we are aggregating on */
-	PlannerInfo *subroot;		/* modified "root" for planning the subquery */
+	PlannerInfo *subroot pg_node_attr(readwrite_ignore);		/* modified "root" for planning the subquery */
 	Path	   *path;			/* access path for subquery */
 	Cost		pathcost;		/* estimated cost to fetch first row */
 	Param	   *param;			/* param for subplan's output */
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3418e23873..62f6413684 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -63,7 +63,7 @@ typedef enum OnCommitAction
 typedef struct RangeVar
 {
 	NodeTag		type;
-	char	   *catalogname;	/* the catalog (database) name, or NULL */
+	char	   *catalogname pg_node_attr(readwrite_ignore);	/* the catalog (database) name, or NULL */
 	char	   *schemaname;		/* the schema name, or NULL */
 	char	   *relname;		/* the relation/sequence name */
 	bool		inh;			/* expand rel by inheritance? recursively act
@@ -196,8 +196,8 @@ typedef struct Var
 	Index		varlevelsup;	/* for subquery variables referencing outer
 								 * relations; 0 in a normal var, >0 means N
 								 * levels up */
-	Index		varnosyn;		/* syntactic relation index (0 if unknown) */
-	AttrNumber	varattnosyn;	/* syntactic attribute number */
+	Index		varnosyn pg_node_attr(equal_ignore);		/* syntactic relation index (0 if unknown) */
+	AttrNumber	varattnosyn pg_node_attr(equal_ignore);	/* syntactic attribute number */
 	int			location;		/* token location, or -1 if unknown */
 } Var;
 
@@ -324,7 +324,7 @@ typedef struct Aggref
 	Oid			aggtype;		/* type Oid of result of the aggregate */
 	Oid			aggcollid;		/* OID of collation of result */
 	Oid			inputcollid;	/* OID of collation that function should use */
-	Oid			aggtranstype;	/* type Oid of aggregate's transition value */
+	Oid			aggtranstype pg_node_attr(equal_ignore);	/* type Oid of aggregate's transition value */
 	List	   *aggargtypes;	/* type Oids of direct and aggregated args */
 	List	   *aggdirectargs;	/* direct arguments, if an ordered-set agg */
 	List	   *args;			/* aggregated arguments and sort expressions */
@@ -371,8 +371,8 @@ typedef struct GroupingFunc
 	Expr		xpr;
 	List	   *args;			/* arguments, not evaluated but kept for
 								 * benefit of EXPLAIN etc. */
-	List	   *refs;			/* ressortgrouprefs of arguments */
-	List	   *cols;			/* actual column positions set by planner */
+	List	   *refs pg_node_attr(equal_ignore);			/* ressortgrouprefs of arguments */
+	List	   *cols pg_node_attr(equal_ignore);			/* actual column positions set by planner */
 	Index		agglevelsup;	/* same as Aggref.agglevelsup */
 	int			location;		/* token location */
 } GroupingFunc;
@@ -540,7 +540,7 @@ typedef struct OpExpr
 {
 	Expr		xpr;
 	Oid			opno;			/* PG_OPERATOR OID of the operator */
-	Oid			opfuncid;		/* PG_PROC OID of underlying function */
+	Oid			opfuncid pg_node_attr(equal_ignore_if_zero);		/* PG_PROC OID of underlying function */
 	Oid			opresulttype;	/* PG_TYPE OID of result value */
 	bool		opretset;		/* true if operator returns set */
 	Oid			opcollid;		/* OID of collation of result */
@@ -589,8 +589,8 @@ typedef struct ScalarArrayOpExpr
 {
 	Expr		xpr;
 	Oid			opno;			/* PG_OPERATOR OID of the operator */
-	Oid			opfuncid;		/* PG_PROC OID of comparison function */
-	Oid			hashfuncid;		/* PG_PROC OID of hash func or InvalidOid */
+	Oid			opfuncid pg_node_attr(equal_ignore_if_zero);		/* PG_PROC OID of comparison function */
+	Oid			hashfuncid pg_node_attr(equal_ignore_if_zero);		/* PG_PROC OID of hash func or InvalidOid */
 	bool		useOr;			/* true for ANY, false for ALL */
 	Oid			inputcollid;	/* OID of collation that operator should use */
 	List	   *args;			/* the scalar and array operands */
-- 
2.31.1


From b9cd1fd53089c833efd39c76c0e4813a974f9a1e Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <[email protected]>
Date: Mon, 7 Jun 2021 16:09:26 +0200
Subject: [PATCH v1 10/10] XXX Debugging support

---
 src/include/pg_config_manual.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/include/pg_config_manual.h b/src/include/pg_config_manual.h
index 27da86e5e0..1c1152fe14 100644
--- a/src/include/pg_config_manual.h
+++ b/src/include/pg_config_manual.h
@@ -365,14 +365,14 @@
  * copyObject(), to facilitate catching errors and omissions in
  * copyObject().
  */
-/* #define COPY_PARSE_PLAN_TREES */
+#define COPY_PARSE_PLAN_TREES
 
 /*
  * Define this to force all parse and plan trees to be passed through
  * outfuncs.c/readfuncs.c, to facilitate catching errors and omissions in
  * those modules.
  */
-/* #define WRITE_READ_PARSE_PLAN_TREES */
+#define WRITE_READ_PARSE_PLAN_TREES
 
 /*
  * Define this to force all raw parse trees for DML statements to be scanned
-- 
2.31.1



Attachments:

  [text/plain] v1-0001-Rename-NodeTag-of-ExprState.patch (1.7K, ../[email protected]/2-v1-0001-Rename-NodeTag-of-ExprState.patch)
  download | inline diff:
From c782871d6cc59e2fed232c78c307d63e72cbb3d5 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <[email protected]>
Date: Mon, 7 Jun 2021 15:45:14 +0200
Subject: [PATCH v1 01/10] Rename NodeTag of ExprState

Rename from tag to type, for consistency with all other node structs.
---
 src/backend/executor/execExpr.c | 4 ++--
 src/include/nodes/execnodes.h   | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index 8c9f8a6aeb..c6ba11d035 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -363,7 +363,7 @@ ExecBuildProjectionInfo(List *targetList,
 
 	projInfo->pi_exprContext = econtext;
 	/* We embed ExprState into ProjectionInfo instead of doing extra palloc */
-	projInfo->pi_state.tag = T_ExprState;
+	projInfo->pi_state.type = T_ExprState;
 	state = &projInfo->pi_state;
 	state->expr = (Expr *) targetList;
 	state->parent = parent;
@@ -531,7 +531,7 @@ ExecBuildUpdateProjection(List *targetList,
 
 	projInfo->pi_exprContext = econtext;
 	/* We embed ExprState into ProjectionInfo instead of doing extra palloc */
-	projInfo->pi_state.tag = T_ExprState;
+	projInfo->pi_state.type = T_ExprState;
 	state = &projInfo->pi_state;
 	if (evalTargetList)
 		state->expr = (Expr *) targetList;
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 7795a69490..8fa9c8aff6 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -60,7 +60,7 @@ typedef Datum (*ExprStateEvalFunc) (struct ExprState *expression,
 
 typedef struct ExprState
 {
-	NodeTag		tag;
+	NodeTag		type;
 
 	uint8		flags;			/* bitmask of EEO_FLAG_* bits, see above */
 
-- 
2.31.1



  [text/plain] v1-0002-Rename-argument-of-_outValue.patch (2.1K, ../[email protected]/3-v1-0002-Rename-argument-of-_outValue.patch)
  download | inline diff:
From 7746ddd4f2a9534322b2b9226007638d3142c0c7 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <[email protected]>
Date: Mon, 7 Jun 2021 15:47:56 +0200
Subject: [PATCH v1 02/10] Rename argument of _outValue()

Rename from value to node, for consistency with similar functions.
---
 src/backend/nodes/outfuncs.c | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 04696f613c..b54c57d09f 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -3402,12 +3402,12 @@ _outAExpr(StringInfo str, const A_Expr *node)
 }
 
 static void
-_outValue(StringInfo str, const Value *value)
+_outValue(StringInfo str, const Value *node)
 {
-	switch (value->type)
+	switch (node->type)
 	{
 		case T_Integer:
-			appendStringInfo(str, "%d", value->val.ival);
+			appendStringInfo(str, "%d", node->val.ival);
 			break;
 		case T_Float:
 
@@ -3415,7 +3415,7 @@ _outValue(StringInfo str, const Value *value)
 			 * We assume the value is a valid numeric literal and so does not
 			 * need quoting.
 			 */
-			appendStringInfoString(str, value->val.str);
+			appendStringInfoString(str, node->val.str);
 			break;
 		case T_String:
 
@@ -3424,20 +3424,20 @@ _outValue(StringInfo str, const Value *value)
 			 * but we don't want it to do anything with an empty string.
 			 */
 			appendStringInfoChar(str, '"');
-			if (value->val.str[0] != '\0')
-				outToken(str, value->val.str);
+			if (node->val.str[0] != '\0')
+				outToken(str, node->val.str);
 			appendStringInfoChar(str, '"');
 			break;
 		case T_BitString:
 			/* internal representation already has leading 'b' */
-			appendStringInfoString(str, value->val.str);
+			appendStringInfoString(str, node->val.str);
 			break;
 		case T_Null:
 			/* this is seen only within A_Const, not in transformed trees */
 			appendStringInfoString(str, "NULL");
 			break;
 		default:
-			elog(ERROR, "unrecognized node type: %d", (int) value->type);
+			elog(ERROR, "unrecognized node type: %d", (int) node->type);
 			break;
 	}
 }
-- 
2.31.1



  [text/plain] v1-0003-Rename-some-node-support-functions-for-consistenc.patch (5.2K, ../[email protected]/4-v1-0003-Rename-some-node-support-functions-for-consistenc.patch)
  download | inline diff:
From 7282e926a6492046e3189f8ddc0ccbbbab8a470b Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <[email protected]>
Date: Mon, 7 Jun 2021 15:51:37 +0200
Subject: [PATCH v1 03/10] Rename some node support functions for consistency

Some node function names didn't match their node type names exactly.
Fix those for consistency.
---
 src/backend/nodes/copyfuncs.c  | 16 ++++++++--------
 src/backend/nodes/equalfuncs.c | 16 ++++++++--------
 src/backend/nodes/outfuncs.c   |  8 ++++----
 3 files changed, 20 insertions(+), 20 deletions(-)

diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 621f7ce068..35a85df442 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2700,7 +2700,7 @@ _copyCommonTableExpr(const CommonTableExpr *from)
 }
 
 static A_Expr *
-_copyAExpr(const A_Expr *from)
+_copyA_Expr(const A_Expr *from)
 {
 	A_Expr	   *newnode = makeNode(A_Expr);
 
@@ -2736,7 +2736,7 @@ _copyParamRef(const ParamRef *from)
 }
 
 static A_Const *
-_copyAConst(const A_Const *from)
+_copyA_Const(const A_Const *from)
 {
 	A_Const    *newnode = makeNode(A_Const);
 
@@ -2787,7 +2787,7 @@ _copyFuncCall(const FuncCall *from)
 }
 
 static A_Star *
-_copyAStar(const A_Star *from)
+_copyA_Star(const A_Star *from)
 {
 	A_Star	   *newnode = makeNode(A_Star);
 
@@ -2795,7 +2795,7 @@ _copyAStar(const A_Star *from)
 }
 
 static A_Indices *
-_copyAIndices(const A_Indices *from)
+_copyA_Indices(const A_Indices *from)
 {
 	A_Indices  *newnode = makeNode(A_Indices);
 
@@ -5711,7 +5711,7 @@ copyObjectImpl(const void *from)
 			retval = _copyDropSubscriptionStmt(from);
 			break;
 		case T_A_Expr:
-			retval = _copyAExpr(from);
+			retval = _copyA_Expr(from);
 			break;
 		case T_ColumnRef:
 			retval = _copyColumnRef(from);
@@ -5720,16 +5720,16 @@ copyObjectImpl(const void *from)
 			retval = _copyParamRef(from);
 			break;
 		case T_A_Const:
-			retval = _copyAConst(from);
+			retval = _copyA_Const(from);
 			break;
 		case T_FuncCall:
 			retval = _copyFuncCall(from);
 			break;
 		case T_A_Star:
-			retval = _copyAStar(from);
+			retval = _copyA_Star(from);
 			break;
 		case T_A_Indices:
-			retval = _copyAIndices(from);
+			retval = _copyA_Indices(from);
 			break;
 		case T_A_Indirection:
 			retval = _copyA_Indirection(from);
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 3033c1934c..e04ec41904 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -2369,7 +2369,7 @@ _equalAlterPolicyStmt(const AlterPolicyStmt *a, const AlterPolicyStmt *b)
 }
 
 static bool
-_equalAExpr(const A_Expr *a, const A_Expr *b)
+_equalA_Expr(const A_Expr *a, const A_Expr *b)
 {
 	COMPARE_SCALAR_FIELD(kind);
 	COMPARE_NODE_FIELD(name);
@@ -2399,7 +2399,7 @@ _equalParamRef(const ParamRef *a, const ParamRef *b)
 }
 
 static bool
-_equalAConst(const A_Const *a, const A_Const *b)
+_equalA_Const(const A_Const *a, const A_Const *b)
 {
 	if (!equal(&a->val, &b->val))	/* hack for in-line Value field */
 		return false;
@@ -2427,13 +2427,13 @@ _equalFuncCall(const FuncCall *a, const FuncCall *b)
 }
 
 static bool
-_equalAStar(const A_Star *a, const A_Star *b)
+_equalA_Star(const A_Star *a, const A_Star *b)
 {
 	return true;
 }
 
 static bool
-_equalAIndices(const A_Indices *a, const A_Indices *b)
+_equalA_Indices(const A_Indices *a, const A_Indices *b)
 {
 	COMPARE_SCALAR_FIELD(is_slice);
 	COMPARE_NODE_FIELD(lidx);
@@ -3702,7 +3702,7 @@ equal(const void *a, const void *b)
 			retval = _equalDropSubscriptionStmt(a, b);
 			break;
 		case T_A_Expr:
-			retval = _equalAExpr(a, b);
+			retval = _equalA_Expr(a, b);
 			break;
 		case T_ColumnRef:
 			retval = _equalColumnRef(a, b);
@@ -3711,16 +3711,16 @@ equal(const void *a, const void *b)
 			retval = _equalParamRef(a, b);
 			break;
 		case T_A_Const:
-			retval = _equalAConst(a, b);
+			retval = _equalA_Const(a, b);
 			break;
 		case T_FuncCall:
 			retval = _equalFuncCall(a, b);
 			break;
 		case T_A_Star:
-			retval = _equalAStar(a, b);
+			retval = _equalA_Star(a, b);
 			break;
 		case T_A_Indices:
-			retval = _equalAIndices(a, b);
+			retval = _equalA_Indices(a, b);
 			break;
 		case T_A_Indirection:
 			retval = _equalA_Indirection(a, b);
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index b54c57d09f..ed08f5acf4 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -3327,7 +3327,7 @@ _outTableSampleClause(StringInfo str, const TableSampleClause *node)
 }
 
 static void
-_outAExpr(StringInfo str, const A_Expr *node)
+_outA_Expr(StringInfo str, const A_Expr *node)
 {
 	WRITE_NODE_TYPE("AEXPR");
 
@@ -3475,7 +3475,7 @@ _outRawStmt(StringInfo str, const RawStmt *node)
 }
 
 static void
-_outAConst(StringInfo str, const A_Const *node)
+_outA_Const(StringInfo str, const A_Const *node)
 {
 	WRITE_NODE_TYPE("A_CONST");
 
@@ -4416,7 +4416,7 @@ outNode(StringInfo str, const void *obj)
 				_outTableSampleClause(str, obj);
 				break;
 			case T_A_Expr:
-				_outAExpr(str, obj);
+				_outA_Expr(str, obj);
 				break;
 			case T_ColumnRef:
 				_outColumnRef(str, obj);
@@ -4428,7 +4428,7 @@ outNode(StringInfo str, const void *obj)
 				_outRawStmt(str, obj);
 				break;
 			case T_A_Const:
-				_outAConst(str, obj);
+				_outA_Const(str, obj);
 				break;
 			case T_A_Star:
 				_outA_Star(str, obj);
-- 
2.31.1



  [text/plain] v1-0004-Change-SeqScan-node-to-contain-Scan-node.patch (3.8K, ../[email protected]/5-v1-0004-Change-SeqScan-node-to-contain-Scan-node.patch)
  download | inline diff:
From 06d24333eabc3c9c5ef4df8ef100dbaaacd3dc16 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <[email protected]>
Date: Mon, 7 Jun 2021 15:55:40 +0200
Subject: [PATCH v1 04/10] Change SeqScan node to contain Scan node

This makes the structure of all Scan-derived nodes the same,
independent of whether they have additional fields.
---
 src/backend/executor/nodeSeqscan.c      |  4 ++--
 src/backend/nodes/readfuncs.c           |  2 +-
 src/backend/optimizer/plan/createplan.c |  6 +++---
 src/backend/optimizer/plan/setrefs.c    | 10 +++++-----
 src/include/nodes/plannodes.h           |  5 ++++-
 5 files changed, 15 insertions(+), 12 deletions(-)

diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c
index 066f9ae37e..4d2bf16a6f 100644
--- a/src/backend/executor/nodeSeqscan.c
+++ b/src/backend/executor/nodeSeqscan.c
@@ -151,7 +151,7 @@ ExecInitSeqScan(SeqScan *node, EState *estate, int eflags)
 	 */
 	scanstate->ss.ss_currentRelation =
 		ExecOpenScanRelation(estate,
-							 node->scanrelid,
+							 node->scan.scanrelid,
 							 eflags);
 
 	/* and create slot with the appropriate rowtype */
@@ -169,7 +169,7 @@ ExecInitSeqScan(SeqScan *node, EState *estate, int eflags)
 	 * initialize child expressions
 	 */
 	scanstate->ss.ps.qual =
-		ExecInitQual(node->plan.qual, (PlanState *) scanstate);
+		ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
 
 	return scanstate;
 }
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index f0b34ecfac..fef4de94e6 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -1832,7 +1832,7 @@ _readSeqScan(void)
 {
 	READ_LOCALS_NO_FIELDS(SeqScan);
 
-	ReadCommonScan(local_node);
+	ReadCommonScan(&local_node->scan);
 
 	READ_DONE();
 }
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 439e6b6426..769b5a0cf7 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -2858,7 +2858,7 @@ create_seqscan_plan(PlannerInfo *root, Path *best_path,
 							 scan_clauses,
 							 scan_relid);
 
-	copy_generic_path_info(&scan_plan->plan, best_path);
+	copy_generic_path_info(&scan_plan->scan.plan, best_path);
 
 	return scan_plan;
 }
@@ -5372,13 +5372,13 @@ make_seqscan(List *qptlist,
 			 Index scanrelid)
 {
 	SeqScan    *node = makeNode(SeqScan);
-	Plan	   *plan = &node->plan;
+	Plan	   *plan = &node->scan.plan;
 
 	plan->targetlist = qptlist;
 	plan->qual = qpqual;
 	plan->lefttree = NULL;
 	plan->righttree = NULL;
-	node->scanrelid = scanrelid;
+	node->scan.scanrelid = scanrelid;
 
 	return node;
 }
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 61ccfd300b..6cb2d731c8 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -516,12 +516,12 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
 			{
 				SeqScan    *splan = (SeqScan *) plan;
 
-				splan->scanrelid += rtoffset;
-				splan->plan.targetlist =
-					fix_scan_list(root, splan->plan.targetlist,
+				splan->scan.scanrelid += rtoffset;
+				splan->scan.plan.targetlist =
+					fix_scan_list(root, splan->scan.plan.targetlist,
 								  rtoffset, NUM_EXEC_TLIST(plan));
-				splan->plan.qual =
-					fix_scan_list(root, splan->plan.qual,
+				splan->scan.plan.qual =
+					fix_scan_list(root, splan->scan.plan.qual,
 								  rtoffset, NUM_EXEC_QUAL(plan));
 			}
 			break;
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index aaa3b65d04..35ff94fbb6 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -348,7 +348,10 @@ typedef struct Scan
  *		sequential scan node
  * ----------------
  */
-typedef Scan SeqScan;
+typedef struct SeqScan
+{
+	Scan		scan;
+} SeqScan;
 
 /* ----------------
  *		table sample scan node
-- 
2.31.1



  [text/plain] v1-0005-Change-NestPath-node-to-contain-JoinPath-node.patch (10.2K, ../[email protected]/6-v1-0005-Change-NestPath-node-to-contain-JoinPath-node.patch)
  download | inline diff:
From d3dc7a83e23fe89a2767716a51c3d182f0719148 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <[email protected]>
Date: Mon, 7 Jun 2021 16:00:31 +0200
Subject: [PATCH v1 05/10] Change NestPath node to contain JoinPath node

This makes the structure of all JoinPath-derived nodes the same,
independent of whether they have additional fields.
---
 src/backend/optimizer/path/costsize.c   | 35 +++++++++++++------------
 src/backend/optimizer/plan/createplan.c | 24 ++++++++---------
 src/backend/optimizer/util/pathnode.c   | 32 +++++++++++-----------
 src/include/nodes/pathnodes.h           |  5 +++-
 4 files changed, 51 insertions(+), 45 deletions(-)

diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index 8577c7b138..64891342fc 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -167,7 +167,7 @@ static bool cost_qual_eval_walker(Node *node, cost_qual_eval_context *context);
 static void get_restriction_qual_cost(PlannerInfo *root, RelOptInfo *baserel,
 									  ParamPathInfo *param_info,
 									  QualCost *qpqual_cost);
-static bool has_indexed_join_quals(NestPath *joinpath);
+static bool has_indexed_join_quals(NestPath *path);
 static double approx_tuple_count(PlannerInfo *root, JoinPath *path,
 								 List *quals);
 static double calc_joinrel_size_estimate(PlannerInfo *root,
@@ -2978,8 +2978,8 @@ final_cost_nestloop(PlannerInfo *root, NestPath *path,
 					JoinCostWorkspace *workspace,
 					JoinPathExtraData *extra)
 {
-	Path	   *outer_path = path->outerjoinpath;
-	Path	   *inner_path = path->innerjoinpath;
+	Path	   *outer_path = path->jpath.outerjoinpath;
+	Path	   *inner_path = path->jpath.innerjoinpath;
 	double		outer_path_rows = outer_path->rows;
 	double		inner_path_rows = inner_path->rows;
 	Cost		startup_cost = workspace->startup_cost;
@@ -2994,18 +2994,18 @@ final_cost_nestloop(PlannerInfo *root, NestPath *path,
 	if (inner_path_rows <= 0)
 		inner_path_rows = 1;
 	/* Mark the path with the correct row estimate */
-	if (path->path.param_info)
-		path->path.rows = path->path.param_info->ppi_rows;
+	if (path->jpath.path.param_info)
+		path->jpath.path.rows = path->jpath.path.param_info->ppi_rows;
 	else
-		path->path.rows = path->path.parent->rows;
+		path->jpath.path.rows = path->jpath.path.parent->rows;
 
 	/* For partial paths, scale row estimate. */
-	if (path->path.parallel_workers > 0)
+	if (path->jpath.path.parallel_workers > 0)
 	{
-		double		parallel_divisor = get_parallel_divisor(&path->path);
+		double		parallel_divisor = get_parallel_divisor(&path->jpath.path);
 
-		path->path.rows =
-			clamp_row_est(path->path.rows / parallel_divisor);
+		path->jpath.path.rows =
+			clamp_row_est(path->jpath.path.rows / parallel_divisor);
 	}
 
 	/*
@@ -3018,7 +3018,7 @@ final_cost_nestloop(PlannerInfo *root, NestPath *path,
 
 	/* cost of inner-relation source data (we already dealt with outer rel) */
 
-	if (path->jointype == JOIN_SEMI || path->jointype == JOIN_ANTI ||
+	if (path->jpath.jointype == JOIN_SEMI || path->jpath.jointype == JOIN_ANTI ||
 		extra->inner_unique)
 	{
 		/*
@@ -3136,17 +3136,17 @@ final_cost_nestloop(PlannerInfo *root, NestPath *path,
 	}
 
 	/* CPU costs */
-	cost_qual_eval(&restrict_qual_cost, path->joinrestrictinfo, root);
+	cost_qual_eval(&restrict_qual_cost, path->jpath.joinrestrictinfo, root);
 	startup_cost += restrict_qual_cost.startup;
 	cpu_per_tuple = cpu_tuple_cost + restrict_qual_cost.per_tuple;
 	run_cost += cpu_per_tuple * ntuples;
 
 	/* tlist eval costs are paid per output row, not per tuple scanned */
-	startup_cost += path->path.pathtarget->cost.startup;
-	run_cost += path->path.pathtarget->cost.per_tuple * path->path.rows;
+	startup_cost += path->jpath.path.pathtarget->cost.startup;
+	run_cost += path->jpath.path.pathtarget->cost.per_tuple * path->jpath.path.rows;
 
-	path->path.startup_cost = startup_cost;
-	path->path.total_cost = startup_cost + run_cost;
+	path->jpath.path.startup_cost = startup_cost;
+	path->jpath.path.total_cost = startup_cost + run_cost;
 }
 
 /*
@@ -4774,8 +4774,9 @@ compute_semi_anti_join_factors(PlannerInfo *root,
  * expensive.
  */
 static bool
-has_indexed_join_quals(NestPath *joinpath)
+has_indexed_join_quals(NestPath *path)
 {
+	JoinPath   *joinpath = &path->jpath;
 	Relids		joinrelids = joinpath->path.parent->relids;
 	Path	   *innerpath = joinpath->innerjoinpath;
 	List	   *indexclauses;
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 769b5a0cf7..06a8588f4e 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -4224,8 +4224,8 @@ create_nestloop_plan(PlannerInfo *root,
 	NestLoop   *join_plan;
 	Plan	   *outer_plan;
 	Plan	   *inner_plan;
-	List	   *tlist = build_path_tlist(root, &best_path->path);
-	List	   *joinrestrictclauses = best_path->joinrestrictinfo;
+	List	   *tlist = build_path_tlist(root, &best_path->jpath.path);
+	List	   *joinrestrictclauses = best_path->jpath.joinrestrictinfo;
 	List	   *joinclauses;
 	List	   *otherclauses;
 	Relids		outerrelids;
@@ -4233,13 +4233,13 @@ create_nestloop_plan(PlannerInfo *root,
 	Relids		saveOuterRels = root->curOuterRels;
 
 	/* NestLoop can project, so no need to be picky about child tlists */
-	outer_plan = create_plan_recurse(root, best_path->outerjoinpath, 0);
+	outer_plan = create_plan_recurse(root, best_path->jpath.outerjoinpath, 0);
 
 	/* For a nestloop, include outer relids in curOuterRels for inner side */
 	root->curOuterRels = bms_union(root->curOuterRels,
-								   best_path->outerjoinpath->parent->relids);
+								   best_path->jpath.outerjoinpath->parent->relids);
 
-	inner_plan = create_plan_recurse(root, best_path->innerjoinpath, 0);
+	inner_plan = create_plan_recurse(root, best_path->jpath.innerjoinpath, 0);
 
 	/* Restore curOuterRels */
 	bms_free(root->curOuterRels);
@@ -4250,10 +4250,10 @@ create_nestloop_plan(PlannerInfo *root,
 
 	/* Get the join qual clauses (in plain expression form) */
 	/* Any pseudoconstant clauses are ignored here */
-	if (IS_OUTER_JOIN(best_path->jointype))
+	if (IS_OUTER_JOIN(best_path->jpath.jointype))
 	{
 		extract_actual_join_clauses(joinrestrictclauses,
-									best_path->path.parent->relids,
+									best_path->jpath.path.parent->relids,
 									&joinclauses, &otherclauses);
 	}
 	else
@@ -4264,7 +4264,7 @@ create_nestloop_plan(PlannerInfo *root,
 	}
 
 	/* Replace any outer-relation variables with nestloop params */
-	if (best_path->path.param_info)
+	if (best_path->jpath.path.param_info)
 	{
 		joinclauses = (List *)
 			replace_nestloop_params(root, (Node *) joinclauses);
@@ -4276,7 +4276,7 @@ create_nestloop_plan(PlannerInfo *root,
 	 * Identify any nestloop parameters that should be supplied by this join
 	 * node, and remove them from root->curOuterParams.
 	 */
-	outerrelids = best_path->outerjoinpath->parent->relids;
+	outerrelids = best_path->jpath.outerjoinpath->parent->relids;
 	nestParams = identify_current_nestloop_params(root, outerrelids);
 
 	join_plan = make_nestloop(tlist,
@@ -4285,10 +4285,10 @@ create_nestloop_plan(PlannerInfo *root,
 							  nestParams,
 							  outer_plan,
 							  inner_plan,
-							  best_path->jointype,
-							  best_path->inner_unique);
+							  best_path->jpath.jointype,
+							  best_path->jpath.inner_unique);
 
-	copy_generic_path_info(&join_plan->join.plan, &best_path->path);
+	copy_generic_path_info(&join_plan->join.plan, &best_path->jpath.path);
 
 	return join_plan;
 }
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index 9ce5f95e3b..cd6ef1bb32 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -2446,10 +2446,10 @@ create_nestloop_path(PlannerInfo *root,
 		restrict_clauses = jclauses;
 	}
 
-	pathnode->path.pathtype = T_NestLoop;
-	pathnode->path.parent = joinrel;
-	pathnode->path.pathtarget = joinrel->reltarget;
-	pathnode->path.param_info =
+	pathnode->jpath.path.pathtype = T_NestLoop;
+	pathnode->jpath.path.parent = joinrel;
+	pathnode->jpath.path.pathtarget = joinrel->reltarget;
+	pathnode->jpath.path.param_info =
 		get_joinrel_parampathinfo(root,
 								  joinrel,
 								  outer_path,
@@ -2457,17 +2457,17 @@ create_nestloop_path(PlannerInfo *root,
 								  extra->sjinfo,
 								  required_outer,
 								  &restrict_clauses);
-	pathnode->path.parallel_aware = false;
-	pathnode->path.parallel_safe = joinrel->consider_parallel &&
+	pathnode->jpath.path.parallel_aware = false;
+	pathnode->jpath.path.parallel_safe = joinrel->consider_parallel &&
 		outer_path->parallel_safe && inner_path->parallel_safe;
 	/* This is a foolish way to estimate parallel_workers, but for now... */
-	pathnode->path.parallel_workers = outer_path->parallel_workers;
-	pathnode->path.pathkeys = pathkeys;
-	pathnode->jointype = jointype;
-	pathnode->inner_unique = extra->inner_unique;
-	pathnode->outerjoinpath = outer_path;
-	pathnode->innerjoinpath = inner_path;
-	pathnode->joinrestrictinfo = restrict_clauses;
+	pathnode->jpath.path.parallel_workers = outer_path->parallel_workers;
+	pathnode->jpath.path.pathkeys = pathkeys;
+	pathnode->jpath.jointype = jointype;
+	pathnode->jpath.inner_unique = extra->inner_unique;
+	pathnode->jpath.outerjoinpath = outer_path;
+	pathnode->jpath.innerjoinpath = inner_path;
+	pathnode->jpath.joinrestrictinfo = restrict_clauses;
 
 	final_cost_nestloop(root, pathnode, workspace, extra);
 
@@ -4113,13 +4113,15 @@ do { \
 		case T_NestPath:
 			{
 				JoinPath   *jpath;
+				NestPath   *npath;
 
-				FLAT_COPY_PATH(jpath, path, NestPath);
+				FLAT_COPY_PATH(npath, path, NestPath);
 
+				jpath = (JoinPath *) npath;
 				REPARAMETERIZE_CHILD_PATH(jpath->outerjoinpath);
 				REPARAMETERIZE_CHILD_PATH(jpath->innerjoinpath);
 				ADJUST_CHILD_ATTRS(jpath->joinrestrictinfo);
-				new_path = (Path *) jpath;
+				new_path = (Path *) npath;
 			}
 			break;
 
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index b7b2817a5d..f8ddb9e910 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -1595,7 +1595,10 @@ typedef struct JoinPath
  * A nested-loop path needs no special fields.
  */
 
-typedef JoinPath NestPath;
+typedef struct NestPath
+{
+	JoinPath	jpath;
+} NestPath;
 
 /*
  * A mergejoin path has these fields.
-- 
2.31.1



  [text/plain] v1-0006-Add-missing-enum-tags-in-enums-used-in-nodes.patch (1.9K, ../[email protected]/7-v1-0006-Add-missing-enum-tags-in-enums-used-in-nodes.patch)
  download | inline diff:
From 3d66bc6725594d5d187fbd9a023a0ed91bf22eaa Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <[email protected]>
Date: Mon, 7 Jun 2021 16:03:07 +0200
Subject: [PATCH v1 06/10] Add missing enum tags in enums used in nodes

---
 src/include/nodes/parsenodes.h | 4 ++--
 src/include/nodes/pathnodes.h  | 2 +-
 src/include/nodes/primnodes.h  | 2 +-
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index ef73342019..cd9115dbb5 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1337,7 +1337,7 @@ typedef struct SortGroupClause
  *
  * SETS( SIMPLE(1,2), CUBE( SIMPLE(3), SIMPLE(4,5) ) )
  */
-typedef enum
+typedef enum GroupingSetKind
 {
 	GROUPING_SET_EMPTY,
 	GROUPING_SET_SIMPLE,
@@ -2113,7 +2113,7 @@ typedef struct CopyStmt
  * preserve the distinction in VariableSetKind for CreateCommandTag().
  * ----------------------
  */
-typedef enum
+typedef enum VariableSetKind
 {
 	VAR_SET_VALUE,				/* SET var = value */
 	VAR_SET_DEFAULT,			/* SET var TO DEFAULT */
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index f8ddb9e910..03c80c1620 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -1525,7 +1525,7 @@ typedef struct ResultCachePath
  * it's convenient to have a UniquePath in the path tree to signal upper-level
  * routines that the input is known distinct.)
  */
-typedef enum
+typedef enum UniquePathMethod
 {
 	UNIQUE_PATH_NOOP,			/* input is known unique already */
 	UNIQUE_PATH_HASH,			/* use hashing */
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 9ae851d847..3418e23873 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -1205,7 +1205,7 @@ typedef enum XmlExprOp
 	IS_DOCUMENT					/* xmlval IS DOCUMENT */
 } XmlExprOp;
 
-typedef enum
+typedef enum XmlOptionType
 {
 	XMLOPTION_DOCUMENT,
 	XMLOPTION_CONTENT
-- 
2.31.1



  [text/plain] v1-0007-Check-the-size-in-COPY_POINTER_FIELD.patch (4.1K, ../[email protected]/8-v1-0007-Check-the-size-in-COPY_POINTER_FIELD.patch)
  download | inline diff:
From bccacca061746724ae7687be8169b9d74b09acf1 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <[email protected]>
Date: Mon, 7 Jun 2021 16:04:12 +0200
Subject: [PATCH v1 07/10] Check the size in COPY_POINTER_FIELD

instead of making each caller do it.
---
 src/backend/nodes/copyfuncs.c | 54 ++++++++++++++---------------------
 1 file changed, 21 insertions(+), 33 deletions(-)

diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 35a85df442..dde2d47338 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -57,8 +57,11 @@
 #define COPY_POINTER_FIELD(fldname, sz) \
 	do { \
 		Size	_size = (sz); \
-		newnode->fldname = palloc(_size); \
-		memcpy(newnode->fldname, from->fldname, _size); \
+		if (_size > 0) \
+		{ \
+			newnode->fldname = palloc(_size); \
+			memcpy(newnode->fldname, from->fldname, _size); \
+		} \
 	} while (0)
 
 /* Copy a parse location field (for Copy, this is same as scalar case) */
@@ -296,12 +299,9 @@ _copyRecursiveUnion(const RecursiveUnion *from)
 	 */
 	COPY_SCALAR_FIELD(wtParam);
 	COPY_SCALAR_FIELD(numCols);
-	if (from->numCols > 0)
-	{
-		COPY_POINTER_FIELD(dupColIdx, from->numCols * sizeof(AttrNumber));
-		COPY_POINTER_FIELD(dupOperators, from->numCols * sizeof(Oid));
-		COPY_POINTER_FIELD(dupCollations, from->numCols * sizeof(Oid));
-	}
+	COPY_POINTER_FIELD(dupColIdx, from->numCols * sizeof(AttrNumber));
+	COPY_POINTER_FIELD(dupOperators, from->numCols * sizeof(Oid));
+	COPY_POINTER_FIELD(dupCollations, from->numCols * sizeof(Oid));
 	COPY_SCALAR_FIELD(numGroups);
 
 	return newnode;
@@ -896,13 +896,10 @@ _copyMergeJoin(const MergeJoin *from)
 	COPY_SCALAR_FIELD(skip_mark_restore);
 	COPY_NODE_FIELD(mergeclauses);
 	numCols = list_length(from->mergeclauses);
-	if (numCols > 0)
-	{
-		COPY_POINTER_FIELD(mergeFamilies, numCols * sizeof(Oid));
-		COPY_POINTER_FIELD(mergeCollations, numCols * sizeof(Oid));
-		COPY_POINTER_FIELD(mergeStrategies, numCols * sizeof(int));
-		COPY_POINTER_FIELD(mergeNullsFirst, numCols * sizeof(bool));
-	}
+	COPY_POINTER_FIELD(mergeFamilies, numCols * sizeof(Oid));
+	COPY_POINTER_FIELD(mergeCollations, numCols * sizeof(Oid));
+	COPY_POINTER_FIELD(mergeStrategies, numCols * sizeof(int));
+	COPY_POINTER_FIELD(mergeNullsFirst, numCols * sizeof(bool));
 
 	return newnode;
 }
@@ -1064,12 +1061,9 @@ _copyAgg(const Agg *from)
 	COPY_SCALAR_FIELD(aggstrategy);
 	COPY_SCALAR_FIELD(aggsplit);
 	COPY_SCALAR_FIELD(numCols);
-	if (from->numCols > 0)
-	{
-		COPY_POINTER_FIELD(grpColIdx, from->numCols * sizeof(AttrNumber));
-		COPY_POINTER_FIELD(grpOperators, from->numCols * sizeof(Oid));
-		COPY_POINTER_FIELD(grpCollations, from->numCols * sizeof(Oid));
-	}
+	COPY_POINTER_FIELD(grpColIdx, from->numCols * sizeof(AttrNumber));
+	COPY_POINTER_FIELD(grpOperators, from->numCols * sizeof(Oid));
+	COPY_POINTER_FIELD(grpCollations, from->numCols * sizeof(Oid));
 	COPY_SCALAR_FIELD(numGroups);
 	COPY_SCALAR_FIELD(transitionSpace);
 	COPY_BITMAPSET_FIELD(aggParams);
@@ -1091,19 +1085,13 @@ _copyWindowAgg(const WindowAgg *from)
 
 	COPY_SCALAR_FIELD(winref);
 	COPY_SCALAR_FIELD(partNumCols);
-	if (from->partNumCols > 0)
-	{
-		COPY_POINTER_FIELD(partColIdx, from->partNumCols * sizeof(AttrNumber));
-		COPY_POINTER_FIELD(partOperators, from->partNumCols * sizeof(Oid));
-		COPY_POINTER_FIELD(partCollations, from->partNumCols * sizeof(Oid));
-	}
+	COPY_POINTER_FIELD(partColIdx, from->partNumCols * sizeof(AttrNumber));
+	COPY_POINTER_FIELD(partOperators, from->partNumCols * sizeof(Oid));
+	COPY_POINTER_FIELD(partCollations, from->partNumCols * sizeof(Oid));
 	COPY_SCALAR_FIELD(ordNumCols);
-	if (from->ordNumCols > 0)
-	{
-		COPY_POINTER_FIELD(ordColIdx, from->ordNumCols * sizeof(AttrNumber));
-		COPY_POINTER_FIELD(ordOperators, from->ordNumCols * sizeof(Oid));
-		COPY_POINTER_FIELD(ordCollations, from->ordNumCols * sizeof(Oid));
-	}
+	COPY_POINTER_FIELD(ordColIdx, from->ordNumCols * sizeof(AttrNumber));
+	COPY_POINTER_FIELD(ordOperators, from->ordNumCols * sizeof(Oid));
+	COPY_POINTER_FIELD(ordCollations, from->ordNumCols * sizeof(Oid));
 	COPY_SCALAR_FIELD(frameOptions);
 	COPY_NODE_FIELD(startOffset);
 	COPY_NODE_FIELD(endOffset);
-- 
2.31.1



  [text/plain] v1-0008-Remove-T_MemoryContext.patch (689B, ../[email protected]/9-v1-0008-Remove-T_MemoryContext.patch)
  download | inline diff:
From 00fa8e67a141c90e38fcbec45e1cc2135c4fda5a Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <[email protected]>
Date: Mon, 7 Jun 2021 16:05:16 +0200
Subject: [PATCH v1 08/10] Remove T_MemoryContext

This is an abstract node that shouldn't have a node tag defined.
---
 src/include/nodes/nodes.h | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index d9e417bcd7..5e049a67e1 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -284,7 +284,6 @@ typedef enum NodeTag
 	/*
 	 * TAGS FOR MEMORY NODES (memnodes.h)
 	 */
-	T_MemoryContext,
 	T_AllocSetContext,
 	T_SlabContext,
 	T_GenerationContext,
-- 
2.31.1



  [text/plain] v1-0009-Add-script-to-generate-node-support-functions.patch (52.3K, ../[email protected]/10-v1-0009-Add-script-to-generate-node-support-functions.patch)
  download | inline diff:
From 79da68dba05e3ea3b506c4ebf6ea1a1c1e2de6f3 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <[email protected]>
Date: Mon, 7 Jun 2021 16:08:54 +0200
Subject: [PATCH v1 09/10] Add script to generate node support functions

---
 src/backend/Makefile                |   8 +-
 src/backend/nodes/.gitignore        |   3 +
 src/backend/nodes/Makefile          |  55 +++
 src/backend/nodes/copyfuncs.c       |  15 +
 src/backend/nodes/equalfuncs.c      |  19 +-
 src/backend/nodes/gen_node_stuff.pl | 641 ++++++++++++++++++++++++++++
 src/backend/nodes/outfuncs.c        |  25 ++
 src/backend/nodes/readfuncs.c       |  19 +-
 src/include/nodes/.gitignore        |   2 +
 src/include/nodes/nodes.h           |   8 +
 src/include/nodes/parsenodes.h      |   2 +-
 src/include/nodes/pathnodes.h       | 116 ++---
 src/include/nodes/primnodes.h       |  18 +-
 13 files changed, 857 insertions(+), 74 deletions(-)
 create mode 100644 src/backend/nodes/.gitignore
 create mode 100644 src/backend/nodes/gen_node_stuff.pl
 create mode 100644 src/include/nodes/.gitignore

diff --git a/src/backend/Makefile b/src/backend/Makefile
index 0da848b1fd..a33db1ae01 100644
--- a/src/backend/Makefile
+++ b/src/backend/Makefile
@@ -143,11 +143,15 @@ storage/lmgr/lwlocknames.h: storage/lmgr/generate-lwlocknames.pl storage/lmgr/lw
 submake-catalog-headers:
 	$(MAKE) -C catalog distprep generated-header-symlinks
 
+# run this unconditionally to avoid needing to know its dependencies here:
+submake-nodes-headers:
+	$(MAKE) -C nodes distprep generated-header-symlinks
+
 # run this unconditionally to avoid needing to know its dependencies here:
 submake-utils-headers:
 	$(MAKE) -C utils distprep generated-header-symlinks
 
-.PHONY: submake-catalog-headers submake-utils-headers
+.PHONY: submake-catalog-headers submake-nodes-headers submake-utils-headers
 
 # Make symlinks for these headers in the include directory. That way
 # we can cut down on the -I options. Also, a symlink is automatically
@@ -162,7 +166,7 @@ submake-utils-headers:
 
 .PHONY: generated-headers
 
-generated-headers: $(top_builddir)/src/include/parser/gram.h $(top_builddir)/src/include/storage/lwlocknames.h submake-catalog-headers submake-utils-headers
+generated-headers: $(top_builddir)/src/include/parser/gram.h $(top_builddir)/src/include/storage/lwlocknames.h submake-catalog-headers submake-nodes-headers submake-utils-headers
 
 $(top_builddir)/src/include/parser/gram.h: parser/gram.h
 	prereqdir=`cd '$(dir $<)' >/dev/null && pwd` && \
diff --git a/src/backend/nodes/.gitignore b/src/backend/nodes/.gitignore
new file mode 100644
index 0000000000..232c6e1817
--- /dev/null
+++ b/src/backend/nodes/.gitignore
@@ -0,0 +1,3 @@
+/node-stuff-stamp
+/nodetags.h
+/*funcs.inc?.c
diff --git a/src/backend/nodes/Makefile b/src/backend/nodes/Makefile
index 5d2b12a993..eb93166e9e 100644
--- a/src/backend/nodes/Makefile
+++ b/src/backend/nodes/Makefile
@@ -30,3 +30,58 @@ OBJS = \
 	value.o
 
 include $(top_srcdir)/src/backend/common.mk
+
+node_headers = \
+	nodes/nodes.h \
+	nodes/execnodes.h \
+	nodes/plannodes.h \
+	nodes/primnodes.h \
+	nodes/pathnodes.h \
+	nodes/memnodes.h \
+	nodes/extensible.h \
+	nodes/parsenodes.h \
+	nodes/replnodes.h \
+	commands/trigger.h \
+	commands/event_trigger.h \
+	foreign/fdwapi.h \
+	access/amapi.h \
+	access/tableam.h \
+	access/tsmapi.h \
+	utils/rel.h \
+	nodes/supportnodes.h \
+	executor/tuptable.h \
+	nodes/lockoptions.h \
+	access/sdir.h
+
+node_sources = \
+	executor/nodeWindowAgg.c \
+	nodes/tidbitmap.c \
+	utils/mmgr/aset.c \
+	utils/mmgr/generation.c \
+	utils/mmgr/slab.c
+
+node_files = $(addprefix $(top_srcdir)/src/include/,$(node_headers)) $(addprefix $(top_srcdir)/src/backend/,$(node_sources))
+
+# see also catalog/Makefile for an explanation of these make rules
+
+all: distprep generated-header-symlinks
+
+distprep: node-stuff-stamp
+
+.PHONY: generated-header-symlinks
+
+generated-header-symlinks: $(top_builddir)/src/include/nodes/header-stamp
+
+node-stuff-stamp: gen_node_stuff.pl
+	$(PERL) $< $(node_files)
+	touch $@
+
+$(top_builddir)/src/include/nodes/header-stamp: node-stuff-stamp
+	prereqdir=`cd '$(dir $<)' >/dev/null && pwd` && \
+	cd '$(dir $@)' && for file in nodetags.h; do \
+	  rm -f $$file && $(LN_S) "$$prereqdir/$$file" . ; \
+	done
+	touch $@
+
+maintainer-clean: clean
+	rm -f node-stuff-stamp *funcs.inc?.c nodetags.h
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index dde2d47338..4f57923e9e 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -69,6 +69,9 @@
 	(newnode->fldname = from->fldname)
 
 
+#include "copyfuncs.inc1.c"
+
+#ifdef OBSOLETE
 /* ****************************************************************
  *					 plannodes.h copy functions
  * ****************************************************************
@@ -1450,6 +1453,7 @@ _copyVar(const Var *from)
 
 	return newnode;
 }
+#endif /*OBSOLETE*/
 
 /*
  * _copyConst
@@ -1489,6 +1493,7 @@ _copyConst(const Const *from)
 	return newnode;
 }
 
+#ifdef OBSOLETE
 /*
  * _copyParam
  */
@@ -2722,6 +2727,7 @@ _copyParamRef(const ParamRef *from)
 
 	return newnode;
 }
+#endif /*OBSOLETE*/
 
 static A_Const *
 _copyA_Const(const A_Const *from)
@@ -2754,6 +2760,7 @@ _copyA_Const(const A_Const *from)
 	return newnode;
 }
 
+#ifdef OBSOLETE
 static FuncCall *
 _copyFuncCall(const FuncCall *from)
 {
@@ -4863,6 +4870,7 @@ _copyDropSubscriptionStmt(const DropSubscriptionStmt *from)
 
 	return newnode;
 }
+#endif /*OBSOLETE*/
 
 /* ****************************************************************
  *					extensible.h copy functions
@@ -4919,6 +4927,7 @@ _copyValue(const Value *from)
 }
 
 
+#ifdef OBSOLETE
 static ForeignKeyCacheInfo *
 _copyForeignKeyCacheInfo(const ForeignKeyCacheInfo *from)
 {
@@ -4935,6 +4944,7 @@ _copyForeignKeyCacheInfo(const ForeignKeyCacheInfo *from)
 
 	return newnode;
 }
+#endif /*OBSOLETE*/
 
 
 /*
@@ -4956,6 +4966,8 @@ copyObjectImpl(const void *from)
 
 	switch (nodeTag(from))
 	{
+#include "copyfuncs.inc2.c"
+#ifdef OBSOLETE
 			/*
 			 * PLAN NODES
 			 */
@@ -5297,6 +5309,7 @@ copyObjectImpl(const void *from)
 		case T_PlaceHolderInfo:
 			retval = _copyPlaceHolderInfo(from);
 			break;
+#endif /*OBSOLETE*/
 
 			/*
 			 * VALUE NODES
@@ -5325,6 +5338,7 @@ copyObjectImpl(const void *from)
 			retval = list_copy(from);
 			break;
 
+#ifdef OBSOLETE
 			/*
 			 * EXTENSIBLE NODES
 			 */
@@ -5858,6 +5872,7 @@ copyObjectImpl(const void *from)
 		case T_ForeignKeyCacheInfo:
 			retval = _copyForeignKeyCacheInfo(from);
 			break;
+#endif /*OBSOLETE*/
 
 		default:
 			elog(ERROR, "unrecognized node type: %d", (int) nodeTag(from));
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index e04ec41904..6757337062 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -10,9 +10,6 @@
  * because the circular linkages between RelOptInfo and Path nodes can't
  * be handled easily in a simple depth-first traversal.
  *
- * Currently, in fact, equal() doesn't know how to compare Plan trees
- * either.  This might need to be fixed someday.
- *
  * NOTE: it is intentional that parse location fields (in nodes that have
  * one) are not compared.  This is because we want, for example, a variable
  * "x" to be considered equal() to another reference to "x" in the query.
@@ -33,6 +30,7 @@
 #include "nodes/extensible.h"
 #include "nodes/pathnodes.h"
 #include "utils/datum.h"
+#include "utils/rel.h"
 
 
 /*
@@ -90,6 +88,9 @@
 	((void) 0)
 
 
+#include "equalfuncs.inc1.c"
+
+#ifdef OBSOLETE
 /*
  *	Stuff from primnodes.h
  */
@@ -178,6 +179,7 @@ _equalVar(const Var *a, const Var *b)
 
 	return true;
 }
+#endif /*OBSOLETE*/
 
 static bool
 _equalConst(const Const *a, const Const *b)
@@ -200,6 +202,7 @@ _equalConst(const Const *a, const Const *b)
 						a->constbyval, a->constlen);
 }
 
+#ifdef OBSOLETE
 static bool
 _equalParam(const Param *a, const Param *b)
 {
@@ -933,6 +936,7 @@ _equalPlaceHolderInfo(const PlaceHolderInfo *a, const PlaceHolderInfo *b)
 
 	return true;
 }
+#endif /*OBSOLETE*/
 
 /*
  * Stuff from extensible.h
@@ -954,6 +958,7 @@ _equalExtensibleNode(const ExtensibleNode *a, const ExtensibleNode *b)
 	return true;
 }
 
+#ifdef OBSOLETE
 /*
  * Stuff from parsenodes.h
  */
@@ -2397,6 +2402,7 @@ _equalParamRef(const ParamRef *a, const ParamRef *b)
 
 	return true;
 }
+#endif /*OBSOLETE*/
 
 static bool
 _equalA_Const(const A_Const *a, const A_Const *b)
@@ -2408,6 +2414,7 @@ _equalA_Const(const A_Const *a, const A_Const *b)
 	return true;
 }
 
+#ifdef OBSOLETE
 static bool
 _equalFuncCall(const FuncCall *a, const FuncCall *b)
 {
@@ -3016,6 +3023,7 @@ _equalPartitionCmd(const PartitionCmd *a, const PartitionCmd *b)
 
 	return true;
 }
+#endif /*OBSOLETE*/
 
 /*
  * Stuff from pg_list.h
@@ -3135,6 +3143,8 @@ equal(const void *a, const void *b)
 
 	switch (nodeTag(a))
 	{
+#include "equalfuncs.inc2.c"
+#ifdef OBSOLETE
 			/*
 			 * PRIMITIVE NODES
 			 */
@@ -3313,6 +3323,7 @@ equal(const void *a, const void *b)
 		case T_PlaceHolderInfo:
 			retval = _equalPlaceHolderInfo(a, b);
 			break;
+#endif /*OBSOLETE*/
 
 		case T_List:
 		case T_IntList:
@@ -3328,6 +3339,7 @@ equal(const void *a, const void *b)
 			retval = _equalValue(a, b);
 			break;
 
+#ifdef OBSOLETE
 			/*
 			 * EXTENSIBLE NODES
 			 */
@@ -3854,6 +3866,7 @@ equal(const void *a, const void *b)
 		case T_PartitionCmd:
 			retval = _equalPartitionCmd(a, b);
 			break;
+#endif /*OBSOLETE*/
 
 		default:
 			elog(ERROR, "unrecognized node type: %d",
diff --git a/src/backend/nodes/gen_node_stuff.pl b/src/backend/nodes/gen_node_stuff.pl
new file mode 100644
index 0000000000..aafe320e0a
--- /dev/null
+++ b/src/backend/nodes/gen_node_stuff.pl
@@ -0,0 +1,641 @@
+#!/usr/bin/perl
+#----------------------------------------------------------------------
+#
+# Generate node support files:
+# - nodetags.h
+# - copyfuncs
+# - equalfuncs
+# - readfuncs
+# - outfuncs
+#
+# src/backend/nodes/gen_node_stuff.pl
+#
+#----------------------------------------------------------------------
+
+use strict;
+use warnings;
+
+use experimental 'smartmatch';
+
+use File::Basename;
+
+use FindBin;
+use lib "$FindBin::RealBin/../catalog";
+
+use Catalog;  # for RenameTempFile
+
+
+my @node_types = qw(Node);
+my %node_type_info;
+
+my @no_copy;
+my @no_read_write;
+
+my @scalar_types = qw(
+	bits32 bool char double int int8 int16 int32 int64 long uint8 uint16 uint32 uint64
+	AclMode AttrNumber Cost Index Oid Selectivity Size StrategyNumber SubTransactionId TimeLineID XLogRecPtr
+);
+
+my @enum_types;
+
+# For abstract types we track their fields, so that subtypes can use
+# them, but we don't emit a node tag, so you can't instantiate them.
+my @abstract_types = qw(
+	Node
+	BufferHeapTupleTableSlot HeapTupleTableSlot MinimalTupleTableSlot VirtualTupleTableSlot
+	JoinPath
+	MemoryContextData
+	PartitionPruneStep
+);
+
+# These are additional node tags that don't have their own struct.
+my @extra_tags = qw(IntList OidList Integer Float String BitString Null);
+
+# These are regular nodes, but we skip parsing them from their header
+# files since we won't use their internal structure here anyway.
+push @node_types, qw(List Value);
+
+# XXX maybe this should be abstract?
+push @no_copy, qw(Expr);
+push @no_read_write, qw(Expr);
+
+# pathnodes.h exceptions
+push @no_copy, qw(
+	RelOptInfo IndexOptInfo Path PlannerGlobal EquivalenceClass EquivalenceMember ForeignKeyOptInfo
+	GroupingSetData IncrementalSortPath IndexClause MinMaxAggInfo PathTarget PlannerInfo PlannerParamItem
+	ParamPathInfo RollupData RowIdentityVarInfo StatisticExtInfo
+);
+push @scalar_types, qw(EquivalenceClass* EquivalenceMember* QualCost);
+
+# XXX various things we are not publishing right now to stay level
+# with the manual system
+push @no_copy, qw(CallContext InlineCodeBlock);
+push @no_read_write, qw(AccessPriv AlterTableCmd CallContext CreateOpClassItem FunctionParameter InferClause InlineCodeBlock ObjectWithArgs OnConflictClause PartitionCmd RoleSpec VacuumRelation);
+
+
+## read input
+
+foreach my $infile (@ARGV)
+{
+	my $in_struct;
+	my $subline;
+	my $is_node_struct;
+	my $supertype;
+	my $supertype_field;
+
+	my @my_fields;
+	my %my_field_types;
+	my %my_field_attrs;
+
+	open my $ifh, '<', $infile or die "could not open \"$infile\": $!";
+
+	while (my $line = <$ifh>)
+	{
+		chomp $line;
+		$line =~ s!/\*.*$!!;
+		$line =~ s/\s*$//;
+		next if $line eq '';
+		next if $line =~ m!^\s*\*.*$!;  # line starts with *, probably comment continuation
+		next if $line =~ /^#(define|ifdef|endif)/;
+
+		if ($in_struct)
+		{
+			$subline++;
+
+			# first line should have opening brace
+			if ($subline == 1)
+			{
+				$is_node_struct = 0;
+				$supertype = undef;
+				next if $line eq '{';
+				die;
+			}
+			# second line should have node tag or supertype
+			elsif ($subline == 2)
+			{
+				if ($line =~ /^\s*NodeTag\s+type;/)
+				{
+					$is_node_struct = 1;
+					next;
+				}
+				elsif ($line =~ /\s*(\w+)\s+(\w+);/ && $1 ~~ @node_types)
+				{
+					$is_node_struct = 1;
+					$supertype = $1;
+					$supertype_field = $2;
+					next;
+				}
+			}
+
+			# end of struct
+			if ($line =~ /^\}\s*$in_struct;$/ || $line =~ /^\};$/)
+			{
+				if ($is_node_struct)
+				{
+					push @node_types, $in_struct;
+					my @f = @my_fields;
+					my %ft = %my_field_types;
+					my %fa = %my_field_attrs;
+					if ($supertype)
+					{
+						my @superfields;
+						foreach my $sf (@{$node_type_info{$supertype}->{fields}})
+						{
+							my $fn = "${supertype_field}.$sf";
+							push @superfields, $fn;
+							$ft{$fn} = $node_type_info{$supertype}->{field_types}{$sf};
+							$fa{$fn} = $node_type_info{$supertype}->{field_attrs}{$sf};
+						}
+						unshift @f, @superfields;
+					}
+					$node_type_info{$in_struct}->{fields} = \@f;
+					$node_type_info{$in_struct}->{field_types} = \%ft;
+					$node_type_info{$in_struct}->{field_attrs} = \%fa;
+
+					if (basename($infile) eq 'execnodes.h' ||
+						basename($infile) eq 'trigger.h' ||
+						basename($infile) eq 'event_trigger.h' ||
+						basename($infile) eq 'amapi.h' ||
+						basename($infile) eq 'tableam.h' ||
+						basename($infile) eq 'tsmapi.h' ||
+						basename($infile) eq 'fdwapi.h' ||
+						basename($infile) eq 'tuptable.h' ||
+						basename($infile) eq 'replnodes.h' ||
+						basename($infile) eq 'supportnodes.h' ||
+						$infile =~ /\.c$/
+					)
+					{
+						push @no_copy, $in_struct;
+						push @no_read_write, $in_struct;
+					}
+
+					if ($supertype && ($supertype eq 'Path' || $supertype eq 'JoinPath'))
+					{
+						push @no_copy, $in_struct;
+					}
+				}
+
+				# start new cycle
+				$in_struct = undef;
+				@my_fields = ();
+				%my_field_types = ();
+				%my_field_attrs = ();
+			}
+			# normal struct field
+			elsif ($line =~ /^\s*(.+)\s*\b(\w+)(\[\w+\])?\s*(?:pg_node_attr\(([\w ]*)\))?;/)
+			{
+				if ($is_node_struct)
+				{
+					my $type = $1;
+					my $name = $2;
+					my $array_size = $3;
+					my $attr = $4;
+
+					$type =~ s/^const\s*//;
+					$type =~ s/\s*$//;
+					$type =~ s/\s+\*$/*/;
+					die if $type eq '';
+					$type = $type . $array_size if $array_size;
+					push @my_fields, $name;
+					$my_field_types{$name} = $type;
+					$my_field_attrs{$name} = $attr;
+				}
+			}
+			else
+			{
+				if ($is_node_struct)
+				{
+					#warn "$infile:$.: could not parse \"$line\"\n";
+				}
+			}
+		}
+		# not in a struct
+		else
+		{
+			# start of a struct?
+			if ($line =~ /^(?:typedef )?struct (\w+)(\s*\/\*.*)?$/ && $1 ne 'Node')
+			{
+				$in_struct = $1;
+				$subline = 0;
+			}
+			# one node type typedef'ed directly from another
+			elsif ($line =~ /^typedef (\w+) (\w+);$/ && $1 ~~ @node_types)
+			{
+				my $alias_of = $1;
+				my $n = $2;
+
+				push @node_types, $n;
+				my @f = @{$node_type_info{$alias_of}->{fields}};
+				my %ft = %{$node_type_info{$alias_of}->{field_types}};
+				my %fa = %{$node_type_info{$alias_of}->{field_attrs}};
+				$node_type_info{$n}->{fields} = \@f;
+				$node_type_info{$n}->{field_types} = \%ft;
+				$node_type_info{$n}->{field_attrs} = \%fa;
+			}
+			# collect enum names
+			elsif ($line =~ /^typedef enum (\w+)(\s*\/\*.*)?$/)
+			{
+				push @enum_types, $1;
+			}
+		}
+	}
+
+	if ($in_struct)
+	{
+		die "runaway \"$in_struct\" in file \"$infile\"\n";
+	}
+
+	close $ifh;
+} # for each file
+
+
+## write output
+
+my $tmpext  = ".tmp$$";
+
+# nodetags.h
+
+open my $nt, '>', 'nodetags.h' . $tmpext or die $!;
+
+my $i = 1;
+foreach my $n (@node_types,@extra_tags)
+{
+	next if $n ~~ @abstract_types;
+	print $nt "\tT_${n} = $i,\n";
+	$i++;
+}
+
+close $nt;
+
+
+# copyfuncs.c, equalfuncs.c
+
+open my $cf, '>', 'copyfuncs.inc1.c' . $tmpext or die $!;
+open my $ef, '>', 'equalfuncs.inc1.c' . $tmpext or die $!;
+open my $cf2, '>', 'copyfuncs.inc2.c' . $tmpext or die $!;
+open my $ef2, '>', 'equalfuncs.inc2.c' . $tmpext or die $!;
+
+my @custom_copy = qw(A_Const Const ExtensibleNode);
+
+foreach my $n (@node_types)
+{
+	next if $n ~~ @abstract_types;
+	next if $n ~~ @no_copy;
+	next if $n eq 'List';
+	next if $n eq 'Value';
+
+	print $cf2 "
+\t\tcase T_${n}:
+\t\t\tretval = _copy${n}(from);
+\t\t\tbreak;";
+
+	print $ef2 "
+\t\tcase T_${n}:
+\t\t\tretval = _equal${n}(a, b);
+\t\t\tbreak;";
+
+	next if $n ~~ @custom_copy;
+
+	print $cf "
+static $n *
+_copy${n}(const $n *from)
+{
+\t${n} *newnode = makeNode($n);
+
+";
+
+	print $ef "
+static bool
+_equal${n}(const $n *a, const $n *b)
+{
+";
+
+	my $last_array_size_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 $copy_ignore = ($a =~ /\bcopy_ignore\b/);
+		my $equal_ignore = ($a =~ /\bequal_ignore\b/);
+		if ($t eq 'char*')
+		{
+			print $cf "\tCOPY_STRING_FIELD($f);\n" unless $copy_ignore;
+			print $ef "\tCOMPARE_STRING_FIELD($f);\n" unless $equal_ignore;
+		}
+		elsif ($t eq 'Bitmapset*' || $t eq 'Relids')
+		{
+			print $cf "\tCOPY_BITMAPSET_FIELD($f);\n" unless $copy_ignore;
+			print $ef "\tCOMPARE_BITMAPSET_FIELD($f);\n" unless $equal_ignore;
+		}
+		elsif ($t eq 'int' && $f =~ 'location$')
+		{
+			print $cf "\tCOPY_LOCATION_FIELD($f);\n" unless $copy_ignore;
+			print $ef "\tCOMPARE_LOCATION_FIELD($f);\n" unless $equal_ignore;
+		}
+		elsif ($t ~~ @scalar_types || $t ~~ @enum_types)
+		{
+			print $cf "\tCOPY_SCALAR_FIELD($f);\n" unless $copy_ignore;
+			if ($a =~ /\bequal_ignore_if_zero\b/)
+			{
+				print $ef "\tif (a->$f != b->$f && a->$f != 0 && b->$f != 0)\n\t\treturn false;\n";
+			}
+			else
+			{
+				print $ef "\tCOMPARE_SCALAR_FIELD($f);\n" unless $equal_ignore || $t eq 'CoercionForm';
+			}
+			$last_array_size_field = "from->$f";
+		}
+		elsif ($t =~ /(\w+)\*/ && $1 ~~ @scalar_types)
+		{
+			my $tt = $1;
+			print $cf "\tCOPY_POINTER_FIELD($f, $last_array_size_field * sizeof($tt));\n" unless $copy_ignore;
+			(my $l2 = $last_array_size_field) =~ s/from/a/;
+			print $ef "\tCOMPARE_POINTER_FIELD($f, $l2 * sizeof($tt));\n" unless $equal_ignore;
+		}
+		elsif ($t =~ /(\w+)\*/ && $1 ~~ @node_types)
+		{
+			print $cf "\tCOPY_NODE_FIELD($f);\n" unless $copy_ignore;
+			print $ef "\tCOMPARE_NODE_FIELD($f);\n" unless $equal_ignore;
+			$last_array_size_field = "list_length(from->$f)" if $t eq 'List*';
+		}
+		elsif ($t =~ /\w+\[/)
+		{
+			# COPY_SCALAR_FIELD might work for these, but let's not assume that
+			print $cf "\tmemcpy(newnode->$f, from->$f, sizeof(newnode->$f));\n" unless $copy_ignore;
+			print $ef "\tCOMPARE_POINTER_FIELD($f, sizeof(a->$f));\n" unless $equal_ignore;
+		}
+		elsif ($t eq 'struct CustomPathMethods*' ||	$t eq 'struct CustomScanMethods*')
+		{
+			print $cf "\tCOPY_SCALAR_FIELD($f);\n" unless $copy_ignore;
+			print $ef "\tCOMPARE_SCALAR_FIELD($f);\n" unless $equal_ignore;
+		}
+		else
+		{
+			die "could not handle type \"$t\" in struct \"$n\" field \"$f\"";
+		}
+	}
+
+	print $cf "
+\treturn newnode;
+}
+";
+	print $ef "
+\treturn true;
+}
+";
+}
+
+close $cf;
+close $ef;
+close $cf2;
+close $ef2;
+
+
+# outfuncs.c, readfuncs.c
+
+open my $of, '>', 'outfuncs.inc1.c' . $tmpext or die $!;
+open my $rf, '>', 'readfuncs.inc1.c' . $tmpext or die $!;
+open my $of2, '>', 'outfuncs.inc2.c' . $tmpext or die $!;
+open my $rf2, '>', 'readfuncs.inc2.c' . $tmpext or die $!;
+
+my %name_map = (
+	'ARRAYEXPR' => 'ARRAY',
+	'CASEEXPR' => 'CASE',
+	'CASEWHEN' => 'WHEN',
+	'COALESCEEXPR' => 'COALESCE',
+	'COLLATEEXPR' => 'COLLATE',
+	'DECLARECURSORSTMT' => 'DECLARECURSOR',
+	'MINMAXEXPR' => 'MINMAX',
+	'NOTIFYSTMT' => 'NOTIFY',
+	'RANGETBLENTRY' => 'RTE',
+	'ROWCOMPAREEXPR' => 'ROWCOMPARE',
+	'ROWEXPR' => 'ROW',
+);
+
+my @custom_readwrite = qw(A_Const A_Expr BoolExpr Const Constraint ExtensibleNode Query RangeTblEntry);
+
+foreach my $n (@node_types)
+{
+	next if $n ~~ @abstract_types;
+	next if $n ~~ @no_read_write;
+	next if $n eq 'List';
+	next if $n eq 'Value';
+
+	# XXX For now, skip all "Stmt"s except that ones that were there before.
+	if ($n =~ /Stmt$/)
+	{
+		my @keep = qw(AlterStatsStmt CreateForeignTableStmt CreateStatsStmt CreateStmt DeclareCursorStmt ImportForeignSchemaStmt IndexStmt NotifyStmt PlannedStmt PLAssignStmt RawStmt ReturnStmt SelectStmt SetOperationStmt);
+		next unless $n ~~ @keep;
+	}
+
+	# XXX Also skip read support for those that didn't have it before.
+	my $no_read = ($n eq 'A_Star' || $n eq 'A_Const' || $n eq 'A_Expr' || $n eq 'Constraint' || $n =~ /Path$/ || $n eq 'ForeignKeyCacheInfo' || $n eq 'ForeignKeyOptInfo' || $n eq 'PathTarget');
+
+	my $N = uc $n;
+	$N =~ s/_//g;
+	$N = $name_map{$N} if $name_map{$N};
+
+	print $of2 "\t\t\tcase T_${n}:\n".
+	  "\t\t\t\t_out${n}(str, obj);\n".
+	  "\t\t\t\tbreak;\n";
+
+	print $rf2 "\telse if (MATCH(\"$N\", " . length($N) . "))\n".
+	  "\t\treturn_value = _read${n}();\n" unless $no_read;
+
+	next if $n ~~ @custom_readwrite;
+
+	print $of "
+static void
+_out${n}(StringInfo str, const $n *node)
+{
+\tWRITE_NODE_TYPE(\"$N\");
+
+";
+
+	print $rf "
+static $n *
+_read${n}(void)
+{
+\tREAD_LOCALS($n);
+
+" unless $no_read;
+
+	my $last_array_size_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 $readwrite_ignore = ($a =~ /\breadwrite_ignore\b/);
+		next if $readwrite_ignore;
+
+		# XXX Previously, for subtyping, only the leaf field name is
+		# used. Ponder whether we want to keep it that way.
+
+		if ($t eq 'bool')
+		{
+			print $of "\tWRITE_BOOL_FIELD($f);\n";
+			print $rf "\tREAD_BOOL_FIELD($f);\n" unless $no_read;
+		}
+		elsif ($t eq 'int' && $f =~ 'location$')
+		{
+			print $of "\tWRITE_LOCATION_FIELD($f);\n";
+			print $rf "\tREAD_LOCATION_FIELD($f);\n" unless $no_read;
+		}
+		elsif ($t eq 'int' || $t eq 'int32' || $t eq 'AttrNumber' || $t eq 'StrategyNumber')
+		{
+			print $of "\tWRITE_INT_FIELD($f);\n";
+			print $rf "\tREAD_INT_FIELD($f);\n" unless $no_read;
+			$last_array_size_field = "node->$f" if $t eq 'int';
+		}
+		elsif ($t eq 'uint32' || $t eq 'bits32' || $t eq 'AclMode' || $t eq 'BlockNumber' || $t eq 'Index' || $t eq 'SubTransactionId')
+		{
+			print $of "\tWRITE_UINT_FIELD($f);\n";
+			print $rf "\tREAD_UINT_FIELD($f);\n" unless $no_read;
+		}
+		elsif ($t eq 'uint64')
+		{
+			print $of "\tWRITE_UINT64_FIELD($f);\n";
+			print $rf "\tREAD_UINT64_FIELD($f);\n" unless $no_read;
+		}
+		elsif ($t eq 'Oid')
+		{
+			print $of "\tWRITE_OID_FIELD($f);\n";
+			print $rf "\tREAD_OID_FIELD($f);\n" unless $no_read;
+		}
+		elsif ($t eq 'long')
+		{
+			print $of "\tWRITE_LONG_FIELD($f);\n";
+			print $rf "\tREAD_LONG_FIELD($f);\n" unless $no_read;
+		}
+		elsif ($t eq 'char')
+		{
+			print $of "\tWRITE_CHAR_FIELD($f);\n";
+			print $rf "\tREAD_CHAR_FIELD($f);\n" unless $no_read;
+		}
+		elsif ($t eq 'double')
+		{
+			# XXX We out to split these into separate types, like Cost
+			# etc.
+			if ($f eq 'allvisfrac')
+			{
+				print $of "\tWRITE_FLOAT_FIELD($f, \"%.6f\");\n";
+			}
+			else
+			{
+				print $of "\tWRITE_FLOAT_FIELD($f, \"%.0f\");\n";
+			}
+			print $rf "\tREAD_FLOAT_FIELD($f);\n" unless $no_read;
+		}
+		elsif ($t eq 'Cost')
+		{
+			print $of "\tWRITE_FLOAT_FIELD($f, \"%.2f\");\n";
+			print $rf "\tREAD_FLOAT_FIELD($f);\n" unless $no_read;
+		}
+		elsif ($t eq 'QualCost')
+		{
+			print $of "\tWRITE_FLOAT_FIELD($f.startup, \"%.2f\");\n";
+			print $of "\tWRITE_FLOAT_FIELD($f.per_tuple, \"%.2f\");\n";
+			print $rf "\tREAD_FLOAT_FIELD($f.startup);\n" unless $no_read;
+			print $rf "\tREAD_FLOAT_FIELD($f.per_tuple);\n" unless $no_read;
+		}
+		elsif ($t eq 'Selectivity')
+		{
+			print $of "\tWRITE_FLOAT_FIELD($f, \"%.4f\");\n";
+			print $rf "\tREAD_FLOAT_FIELD($f);\n" unless $no_read;
+		}
+		elsif ($t eq 'char*')
+		{
+			print $of "\tWRITE_STRING_FIELD($f);\n";
+			print $rf "\tREAD_STRING_FIELD($f);\n" unless $no_read;
+		}
+		elsif ($t eq 'Bitmapset*' || $t eq 'Relids')
+		{
+			print $of "\tWRITE_BITMAPSET_FIELD($f);\n";
+			print $rf "\tREAD_BITMAPSET_FIELD($f);\n" unless $no_read;
+		}
+		elsif ($t ~~ @enum_types)
+		{
+			print $of "\tWRITE_ENUM_FIELD($f, $t);\n";
+			print $rf "\tREAD_ENUM_FIELD($f, $t);\n" unless $no_read;
+		}
+		elsif ($t =~ /(\w+)(\*|\[)/ && $1 ~~ @scalar_types)
+		{
+			warn "$t $n.$f" unless $last_array_size_field;
+			my $tt = uc $1;
+			print $of "\tWRITE_${tt}_ARRAY($f, $last_array_size_field);\n";
+			(my $l2 = $last_array_size_field) =~ s/node/local_node/;
+			print $rf "\tREAD_${tt}_ARRAY($f, $l2);\n" unless $no_read;
+		}
+		elsif ($t eq 'RelOptInfo*' && $a eq 'path_hack1')
+		{
+			print $of "\tappendStringInfoString(str, \" :parent_relids \");\n".
+			  "\toutBitmapset(str, node->$f->relids);\n";
+		}
+		elsif ($t eq 'PathTarget*' && $a eq 'path_hack2')
+		{
+			(my $f2 = $f) =~ s/pathtarget/parent/;
+			print $of "\tif (node->$f != node->$f2->reltarget)\n".
+			  "\t\tWRITE_NODE_FIELD($f);\n";
+		}
+		elsif ($t eq 'ParamPathInfo*' && $a eq 'path_hack3')
+		{
+			print $of "\tif (node->$f)\n".
+			  "\t\toutBitmapset(str, node->$f->ppi_req_outer);\n".
+			  "\telse\n".
+			  "\t\toutBitmapset(str, NULL);\n";
+		}
+		elsif ($t =~ /(\w+)\*/ && $1 ~~ @node_types)
+		{
+			print $of "\tWRITE_NODE_FIELD($f);\n";
+			print $rf "\tREAD_NODE_FIELD($f);\n" unless $no_read;
+			$last_array_size_field = "list_length(node->$f)" if $t eq 'List*';
+		}
+		elsif ($t eq 'struct CustomPathMethods*' ||	$t eq 'struct CustomScanMethods*')
+		{
+			print $of q{
+	appendStringInfoString(str, " :methods ");
+	outToken(str, node->methods->CustomName);
+};
+			print $rf q!
+	{
+		/* Lookup CustomScanMethods by CustomName */
+		char	   *custom_name;
+		const CustomScanMethods *methods;
+		token = pg_strtok(&length); /* skip methods: */
+		token = pg_strtok(&length); /* CustomName */
+		custom_name = nullable_string(token, length);
+		methods = GetCustomScanMethods(custom_name, false);
+		local_node->methods = methods;
+	}
+! unless $no_read;
+		}
+		elsif ($t eq 'ParamListInfo' || $t =~ /PartitionBoundInfoData/ || $t eq 'PartitionDirectory' || $t eq 'PartitionScheme' || $t eq 'void*' || $t =~ /\*\*$/)
+		{
+			# ignore
+		}
+		else
+		{
+			die "could not handle type \"$t\" in struct \"$n\" field \"$f\"";
+		}
+	}
+
+	print $of "}
+";
+	print $rf "
+\tREAD_DONE();
+}
+" unless $no_read;
+}
+
+close $of;
+close $rf;
+close $of2;
+close $rf2;
+
+
+foreach my $file (qw(nodetags.h copyfuncs.inc1.c copyfuncs.inc2.c equalfuncs.inc1.c equalfuncs.inc2.c outfuncs.inc1.c outfuncs.inc2.c readfuncs.inc1.c readfuncs.inc2.c))
+{
+	Catalog::RenameTempFile($file, $tmpext);
+}
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index ed08f5acf4..0978bc4e81 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -124,6 +124,8 @@ static void outChar(StringInfo str, char c);
 			appendStringInfo(str, " %u", node->fldname[i]); \
 	} while(0)
 
+#define WRITE_INDEX_ARRAY(fldname, len) WRITE_OID_ARRAY(fldname, len)
+
 #define WRITE_INT_ARRAY(fldname, len) \
 	do { \
 		appendStringInfoString(str, " :" CppAsString(fldname) " "); \
@@ -288,6 +290,9 @@ outDatum(StringInfo str, Datum value, int typlen, bool typbyval)
 }
 
 
+#include "outfuncs.inc1.c"
+
+#ifdef OBSOLETE
 /*
  *	Stuff from plannodes.h
  */
@@ -1126,6 +1131,7 @@ _outVar(StringInfo str, const Var *node)
 	WRITE_INT_FIELD(varattnosyn);
 	WRITE_LOCATION_FIELD(location);
 }
+#endif /*OBSOLETE*/
 
 static void
 _outConst(StringInfo str, const Const *node)
@@ -1147,6 +1153,7 @@ _outConst(StringInfo str, const Const *node)
 		outDatum(str, node->constvalue, node->constlen, node->constbyval);
 }
 
+#ifdef OBSOLETE
 static void
 _outParam(StringInfo str, const Param *node)
 {
@@ -1316,6 +1323,7 @@ _outScalarArrayOpExpr(StringInfo str, const ScalarArrayOpExpr *node)
 	WRITE_NODE_FIELD(args);
 	WRITE_LOCATION_FIELD(location);
 }
+#endif /*OBSOLETE*/
 
 static void
 _outBoolExpr(StringInfo str, const BoolExpr *node)
@@ -1344,6 +1352,7 @@ _outBoolExpr(StringInfo str, const BoolExpr *node)
 	WRITE_LOCATION_FIELD(location);
 }
 
+#ifdef OBSOLETE
 static void
 _outSubLink(StringInfo str, const SubLink *node)
 {
@@ -2658,6 +2667,7 @@ _outPlannerParamItem(StringInfo str, const PlannerParamItem *node)
 	WRITE_NODE_FIELD(item);
 	WRITE_INT_FIELD(paramId);
 }
+#endif /*OBSOLETE*/
 
 /*****************************************************************************
  *
@@ -2680,6 +2690,7 @@ _outExtensibleNode(StringInfo str, const ExtensibleNode *node)
 	methods->nodeOut(str, node);
 }
 
+#ifdef OBSOLETE
 /*****************************************************************************
  *
  *	Stuff from parsenodes.h.
@@ -3012,6 +3023,7 @@ _outStatsElem(StringInfo str, const StatsElem *node)
 	WRITE_STRING_FIELD(name);
 	WRITE_NODE_FIELD(expr);
 }
+#endif /*OBSOLETE*/
 
 static void
 _outQuery(StringInfo str, const Query *node)
@@ -3084,6 +3096,7 @@ _outQuery(StringInfo str, const Query *node)
 	WRITE_INT_FIELD(stmt_len);
 }
 
+#ifdef OBSOLETE
 static void
 _outWithCheckOption(StringInfo str, const WithCheckOption *node)
 {
@@ -3222,6 +3235,7 @@ _outSetOperationStmt(StringInfo str, const SetOperationStmt *node)
 	WRITE_NODE_FIELD(colCollations);
 	WRITE_NODE_FIELD(groupClauses);
 }
+#endif /*OBSOLETE*/
 
 static void
 _outRangeTblEntry(StringInfo str, const RangeTblEntry *node)
@@ -3302,6 +3316,7 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node)
 	WRITE_NODE_FIELD(securityQuals);
 }
 
+#ifdef OBSOLETE
 static void
 _outRangeTblFunction(StringInfo str, const RangeTblFunction *node)
 {
@@ -3325,6 +3340,7 @@ _outTableSampleClause(StringInfo str, const TableSampleClause *node)
 	WRITE_NODE_FIELD(args);
 	WRITE_NODE_FIELD(repeatable);
 }
+#endif /*OBSOLETE*/
 
 static void
 _outA_Expr(StringInfo str, const A_Expr *node)
@@ -3442,6 +3458,7 @@ _outValue(StringInfo str, const Value *node)
 	}
 }
 
+#ifdef OBSOLETE
 static void
 _outColumnRef(StringInfo str, const ColumnRef *node)
 {
@@ -3473,6 +3490,7 @@ _outRawStmt(StringInfo str, const RawStmt *node)
 	WRITE_LOCATION_FIELD(stmt_location);
 	WRITE_INT_FIELD(stmt_len);
 }
+#endif /*OBSOLETE*/
 
 static void
 _outA_Const(StringInfo str, const A_Const *node)
@@ -3484,6 +3502,7 @@ _outA_Const(StringInfo str, const A_Const *node)
 	WRITE_LOCATION_FIELD(location);
 }
 
+#ifdef OBSOLETE
 static void
 _outA_Star(StringInfo str, const A_Star *node)
 {
@@ -3628,6 +3647,7 @@ _outRangeTableFuncCol(StringInfo str, const RangeTableFuncCol *node)
 	WRITE_NODE_FIELD(coldefexpr);
 	WRITE_LOCATION_FIELD(location);
 }
+#endif /*OBSOLETE*/
 
 static void
 _outConstraint(StringInfo str, const Constraint *node)
@@ -3748,6 +3768,7 @@ _outConstraint(StringInfo str, const Constraint *node)
 	}
 }
 
+#ifdef OBSOLETE
 static void
 _outForeignKeyCacheInfo(StringInfo str, const ForeignKeyCacheInfo *node)
 {
@@ -3808,6 +3829,7 @@ _outPartitionRangeDatum(StringInfo str, const PartitionRangeDatum *node)
 	WRITE_NODE_FIELD(value);
 	WRITE_LOCATION_FIELD(location);
 }
+#endif /*OBSOLETE*/
 
 /*
  * outNode -
@@ -3836,6 +3858,8 @@ outNode(StringInfo str, const void *obj)
 		appendStringInfoChar(str, '{');
 		switch (nodeTag(obj))
 		{
+#include "outfuncs.inc2.c"
+#ifdef OBSOLETE
 			case T_PlannedStmt:
 				_outPlannedStmt(str, obj);
 				break;
@@ -4505,6 +4529,7 @@ outNode(StringInfo str, const void *obj)
 			case T_PartitionRangeDatum:
 				_outPartitionRangeDatum(str, obj);
 				break;
+#endif /*OBSOLETE*/
 
 			default:
 
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index fef4de94e6..b4b9379d9d 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -240,6 +240,8 @@ readBitmapset(void)
 	return _readBitmapset();
 }
 
+#include "readfuncs.inc1.c"
+
 /*
  * _readQuery
  */
@@ -291,6 +293,7 @@ _readQuery(void)
 	READ_DONE();
 }
 
+#ifdef OBSOLETE
 /*
  * _readNotifyStmt
  */
@@ -589,6 +592,7 @@ _readVar(void)
 
 	READ_DONE();
 }
+#endif /*OBSOLETE*/
 
 /*
  * _readConst
@@ -615,6 +619,7 @@ _readConst(void)
 	READ_DONE();
 }
 
+#ifdef OBSOLETE
 /*
  * _readParam
  */
@@ -839,6 +844,7 @@ _readScalarArrayOpExpr(void)
 
 	READ_DONE();
 }
+#endif /*OBSOLETE*/
 
 /*
  * _readBoolExpr
@@ -866,6 +872,7 @@ _readBoolExpr(void)
 	READ_DONE();
 }
 
+#ifdef OBSOLETE
 /*
  * _readSubLink
  */
@@ -1420,6 +1427,7 @@ _readAppendRelInfo(void)
 /*
  *	Stuff from parsenodes.h.
  */
+#endif /*OBSOLETE*/
 
 /*
  * _readRangeTblEntry
@@ -1515,6 +1523,7 @@ _readRangeTblEntry(void)
 	READ_DONE();
 }
 
+#ifdef OBSOLETE
 /*
  * _readRangeTblFunction
  */
@@ -2635,6 +2644,7 @@ _readAlternativeSubPlan(void)
 
 	READ_DONE();
 }
+#endif /*OBSOLETE*/
 
 /*
  * _readExtensibleNode
@@ -2666,6 +2676,7 @@ _readExtensibleNode(void)
 	READ_DONE();
 }
 
+#ifdef OBSOLETE
 /*
  * _readPartitionBoundSpec
  */
@@ -2700,6 +2711,7 @@ _readPartitionRangeDatum(void)
 
 	READ_DONE();
 }
+#endif /*OBSOLETE*/
 
 /*
  * parseNodeString
@@ -2724,7 +2736,11 @@ parseNodeString(void)
 #define MATCH(tokname, namelen) \
 	(length == namelen && memcmp(token, tokname, namelen) == 0)
 
-	if (MATCH("QUERY", 5))
+	if (false)
+		;
+#include "readfuncs.inc2.c"
+#ifdef OBSOLETE
+	else if (MATCH("QUERY", 5))
 		return_value = _readQuery();
 	else if (MATCH("WITHCHECKOPTION", 15))
 		return_value = _readWithCheckOption();
@@ -2972,6 +2988,7 @@ parseNodeString(void)
 		return_value = _readPartitionBoundSpec();
 	else if (MATCH("PARTITIONRANGEDATUM", 19))
 		return_value = _readPartitionRangeDatum();
+#endif /*OBSOLETE*/
 	else
 	{
 		elog(ERROR, "badly formatted node string \"%.32s\"...", token);
diff --git a/src/include/nodes/.gitignore b/src/include/nodes/.gitignore
new file mode 100644
index 0000000000..99fb1d3787
--- /dev/null
+++ b/src/include/nodes/.gitignore
@@ -0,0 +1,2 @@
+/nodetags.h
+/header-stamp
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 5e049a67e1..cb140a3b93 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -27,6 +27,8 @@ typedef enum NodeTag
 {
 	T_Invalid = 0,
 
+#include "nodes/nodetags.h"
+#ifdef OBSOLETE
 	/*
 	 * TAGS FOR EXECUTOR NODES (execnodes.h)
 	 */
@@ -527,8 +529,14 @@ typedef enum NodeTag
 	T_SupportRequestCost,		/* in nodes/supportnodes.h */
 	T_SupportRequestRows,		/* in nodes/supportnodes.h */
 	T_SupportRequestIndexCondition	/* in nodes/supportnodes.h */
+#endif /*OBSOLETE*/
 } NodeTag;
 
+/*
+ * used in node definitions to set extra information for gen_node_stuff.pl
+ */
+#define pg_node_attr(x)
+
 /*
  * The first field of a node of any type is guaranteed to be the NodeTag.
  * Hence the type of any node can be gotten by casting it to Node. Declaring
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index cd9115dbb5..b35d1414de 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -121,7 +121,7 @@ typedef struct Query
 
 	QuerySource querySource;	/* where did I come from? */
 
-	uint64		queryId;		/* query identifier (can be set by plugins) */
+	uint64		queryId pg_node_attr(equal_ignore);		/* query identifier (can be set by plugins) */
 
 	bool		canSetTag;		/* do I set the command result tag? */
 
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 03c80c1620..b98d1958fc 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -226,7 +226,7 @@ struct PlannerInfo
 	 * GEQO.
 	 */
 	List	   *join_rel_list;	/* list of join-relation RelOptInfos */
-	struct HTAB *join_rel_hash; /* optional hashtable for join relations */
+	struct HTAB *join_rel_hash pg_node_attr(readwrite_ignore); /* optional hashtable for join relations */
 
 	/*
 	 * When doing a dynamic-programming-style join search, join_rel_level[k]
@@ -331,7 +331,7 @@ struct PlannerInfo
 	AttrNumber *grouping_map;	/* for GroupingFunc fixup */
 	List	   *minmax_aggs;	/* List of MinMaxAggInfos */
 
-	MemoryContext planner_cxt;	/* context holding PlannerInfo */
+	MemoryContext planner_cxt pg_node_attr(readwrite_ignore);	/* context holding PlannerInfo */
 
 	double		total_table_pages;	/* # of pages in all non-dummy tables of
 									 * query */
@@ -706,8 +706,8 @@ typedef struct RelOptInfo
 	RTEKind		rtekind;		/* RELATION, SUBQUERY, FUNCTION, etc */
 	AttrNumber	min_attr;		/* smallest attrno of rel (often <0) */
 	AttrNumber	max_attr;		/* largest attrno of rel */
-	Relids	   *attr_needed;	/* array indexed [min_attr .. max_attr] */
-	int32	   *attr_widths;	/* array indexed [min_attr .. max_attr] */
+	Relids	   *attr_needed pg_node_attr(readwrite_ignore);	/* array indexed [min_attr .. max_attr] */
+	int32	   *attr_widths pg_node_attr(readwrite_ignore);	/* array indexed [min_attr .. max_attr] */
 	List	   *lateral_vars;	/* LATERAL Vars and PHVs referenced by rel */
 	Relids		lateral_referencers;	/* rels that reference me laterally */
 	List	   *indexlist;		/* list of IndexOptInfo */
@@ -728,13 +728,13 @@ typedef struct RelOptInfo
 	Oid			userid;			/* identifies user to check access as */
 	bool		useridiscurrent;	/* join is only valid for current user */
 	/* use "struct FdwRoutine" to avoid including fdwapi.h here */
-	struct FdwRoutine *fdwroutine;
+	struct FdwRoutine *fdwroutine pg_node_attr(readwrite_ignore);
 	void	   *fdw_private;
 
 	/* cache space for remembering if we have proven this relation unique */
-	List	   *unique_for_rels;	/* known unique for these other relid
+	List	   *unique_for_rels pg_node_attr(readwrite_ignore);	/* known unique for these other relid
 									 * set(s) */
-	List	   *non_unique_for_rels;	/* known not unique for these set(s) */
+	List	   *non_unique_for_rels pg_node_attr(readwrite_ignore);	/* known not unique for these set(s) */
 
 	/* used by various scans and joins: */
 	List	   *baserestrictinfo;	/* RestrictInfo structures (if base rel) */
@@ -829,7 +829,7 @@ struct IndexOptInfo
 
 	Oid			indexoid;		/* OID of the index relation */
 	Oid			reltablespace;	/* tablespace of index (not table) */
-	RelOptInfo *rel;			/* back-link to index's table */
+	RelOptInfo *rel pg_node_attr(readwrite_ignore);			/* back-link to index's table */
 
 	/* index-size statistics (from pg_class and elsewhere) */
 	BlockNumber pages;			/* number of disk pages in index */
@@ -839,20 +839,20 @@ struct IndexOptInfo
 	/* index descriptor information */
 	int			ncolumns;		/* number of columns in index */
 	int			nkeycolumns;	/* number of key columns in index */
-	int		   *indexkeys;		/* column numbers of index's attributes both
+	int		   *indexkeys pg_node_attr(readwrite_ignore);		/* column numbers of index's attributes both
 								 * key and included columns, or 0 */
-	Oid		   *indexcollations;	/* OIDs of collations of index columns */
-	Oid		   *opfamily;		/* OIDs of operator families for columns */
-	Oid		   *opcintype;		/* OIDs of opclass declared input data types */
-	Oid		   *sortopfamily;	/* OIDs of btree opfamilies, if orderable */
-	bool	   *reverse_sort;	/* is sort order descending? */
-	bool	   *nulls_first;	/* do NULLs come first in the sort order? */
-	bytea	  **opclassoptions; /* opclass-specific options for columns */
-	bool	   *canreturn;		/* which index cols can be returned in an
+	Oid		   *indexcollations pg_node_attr(readwrite_ignore);	/* OIDs of collations of index columns */
+	Oid		   *opfamily pg_node_attr(readwrite_ignore);		/* OIDs of operator families for columns */
+	Oid		   *opcintype pg_node_attr(readwrite_ignore);		/* OIDs of opclass declared input data types */
+	Oid		   *sortopfamily pg_node_attr(readwrite_ignore);	/* OIDs of btree opfamilies, if orderable */
+	bool	   *reverse_sort pg_node_attr(readwrite_ignore);	/* is sort order descending? */
+	bool	   *nulls_first pg_node_attr(readwrite_ignore);	/* do NULLs come first in the sort order? */
+	bytea	  **opclassoptions pg_node_attr(readwrite_ignore); /* opclass-specific options for columns */
+	bool	   *canreturn pg_node_attr(readwrite_ignore);		/* which index cols can be returned in an
 								 * index-only scan? */
 	Oid			relam;			/* OID of the access method (in pg_am) */
 
-	List	   *indexprs;		/* expressions for non-simple index columns */
+	List	   *indexprs pg_node_attr(readwrite_ignore);		/* expressions for non-simple index columns */
 	List	   *indpred;		/* predicate if a partial index, else NIL */
 
 	List	   *indextlist;		/* targetlist representing index columns */
@@ -869,14 +869,14 @@ struct IndexOptInfo
 	bool		hypothetical;	/* true if index doesn't really exist */
 
 	/* Remaining fields are copied from the index AM's API struct: */
-	bool		amcanorderbyop; /* does AM support order by operator result? */
-	bool		amoptionalkey;	/* can query omit key for the first column? */
-	bool		amsearcharray;	/* can AM handle ScalarArrayOpExpr quals? */
-	bool		amsearchnulls;	/* can AM search for NULL/NOT NULL entries? */
-	bool		amhasgettuple;	/* does AM have amgettuple interface? */
-	bool		amhasgetbitmap; /* does AM have amgetbitmap interface? */
-	bool		amcanparallel;	/* does AM support parallel scan? */
-	bool		amcanmarkpos;	/* does AM support mark/restore? */
+	bool		amcanorderbyop pg_node_attr(readwrite_ignore); /* does AM support order by operator result? */
+	bool		amoptionalkey pg_node_attr(readwrite_ignore);	/* can query omit key for the first column? */
+	bool		amsearcharray pg_node_attr(readwrite_ignore);	/* can AM handle ScalarArrayOpExpr quals? */
+	bool		amsearchnulls pg_node_attr(readwrite_ignore);	/* can AM search for NULL/NOT NULL entries? */
+	bool		amhasgettuple pg_node_attr(readwrite_ignore);	/* does AM have amgettuple interface? */
+	bool		amhasgetbitmap pg_node_attr(readwrite_ignore); /* does AM have amgetbitmap interface? */
+	bool		amcanparallel pg_node_attr(readwrite_ignore);	/* does AM support parallel scan? */
+	bool		amcanmarkpos pg_node_attr(readwrite_ignore);	/* does AM support mark/restore? */
 	/* Rather than include amapi.h here, we declare amcostestimate like this */
 	void		(*amcostestimate) ();	/* AM's cost estimator */
 };
@@ -926,7 +926,7 @@ typedef struct StatisticExtInfo
 	NodeTag		type;
 
 	Oid			statOid;		/* OID of the statistics row */
-	RelOptInfo *rel;			/* back-link to statistic's table */
+	RelOptInfo *rel pg_node_attr(readwrite_ignore);			/* back-link to statistic's table */
 	char		kind;			/* statistics kind of this entry */
 	Bitmapset  *keys;			/* attnums of the columns covered */
 	List	   *exprs;			/* expressions */
@@ -1171,10 +1171,10 @@ typedef struct Path
 
 	NodeTag		pathtype;		/* tag identifying scan/join method */
 
-	RelOptInfo *parent;			/* the relation this path can build */
-	PathTarget *pathtarget;		/* list of Vars/Exprs, cost, width */
+	RelOptInfo *parent pg_node_attr(path_hack1);			/* the relation this path can build */
+	PathTarget *pathtarget pg_node_attr(path_hack2);		/* list of Vars/Exprs, cost, width */
 
-	ParamPathInfo *param_info;	/* parameterization info, or NULL if none */
+	ParamPathInfo *param_info pg_node_attr(path_hack3);	/* parameterization info, or NULL if none */
 
 	bool		parallel_aware; /* engage parallel-aware logic? */
 	bool		parallel_safe;	/* OK to use as part of parallel plan? */
@@ -2051,19 +2051,19 @@ typedef struct RestrictInfo
 
 	bool		outerjoin_delayed;	/* true if delayed by lower outer join */
 
-	bool		can_join;		/* see comment above */
+	bool		can_join pg_node_attr(equal_ignore);		/* see comment above */
 
-	bool		pseudoconstant; /* see comment above */
+	bool		pseudoconstant pg_node_attr(equal_ignore); /* see comment above */
 
-	bool		leakproof;		/* true if known to contain no leaked Vars */
+	bool		leakproof pg_node_attr(equal_ignore);		/* true if known to contain no leaked Vars */
 
-	VolatileFunctionStatus has_volatile;	/* to indicate if clause contains
+	VolatileFunctionStatus has_volatile pg_node_attr(equal_ignore);	/* to indicate if clause contains
 											 * any volatile functions. */
 
 	Index		security_level; /* see comment above */
 
 	/* The set of relids (varnos) actually referenced in the clause: */
-	Relids		clause_relids;
+	Relids		clause_relids pg_node_attr(equal_ignore);
 
 	/* The set of relids required to evaluate the clause: */
 	Relids		required_relids;
@@ -2075,47 +2075,47 @@ typedef struct RestrictInfo
 	Relids		nullable_relids;
 
 	/* These fields are set for any binary opclause: */
-	Relids		left_relids;	/* relids in left side of clause */
-	Relids		right_relids;	/* relids in right side of clause */
+	Relids		left_relids pg_node_attr(equal_ignore);	/* relids in left side of clause */
+	Relids		right_relids pg_node_attr(equal_ignore);	/* relids in right side of clause */
 
 	/* This field is NULL unless clause is an OR clause: */
-	Expr	   *orclause;		/* modified clause with RestrictInfos */
+	Expr	   *orclause pg_node_attr(equal_ignore);		/* modified clause with RestrictInfos */
 
 	/* This field is NULL unless clause is potentially redundant: */
-	EquivalenceClass *parent_ec;	/* generating EquivalenceClass */
+	EquivalenceClass *parent_ec pg_node_attr(equal_ignore readwrite_ignore);	/* generating EquivalenceClass */
 
 	/* cache space for cost and selectivity */
-	QualCost	eval_cost;		/* eval cost of clause; -1 if not yet set */
-	Selectivity norm_selec;		/* selectivity for "normal" (JOIN_INNER)
+	QualCost	eval_cost pg_node_attr(equal_ignore);		/* eval cost of clause; -1 if not yet set */
+	Selectivity norm_selec pg_node_attr(equal_ignore);		/* selectivity for "normal" (JOIN_INNER)
 								 * semantics; -1 if not yet set; >1 means a
 								 * redundant clause */
-	Selectivity outer_selec;	/* selectivity for outer join semantics; -1 if
+	Selectivity outer_selec pg_node_attr(equal_ignore);	/* selectivity for outer join semantics; -1 if
 								 * not yet set */
 
 	/* valid if clause is mergejoinable, else NIL */
-	List	   *mergeopfamilies;	/* opfamilies containing clause operator */
+	List	   *mergeopfamilies pg_node_attr(equal_ignore);	/* opfamilies containing clause operator */
 
 	/* cache space for mergeclause processing; NULL if not yet set */
-	EquivalenceClass *left_ec;	/* EquivalenceClass containing lefthand */
-	EquivalenceClass *right_ec; /* EquivalenceClass containing righthand */
-	EquivalenceMember *left_em; /* EquivalenceMember for lefthand */
-	EquivalenceMember *right_em;	/* EquivalenceMember for righthand */
-	List	   *scansel_cache;	/* list of MergeScanSelCache structs */
+	EquivalenceClass *left_ec pg_node_attr(equal_ignore readwrite_ignore);	/* EquivalenceClass containing lefthand */
+	EquivalenceClass *right_ec pg_node_attr(equal_ignore readwrite_ignore); /* EquivalenceClass containing righthand */
+	EquivalenceMember *left_em pg_node_attr(equal_ignore); /* EquivalenceMember for lefthand */
+	EquivalenceMember *right_em pg_node_attr(equal_ignore);	/* EquivalenceMember for righthand */
+	List	   *scansel_cache pg_node_attr(copy_ignore equal_ignore);	/* list of MergeScanSelCache structs */
 
 	/* transient workspace for use while considering a specific join path */
-	bool		outer_is_left;	/* T = outer var on left, F = on right */
+	bool		outer_is_left pg_node_attr(equal_ignore);	/* T = outer var on left, F = on right */
 
 	/* valid if clause is hashjoinable, else InvalidOid: */
-	Oid			hashjoinoperator;	/* copy of clause operator */
+	Oid			hashjoinoperator pg_node_attr(equal_ignore);	/* copy of clause operator */
 
 	/* cache space for hashclause processing; -1 if not yet set */
-	Selectivity left_bucketsize;	/* avg bucketsize of left side */
-	Selectivity right_bucketsize;	/* avg bucketsize of right side */
-	Selectivity left_mcvfreq;	/* left side's most common val's freq */
-	Selectivity right_mcvfreq;	/* right side's most common val's freq */
+	Selectivity left_bucketsize pg_node_attr(equal_ignore);	/* avg bucketsize of left side */
+	Selectivity right_bucketsize pg_node_attr(equal_ignore);	/* avg bucketsize of right side */
+	Selectivity left_mcvfreq pg_node_attr(equal_ignore);	/* left side's most common val's freq */
+	Selectivity right_mcvfreq pg_node_attr(equal_ignore);	/* right side's most common val's freq */
 
 	/* hash equality operator used for result cache, else InvalidOid */
-	Oid			hasheqoperator;
+	Oid			hasheqoperator pg_node_attr(equal_ignore);
 } RestrictInfo;
 
 /*
@@ -2170,8 +2170,8 @@ typedef struct MergeScanSelCache
 typedef struct PlaceHolderVar
 {
 	Expr		xpr;
-	Expr	   *phexpr;			/* the represented expression */
-	Relids		phrels;			/* base relids syntactically within expr src */
+	Expr	   *phexpr pg_node_attr(equal_ignore);			/* the represented expression */
+	Relids		phrels pg_node_attr(equal_ignore);			/* base relids syntactically within expr src */
 	Index		phid;			/* ID for PHV (unique within planner run) */
 	Index		phlevelsup;		/* > 0 if PHV belongs to outer query */
 } PlaceHolderVar;
@@ -2420,7 +2420,7 @@ typedef struct MinMaxAggInfo
 	Oid			aggfnoid;		/* pg_proc Oid of the aggregate */
 	Oid			aggsortop;		/* Oid of its sort operator */
 	Expr	   *target;			/* expression we are aggregating on */
-	PlannerInfo *subroot;		/* modified "root" for planning the subquery */
+	PlannerInfo *subroot pg_node_attr(readwrite_ignore);		/* modified "root" for planning the subquery */
 	Path	   *path;			/* access path for subquery */
 	Cost		pathcost;		/* estimated cost to fetch first row */
 	Param	   *param;			/* param for subplan's output */
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3418e23873..62f6413684 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -63,7 +63,7 @@ typedef enum OnCommitAction
 typedef struct RangeVar
 {
 	NodeTag		type;
-	char	   *catalogname;	/* the catalog (database) name, or NULL */
+	char	   *catalogname pg_node_attr(readwrite_ignore);	/* the catalog (database) name, or NULL */
 	char	   *schemaname;		/* the schema name, or NULL */
 	char	   *relname;		/* the relation/sequence name */
 	bool		inh;			/* expand rel by inheritance? recursively act
@@ -196,8 +196,8 @@ typedef struct Var
 	Index		varlevelsup;	/* for subquery variables referencing outer
 								 * relations; 0 in a normal var, >0 means N
 								 * levels up */
-	Index		varnosyn;		/* syntactic relation index (0 if unknown) */
-	AttrNumber	varattnosyn;	/* syntactic attribute number */
+	Index		varnosyn pg_node_attr(equal_ignore);		/* syntactic relation index (0 if unknown) */
+	AttrNumber	varattnosyn pg_node_attr(equal_ignore);	/* syntactic attribute number */
 	int			location;		/* token location, or -1 if unknown */
 } Var;
 
@@ -324,7 +324,7 @@ typedef struct Aggref
 	Oid			aggtype;		/* type Oid of result of the aggregate */
 	Oid			aggcollid;		/* OID of collation of result */
 	Oid			inputcollid;	/* OID of collation that function should use */
-	Oid			aggtranstype;	/* type Oid of aggregate's transition value */
+	Oid			aggtranstype pg_node_attr(equal_ignore);	/* type Oid of aggregate's transition value */
 	List	   *aggargtypes;	/* type Oids of direct and aggregated args */
 	List	   *aggdirectargs;	/* direct arguments, if an ordered-set agg */
 	List	   *args;			/* aggregated arguments and sort expressions */
@@ -371,8 +371,8 @@ typedef struct GroupingFunc
 	Expr		xpr;
 	List	   *args;			/* arguments, not evaluated but kept for
 								 * benefit of EXPLAIN etc. */
-	List	   *refs;			/* ressortgrouprefs of arguments */
-	List	   *cols;			/* actual column positions set by planner */
+	List	   *refs pg_node_attr(equal_ignore);			/* ressortgrouprefs of arguments */
+	List	   *cols pg_node_attr(equal_ignore);			/* actual column positions set by planner */
 	Index		agglevelsup;	/* same as Aggref.agglevelsup */
 	int			location;		/* token location */
 } GroupingFunc;
@@ -540,7 +540,7 @@ typedef struct OpExpr
 {
 	Expr		xpr;
 	Oid			opno;			/* PG_OPERATOR OID of the operator */
-	Oid			opfuncid;		/* PG_PROC OID of underlying function */
+	Oid			opfuncid pg_node_attr(equal_ignore_if_zero);		/* PG_PROC OID of underlying function */
 	Oid			opresulttype;	/* PG_TYPE OID of result value */
 	bool		opretset;		/* true if operator returns set */
 	Oid			opcollid;		/* OID of collation of result */
@@ -589,8 +589,8 @@ typedef struct ScalarArrayOpExpr
 {
 	Expr		xpr;
 	Oid			opno;			/* PG_OPERATOR OID of the operator */
-	Oid			opfuncid;		/* PG_PROC OID of comparison function */
-	Oid			hashfuncid;		/* PG_PROC OID of hash func or InvalidOid */
+	Oid			opfuncid pg_node_attr(equal_ignore_if_zero);		/* PG_PROC OID of comparison function */
+	Oid			hashfuncid pg_node_attr(equal_ignore_if_zero);		/* PG_PROC OID of hash func or InvalidOid */
 	bool		useOr;			/* true for ANY, false for ALL */
 	Oid			inputcollid;	/* OID of collation that operator should use */
 	List	   *args;			/* the scalar and array operands */
-- 
2.31.1



  [text/plain] v1-0010-XXX-Debugging-support.patch (1015B, ../[email protected]/11-v1-0010-XXX-Debugging-support.patch)
  download | inline diff:
From b9cd1fd53089c833efd39c76c0e4813a974f9a1e Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <[email protected]>
Date: Mon, 7 Jun 2021 16:09:26 +0200
Subject: [PATCH v1 10/10] XXX Debugging support

---
 src/include/pg_config_manual.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/include/pg_config_manual.h b/src/include/pg_config_manual.h
index 27da86e5e0..1c1152fe14 100644
--- a/src/include/pg_config_manual.h
+++ b/src/include/pg_config_manual.h
@@ -365,14 +365,14 @@
  * copyObject(), to facilitate catching errors and omissions in
  * copyObject().
  */
-/* #define COPY_PARSE_PLAN_TREES */
+#define COPY_PARSE_PLAN_TREES
 
 /*
  * Define this to force all parse and plan trees to be passed through
  * outfuncs.c/readfuncs.c, to facilitate catching errors and omissions in
  * those modules.
  */
-/* #define WRITE_READ_PARSE_PLAN_TREES */
+#define WRITE_READ_PARSE_PLAN_TREES
 
 /*
  * Define this to force all raw parse trees for DML statements to be scanned
-- 
2.31.1



view thread (77+ messages)  latest in thread

reply

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Reply to all the recipients using the --to and --cc options:
  reply via email

  To: [email protected]
  Cc: [email protected]
  Subject: Re: automatically generating node support functions
  In-Reply-To: <[email protected]>

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox