On 03.07.22 21:14, Tom Lane wrote:
Peter Eisentraut <peter.eisentr...@enterprisedb.com> writes:
Here is a patch that reformats the relevant (and a few more) comments
that way. This has been run through pgindent, so the formatting should
be stable.
Now that that's been pushed, the main patch is of course quite broken.
Are you working on a rebase?
attached
* AFAICT, the infrastructure for removing the generated files at
"make *clean" is incomplete.
I have fixed all the makefiles per your suggestions.
and something similar to this for the "clean" rule:
# fmgroids.h, fmgrprotos.h, fmgrtab.c, fmgr-stamp, and errcodes.h are in the
# distribution tarball, so they are not cleaned here.
Except this one, since there is no clean rule. I think seeing that
files are listed under a maintainer-clean target conveys that same message.
Also, I share David's upthread allergy to the option names "path_hackN"
and to documenting those only inside the conversion script.
I'll look into that again.
BTW, I think this: "Unknown attributes are ignored" is a seriously
bad idea; it will allow typos to escape detection.
good point
From 0ad86183662b6637c9ec9a29374fecb4d11202e2 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <pe...@eisentraut.org>
Date: Mon, 4 Jul 2022 14:17:02 +0200
Subject: [PATCH v6] 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.funcs.c and copyfuncs.switch.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. All the
code comments that are worth keeping from those sections have already
been moved to the header files where the structs are defined.
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 | 10 +-
src/backend/nodes/.gitignore | 4 +
src/backend/nodes/Makefile | 54 ++
src/backend/nodes/copyfuncs.c | 19 +-
src/backend/nodes/equalfuncs.c | 22 +-
src/backend/nodes/gen_node_support.pl | 729 ++++++++++++++++++++++++++
src/backend/nodes/outfuncs.c | 34 +-
src/backend/nodes/readfuncs.c | 23 +-
src/include/Makefile | 1 +
src/include/nodes/.gitignore | 2 +
src/include/nodes/nodes.h | 27 +
src/include/nodes/parsenodes.h | 4 +-
src/include/nodes/pathnodes.h | 175 ++++---
src/include/nodes/plannodes.h | 90 ++--
src/include/nodes/primnodes.h | 34 +-
src/include/utils/rel.h | 6 +-
src/tools/msvc/Solution.pm | 46 ++
17 files changed, 1124 insertions(+), 156 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 4a02006788..953c80db5a 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` && \
@@ -185,6 +189,7 @@ distprep:
$(MAKE) -C parser gram.c gram.h scan.c
$(MAKE) -C bootstrap bootparse.c bootscanner.c
$(MAKE) -C catalog distprep
+ $(MAKE) -C nodes distprep
$(MAKE) -C replication repl_gram.c repl_scanner.c syncrep_gram.c
syncrep_scanner.c
$(MAKE) -C storage/lmgr lwlocknames.h lwlocknames.c
$(MAKE) -C utils distprep
@@ -297,6 +302,7 @@ distclean: clean
maintainer-clean: distclean
$(MAKE) -C catalog $@
+ $(MAKE) -C nodes $@
$(MAKE) -C utils $@
rm -f bootstrap/bootparse.c \
bootstrap/bootscanner.c \
diff --git a/src/backend/nodes/.gitignore b/src/backend/nodes/.gitignore
new file mode 100644
index 0000000000..0c14b5697b
--- /dev/null
+++ b/src/backend/nodes/.gitignore
@@ -0,0 +1,4 @@
+/node-support-stamp
+/nodetags.h
+/*funcs.funcs.c
+/*funcs.switch.c
diff --git a/src/backend/nodes/Makefile b/src/backend/nodes/Makefile
index 5d2b12a993..20c1ec08e8 100644
--- a/src/backend/nodes/Makefile
+++ b/src/backend/nodes/Makefile
@@ -30,3 +30,57 @@ 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 records the last time we ran gen_node_support.pl.
+# We don't rely on the timestamps of the individual output files,
+# because the Perl script won't update them if they didn't change (to
+# avoid unnecessary recompiles).
+node-support-stamp: gen_node_support.pl $(addprefix
$(top_srcdir)/src/include/,$(node_headers))
+ $(PERL) $^
+ touch $@
+
+# These generated headers must be symlinked into builddir/src/include/,
+# using absolute links for the reasons explained in src/backend/Makefile.
+# We use header-stamp to record that we've done this because the symlinks
+# themselves may appear older than node-support-stamp.
+$(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.funcs.c *funcs.switch.c nodetags.h
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 51d630fa89..b756744502 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.funcs.c"
+
+#ifdef OBSOLETE
/* ****************************************************************
* plannodes.h copy functions
* ****************************************************************
@@ -1465,6 +1464,7 @@ _copyVar(const Var *from)
return newnode;
}
+#endif /*OBSOLETE*/
/*
* _copyConst
@@ -1504,6 +1504,7 @@ _copyConst(const Const *from)
return newnode;
}
+#ifdef OBSOLETE
/*
* _copyParam
*/
@@ -3247,6 +3248,7 @@ _copyParamRef(const ParamRef *from)
return newnode;
}
+#endif /*OBSOLETE*/
static A_Const *
_copyA_Const(const A_Const *from)
@@ -3287,6 +3289,7 @@ _copyA_Const(const A_Const *from)
return newnode;
}
+#ifdef OBSOLETE
static FuncCall *
_copyFuncCall(const FuncCall *from)
{
@@ -5452,6 +5455,7 @@ _copyDropSubscriptionStmt(const DropSubscriptionStmt
*from)
return newnode;
}
+#endif /*OBSOLETE*/
/* ****************************************************************
* extensible.h copy functions
@@ -5474,6 +5478,7 @@ _copyExtensibleNode(const ExtensibleNode *from)
return newnode;
}
+#ifdef OBSOLETE
/* ****************************************************************
* value.h copy functions
* ****************************************************************
@@ -5544,6 +5549,7 @@ _copyForeignKeyCacheInfo(const ForeignKeyCacheInfo *from)
return newnode;
}
+#endif /*OBSOLETE*/
/*
* copyObjectImpl -- implementation of copyObject(); see nodes/nodes.h
@@ -5564,6 +5570,8 @@ copyObjectImpl(const void *from)
switch (nodeTag(from))
{
+#include "copyfuncs.switch.c"
+#ifdef OBSOLETE
/*
* PLAN NODES
*/
@@ -6008,6 +6016,7 @@ copyObjectImpl(const void *from)
case T_BitString:
retval = _copyBitString(from);
break;
+#endif /*OBSOLETE*/
/*
* LIST NODES
@@ -6025,6 +6034,7 @@ copyObjectImpl(const void *from)
retval = list_copy(from);
break;
+#ifdef OBSOLETE
/*
* EXTENSIBLE NODES
*/
@@ -6576,6 +6586,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 e747e1667d..f9f39d782f 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.funcs.c"
+
+#ifdef OBSOLETE
/*
* Stuff from primnodes.h
*/
@@ -242,6 +240,7 @@ _equalVar(const Var *a, const Var *b)
return true;
}
+#endif /*OBSOLETE*/
static bool
_equalConst(const Const *a, const Const *b)
@@ -264,6 +263,7 @@ _equalConst(const Const *a, const Const *b)
a->constbyval, a->constlen);
}
+#ifdef OBSOLETE
static bool
_equalParam(const Param *a, const Param *b)
{
@@ -1288,6 +1288,7 @@ _equalPlaceHolderInfo(const PlaceHolderInfo *a, const
PlaceHolderInfo *b)
return true;
}
+#endif /*OBSOLETE*/
/*
* Stuff from extensible.h
@@ -1309,6 +1310,7 @@ _equalExtensibleNode(const ExtensibleNode *a, const
ExtensibleNode *b)
return true;
}
+#ifdef OBSOLETE
/*
* Stuff from parsenodes.h
*/
@@ -2799,6 +2801,7 @@ _equalParamRef(const ParamRef *a, const ParamRef *b)
return true;
}
+#endif /*OBSOLETE*/
static bool
_equalA_Const(const A_Const *a, const A_Const *b)
@@ -2815,6 +2818,7 @@ _equalA_Const(const A_Const *a, const A_Const *b)
return true;
}
+#ifdef OBSOLETE
static bool
_equalFuncCall(const FuncCall *a, const FuncCall *b)
{
@@ -3452,6 +3456,7 @@ _equalPartitionCmd(const PartitionCmd *a, const
PartitionCmd *b)
return true;
}
+#endif /*OBSOLETE*/
/*
* Stuff from pg_list.h
@@ -3512,6 +3517,7 @@ _equalList(const List *a, const List *b)
return true;
}
+#ifdef OBSOLETE
/*
* Stuff from value.h
*/
@@ -3555,6 +3561,7 @@ _equalBitString(const BitString *a, const BitString *b)
return true;
}
+#endif /*OBSOLETE*/
/*
* equal
@@ -3585,6 +3592,8 @@ equal(const void *a, const void *b)
switch (nodeTag(a))
{
+#include "equalfuncs.switch.c"
+#ifdef OBSOLETE
/*
* PRIMITIVE NODES
*/
@@ -3805,6 +3814,7 @@ equal(const void *a, const void *b)
case T_PlaceHolderInfo:
retval = _equalPlaceHolderInfo(a, b);
break;
+#endif /*OBSOLETE*/
case T_List:
case T_IntList:
@@ -3812,6 +3822,7 @@ equal(const void *a, const void *b)
retval = _equalList(a, b);
break;
+#ifdef OBSOLETE
case T_Integer:
retval = _equalInteger(a, b);
break;
@@ -4411,6 +4422,7 @@ equal(const void *a, const void *b)
case T_JsonTableColumn:
retval = _equalJsonTableColumn(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..edbafd3e5b
--- /dev/null
+++ b/src/backend/nodes/gen_node_support.pl
@@ -0,0 +1,729 @@
+#!/usr/bin/perl
+#----------------------------------------------------------------------
+#
+# Generate node support files:
+# - nodetags.h
+# - copyfuncs
+# - equalfuncs
+# - readfuncs
+# - outfuncs
+#
+# Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
+# Portions Copyright (c) 1994, Regents of the University of California
+#
+# 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
+
+
+# Test whether first argument is element of the list in the second
+# argument
+sub elem
+{
+ my $x = shift;
+ return grep { $_ eq $x } @_;
+}
+
+
+# collect node names
+my @node_types = qw(Node);
+# collect info for each node type
+my %node_type_info;
+
+# node types we don't want copy support for
+my @no_copy;
+# node types we don't want read/write support for
+my @no_read_write;
+
+# types that are copied by straight assignment
+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
+);
+
+# collect enum types
+my @enum_types;
+
+# Abstract types are types that cannot be instantiated but that can be
+# supertypes of other 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: We don't support copying RelOptInfo,
+# IndexOptInfo, or Path nodes. There are some subsidiary structs that
+# are useful to copy, though.
+push @no_copy, qw(
+ RelOptInfo IndexOptInfo Path PlannerGlobal EquivalenceClass
EquivalenceMember ForeignKeyOptInfo
+ GroupingSetData IncrementalSortPath IndexClause MinMaxAggInfo
PathTarget PlannerInfo PlannerParamItem
+ ParamPathInfo RollupData RowIdentityVarInfo StatisticExtInfo
+);
+# EquivalenceClasses are never moved, so just shallow-copy the pointer
+push @scalar_types, qw(EquivalenceClass* EquivalenceMember*);
+push @scalar_types, qw(QualCost);
+
+# See special treatment in outNode() and nodeRead() for these.
+push @no_read_write, qw(BitString Boolean Float Integer List String);
+
+# 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)/;
+
+ # we are analyzing a struct definition
+ 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)
+ {
+ # This is the end of a node struct
definition.
+ # Save everything we have collected.
+
+ # node name
+ push @node_types, $in_struct;
+
+ # field names, types, attributes
+ my @f = @my_fields;
+ my %ft = %my_field_types;
+ my %fa = %my_field_attrs;
+
+ # If there is a supertype, add those
fields, too.
+ 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;
+ }
+ # save in global info structure
+ $node_type_info{$in_struct}->{fields} =
\@f;
+
$node_type_info{$in_struct}->{field_types} = \%ft;
+
$node_type_info{$in_struct}->{field_attrs} = \%fa;
+
+ # Nodes from these files don't need to
be
+ # supported, except the node tags.
+ 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;
+ }
+
+ # We do not support copying Path trees,
mainly
+ # because the circular linkages between
RelOptInfo
+ # and Path nodes can't be handled
easily in a
+ # simple depth-first traversal.
+ 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;
+
+ # strip "const"
+ $type =~ s/^const\s*//;
+ # strip trailing space
+ $type =~ s/\s*$//;
+ # strip space between type and "*"
(pointer) */
+ $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;
+
+ # copy everything over
+ 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;
+
+
+# make #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 $cff, '>', 'copyfuncs.funcs.c' . $tmpext or die $!;
+open my $eff, '>', 'equalfuncs.funcs.c' . $tmpext or die $!;
+open my $cfs, '>', 'copyfuncs.switch.c' . $tmpext or die $!;
+open my $efs, '>', 'equalfuncs.switch.c' . $tmpext or die $!;
+
+# add required #include lines to each file set
+print $cff $node_includes;
+print $eff $node_includes;
+
+# Nodes with custom copy implementations are skipped from .funcs.c but
+# need case statements in .switch.c.
+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 $cfs "
+\t\tcase T_${n}:
+\t\t\tretval = _copy${n}(from);
+\t\t\tbreak;";
+
+ print $efs "
+\t\tcase T_${n}:
+\t\t\tretval = _equal${n}(a, b);
+\t\t\tbreak;";
+
+ next if elem $n, @custom_copy;
+
+ print $cff "
+static $n *
+_copy${n}(const $n *from)
+{
+\t${n} *newnode = makeNode($n);
+
+";
+
+ print $eff "
+static bool
+_equal${n}(const $n *a, const $n *b)
+{
+";
+
+ # print instructions for each field
+ foreach my $f (@{$node_type_info{$n}->{fields}})
+ {
+ my $t = $node_type_info{$n}->{field_types}{$f};
+ my $a = $node_type_info{$n}->{field_attrs}{$f} || '';
+ my $copy_ignore = ($a =~ /\bcopy_ignore\b/);
+ my $equal_ignore = ($a =~ /\bequal_ignore\b/);
+
+ # select instructions by field type
+ if ($t eq 'char*')
+ {
+ print $cff "\tCOPY_STRING_FIELD($f);\n" unless
$copy_ignore;
+ print $eff "\tCOMPARE_STRING_FIELD($f);\n" unless
$equal_ignore;
+ }
+ elsif ($t eq 'Bitmapset*' || $t eq 'Relids')
+ {
+ print $cff "\tCOPY_BITMAPSET_FIELD($f);\n" unless
$copy_ignore;
+ print $eff "\tCOMPARE_BITMAPSET_FIELD($f);\n" unless
$equal_ignore;
+ }
+ elsif ($t eq 'int' && $f =~ 'location$')
+ {
+ print $cff "\tCOPY_LOCATION_FIELD($f);\n" unless
$copy_ignore;
+ print $eff "\tCOMPARE_LOCATION_FIELD($f);\n" unless
$equal_ignore;
+ }
+ elsif (elem $t, @scalar_types or elem $t, @enum_types)
+ {
+ print $cff "\tCOPY_SCALAR_FIELD($f);\n" unless
$copy_ignore;
+ if ($a =~ /\bequal_ignore_if_zero\b/)
+ {
+ print $eff "\tif (a->$f != b->$f && a->$f != 0
&& b->$f != 0)\n\t\treturn false;\n";
+ }
+ else
+ {
+ print $eff "\tCOMPARE_SCALAR_FIELD($f);\n"
unless $equal_ignore || $t eq 'CoercionForm';
+ }
+ }
+ # scalar type pointer
+ 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 $cff "\tCOPY_POINTER_FIELD($f,
list_length(from->$array_size_field) * sizeof($tt));\n" unless $copy_ignore;
+ print $eff "\tCOMPARE_POINTER_FIELD($f,
list_length(a->$array_size_field) * sizeof($tt));\n" unless $equal_ignore;
+ }
+ else
+ {
+ print $cff "\tCOPY_POINTER_FIELD($f,
from->$array_size_field * sizeof($tt));\n" unless $copy_ignore;
+ print $eff "\tCOMPARE_POINTER_FIELD($f,
a->$array_size_field * sizeof($tt));\n" unless $equal_ignore;
+ }
+ }
+ # node type
+ elsif ($t =~ /(\w+)\*/ and elem $1, @node_types)
+ {
+ print $cff "\tCOPY_NODE_FIELD($f);\n" unless
$copy_ignore;
+ print $eff "\tCOMPARE_NODE_FIELD($f);\n" unless
$equal_ignore;
+ }
+ # array (inline)
+ elsif ($t =~ /\w+\[/)
+ {
+ print $cff "\tCOPY_ARRAY_FIELD($f);\n" unless
$copy_ignore;
+ print $eff "\tCOMPARE_ARRAY_FIELD($f);\n" unless
$equal_ignore;
+ }
+ elsif ($t eq 'struct CustomPathMethods*' || $t eq 'struct
CustomScanMethods*')
+ {
+ # Fields of these types are required to be a pointer to
a
+ # static table of callback functions. So we don't copy
+ # the table itself, just reference the original one.
+ print $cff "\tCOPY_SCALAR_FIELD($f);\n" unless
$copy_ignore;
+ print $eff "\tCOMPARE_SCALAR_FIELD($f);\n" unless
$equal_ignore;
+ }
+ else
+ {
+ die "could not handle type \"$t\" in struct \"$n\"
field \"$f\"";
+ }
+ }
+
+ print $cff "
+\treturn newnode;
+}
+";
+ print $eff "
+\treturn true;
+}
+";
+}
+
+close $cff;
+close $eff;
+close $cfs;
+close $efs;
+
+
+# outfuncs.c, readfuncs.c
+
+open my $off, '>', 'outfuncs.funcs.c' . $tmpext or die $!;
+open my $rff, '>', 'readfuncs.funcs.c' . $tmpext or die $!;
+open my $ofs, '>', 'outfuncs.switch.c' . $tmpext or die $!;
+open my $rfs, '>', 'readfuncs.switch.c' . $tmpext or die $!;
+
+print $off $node_includes;
+print $rff $node_includes;
+
+my @custom_readwrite = qw(A_Const A_Expr BoolExpr Const Constraint
EquivalenceClass ExtensibleNode ForeignKeyOptInfo Query RangeTblEntry);
+
+foreach my $n (@node_types)
+{
+ next if elem $n, @abstract_types;
+ next if elem $n, @no_read_write;
+
+ # 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 'EquivalenceClass' || $n eq
'ForeignKeyCacheInfo' || $n eq 'ForeignKeyOptInfo' || $n eq 'PathTarget');
+
+ # output format starts with upper case node type, underscores stripped
+ my $N = uc $n;
+ $N =~ s/_//g;
+
+ print $ofs "\t\t\tcase T_${n}:\n".
+ "\t\t\t\t_out${n}(str, obj);\n".
+ "\t\t\t\tbreak;\n";
+
+ print $rfs "\telse if (MATCH(\"$N\", " . length($N) . "))\n".
+ "\t\treturn_value = _read${n}();\n" unless $no_read;
+
+ next if elem $n, @custom_readwrite;
+
+ print $off "
+static void
+_out${n}(StringInfo str, const $n *node)
+{
+\tWRITE_NODE_TYPE(\"$N\");
+
+";
+
+ print $rff "
+static $n *
+_read${n}(void)
+{
+\tREAD_LOCALS($n);
+
+" unless $no_read;
+
+ # print instructions for each field
+ foreach my $f (@{$node_type_info{$n}->{fields}})
+ {
+ my $t = $node_type_info{$n}->{field_types}{$f};
+ my $a = $node_type_info{$n}->{field_attrs}{$f} || '';
+ my $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.
+
+ # select instructions by field type
+ if ($t eq 'bool')
+ {
+ print $off "\tWRITE_BOOL_FIELD($f);\n";
+ print $rff "\tREAD_BOOL_FIELD($f);\n" unless $no_read;
+ }
+ elsif ($t eq 'int' && $f =~ 'location$')
+ {
+ print $off "\tWRITE_LOCATION_FIELD($f);\n";
+ print $rff "\tREAD_LOCATION_FIELD($f);\n" unless
$no_read;
+ }
+ elsif ($t eq 'int' || $t eq 'int32' || $t eq 'AttrNumber' || $t
eq 'StrategyNumber')
+ {
+ print $off "\tWRITE_INT_FIELD($f);\n";
+ print $rff "\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 $off "\tWRITE_UINT_FIELD($f);\n";
+ print $rff "\tREAD_UINT_FIELD($f);\n" unless $no_read;
+ }
+ elsif ($t eq 'uint64')
+ {
+ print $off "\tWRITE_UINT64_FIELD($f);\n";
+ print $rff "\tREAD_UINT64_FIELD($f);\n" unless $no_read;
+ }
+ elsif ($t eq 'Oid')
+ {
+ print $off "\tWRITE_OID_FIELD($f);\n";
+ print $rff "\tREAD_OID_FIELD($f);\n" unless $no_read;
+ }
+ elsif ($t eq 'long')
+ {
+ print $off "\tWRITE_LONG_FIELD($f);\n";
+ print $rff "\tREAD_LONG_FIELD($f);\n" unless $no_read;
+ }
+ elsif ($t eq 'char')
+ {
+ print $off "\tWRITE_CHAR_FIELD($f);\n";
+ print $rff "\tREAD_CHAR_FIELD($f);\n" unless $no_read;
+ }
+ elsif ($t eq 'double')
+ {
+ print $off "\tWRITE_FLOAT_FIELD($f, \"%.6f\");\n";
+ print $rff "\tREAD_FLOAT_FIELD($f);\n" unless $no_read;
+ }
+ elsif ($t eq 'Cardinality')
+ {
+ print $off "\tWRITE_FLOAT_FIELD($f, \"%.0f\");\n";
+ print $rff "\tREAD_FLOAT_FIELD($f);\n" unless $no_read;
+ }
+ elsif ($t eq 'Cost')
+ {
+ print $off "\tWRITE_FLOAT_FIELD($f, \"%.2f\");\n";
+ print $rff "\tREAD_FLOAT_FIELD($f);\n" unless $no_read;
+ }
+ elsif ($t eq 'QualCost')
+ {
+ print $off "\tWRITE_FLOAT_FIELD($f.startup,
\"%.2f\");\n";
+ print $off "\tWRITE_FLOAT_FIELD($f.per_tuple,
\"%.2f\");\n";
+ print $rff "\tREAD_FLOAT_FIELD($f.startup);\n" unless
$no_read;
+ print $rff "\tREAD_FLOAT_FIELD($f.per_tuple);\n" unless
$no_read;
+ }
+ elsif ($t eq 'Selectivity')
+ {
+ print $off "\tWRITE_FLOAT_FIELD($f, \"%.4f\");\n";
+ print $rff "\tREAD_FLOAT_FIELD($f);\n" unless $no_read;
+ }
+ elsif ($t eq 'char*')
+ {
+ print $off "\tWRITE_STRING_FIELD($f);\n";
+ print $rff "\tREAD_STRING_FIELD($f);\n" unless $no_read;
+ }
+ elsif ($t eq 'Bitmapset*' || $t eq 'Relids')
+ {
+ print $off "\tWRITE_BITMAPSET_FIELD($f);\n";
+ print $rff "\tREAD_BITMAPSET_FIELD($f);\n" unless
$no_read;
+ }
+ elsif (elem $t, @enum_types)
+ {
+ print $off "\tWRITE_ENUM_FIELD($f, $t);\n";
+ print $rff "\tREAD_ENUM_FIELD($f, $t);\n" unless
$no_read;
+ }
+ # arrays
+ 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 $off "\tWRITE_${tt}_ARRAY($f,
list_length(node->$array_size_field));\n";
+ print $rff "\tREAD_${tt}_ARRAY($f,
list_length(local_node->$array_size_field));\n" unless $no_read;
+ }
+ else
+ {
+ print $off "\tWRITE_${tt}_ARRAY($f,
node->$array_size_field);\n";
+ print $rff "\tREAD_${tt}_ARRAY($f,
local_node->$array_size_field);\n" unless $no_read;
+ }
+ }
+ # Special treatments of several Path node fields
+ #
+ # We do not print the parent, else we'd be in infinite
+ # recursion. We can print the parent's relids for
+ # identification purposes, though. We print the pathtarget
+ # only if it's not the default one for the rel. We also do
+ # not print the whole of param_info, since it's printed via
+ # RelOptInfo; it's sufficient and less cluttering to print
+ # just the required outer relids.
+ elsif ($t eq 'RelOptInfo*' && $a eq 'path_hack1')
+ {
+ print $off "\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 $off "\tif (node->$f != node->$f2->reltarget)\n".
+ "\t\tWRITE_NODE_FIELD($f);\n";
+ }
+ elsif ($t eq 'ParamPathInfo*' && $a eq 'path_hack3')
+ {
+ print $off "\tif (node->$f)\n".
+ "\t\toutBitmapset(str, node->$f->ppi_req_outer);\n".
+ "\telse\n".
+ "\t\toutBitmapset(str, NULL);\n";
+ }
+ # node type
+ elsif ($t =~ /(\w+)\*/ and elem $1, @node_types)
+ {
+ print $off "\tWRITE_NODE_FIELD($f);\n";
+ print $rff "\tREAD_NODE_FIELD($f);\n" unless $no_read;
+ }
+ elsif ($t eq 'struct CustomPathMethods*' || $t eq 'struct
CustomScanMethods*')
+ {
+ print $off q{
+ /* CustomName is a key to lookup CustomScanMethods */
+ appendStringInfoString(str, " :methods ");
+ outToken(str, node->methods->CustomName);
+};
+ print $rff 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;
+ }
+ # various field types to ignore
+ 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 $off "}
+";
+ print $rff "
+\tREAD_DONE();
+}
+" unless $no_read;
+}
+
+close $off;
+close $rff;
+close $ofs;
+close $rfs;
+
+
+# now rename the temporary files to their final name
+foreach my $file (qw(nodetags.h copyfuncs.funcs.c copyfuncs.switch.c
equalfuncs.funcs.c equalfuncs.switch.c outfuncs.funcs.c outfuncs.switch.c
readfuncs.funcs.c readfuncs.switch.c))
+{
+ Catalog::RenameTempFile($file, $tmpext);
+}
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index ce12915592..58ffc9c811 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);
@@ -302,6 +301,9 @@ outDatum(StringInfo str, Datum value, int typlen, bool
typbyval)
}
+#include "outfuncs.funcs.c"
+
+#ifdef OBSOLETE
/*
* Stuff from plannodes.h
*/
@@ -1151,6 +1153,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)
@@ -1172,6 +1175,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)
{
@@ -1342,6 +1346,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)
@@ -1370,6 +1375,7 @@ _outBoolExpr(StringInfo str, const BoolExpr *node)
WRITE_LOCATION_FIELD(location);
}
+#ifdef OBSOLETE
static void
_outSubLink(StringInfo str, const SubLink *node)
{
@@ -2582,6 +2588,7 @@ _outIndexOptInfo(StringInfo str, const IndexOptInfo *node)
WRITE_BOOL_FIELD(hypothetical);
/* we don't bother with fields copied from the index AM's API struct */
}
+#endif /* OBSOLETE */
static void
_outForeignKeyOptInfo(StringInfo str, const ForeignKeyOptInfo *node)
@@ -2609,6 +2616,7 @@ _outForeignKeyOptInfo(StringInfo str, const
ForeignKeyOptInfo *node)
appendStringInfo(str, " %d", list_length(node->rinfos[i]));
}
+#ifdef OBSOLETE
static void
_outStatisticExtInfo(StringInfo str, const StatisticExtInfo *node)
{
@@ -2620,6 +2628,7 @@ _outStatisticExtInfo(StringInfo str, const
StatisticExtInfo *node)
WRITE_CHAR_FIELD(kind);
WRITE_BITMAPSET_FIELD(keys);
}
+#endif /* OBSOLETE */
static void
_outEquivalenceClass(StringInfo str, const EquivalenceClass *node)
@@ -2648,6 +2657,7 @@ _outEquivalenceClass(StringInfo str, const
EquivalenceClass *node)
WRITE_UINT_FIELD(ec_max_security);
}
+#ifdef OBSOLETE
static void
_outEquivalenceMember(StringInfo str, const EquivalenceMember *node)
{
@@ -2832,6 +2842,7 @@ _outPlannerParamItem(StringInfo str, const
PlannerParamItem *node)
WRITE_NODE_FIELD(item);
WRITE_INT_FIELD(paramId);
}
+#endif /*OBSOLETE*/
/*****************************************************************************
*
@@ -2854,6 +2865,7 @@ _outExtensibleNode(StringInfo str, const ExtensibleNode
*node)
methods->nodeOut(str, node);
}
+#ifdef OBSOLETE
/*****************************************************************************
*
* Stuff from parsenodes.h.
@@ -3187,6 +3199,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)
@@ -3261,6 +3274,7 @@ _outQuery(StringInfo str, const Query *node)
WRITE_INT_FIELD(stmt_len);
}
+#ifdef OBSOLETE
static void
_outWithCheckOption(StringInfo str, const WithCheckOption *node)
{
@@ -3426,6 +3440,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)
@@ -3506,6 +3521,7 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry
*node)
WRITE_NODE_FIELD(securityQuals);
}
+#ifdef OBSOLETE
static void
_outRangeTblFunction(StringInfo str, const RangeTblFunction *node)
{
@@ -3529,6 +3545,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)
@@ -3647,6 +3664,7 @@ _outBitString(StringInfo str, const BitString *node)
appendStringInfoString(str, node->bsval);
}
+#ifdef OBSOLETE
static void
_outColumnRef(StringInfo str, const ColumnRef *node)
{
@@ -3678,6 +3696,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)
@@ -3694,6 +3713,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)
{
@@ -3838,6 +3858,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)
@@ -3960,6 +3981,7 @@ _outConstraint(StringInfo str, const Constraint *node)
}
}
+#ifdef OBSOLETE
static void
_outForeignKeyCacheInfo(StringInfo str, const ForeignKeyCacheInfo *node)
{
@@ -4020,6 +4042,7 @@ _outPartitionRangeDatum(StringInfo str, const
PartitionRangeDatum *node)
WRITE_NODE_FIELD(value);
WRITE_LOCATION_FIELD(location);
}
+#endif /*OBSOLETE*/
/*
* outNode -
@@ -4051,6 +4074,8 @@ outNode(StringInfo str, const void *obj)
appendStringInfoChar(str, '{');
switch (nodeTag(obj))
{
+#include "outfuncs.switch.c"
+#ifdef OBSOLETE
case T_PlannedStmt:
_outPlannedStmt(str, obj);
break;
@@ -4762,6 +4787,7 @@ outNode(StringInfo str, const void *obj)
case T_JsonTableSibling:
_outJsonTableSibling(str, obj);
break;
+#endif /*OBSOLETE*/
default:
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 6a05b69415..f427aa05ec 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.funcs.c"
+
/*
* _readQuery
*/
@@ -291,6 +291,7 @@ _readQuery(void)
READ_DONE();
}
+#ifdef OBSOLETE
/*
* _readNotifyStmt
*/
@@ -629,6 +630,7 @@ _readVar(void)
READ_DONE();
}
+#endif /*OBSOLETE*/
/*
* _readConst
@@ -655,6 +657,7 @@ _readConst(void)
READ_DONE();
}
+#ifdef OBSOLETE
/*
* _readParam
*/
@@ -880,6 +883,7 @@ _readScalarArrayOpExpr(void)
READ_DONE();
}
+#endif /*OBSOLETE*/
/*
* _readBoolExpr
@@ -907,6 +911,7 @@ _readBoolExpr(void)
READ_DONE();
}
+#ifdef OBSOLETE
/*
* _readSubLink
*/
@@ -1649,6 +1654,7 @@ _readAppendRelInfo(void)
/*
* Stuff from parsenodes.h.
*/
+#endif /*OBSOLETE*/
/*
* _readRangeTblEntry
@@ -1744,6 +1750,7 @@ _readRangeTblEntry(void)
READ_DONE();
}
+#ifdef OBSOLETE
/*
* _readRangeTblFunction
*/
@@ -2872,6 +2879,7 @@ _readAlternativeSubPlan(void)
READ_DONE();
}
+#endif /*OBSOLETE*/
/*
* _readExtensibleNode
@@ -2903,6 +2911,7 @@ _readExtensibleNode(void)
READ_DONE();
}
+#ifdef OBSOLETE
/*
* _readPartitionBoundSpec
*/
@@ -2937,6 +2946,7 @@ _readPartitionRangeDatum(void)
READ_DONE();
}
+#endif /*OBSOLETE*/
/*
* parseNodeString
@@ -2961,7 +2971,11 @@ parseNodeString(void)
#define MATCH(tokname, namelen) \
(length == namelen && memcmp(token, tokname, namelen) == 0)
- if (MATCH("QUERY", 5))
+ if (false)
+ ;
+#include "readfuncs.switch.c"
+#ifdef OBSOLETE
+ else if (MATCH("QUERY", 5))
return_value = _readQuery();
else if (MATCH("WITHCHECKOPTION", 15))
return_value = _readWithCheckOption();
@@ -3235,6 +3249,7 @@ parseNodeString(void)
return_value = _readJsonTableParent();
else if (MATCH("JSONTABLESIBLING", 16))
return_value = _readJsonTableSibling();
+#endif /*OBSOLETE*/
else
{
elog(ERROR, "badly formatted node string \"%.32s\"...", token);
diff --git a/src/include/Makefile b/src/include/Makefile
index 5f257a958c..17cfd268b8 100644
--- a/src/include/Makefile
+++ b/src/include/Makefile
@@ -81,6 +81,7 @@ clean:
rm -f parser/gram.h storage/lwlocknames.h utils/probes.h
rm -f catalog/schemapg.h catalog/system_fk_info.h
rm -f catalog/pg_*_d.h catalog/header-stamp
+ rm -f nodes/nodetags.h nodes/header-stamp
distclean maintainer-clean: clean
rm -f pg_config.h pg_config_ext.h pg_config_os.h stamp-h stamp-ext-h
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 b3b407579b..8a62a2fee5 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)
*/
@@ -562,8 +564,33 @@ typedef enum NodeTag
T_SupportRequestRows, /* in nodes/supportnodes.h */
T_SupportRequestIndexCondition, /* in nodes/supportnodes.h */
T_SupportRequestWFuncMonotonic /* in nodes/supportnodes.h */
+#endif /*OBSOLETE*/
} NodeTag;
+/*
+ * Used in node definitions to set extra information for gen_node_support.pl
+ *
+ * The argument is a space-separated list of attributes. The following
+ * attributes are currently used:
+ *
+ * - array_size(OTHERFIELD): This field is a dynamically allocated array with
+ * size indicated by the mentioned other field. The other field is either a
+ * scalar or a list, in which case the length of the list is used.
+ *
+ * - copy_ignore: Ignore the field for copy.
+ *
+ * - equal_ignore: Ignore the field for equality.
+ *
+ * - equal_ignore_if_zero: Ignore the field for equality if it is zero.
+ * (Otherwise, compare normally.)
+ *
+ * - readwrite_ignore: Ignore the field for read/write.
+ *
+ * Unknown attributes are ignored. Some additional attributes are used for
+ * special "hack" cases.
+ */
+#define pg_node_attr(attrs)
+
/*
* 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 f93d866548..9a9a3e841c 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -123,8 +123,8 @@ typedef struct Query
QuerySource querySource; /* where did I come from? */
- /* query identifier (can be set by plugins) */
- uint64 queryId;
+ /* query identifier (can be set by plugins); ignored for equal, might
not be set */
+ uint64 queryId pg_node_attr(equal_ignore);
bool canSetTag; /* do I set the command result
tag? */
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index b88cfb8dc0..a4fb9ce8ef 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -227,7 +227,7 @@ struct PlannerInfo
* GEQO.
*/
List *join_rel_list;
- struct HTAB *join_rel_hash;
+ struct HTAB *join_rel_hash pg_node_attr(readwrite_ignore);
/*
* When doing a dynamic-programming-style join search, join_rel_level[k]
@@ -333,12 +333,12 @@ struct PlannerInfo
* Fields filled during create_plan() for use in setrefs.c
*/
/* for GroupingFunc fixup */
- AttrNumber *grouping_map;
+ AttrNumber *grouping_map pg_node_attr(array_size(update_colnos));
/* List of MinMaxAggInfos */
List *minmax_aggs;
/* context holding PlannerInfo */
- MemoryContext planner_cxt;
+ MemoryContext planner_cxt pg_node_attr(readwrite_ignore);
Cardinality total_table_pages; /* # of pages in all non-dummy tables of
*
query */
@@ -378,8 +378,8 @@ struct PlannerInfo
* These fields are workspace for setrefs.c. Each is an array
* corresponding to glob->subplans.
*/
- bool *isAltSubplan;
- bool *isUsedSubplan;
+ bool *isAltSubplan pg_node_attr(readwrite_ignore);
+ bool *isUsedSubplan pg_node_attr(readwrite_ignore);
/* optional private data for join_search_hook, e.g., GEQO */
void *join_search_private;
@@ -747,9 +747,9 @@ typedef struct RelOptInfo
/* largest attrno of rel */
AttrNumber max_attr;
/* array indexed [min_attr .. max_attr] */
- Relids *attr_needed;
+ Relids *attr_needed pg_node_attr(readwrite_ignore);
/* array indexed [min_attr .. max_attr] */
- int32 *attr_widths;
+ int32 *attr_widths pg_node_attr(readwrite_ignore);
/* LATERAL Vars and PHVs referenced by rel */
List *lateral_vars;
/* rels that reference me laterally */
@@ -784,16 +784,16 @@ typedef struct RelOptInfo
/* join is only valid for current user */
bool useridiscurrent;
/* use "struct FdwRoutine" to avoid including fdwapi.h here */
- struct FdwRoutine *fdwroutine;
- void *fdw_private;
+ struct FdwRoutine *fdwroutine pg_node_attr(readwrite_ignore);
+ void *fdw_private pg_node_attr(readwrite_ignore);
/*
* cache space for remembering if we have proven this relation unique
*/
/* known unique for these other relid set(s) */
- List *unique_for_rels;
+ List *unique_for_rels pg_node_attr(readwrite_ignore);
/* known not unique for these set(s) */
- List *non_unique_for_rels;
+ List *non_unique_for_rels pg_node_attr(readwrite_ignore);
/*
* used by various scans and joins:
@@ -917,8 +917,8 @@ struct IndexOptInfo
Oid indexoid;
/* tablespace of index (not table) */
Oid reltablespace;
- /* back-link to index's table */
- RelOptInfo *rel;
+ /* back-link to index's table; don't print, else infinite recursion */
+ RelOptInfo *rel pg_node_attr(readwrite_ignore);
/*
* index-size statistics (from pg_class and elsewhere)
@@ -938,31 +938,34 @@ struct IndexOptInfo
/* number of key columns in index */
int nkeycolumns;
+ /*
+ * array fields aren't really worth the trouble to print
+ */
/*
* column numbers of index's attributes both key and included columns,
or
* 0
*/
- int *indexkeys;
+ int *indexkeys pg_node_attr(readwrite_ignore);
/* OIDs of collations of index columns */
- Oid *indexcollations;
+ Oid *indexcollations pg_node_attr(readwrite_ignore);
/* OIDs of operator families for columns */
- Oid *opfamily;
+ Oid *opfamily pg_node_attr(readwrite_ignore);
/* OIDs of opclass declared input data types */
- Oid *opcintype;
+ Oid *opcintype pg_node_attr(readwrite_ignore);
/* OIDs of btree opfamilies, if orderable */
- Oid *sortopfamily;
+ Oid *sortopfamily pg_node_attr(readwrite_ignore);
/* is sort order descending? */
- bool *reverse_sort;
+ bool *reverse_sort pg_node_attr(readwrite_ignore);
/* do NULLs come first in the sort order? */
- bool *nulls_first;
+ bool *nulls_first pg_node_attr(readwrite_ignore);
/* opclass-specific options for columns */
- bytea **opclassoptions;
+ bytea **opclassoptions pg_node_attr(readwrite_ignore);
/* which index cols can be returned in an index-only scan? */
- bool *canreturn;
+ bool *canreturn pg_node_attr(readwrite_ignore);
/* OID of the access method (in pg_am) */
Oid relam;
- /* expressions for non-simple index columns */
- List *indexprs;
+ /* expressions for non-simple index columns; redundant to print since
we print indextlist */
+ List *indexprs pg_node_attr(readwrite_ignore);
/* predicate if a partial index, else NIL */
List *indpred;
@@ -989,17 +992,17 @@ struct IndexOptInfo
* Remaining fields are copied from the index AM's API struct
* (IndexAmRoutine)
*/
- bool amcanorderbyop;
- bool amoptionalkey;
- bool amsearcharray;
- bool amsearchnulls;
+ bool amcanorderbyop pg_node_attr(readwrite_ignore);
+ bool amoptionalkey pg_node_attr(readwrite_ignore);
+ bool amsearcharray pg_node_attr(readwrite_ignore);
+ bool amsearchnulls pg_node_attr(readwrite_ignore);
/* does AM have amgettuple interface? */
- bool amhasgettuple;
+ bool amhasgettuple pg_node_attr(readwrite_ignore);
/* does AM have amgetbitmap interface? */
- bool amhasgetbitmap;
- bool amcanparallel;
+ bool amhasgetbitmap pg_node_attr(readwrite_ignore);
+ bool amcanparallel pg_node_attr(readwrite_ignore);
/* does AM have ammarkpos interface? */
- bool amcanmarkpos;
+ bool amcanmarkpos pg_node_attr(readwrite_ignore);
/* Rather than include amapi.h here, we declare amcostestimate like
this */
void (*amcostestimate) (); /* AM's cost estimator */
};
@@ -1027,11 +1030,11 @@ typedef struct ForeignKeyOptInfo
/* number of columns in the foreign key */
int nkeys;
/* cols in referencing table */
- AttrNumber conkey[INDEX_MAX_KEYS];
+ AttrNumber conkey[INDEX_MAX_KEYS] pg_node_attr(array_size(nkeys));
/* cols in referenced table */
- AttrNumber confkey[INDEX_MAX_KEYS];
+ AttrNumber confkey[INDEX_MAX_KEYS] pg_node_attr(array_size(nkeys));
/* PK = FK operator OIDs */
- Oid conpfeqop[INDEX_MAX_KEYS];
+ Oid conpfeqop[INDEX_MAX_KEYS]
pg_node_attr(array_size(nkeys));
/*
* Derived info about whether FK's equality conditions match the query:
@@ -1068,10 +1071,10 @@ typedef struct StatisticExtInfo
Oid statOid;
/* includes child relations */
- bool inherit;
+ bool inherit pg_node_attr(readwrite_ignore);
- /* back-link to statistic's table */
- RelOptInfo *rel;
+ /* back-link to statistic's table; don't print, infinite recursion on
plan tree dump */
+ RelOptInfo *rel pg_node_attr(readwrite_ignore);
/* statistics kind of this entry */
char kind;
@@ -1265,7 +1268,7 @@ typedef struct PathTarget
List *exprs;
/* corresponding sort/group refnos, or 0 */
- Index *sortgrouprefs;
+ Index *sortgrouprefs pg_node_attr(array_size(exprs));
/* cost of evaluating the expressions */
QualCost cost;
@@ -1343,13 +1346,13 @@ typedef struct Path
NodeTag pathtype;
/* the relation this path can build */
- RelOptInfo *parent;
+ RelOptInfo *parent pg_node_attr(path_hack1);
/* list of Vars/Exprs, cost, width */
- PathTarget *pathtarget;
+ PathTarget *pathtarget pg_node_attr(path_hack2);
/* parameterization info, or NULL if none */
- ParamPathInfo *param_info;
+ ParamPathInfo *param_info pg_node_attr(path_hack3);
/* engage parallel-aware logic? */
bool parallel_aware;
@@ -2224,6 +2227,12 @@ typedef struct LimitPath
* apply only one. We mark clauses of this kind by setting parent_ec to
* point to the generating EquivalenceClass. Multiple clauses with the same
* parent_ec in the same join are redundant.
+ *
+ * Most fields are ignored for equality, since they may not be set yet, and
+ * should be derivable from the clause anyway.
+ *
+ * parent_ec, left_ec, right_ec are not printed, lest it lead to infinite
+ * recursion in plan tree dump.
*/
typedef struct RestrictInfo
@@ -2240,22 +2249,22 @@ typedef struct RestrictInfo
bool outerjoin_delayed;
/* see comment above */
- bool can_join;
+ bool can_join pg_node_attr(equal_ignore);
/* see comment above */
- bool pseudoconstant;
+ bool pseudoconstant pg_node_attr(equal_ignore);
/* true if known to contain no leaked Vars */
- bool leakproof;
+ bool leakproof pg_node_attr(equal_ignore);
/* to indicate if clause contains any volatile functions. */
- VolatileFunctionStatus has_volatile;
+ VolatileFunctionStatus has_volatile pg_node_attr(equal_ignore);
/* see comment above */
Index security_level;
/* 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;
@@ -2270,84 +2279,89 @@ typedef struct RestrictInfo
* Relids in the left/right side of the clause. These fields are set
for
* any binary opclause.
*/
- Relids left_relids;
- Relids right_relids;
+ Relids left_relids pg_node_attr(equal_ignore);
+ Relids right_relids pg_node_attr(equal_ignore);
/*
* Modified clause with RestrictInfos. This field is NULL unless clause
* is an OR clause.
*/
- Expr *orclause;
+ Expr *orclause pg_node_attr(equal_ignore);
/*
* Generating EquivalenceClass. This field is NULL unless clause is
* potentially redundant.
*/
- EquivalenceClass *parent_ec;
+ EquivalenceClass *parent_ec pg_node_attr(equal_ignore readwrite_ignore);
/*
* cache space for cost and selectivity
*/
/* eval cost of clause; -1 if not yet set */
- QualCost eval_cost;
+ QualCost eval_cost pg_node_attr(equal_ignore);
/*
* selectivity for "normal" (JOIN_INNER) semantics; -1 if not yet set;
>1
* means a redundant clause
*/
- Selectivity norm_selec;
+ Selectivity norm_selec pg_node_attr(equal_ignore);
/* selectivity for outer join semantics; -1 if not yet set */
- Selectivity outer_selec;
+ Selectivity outer_selec pg_node_attr(equal_ignore);
/*
* opfamilies containing clause operator; valid if clause is
* mergejoinable, else NIL
*/
- List *mergeopfamilies;
+ List *mergeopfamilies pg_node_attr(equal_ignore);
/*
* cache space for mergeclause processing; NULL if not yet set
*/
/* EquivalenceClass containing lefthand */
- EquivalenceClass *left_ec;
+ EquivalenceClass *left_ec pg_node_attr(equal_ignore readwrite_ignore);
/* EquivalenceClass containing righthand */
- EquivalenceClass *right_ec;
+ EquivalenceClass *right_ec pg_node_attr(equal_ignore readwrite_ignore);
/* EquivalenceMember for lefthand */
- EquivalenceMember *left_em;
+ EquivalenceMember *left_em pg_node_attr(equal_ignore);
/* EquivalenceMember for righthand */
- EquivalenceMember *right_em;
- /* list of MergeScanSelCache structs */
- List *scansel_cache;
+ EquivalenceMember *right_em pg_node_attr(equal_ignore);
+
+ /*
+ * List of MergeScanSelCache structs. Those aren't Nodes, so hard to
+ * copy. Ignoring it will have the effect that copying will just reset
+ * the cache.
+ */
+ List *scansel_cache pg_node_attr(copy_ignore equal_ignore);
/*
* transient workspace for use while considering a specific join path;
T =
* outer var on left, F = on right
*/
- bool outer_is_left;
+ bool outer_is_left pg_node_attr(equal_ignore);
/*
* copy of clause operator; valid if clause is hashjoinable, else
* InvalidOid
*/
- Oid hashjoinoperator;
+ Oid hashjoinoperator pg_node_attr(equal_ignore);
/*
* cache space for hashclause processing; -1 if not yet set
*/
/* avg bucketsize of left side */
- Selectivity left_bucketsize;
+ Selectivity left_bucketsize pg_node_attr(equal_ignore);
/* avg bucketsize of right side */
- Selectivity right_bucketsize;
+ Selectivity right_bucketsize pg_node_attr(equal_ignore);
/* left side's most common val's freq */
- Selectivity left_mcvfreq;
+ Selectivity left_mcvfreq pg_node_attr(equal_ignore);
/* right side's most common val's freq */
- Selectivity right_mcvfreq;
+ Selectivity right_mcvfreq pg_node_attr(equal_ignore);
/* 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;
/*
@@ -2397,6 +2411,17 @@ typedef struct MergeScanSelCache
* Although the planner treats this as an expression node type, it is not
* recognized by the parser or executor, so we declare it here rather than
* in primnodes.h.
+ *
+ * We intentionally do not compare phexpr. Two PlaceHolderVars with the
+ * same ID and levelsup should be considered equal even if the contained
+ * expressions have managed to mutate to different states. This will
+ * happen during final plan construction when there are nested PHVs, since
+ * the inner PHV will get replaced by a Param in some copies of the outer
+ * PHV. Another way in which it can happen is that initplan sublinks
+ * could get replaced by differently-numbered Params when sublink folding
+ * is done. (The end result of such a situation would be some
+ * unreferenced initplans, which is annoying but not really a problem.) On
+ * the same reasoning, there is no need to examine phrels.
*/
typedef struct PlaceHolderVar
@@ -2404,10 +2429,10 @@ typedef struct PlaceHolderVar
Expr xpr;
/* the represented expression */
- Expr *phexpr;
+ Expr *phexpr pg_node_attr(equal_ignore);
/* base relids syntactically within expr src */
- Relids phrels;
+ Relids phrels pg_node_attr(equal_ignore);
/* ID for PHV (unique within planner run) */
Index phid;
@@ -2572,7 +2597,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;
+ AttrNumber *parent_colnos pg_node_attr(array_size(num_child_cols));
/*
* We store the parent table's OID here for inheritance, or InvalidOid
for
@@ -2643,7 +2668,7 @@ typedef struct PlaceHolderInfo
/* ID for PH (unique within planner run) */
Index phid;
- /* copy of PlaceHolderVar tree */
+ /* copy of PlaceHolderVar tree (should be redundant for comparison,
could be ignored) */
PlaceHolderVar *ph_var;
/* lowest level we can evaluate value at */
@@ -2677,8 +2702,8 @@ typedef struct MinMaxAggInfo
/* expression we are aggregating on */
Expr *target;
- /* modified "root" for planning the subquery */
- PlannerInfo *subroot;
+ /* modified "root" for planning the subquery; not printed, too large,
not interesting enough */
+ PlannerInfo *subroot pg_node_attr(readwrite_ignore);
/* access path for subquery */
Path *path;
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index d5c0ebe859..04f01d5e8f 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -286,16 +286,16 @@ typedef struct MergeAppend
int numCols;
/* their indexes in the target list */
- AttrNumber *sortColIdx;
+ AttrNumber *sortColIdx pg_node_attr(array_size(numCols));
/* OIDs of operators to sort them by */
- Oid *sortOperators;
+ Oid *sortOperators pg_node_attr(array_size(numCols));
/* OIDs of collations */
- Oid *collations;
+ Oid *collations pg_node_attr(array_size(numCols));
/* NULLS FIRST/LAST directions */
- bool *nullsFirst;
+ bool *nullsFirst pg_node_attr(array_size(numCols));
/* Info for run-time subplan pruning; NULL if we're not doing that */
struct PartitionPruneInfo *part_prune_info;
@@ -322,11 +322,11 @@ typedef struct RecursiveUnion
int numCols;
/* their indexes in the target list */
- AttrNumber *dupColIdx;
+ AttrNumber *dupColIdx pg_node_attr(array_size(numCols));
/* equality operators to compare with */
- Oid *dupOperators;
- Oid *dupCollations;
+ Oid *dupOperators pg_node_attr(array_size(numCols));
+ Oid *dupCollations pg_node_attr(array_size(numCols));
/* estimated number of groups in input */
long numGroups;
@@ -812,16 +812,16 @@ typedef struct MergeJoin
/* these are arrays, but have the same length as the mergeclauses list:
*/
/* per-clause OIDs of btree opfamilies */
- Oid *mergeFamilies;
+ Oid *mergeFamilies
pg_node_attr(array_size(mergeclauses));
/* per-clause OIDs of collations */
- Oid *mergeCollations;
+ Oid *mergeCollations
pg_node_attr(array_size(mergeclauses));
/* per-clause ordering (ASC or DESC) */
- int *mergeStrategies;
+ int *mergeStrategies
pg_node_attr(array_size(mergeclauses));
/* per-clause nulls ordering */
- bool *mergeNullsFirst;
+ bool *mergeNullsFirst pg_node_attr(array_size(mergeclauses));
} MergeJoin;
/* ----------------
@@ -863,10 +863,10 @@ typedef struct Memoize
int numKeys;
/* hash operators for each key */
- Oid *hashOperators;
+ Oid *hashOperators pg_node_attr(array_size(numKeys));
/* collations for each key */
- Oid *collations;
+ Oid *collations pg_node_attr(array_size(numKeys));
/* cache keys in the form of exprs containing parameters */
List *param_exprs;
@@ -905,16 +905,16 @@ typedef struct Sort
int numCols;
/* their indexes in the target list */
- AttrNumber *sortColIdx;
+ AttrNumber *sortColIdx pg_node_attr(array_size(numCols));
/* OIDs of operators to sort them by */
- Oid *sortOperators;
+ Oid *sortOperators pg_node_attr(array_size(numCols));
/* OIDs of collations */
- Oid *collations;
+ Oid *collations pg_node_attr(array_size(numCols));
/* NULLS FIRST/LAST directions */
- bool *nullsFirst;
+ bool *nullsFirst pg_node_attr(array_size(numCols));
} Sort;
/* ----------------
@@ -941,11 +941,11 @@ typedef struct Group
int numCols;
/* their indexes in the target list */
- AttrNumber *grpColIdx;
+ AttrNumber *grpColIdx pg_node_attr(array_size(numCols));
/* equality operators to compare with */
- Oid *grpOperators;
- Oid *grpCollations;
+ Oid *grpOperators pg_node_attr(array_size(numCols));
+ Oid *grpCollations pg_node_attr(array_size(numCols));
} Group;
/* ---------------
@@ -976,11 +976,11 @@ typedef struct Agg
int numCols;
/* their indexes in the target list */
- AttrNumber *grpColIdx;
+ AttrNumber *grpColIdx pg_node_attr(array_size(numCols));
/* equality operators to compare with */
- Oid *grpOperators;
- Oid *grpCollations;
+ Oid *grpOperators pg_node_attr(array_size(numCols));
+ Oid *grpCollations pg_node_attr(array_size(numCols));
/* estimated number of groups in input */
long numGroups;
@@ -1015,25 +1015,25 @@ typedef struct WindowAgg
int partNumCols;
/* their indexes in the target list */
- AttrNumber *partColIdx;
+ AttrNumber *partColIdx pg_node_attr(array_size(partNumCols));
/* equality operators for partition columns */
- Oid *partOperators;
+ Oid *partOperators pg_node_attr(array_size(partNumCols));
/* collations for partition columns */
- Oid *partCollations;
+ Oid *partCollations
pg_node_attr(array_size(partNumCols));
/* number of columns in ordering clause */
int ordNumCols;
/* their indexes in the target list */
- AttrNumber *ordColIdx;
+ AttrNumber *ordColIdx pg_node_attr(array_size(ordNumCols));
/* equality operators for ordering columns */
- Oid *ordOperators;
+ Oid *ordOperators pg_node_attr(array_size(ordNumCols));
/* collations for ordering columns */
- Oid *ordCollations;
+ Oid *ordCollations pg_node_attr(array_size(ordNumCols));
/* frame_clause options, see WindowDef */
int frameOptions;
@@ -1086,13 +1086,13 @@ typedef struct Unique
int numCols;
/* their indexes in the target list */
- AttrNumber *uniqColIdx;
+ AttrNumber *uniqColIdx pg_node_attr(array_size(numCols));
/* equality operators to compare with */
- Oid *uniqOperators;
+ Oid *uniqOperators pg_node_attr(array_size(numCols));
/* collations for equality comparisons */
- Oid *uniqCollations;
+ Oid *uniqCollations pg_node_attr(array_size(numCols));
} Unique;
/* ------------
@@ -1137,16 +1137,16 @@ typedef struct GatherMerge
int numCols;
/* their indexes in the target list */
- AttrNumber *sortColIdx;
+ AttrNumber *sortColIdx pg_node_attr(array_size(numCols));
/* OIDs of operators to sort them by */
- Oid *sortOperators;
+ Oid *sortOperators pg_node_attr(array_size(numCols));
/* OIDs of collations */
- Oid *collations;
+ Oid *collations pg_node_attr(array_size(numCols));
/* NULLS FIRST/LAST directions */
- bool *nullsFirst;
+ bool *nullsFirst pg_node_attr(array_size(numCols));
/*
* param id's of initplans which are referred at gather merge or one of
@@ -1197,11 +1197,11 @@ typedef struct SetOp
int numCols;
/* their indexes in the target list */
- AttrNumber *dupColIdx;
+ AttrNumber *dupColIdx pg_node_attr(array_size(numCols));
/* equality operators to compare with */
- Oid *dupOperators;
- Oid *dupCollations;
+ Oid *dupOperators pg_node_attr(array_size(numCols));
+ Oid *dupCollations pg_node_attr(array_size(numCols));
/* where is the flag column, if any */
AttrNumber flagColIdx;
@@ -1253,13 +1253,13 @@ typedef struct Limit
int uniqNumCols;
/* their indexes in the target list */
- AttrNumber *uniqColIdx;
+ AttrNumber *uniqColIdx pg_node_attr(array_size(uniqNumCols));
/* equality operators to compare with */
- Oid *uniqOperators;
+ Oid *uniqOperators pg_node_attr(array_size(uniqNumCols));
/* collations for equality comparisons */
- Oid *uniqCollations;
+ Oid *uniqCollations
pg_node_attr(array_size(uniqNumCols));
} Limit;
@@ -1425,13 +1425,13 @@ typedef struct PartitionedRelPruneInfo
int nparts;
/* subplan index by partition index, or -1 */
- int *subplan_map;
+ int *subplan_map pg_node_attr(array_size(nparts));
/* subpart index by partition index, or -1 */
- int *subpart_map;
+ int *subpart_map pg_node_attr(array_size(nparts));
/* relation OID by partition index, or 0 */
- Oid *relid_map;
+ Oid *relid_map pg_node_attr(array_size(nparts));
/*
* 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 732c00c098..f5d756d49f 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -64,8 +64,9 @@ typedef struct RangeVar
{
NodeTag type;
- /* the catalog (database) name, or NULL */
- char *catalogname;
+ /* the catalog (database) name, or NULL; ignored for read/write, since
it
+ * is presently not semantically meaningful */
+ char *catalogname pg_node_attr(readwrite_ignore);
/* the schema name, or NULL */
char *schemaname;
@@ -233,10 +234,15 @@ typedef struct Var
*/
Index varlevelsup;
+ /*
+ * varnosyn/varattnosyn are ignored for equality, because Vars with
+ * different syntactic identifiers are semantically the same as long as
+ * their varno/varattno match.
+ */
/* syntactic relation index (0 if unknown) */
- Index varnosyn;
+ Index varnosyn pg_node_attr(equal_ignore);
/* syntactic attribute number */
- AttrNumber varattnosyn;
+ AttrNumber varattnosyn pg_node_attr(equal_ignore);
/* token location, or -1 if unknown */
int location;
@@ -374,8 +380,8 @@ typedef struct Aggref
/* OID of collation that function should use */
Oid inputcollid;
- /* type Oid of aggregate's transition value */
- Oid aggtranstype;
+ /* type Oid of aggregate's transition value; ignored for equal since it
might not be set yet */
+ Oid aggtranstype pg_node_attr(equal_ignore);
/* type Oids of direct and aggregated args */
List *aggargtypes;
@@ -455,10 +461,10 @@ typedef struct GroupingFunc
List *args;
/* ressortgrouprefs of arguments */
- List *refs;
+ List *refs pg_node_attr(equal_ignore);
/* actual column positions set by planner */
- List *cols;
+ List *cols pg_node_attr(equal_ignore);
/* same as Aggref.agglevelsup */
Index agglevelsup;
@@ -634,7 +640,7 @@ typedef struct OpExpr
Oid opno;
/* PG_PROC OID of underlying function */
- Oid opfuncid;
+ Oid opfuncid pg_node_attr(equal_ignore_if_zero);
/* PG_TYPE OID of result value */
Oid opresulttype;
@@ -698,6 +704,10 @@ typedef OpExpr NullIfExpr;
* corresponding function and won't be used during execution. For
* non-hashtable based NOT INs, negfuncid will be set to InvalidOid. See
* convert_saop_to_hashed_saop().
+ *
+ * Similar to OpExpr, opfuncid, hashfuncid, and negfuncid are not necessarily
+ * filled in right away, so will be ignored for equality if they are not set
+ * yet.
*/
typedef struct ScalarArrayOpExpr
{
@@ -707,13 +717,13 @@ typedef struct ScalarArrayOpExpr
Oid opno;
/* PG_PROC OID of comparison function */
- Oid opfuncid;
+ Oid opfuncid pg_node_attr(equal_ignore_if_zero);
/* PG_PROC OID of hash func or InvalidOid */
- Oid hashfuncid;
+ Oid hashfuncid pg_node_attr(equal_ignore_if_zero);
/* PG_PROC OID of negator of opfuncid function or InvalidOid. See
above */
- Oid negfuncid;
+ Oid negfuncid pg_node_attr(equal_ignore_if_zero);
/* true for ANY, false for ALL */
bool useOr;
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 1896a9a06d..ece4ace51d 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 d30e8fcb11..286b5810c9 100644
--- a/src/tools/msvc/Solution.pm
+++ b/src/tools/msvc/Solution.pm
@@ -841,6 +841,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: 55f4802785f66a584c05dca40e5d9b25491674b2
--
2.36.1