On 12.10.21 15:52, Andrew Dunstan wrote:
I haven't been through the whole thing, but I did notice this: the
comment stripping code looks rather fragile. I think it would blow up if
there were a continuation line not starting with  qr/\s*\*/. It's a lot
simpler and more robust to do this if you slurp the file in whole.
Here's what we do in the buildfarm code:

     my $src = file_contents($_);
     # strip C comments
     # We used to use the recipe in perlfaq6 but there is actually no point.
     # We don't need to keep the quoted string values anyway, and
     # on some platforms the complex regex causes perl to barf and crash.
     $src =~ s{/\*.*?\*/}{}gs;

After you've done that splitting it into lines is pretty simple.

Here is an updated patch, with some general rebasing, and the above improvement. It now also generates #include lines necessary in copyfuncs etc. to pull in all the node types it operates on.

Further, I have looked more into the "metadata" approach discussed in [0]. It's pretty easy to generate that kind of output from the data structures my script produces. You just loop over all the node types and print stuff and keep a few counters. I don't plan to work on that at this time, but I just wanted to point out that if people wanted to move into that direction, my patch wouldn't be in the way.


[0]: https://www.postgresql.org/message-id/flat/20190828234136.fk2ndqtld3onfrrp%40alap3.anarazel.de
From e2c08d8b793200a07b8fe5ae85dd23f401ddcef1 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <pe...@eisentraut.org>
Date: Wed, 29 Dec 2021 12:00:41 +0100
Subject: [PATCH v3] Automatically generate node support functions

Add 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.

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.

TODO: 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 now do out/read coverage of
utility statement nodes, 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.

Discussion: 
https://www.postgresql.org/message-id/flat/c1097590-a6a4-486a-64b1-e1f9cc0533ce%40enterprisedb.com
---
 src/backend/Makefile                  |   8 +-
 src/backend/nodes/.gitignore          |   3 +
 src/backend/nodes/Makefile            |  46 ++
 src/backend/nodes/copyfuncs.c         |  19 +-
 src/backend/nodes/equalfuncs.c        |  22 +-
 src/backend/nodes/gen_node_support.pl | 660 ++++++++++++++++++++++++++
 src/backend/nodes/outfuncs.c          |  30 +-
 src/backend/nodes/readfuncs.c         |  23 +-
 src/include/nodes/.gitignore          |   2 +
 src/include/nodes/nodes.h             |   8 +
 src/include/nodes/parsenodes.h        |   2 +-
 src/include/nodes/pathnodes.h         | 134 +++---
 src/include/nodes/plannodes.h         |  90 ++--
 src/include/nodes/primnodes.h         |  20 +-
 src/include/pg_config_manual.h        |   4 +-
 src/include/utils/rel.h               |   6 +-
 src/tools/msvc/Solution.pm            |  46 ++
 17 files changed, 976 insertions(+), 147 deletions(-)
 create mode 100644 src/backend/nodes/.gitignore
 create mode 100644 src/backend/nodes/gen_node_support.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..2a79ee6ed8
--- /dev/null
+++ b/src/backend/nodes/.gitignore
@@ -0,0 +1,3 @@
+/node-support-stamp
+/nodetags.h
+/*funcs.inc?.c
diff --git a/src/backend/nodes/Makefile b/src/backend/nodes/Makefile
index 5d2b12a993..56cddc06da 100644
--- a/src/backend/nodes/Makefile
+++ b/src/backend/nodes/Makefile
@@ -30,3 +30,49 @@ 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/extensible.h \
+       nodes/parsenodes.h \
+       nodes/replnodes.h \
+       nodes/value.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
+
+# see also catalog/Makefile for an explanation of these make rules
+
+all: distprep generated-header-symlinks
+
+distprep: node-support-stamp
+
+.PHONY: generated-header-symlinks
+
+generated-header-symlinks: $(top_builddir)/src/include/nodes/header-stamp
+
+node-support-stamp: gen_node_support.pl $(addprefix 
$(top_srcdir)/src/include/,$(node_headers))
+       $(PERL) $^
+       touch $@
+
+$(top_builddir)/src/include/nodes/header-stamp: node-support-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-support-stamp *funcs.inc?.c nodetags.h
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index df0b747883..6932178694 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -23,11 +23,7 @@
 #include "postgres.h"
 
 #include "miscadmin.h"
-#include "nodes/extensible.h"
-#include "nodes/pathnodes.h"
-#include "nodes/plannodes.h"
 #include "utils/datum.h"
-#include "utils/rel.h"
 
 
 /*
@@ -73,6 +69,9 @@
        (newnode->fldname = from->fldname)
 
 
+#include "copyfuncs.inc1.c"
+
+#ifdef OBSOLETE
 /* ****************************************************************
  *                                      plannodes.h copy functions
  * ****************************************************************
@@ -1456,6 +1455,7 @@ _copyVar(const Var *from)
 
        return newnode;
 }
+#endif /*OBSOLETE*/
 
 /*
  * _copyConst
@@ -1495,6 +1495,7 @@ _copyConst(const Const *from)
        return newnode;
 }
 
+#ifdef OBSOLETE
 /*
  * _copyParam
  */
@@ -2730,6 +2731,7 @@ _copyParamRef(const ParamRef *from)
 
        return newnode;
 }
+#endif /*OBSOLETE*/
 
 static A_Const *
 _copyA_Const(const A_Const *from)
@@ -2767,6 +2769,7 @@ _copyA_Const(const A_Const *from)
        return newnode;
 }
 
+#ifdef OBSOLETE
 static FuncCall *
 _copyFuncCall(const FuncCall *from)
 {
@@ -4902,6 +4905,7 @@ _copyDropSubscriptionStmt(const DropSubscriptionStmt 
*from)
 
        return newnode;
 }
+#endif /*OBSOLETE*/
 
 /* ****************************************************************
  *                                     extensible.h copy functions
@@ -4924,6 +4928,7 @@ _copyExtensibleNode(const ExtensibleNode *from)
        return newnode;
 }
 
+#ifdef OBSOLETE
 /* ****************************************************************
  *                                     value.h copy functions
  * ****************************************************************
@@ -4984,6 +4989,7 @@ _copyForeignKeyCacheInfo(const ForeignKeyCacheInfo *from)
 
        return newnode;
 }
+#endif /*OBSOLETE*/
 
 /*
  * copyObjectImpl -- implementation of copyObject(); see nodes/nodes.h
@@ -5004,6 +5010,8 @@ copyObjectImpl(const void *from)
 
        switch (nodeTag(from))
        {
+#include "copyfuncs.inc2.c"
+#ifdef OBSOLETE
                        /*
                         * PLAN NODES
                         */
@@ -5361,6 +5369,7 @@ copyObjectImpl(const void *from)
                case T_BitString:
                        retval = _copyBitString(from);
                        break;
+#endif /*OBSOLETE*/
 
                        /*
                         * LIST NODES
@@ -5378,6 +5387,7 @@ copyObjectImpl(const void *from)
                        retval = list_copy(from);
                        break;
 
+#ifdef OBSOLETE
                        /*
                         * EXTENSIBLE NODES
                         */
@@ -5917,6 +5927,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 cb7ddd463c..c61fd6650b 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.
@@ -30,8 +27,6 @@
 #include "postgres.h"
 
 #include "miscadmin.h"
-#include "nodes/extensible.h"
-#include "nodes/pathnodes.h"
 #include "utils/datum.h"
 
 
@@ -97,6 +92,9 @@
        ((void) 0)
 
 
+#include "equalfuncs.inc1.c"
+
+#ifdef OBSOLETE
 /*
  *     Stuff from primnodes.h
  */
@@ -185,6 +183,7 @@ _equalVar(const Var *a, const Var *b)
 
        return true;
 }
+#endif /*OBSOLETE*/
 
 static bool
 _equalConst(const Const *a, const Const *b)
@@ -207,6 +206,7 @@ _equalConst(const Const *a, const Const *b)
                                                a->constbyval, a->constlen);
 }
 
+#ifdef OBSOLETE
 static bool
 _equalParam(const Param *a, const Param *b)
 {
@@ -946,6 +946,7 @@ _equalPlaceHolderInfo(const PlaceHolderInfo *a, const 
PlaceHolderInfo *b)
 
        return true;
 }
+#endif /*OBSOLETE*/
 
 /*
  * Stuff from extensible.h
@@ -967,6 +968,7 @@ _equalExtensibleNode(const ExtensibleNode *a, const 
ExtensibleNode *b)
        return true;
 }
 
+#ifdef OBSOLETE
 /*
  * Stuff from parsenodes.h
  */
@@ -2432,6 +2434,7 @@ _equalParamRef(const ParamRef *a, const ParamRef *b)
 
        return true;
 }
+#endif /*OBSOLETE*/
 
 static bool
 _equalA_Const(const A_Const *a, const A_Const *b)
@@ -2449,6 +2452,7 @@ _equalA_Const(const A_Const *a, const A_Const *b)
        return true;
 }
 
+#ifdef OBSOLETE
 static bool
 _equalFuncCall(const FuncCall *a, const FuncCall *b)
 {
@@ -3058,6 +3062,7 @@ _equalPartitionCmd(const PartitionCmd *a, const 
PartitionCmd *b)
 
        return true;
 }
+#endif /*OBSOLETE*/
 
 /*
  * Stuff from pg_list.h
@@ -3118,6 +3123,7 @@ _equalList(const List *a, const List *b)
        return true;
 }
 
+#ifdef OBSOLETE
 /*
  * Stuff from value.h
  */
@@ -3153,6 +3159,7 @@ _equalBitString(const BitString *a, const BitString *b)
 
        return true;
 }
+#endif /*OBSOLETE*/
 
 /*
  * equal
@@ -3183,6 +3190,8 @@ equal(const void *a, const void *b)
 
        switch (nodeTag(a))
        {
+#include "equalfuncs.inc2.c"
+#ifdef OBSOLETE
                        /*
                         * PRIMITIVE NODES
                         */
@@ -3361,6 +3370,7 @@ equal(const void *a, const void *b)
                case T_PlaceHolderInfo:
                        retval = _equalPlaceHolderInfo(a, b);
                        break;
+#endif /*OBSOLETE*/
 
                case T_List:
                case T_IntList:
@@ -3368,6 +3378,7 @@ equal(const void *a, const void *b)
                        retval = _equalList(a, b);
                        break;
 
+#ifdef OBSOLETE
                case T_Integer:
                        retval = _equalInteger(a, b);
                        break;
@@ -3913,6 +3924,7 @@ equal(const void *a, const void *b)
                case T_PublicationTable:
                        retval = _equalPublicationTable(a, b);
                        break;
+#endif /*OBSOLETE*/
 
                default:
                        elog(ERROR, "unrecognized node type: %d",
diff --git a/src/backend/nodes/gen_node_support.pl 
b/src/backend/nodes/gen_node_support.pl
new file mode 100644
index 0000000000..75488377a5
--- /dev/null
+++ b/src/backend/nodes/gen_node_support.pl
@@ -0,0 +1,660 @@
+#!/usr/bin/perl
+#----------------------------------------------------------------------
+#
+# Generate node support files:
+# - nodetags.h
+# - copyfuncs
+# - equalfuncs
+# - readfuncs
+# - outfuncs
+#
+# src/backend/nodes/gen_node_support.pl
+#
+#----------------------------------------------------------------------
+
+use strict;
+use warnings;
+
+use File::Basename;
+
+use FindBin;
+use lib "$FindBin::RealBin/../catalog";
+
+use Catalog;  # for RenameTempFile
+
+
+sub elem
+{
+       my $x = shift;
+       return grep { $_ eq $x } @_;
+}
+
+
+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 Cardinality 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 Expr
+       BufferHeapTupleTableSlot HeapTupleTableSlot MinimalTupleTableSlot 
VirtualTupleTableSlot
+       JoinPath
+       PartitionPruneStep
+);
+
+# Special cases that either don't have their own struct or the struct
+# is not in a header file.  We just generate node tags for them, but
+# they otherwise don't participate in node support.
+my @extra_tags = qw(
+       IntList OidList
+       AllocSetContext GenerationContext SlabContext
+       TIDBitmap
+       WindowObjectData
+);
+
+# This is a regular node, but we skip parsing it from its header file
+# since we won't use its internal structure here anyway.
+push @node_types, qw(List);
+
+# 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\": $!";
+
+       my $file_content = do { local $/; <$ifh> };
+
+       # strip C comments
+       $file_content =~ s{/\*.*?\*/}{}gs;
+
+       foreach my $line (split /\n/, $file_content)
+       {
+               chomp $line;
+               $line =~ s/\s*$//;
+               next if $line eq '';
+               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+);/ and elem 
$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};
+                                                       $fa{$fn} =~ 
s/array_size\((\w+)\)/array_size(${supertype_field}.$1)/ if $fa{$fn};
+                                               }
+                                               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 (elem basename($infile),
+                                               qw(execnodes.h trigger.h 
event_trigger.h amapi.h tableam.h
+                                                       tsmapi.h fdwapi.h 
tuptable.h replnodes.h supportnodes.h))
+                                       {
+                                               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+);$/ and elem $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 elem $n, @abstract_types;
+       print $nt "\tT_${n} = $i,\n";
+       $i++;
+}
+
+close $nt;
+
+
+# #include lines necessary to pull in all the struct definitions
+my $node_includes = '';
+foreach my $infile (sort @ARGV)
+{
+       $infile =~ s!.*src/include/!!;
+       $node_includes .= qq{#include "$infile"\n};
+}
+
+
+# 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 $!;
+
+print $cf $node_includes;
+print $ef $node_includes;
+
+my @custom_copy = qw(A_Const Const ExtensibleNode);
+
+foreach my $n (@node_types)
+{
+       next if elem $n, @abstract_types;
+       next if elem $n, @no_copy;
+       next if $n eq 'List';
+
+       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 elem $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)
+{
+";
+
+       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 (elem $t, @scalar_types or elem $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';
+                       }
+               }
+               elsif ($t =~ /(\w+)\*/ and elem $1, @scalar_types)
+               {
+                       my $tt = $1;
+                       my $array_size_field;
+                       if ($a =~ /\barray_size.([\w.]+)/)
+                       {
+                               $array_size_field = $1;
+                       }
+                       else
+                       {
+                               die "no array size defined for $n.$f of type 
$t";
+                       }
+                       if 
($node_type_info{$n}->{field_types}{$array_size_field} eq 'List*')
+                       {
+                               print $cf "\tCOPY_POINTER_FIELD($f, 
list_length(from->$array_size_field) * sizeof($tt));\n" unless $copy_ignore;
+                               print $ef "\tCOMPARE_POINTER_FIELD($f, 
list_length(a->$array_size_field) * sizeof($tt));\n" unless $equal_ignore;
+                       }
+                       else
+                       {
+                               print $cf "\tCOPY_POINTER_FIELD($f, 
from->$array_size_field * sizeof($tt));\n" unless $copy_ignore;
+                               print $ef "\tCOMPARE_POINTER_FIELD($f, 
a->$array_size_field * sizeof($tt));\n" unless $equal_ignore;
+                       }
+               }
+               elsif ($t =~ /(\w+)\*/ and elem $1, @node_types)
+               {
+                       print $cf "\tCOPY_NODE_FIELD($f);\n" unless 
$copy_ignore;
+                       print $ef "\tCOMPARE_NODE_FIELD($f);\n" unless 
$equal_ignore;
+               }
+               elsif ($t =~ /\w+\[/)
+               {
+                       print $cf "\tCOPY_ARRAY_FIELD($f);\n" unless 
$copy_ignore;
+                       print $ef "\tCOMPARE_ARRAY_FIELD($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 $!;
+
+print $of $node_includes;
+print $rf $node_includes;
+
+my @custom_readwrite = qw(A_Const A_Expr BoolExpr Const Constraint 
ExtensibleNode Query RangeTblEntry);
+
+foreach my $n (@node_types)
+{
+       next if elem $n, @abstract_types;
+       next if elem $n, @no_read_write;
+       next if $n eq 'List';
+       next if elem $n, qw(BitString Float Integer String);
+
+       # 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 elem $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;
+
+       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 elem $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;
+
+       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;
+               }
+               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')
+               {
+                       print $of "\tWRITE_FLOAT_FIELD($f, \"%.6f\");\n";
+                       print $rf "\tREAD_FLOAT_FIELD($f);\n" unless $no_read;
+               }
+               elsif ($t eq 'Cardinality')
+               {
+                       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 (elem $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+)(\*|\[)/ and elem $1, @scalar_types)
+               {
+                       my $tt = uc $1;
+                       my $array_size_field;
+                       if ($a =~ /\barray_size.([\w.]+)/)
+                       {
+                               $array_size_field = $1;
+                       }
+                       else
+                       {
+                               die "no array size defined for $n.$f of type 
$t";
+                       }
+                       if 
($node_type_info{$n}->{field_types}{$array_size_field} eq 'List*')
+                       {
+                               print $of "\tWRITE_${tt}_ARRAY($f, 
list_length(node->$array_size_field));\n";
+                               print $rf "\tREAD_${tt}_ARRAY($f, 
list_length(local_node->$array_size_field));\n" unless $no_read;
+                       }
+                       else
+                       {
+                               print $of "\tWRITE_${tt}_ARRAY($f, 
node->$array_size_field);\n";
+                               print $rf "\tREAD_${tt}_ARRAY($f, 
local_node->$array_size_field);\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+)\*/ and elem $1, @node_types)
+               {
+                       print $of "\tWRITE_NODE_FIELD($f);\n";
+                       print $rf "\tREAD_NODE_FIELD($f);\n" unless $no_read;
+               }
+               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 91a89b6d51..264bbf3f7f 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -31,11 +31,10 @@
 
 #include "lib/stringinfo.h"
 #include "miscadmin.h"
-#include "nodes/extensible.h"
-#include "nodes/pathnodes.h"
-#include "nodes/plannodes.h"
+#include "nodes/bitmapset.h"
+#include "nodes/nodes.h"
+#include "nodes/pg_list.h"
 #include "utils/datum.h"
-#include "utils/rel.h"
 
 static void outChar(StringInfo str, char c);
 
@@ -295,6 +294,9 @@ outDatum(StringInfo str, Datum value, int typlen, bool 
typbyval)
 }
 
 
+#include "outfuncs.inc1.c"
+
+#ifdef OBSOLETE
 /*
  *     Stuff from plannodes.h
  */
@@ -1135,6 +1137,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)
@@ -1156,6 +1159,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)
 {
@@ -1326,6 +1330,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)
@@ -1354,6 +1359,7 @@ _outBoolExpr(StringInfo str, const BoolExpr *node)
        WRITE_LOCATION_FIELD(location);
 }
 
+#ifdef OBSOLETE
 static void
 _outSubLink(StringInfo str, const SubLink *node)
 {
@@ -2674,6 +2680,7 @@ _outPlannerParamItem(StringInfo str, const 
PlannerParamItem *node)
        WRITE_NODE_FIELD(item);
        WRITE_INT_FIELD(paramId);
 }
+#endif /*OBSOLETE*/
 
 /*****************************************************************************
  *
@@ -2696,6 +2703,7 @@ _outExtensibleNode(StringInfo str, const ExtensibleNode 
*node)
        methods->nodeOut(str, node);
 }
 
+#ifdef OBSOLETE
 /*****************************************************************************
  *
  *     Stuff from parsenodes.h.
@@ -3028,6 +3036,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)
@@ -3100,6 +3109,7 @@ _outQuery(StringInfo str, const Query *node)
        WRITE_INT_FIELD(stmt_len);
 }
 
+#ifdef OBSOLETE
 static void
 _outWithCheckOption(StringInfo str, const WithCheckOption *node)
 {
@@ -3238,6 +3248,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)
@@ -3318,6 +3329,7 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry 
*node)
        WRITE_NODE_FIELD(securityQuals);
 }
 
+#ifdef OBSOLETE
 static void
 _outRangeTblFunction(StringInfo str, const RangeTblFunction *node)
 {
@@ -3341,6 +3353,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)
@@ -3453,6 +3466,7 @@ _outBitString(StringInfo str, const BitString *node)
        appendStringInfoString(str, node->val);
 }
 
+#ifdef OBSOLETE
 static void
 _outColumnRef(StringInfo str, const ColumnRef *node)
 {
@@ -3484,6 +3498,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)
@@ -3500,6 +3515,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)
 {
@@ -3644,6 +3660,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)
@@ -3765,6 +3782,7 @@ _outConstraint(StringInfo str, const Constraint *node)
        }
 }
 
+#ifdef OBSOLETE
 static void
 _outForeignKeyCacheInfo(StringInfo str, const ForeignKeyCacheInfo *node)
 {
@@ -3825,6 +3843,7 @@ _outPartitionRangeDatum(StringInfo str, const 
PartitionRangeDatum *node)
        WRITE_NODE_FIELD(value);
        WRITE_LOCATION_FIELD(location);
 }
+#endif /*OBSOLETE*/
 
 /*
  * outNode -
@@ -3854,6 +3873,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;
@@ -4526,6 +4547,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 d79af6e56e..b66a42188b 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -33,9 +33,7 @@
 #include <math.h>
 
 #include "miscadmin.h"
-#include "nodes/extensible.h"
-#include "nodes/parsenodes.h"
-#include "nodes/plannodes.h"
+#include "nodes/bitmapset.h"
 #include "nodes/readfuncs.h"
 
 
@@ -238,6 +236,8 @@ readBitmapset(void)
        return _readBitmapset();
 }
 
+#include "readfuncs.inc1.c"
+
 /*
  * _readQuery
  */
@@ -289,6 +289,7 @@ _readQuery(void)
        READ_DONE();
 }
 
+#ifdef OBSOLETE
 /*
  * _readNotifyStmt
  */
@@ -587,6 +588,7 @@ _readVar(void)
 
        READ_DONE();
 }
+#endif /*OBSOLETE*/
 
 /*
  * _readConst
@@ -613,6 +615,7 @@ _readConst(void)
        READ_DONE();
 }
 
+#ifdef OBSOLETE
 /*
  * _readParam
  */
@@ -838,6 +841,7 @@ _readScalarArrayOpExpr(void)
 
        READ_DONE();
 }
+#endif /*OBSOLETE*/
 
 /*
  * _readBoolExpr
@@ -865,6 +869,7 @@ _readBoolExpr(void)
        READ_DONE();
 }
 
+#ifdef OBSOLETE
 /*
  * _readSubLink
  */
@@ -1419,6 +1424,7 @@ _readAppendRelInfo(void)
 /*
  *     Stuff from parsenodes.h.
  */
+#endif /*OBSOLETE*/
 
 /*
  * _readRangeTblEntry
@@ -1514,6 +1520,7 @@ _readRangeTblEntry(void)
        READ_DONE();
 }
 
+#ifdef OBSOLETE
 /*
  * _readRangeTblFunction
  */
@@ -2636,6 +2643,7 @@ _readAlternativeSubPlan(void)
 
        READ_DONE();
 }
+#endif /*OBSOLETE*/
 
 /*
  * _readExtensibleNode
@@ -2667,6 +2675,7 @@ _readExtensibleNode(void)
        READ_DONE();
 }
 
+#ifdef OBSOLETE
 /*
  * _readPartitionBoundSpec
  */
@@ -2701,6 +2710,7 @@ _readPartitionRangeDatum(void)
 
        READ_DONE();
 }
+#endif /*OBSOLETE*/
 
 /*
  * parseNodeString
@@ -2725,7 +2735,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();
@@ -2973,6 +2987,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 7c657c1241..012b478a76 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_support.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 4c5a8a39bf..629a785865 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 324d92880b..9d0e4068f3 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -227,7 +227,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]
@@ -329,10 +329,10 @@ struct PlannerInfo
        List       *update_colnos;
 
        /* Fields filled during create_plan() for use in setrefs.c */
-       AttrNumber *grouping_map;       /* for GroupingFunc fixup */
+       AttrNumber *grouping_map pg_node_attr(array_size(update_colnos));       
/* 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 */
 
        Cardinality     total_table_pages;      /* # of pages in all non-dummy 
tables of
                                                                         * 
query */
@@ -369,8 +369,8 @@ struct PlannerInfo
        List       *curOuterParams; /* not-yet-assigned NestLoopParams */
 
        /* These fields are workspace for setrefs.c */
-       bool       *isAltSubplan;       /* array corresponding to 
glob->subplans */
-       bool       *isUsedSubplan;      /* array corresponding to 
glob->subplans */
+       bool       *isAltSubplan pg_node_attr(array_size(curOuterParams));      
/* array corresponding to glob->subplans */
+       bool       *isUsedSubplan pg_node_attr(array_size(curOuterParams));     
/* array corresponding to glob->subplans */
 
        /* optional private data for join_search_hook, e.g., GEQO */
        void       *join_search_private;
@@ -711,8 +711,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 */
@@ -733,13 +733,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) */
@@ -837,7 +837,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 */
@@ -847,20 +847,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 */
@@ -877,14 +877,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 */
 };
@@ -905,9 +905,9 @@ typedef struct ForeignKeyOptInfo
        Index           con_relid;              /* RT index of the referencing 
table */
        Index           ref_relid;              /* RT index of the referenced 
table */
        int                     nkeys;                  /* number of columns in 
the foreign key */
-       AttrNumber      conkey[INDEX_MAX_KEYS]; /* cols in referencing table */
-       AttrNumber      confkey[INDEX_MAX_KEYS];        /* cols in referenced 
table */
-       Oid                     conpfeqop[INDEX_MAX_KEYS];      /* PK = FK 
operator OIDs */
+       AttrNumber      conkey[INDEX_MAX_KEYS] pg_node_attr(array_size(nkeys)); 
/* cols in referencing table */
+       AttrNumber      confkey[INDEX_MAX_KEYS] 
pg_node_attr(array_size(nkeys));        /* cols in referenced table */
+       Oid                     conpfeqop[INDEX_MAX_KEYS] 
pg_node_attr(array_size(nkeys));      /* PK = FK operator OIDs */
 
        /* Derived info about whether FK's equality conditions match the query: 
*/
        int                     nmatched_ec;    /* # of FK cols matched by ECs 
*/
@@ -934,7 +934,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 */
@@ -1108,7 +1108,7 @@ typedef struct PathTarget
 {
        NodeTag         type;
        List       *exprs;                      /* list of expressions to be 
computed */
-       Index      *sortgrouprefs;      /* corresponding sort/group refnos, or 
0 */
+       Index      *sortgrouprefs pg_node_attr(array_size(exprs));      /* 
corresponding sort/group refnos, or 0 */
        QualCost        cost;                   /* cost of evaluating the 
expressions */
        int                     width;                  /* estimated avg width 
of result tuples */
        VolatileFunctionStatus has_volatile_expr;       /* indicates if exprs 
contain
@@ -1179,10 +1179,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? 
*/
@@ -2061,19 +2061,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;
@@ -2085,48 +2085,48 @@ 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 operators used for memoize nodes, else InvalidOid */
-       Oid                     left_hasheqoperator;
-       Oid                     right_hasheqoperator;
+       Oid                     left_hasheqoperator pg_node_attr(equal_ignore);
+       Oid                     right_hasheqoperator pg_node_attr(equal_ignore);
 } RestrictInfo;
 
 /*
@@ -2181,8 +2181,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;
@@ -2343,7 +2343,7 @@ typedef struct AppendRelInfo
         * child column is dropped or doesn't exist in the parent.
         */
        int                     num_child_cols; /* length of array */
-       AttrNumber *parent_colnos;      /* array of parent attnos, or zeroes */
+       AttrNumber *parent_colnos pg_node_attr(array_size(num_child_cols));     
/* array of parent attnos, or zeroes */
 
        /*
         * We store the parent table's OID here for inheritance, or InvalidOid 
for
@@ -2431,7 +2431,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/plannodes.h b/src/include/nodes/plannodes.h
index be3c30704a..b772d91e2d 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -275,10 +275,10 @@ typedef struct MergeAppend
        List       *mergeplans;
        /* these fields are just like the sort-key info in struct Sort: */
        int                     numCols;                /* number of sort-key 
columns */
-       AttrNumber *sortColIdx;         /* their indexes in the target list */
-       Oid                *sortOperators;      /* OIDs of operators to sort 
them by */
-       Oid                *collations;         /* OIDs of collations */
-       bool       *nullsFirst;         /* NULLS FIRST/LAST directions */
+       AttrNumber *sortColIdx pg_node_attr(array_size(numCols));               
/* their indexes in the target list */
+       Oid                *sortOperators pg_node_attr(array_size(numCols));    
/* OIDs of operators to sort them by */
+       Oid                *collations pg_node_attr(array_size(numCols));       
        /* OIDs of collations */
+       bool       *nullsFirst pg_node_attr(array_size(numCols));               
/* NULLS FIRST/LAST directions */
        /* Info for run-time subplan pruning; NULL if we're not doing that */
        struct PartitionPruneInfo *part_prune_info;
 } MergeAppend;
@@ -298,9 +298,9 @@ typedef struct RecursiveUnion
        /* Remaining fields are zero/null in UNION ALL case */
        int                     numCols;                /* number of columns to 
check for
                                                                 * 
duplicate-ness */
-       AttrNumber *dupColIdx;          /* their indexes in the target list */
-       Oid                *dupOperators;       /* equality operators to 
compare with */
-       Oid                *dupCollations;
+       AttrNumber *dupColIdx pg_node_attr(array_size(numCols));                
/* their indexes in the target list */
+       Oid                *dupOperators pg_node_attr(array_size(numCols));     
/* equality operators to compare with */
+       Oid                *dupCollations pg_node_attr(array_size(numCols));
        long            numGroups;              /* estimated number of groups 
in input */
 } RecursiveUnion;
 
@@ -750,10 +750,10 @@ typedef struct MergeJoin
        bool            skip_mark_restore;      /* Can we skip mark/restore 
calls? */
        List       *mergeclauses;       /* mergeclauses as expression trees */
        /* these are arrays, but have the same length as the mergeclauses list: 
*/
-       Oid                *mergeFamilies;      /* per-clause OIDs of btree 
opfamilies */
-       Oid                *mergeCollations;    /* per-clause OIDs of 
collations */
-       int                *mergeStrategies;    /* per-clause ordering (ASC or 
DESC) */
-       bool       *mergeNullsFirst;    /* per-clause nulls ordering */
+       Oid                *mergeFamilies 
pg_node_attr(array_size(mergeclauses));       /* per-clause OIDs of btree 
opfamilies */
+       Oid                *mergeCollations 
pg_node_attr(array_size(mergeclauses));     /* per-clause OIDs of collations */
+       int                *mergeStrategies 
pg_node_attr(array_size(mergeclauses));     /* per-clause ordering (ASC or 
DESC) */
+       bool       *mergeNullsFirst pg_node_attr(array_size(mergeclauses));     
/* per-clause nulls ordering */
 } MergeJoin;
 
 /* ----------------
@@ -793,8 +793,8 @@ typedef struct Memoize
 
        int                     numKeys;                /* size of the two 
arrays below */
 
-       Oid                *hashOperators;      /* hash operators for each key 
*/
-       Oid                *collations;         /* cache keys */
+       Oid                *hashOperators pg_node_attr(array_size(numKeys));    
/* hash operators for each key */
+       Oid                *collations pg_node_attr(array_size(numKeys));       
        /* cache keys */
        List       *param_exprs;        /* exprs containing parameters */
        bool            singlerow;              /* true if the cache entry 
should be marked as
                                                                 * complete 
after we store the first tuple in
@@ -815,10 +815,10 @@ typedef struct Sort
 {
        Plan            plan;
        int                     numCols;                /* number of sort-key 
columns */
-       AttrNumber *sortColIdx;         /* their indexes in the target list */
-       Oid                *sortOperators;      /* OIDs of operators to sort 
them by */
-       Oid                *collations;         /* OIDs of collations */
-       bool       *nullsFirst;         /* NULLS FIRST/LAST directions */
+       AttrNumber *sortColIdx pg_node_attr(array_size(numCols));               
/* their indexes in the target list */
+       Oid                *sortOperators pg_node_attr(array_size(numCols));    
/* OIDs of operators to sort them by */
+       Oid                *collations pg_node_attr(array_size(numCols));       
        /* OIDs of collations */
+       bool       *nullsFirst pg_node_attr(array_size(numCols));               
/* NULLS FIRST/LAST directions */
 } Sort;
 
 /* ----------------
@@ -841,9 +841,9 @@ typedef struct Group
 {
        Plan            plan;
        int                     numCols;                /* number of grouping 
columns */
-       AttrNumber *grpColIdx;          /* their indexes in the target list */
-       Oid                *grpOperators;       /* equality operators to 
compare with */
-       Oid                *grpCollations;
+       AttrNumber *grpColIdx pg_node_attr(array_size(numCols));                
/* their indexes in the target list */
+       Oid                *grpOperators pg_node_attr(array_size(numCols));     
/* equality operators to compare with */
+       Oid                *grpCollations pg_node_attr(array_size(numCols));
 } Group;
 
 /* ---------------
@@ -866,9 +866,9 @@ typedef struct Agg
        AggStrategy aggstrategy;        /* basic strategy, see nodes.h */
        AggSplit        aggsplit;               /* agg-splitting mode, see 
nodes.h */
        int                     numCols;                /* number of grouping 
columns */
-       AttrNumber *grpColIdx;          /* their indexes in the target list */
-       Oid                *grpOperators;       /* equality operators to 
compare with */
-       Oid                *grpCollations;
+       AttrNumber *grpColIdx pg_node_attr(array_size(numCols));                
/* their indexes in the target list */
+       Oid                *grpOperators pg_node_attr(array_size(numCols));     
/* equality operators to compare with */
+       Oid                *grpCollations pg_node_attr(array_size(numCols));
        long            numGroups;              /* estimated number of groups 
in input */
        uint64          transitionSpace;        /* for pass-by-ref transition 
data */
        Bitmapset  *aggParams;          /* IDs of Params used in Aggref inputs 
*/
@@ -886,13 +886,13 @@ typedef struct WindowAgg
        Plan            plan;
        Index           winref;                 /* ID referenced by window 
functions */
        int                     partNumCols;    /* number of columns in 
partition clause */
-       AttrNumber *partColIdx;         /* their indexes in the target list */
-       Oid                *partOperators;      /* equality operators for 
partition columns */
-       Oid                *partCollations; /* collations for partition columns 
*/
+       AttrNumber *partColIdx pg_node_attr(array_size(partNumCols));           
/* their indexes in the target list */
+       Oid                *partOperators 
pg_node_attr(array_size(partNumCols));        /* equality operators for 
partition columns */
+       Oid                *partCollations 
pg_node_attr(array_size(partNumCols)); /* collations for partition columns */
        int                     ordNumCols;             /* number of columns in 
ordering clause */
-       AttrNumber *ordColIdx;          /* their indexes in the target list */
-       Oid                *ordOperators;       /* equality operators for 
ordering columns */
-       Oid                *ordCollations;      /* collations for ordering 
columns */
+       AttrNumber *ordColIdx pg_node_attr(array_size(ordNumCols));             
/* their indexes in the target list */
+       Oid                *ordOperators pg_node_attr(array_size(ordNumCols));  
/* equality operators for ordering columns */
+       Oid                *ordCollations pg_node_attr(array_size(ordNumCols)); 
/* collations for ordering columns */
        int                     frameOptions;   /* frame_clause options, see 
WindowDef */
        Node       *startOffset;        /* expression for starting bound, if 
any */
        Node       *endOffset;          /* expression for ending bound, if any 
*/
@@ -912,9 +912,9 @@ typedef struct Unique
 {
        Plan            plan;
        int                     numCols;                /* number of columns to 
check for uniqueness */
-       AttrNumber *uniqColIdx;         /* their indexes in the target list */
-       Oid                *uniqOperators;      /* equality operators to 
compare with */
-       Oid                *uniqCollations; /* collations for equality 
comparisons */
+       AttrNumber *uniqColIdx pg_node_attr(array_size(numCols));               
/* their indexes in the target list */
+       Oid                *uniqOperators pg_node_attr(array_size(numCols));    
/* equality operators to compare with */
+       Oid                *uniqCollations pg_node_attr(array_size(numCols)); 
/* collations for equality comparisons */
 } Unique;
 
 /* ------------
@@ -950,10 +950,10 @@ typedef struct GatherMerge
        int                     rescan_param;   /* ID of Param that signals a 
rescan, or -1 */
        /* remaining fields are just like the sort-key info in struct Sort */
        int                     numCols;                /* number of sort-key 
columns */
-       AttrNumber *sortColIdx;         /* their indexes in the target list */
-       Oid                *sortOperators;      /* OIDs of operators to sort 
them by */
-       Oid                *collations;         /* OIDs of collations */
-       bool       *nullsFirst;         /* NULLS FIRST/LAST directions */
+       AttrNumber *sortColIdx pg_node_attr(array_size(numCols));               
/* their indexes in the target list */
+       Oid                *sortOperators pg_node_attr(array_size(numCols));    
/* OIDs of operators to sort them by */
+       Oid                *collations pg_node_attr(array_size(numCols));       
        /* OIDs of collations */
+       bool       *nullsFirst pg_node_attr(array_size(numCols));               
/* NULLS FIRST/LAST directions */
        Bitmapset  *initParam;          /* param id's of initplans which are 
referred
                                                                 * at gather 
merge or one of it's child node */
 } GatherMerge;
@@ -993,9 +993,9 @@ typedef struct SetOp
        SetOpStrategy strategy;         /* how to do it, see nodes.h */
        int                     numCols;                /* number of columns to 
check for
                                                                 * 
duplicate-ness */
-       AttrNumber *dupColIdx;          /* their indexes in the target list */
-       Oid                *dupOperators;       /* equality operators to 
compare with */
-       Oid                *dupCollations;
+       AttrNumber *dupColIdx pg_node_attr(array_size(numCols));                
/* their indexes in the target list */
+       Oid                *dupOperators pg_node_attr(array_size(numCols));     
/* equality operators to compare with */
+       Oid                *dupCollations pg_node_attr(array_size(numCols));
        AttrNumber      flagColIdx;             /* where is the flag column, if 
any */
        int                     firstFlag;              /* flag value for first 
input relation */
        long            numGroups;              /* estimated number of groups 
in input */
@@ -1031,9 +1031,9 @@ typedef struct Limit
        Node       *limitCount;         /* COUNT parameter, or NULL if none */
        LimitOption limitOption;        /* limit type */
        int                     uniqNumCols;    /* number of columns to check 
for similarity  */
-       AttrNumber *uniqColIdx;         /* their indexes in the target list */
-       Oid                *uniqOperators;      /* equality operators to 
compare with */
-       Oid                *uniqCollations; /* collations for equality 
comparisons */
+       AttrNumber *uniqColIdx pg_node_attr(array_size(uniqNumCols));           
/* their indexes in the target list */
+       Oid                *uniqOperators 
pg_node_attr(array_size(uniqNumCols));        /* equality operators to compare 
with */
+       Oid                *uniqCollations 
pg_node_attr(array_size(uniqNumCols)); /* collations for equality comparisons */
 } Limit;
 
 
@@ -1192,9 +1192,9 @@ typedef struct PartitionedRelPruneInfo
        Bitmapset  *present_parts;      /* Indexes of all partitions which 
subplans or
                                                                 * subparts are 
present for */
        int                     nparts;                 /* Length of the 
following arrays: */
-       int                *subplan_map;        /* subplan index by partition 
index, or -1 */
-       int                *subpart_map;        /* subpart index by partition 
index, or -1 */
-       Oid                *relid_map;          /* relation OID by partition 
index, or 0 */
+       int                *subplan_map pg_node_attr(array_size(nparts));       
/* subplan index by partition index, or -1 */
+       int                *subpart_map pg_node_attr(array_size(nparts));       
/* subpart index by partition index, or -1 */
+       Oid                *relid_map pg_node_attr(array_size(nparts));         
/* relation OID by partition index, or 0 */
 
        /*
         * initial_pruning_steps shows how to prune during executor startup 
(i.e.,
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 433437643e..d8abd98b55 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 */
@@ -597,9 +597,9 @@ 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                     negfuncid;              /* PG_PROC OID of 
negator of opfuncid function
+       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 */
+       Oid                     negfuncid pg_node_attr(equal_ignore_if_zero);   
        /* PG_PROC OID of negator of opfuncid function
                                                                 * or 
InvalidOid.  See above */
        bool            useOr;                  /* true for ANY, false for ALL 
*/
        Oid                     inputcollid;    /* OID of collation that 
operator should use */
diff --git a/src/include/pg_config_manual.h b/src/include/pg_config_manual.h
index 427ffabd14..b72ff3794e 100644
--- a/src/include/pg_config_manual.h
+++ b/src/include/pg_config_manual.h
@@ -371,14 +371,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
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 31281279cf..20fba84a99 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -273,9 +273,9 @@ typedef struct ForeignKeyCacheInfo
        Oid                     confrelid;              /* relation referenced 
by the foreign key */
        int                     nkeys;                  /* number of columns in 
the foreign key */
        /* these arrays each have nkeys valid entries: */
-       AttrNumber      conkey[INDEX_MAX_KEYS]; /* cols in referencing table */
-       AttrNumber      confkey[INDEX_MAX_KEYS];        /* cols in referenced 
table */
-       Oid                     conpfeqop[INDEX_MAX_KEYS];      /* PK = FK 
operator OIDs */
+       AttrNumber      conkey[INDEX_MAX_KEYS] pg_node_attr(array_size(nkeys)); 
/* cols in referencing table */
+       AttrNumber      confkey[INDEX_MAX_KEYS] 
pg_node_attr(array_size(nkeys));        /* cols in referenced table */
+       Oid                     conpfeqop[INDEX_MAX_KEYS] 
pg_node_attr(array_size(nkeys));      /* PK = FK operator OIDs */
 } ForeignKeyCacheInfo;
 
 
diff --git a/src/tools/msvc/Solution.pm b/src/tools/msvc/Solution.pm
index 2c8cd521e9..290197ed4d 100644
--- a/src/tools/msvc/Solution.pm
+++ b/src/tools/msvc/Solution.pm
@@ -832,6 +832,52 @@ EOF
                close($chs);
        }
 
+       if (IsNewer('src/backend/nodes/node-support-stamp',
+               'src/backend/nodes/gen_node_support.pl'))
+       {
+               # XXX duplicates src/backend/nodes/Makefile
+
+               my @node_headers = qw(
+                       nodes/nodes.h
+                       nodes/execnodes.h
+                       nodes/plannodes.h
+                       nodes/primnodes.h
+                       nodes/pathnodes.h
+                       nodes/extensible.h
+                       nodes/parsenodes.h
+                       nodes/replnodes.h
+                       nodes/value.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
+               );
+
+               chdir('src/backend/nodes');
+
+               my @node_files = map { "../../../src/include/$_" } 
@node_headers;
+
+               system("perl gen_node_support.pl @node_files");
+               open(my $f, '>', 'node-support-stamp') || confess "Could not 
touch node-support-stamp";
+               close($f);
+               chdir('../../..');
+       }
+
+       if (IsNewer(
+                       'src/include/nodes/nodetags.h',
+                       'src/backend/nodes/nodetags.h'))
+       {
+               copyFile('src/backend/nodes/nodetags.h',
+                       'src/include/nodes/nodetags.h');
+       }
+
        open(my $o, '>', "doc/src/sgml/version.sgml")
          || croak "Could not write to version.sgml\n";
        print $o <<EOF;

base-commit: cab5b9ab2c066ba904f13de2681872dcda31e207
-- 
2.34.1

Reply via email to