diff --git a/src/backend/nodes/gen_node_support.pl b/src/backend/nodes/gen_node_support.pl index 056530a657b..7694e04d0a2 100644 --- a/src/backend/nodes/gen_node_support.pl +++ b/src/backend/nodes/gen_node_support.pl @@ -34,6 +34,72 @@ sub elem return grep { $_ eq $x } @_; } + +# This list defines the canonical set of header files to be read by this +# script, and the order they are to be processed in. We must have a stable +# processing order, else the NodeTag enum's order will vary, with catastrophic +# consequences for ABI stability across different builds. +# +# Currently, the various build systems also have copies of this list, +# so that they can do dependency checking properly. In future we may be +# able to make this list the only copy. For now, we just check that +# it matches the list of files passed on the command line. +my @all_input_files = qw( + nodes/nodes.h + nodes/primnodes.h + nodes/parsenodes.h + nodes/pathnodes.h + nodes/plannodes.h + nodes/execnodes.h + access/amapi.h + access/sdir.h + access/tableam.h + access/tsmapi.h + commands/event_trigger.h + commands/trigger.h + executor/tuptable.h + foreign/fdwapi.h + nodes/extensible.h + nodes/lockoptions.h + nodes/replnodes.h + nodes/supportnodes.h + nodes/value.h + utils/rel.h +); + +# Nodes from these input files are automatically treated as nodetag_only. +# In the future we might add explicit pg_node_attr labeling to some of these +# files and remove them from this list, but for now this is the path of least +# resistance. +my @nodetag_only_files = qw( + nodes/execnodes.h + access/amapi.h + access/sdir.h + access/tableam.h + access/tsmapi.h + commands/event_trigger.h + commands/trigger.h + executor/tuptable.h + foreign/fdwapi.h + nodes/lockoptions.h + nodes/replnodes.h + nodes/supportnodes.h +); + +# ARM ABI STABILITY CHECK HERE: +# +# In stable branches, set $last_nodetag to the name of the last node type +# that should receive an auto-generated nodetag number, and $last_nodetag_no +# to its number. (Find these values in the last line of the current +# nodetags.h file.) The script will then complain if those values don't +# match reality, providing a cross-check that we haven't broken ABI by +# adding or removing nodetags. +# In HEAD, these variables should be left undef, since we don't promise +# ABI stability during development. + +my $last_nodetag = undef; +my $last_nodetag_no = undef; + # output file names my @output_files; @@ -90,6 +156,9 @@ my @custom_copy_equal; # Similarly for custom read/write implementations. my @custom_read_write; +# Track node types with manually assigned NodeTag numbers. +my %manual_nodetag_number; + # EquivalenceClasses are never moved, so just shallow-copy the pointer push @scalar_types, qw(EquivalenceClass* EquivalenceMember*); @@ -97,25 +166,6 @@ push @scalar_types, qw(EquivalenceClass* EquivalenceMember*); # currently not required. push @scalar_types, qw(QualCost); -# Nodes from these input files are automatically treated as nodetag_only. -# In the future we might add explicit pg_node_attr labeling to some of these -# files and remove them from this list, but for now this is the path of least -# resistance. -my @nodetag_only_files = qw( - nodes/execnodes.h - access/amapi.h - access/sdir.h - access/tableam.h - access/tsmapi.h - commands/event_trigger.h - commands/trigger.h - executor/tuptable.h - foreign/fdwapi.h - nodes/lockoptions.h - nodes/replnodes.h - nodes/supportnodes.h -); - # XXX various things we are not publishing right now to stay level # with the manual system push @no_read_write, @@ -134,8 +184,13 @@ push @no_read, qw(A_ArrayExpr A_Indices A_Indirection AlterStatsStmt TriggerTransition TypeCast TypeName WindowDef WithClause XmlSerialize); +## check that we have the expected number of files on the command line +die "wrong number of input files, expected @all_input_files\n" + if ($#ARGV != $#all_input_files); + ## read input +my $next_input_file = 0; foreach my $infile (@ARGV) { my $in_struct; @@ -150,11 +205,17 @@ foreach my $infile (@ARGV) my %my_field_types; my %my_field_attrs; + # open file with name from command line, which may have a path prefix open my $ifh, '<', $infile or die "could not open \"$infile\": $!"; # now shorten filename for use below $infile =~ s!.*src/include/!!; + # check it against next member of @all_input_files + die "wrong input file ordering, expected @all_input_files\n" + if ($infile ne $all_input_files[$next_input_file]); + $next_input_file++; + my $raw_file_content = do { local $/; <$ifh> }; # strip C comments, preserving newlines so we can count lines correctly @@ -274,6 +335,10 @@ foreach my $infile (@ARGV) # does in fact exist. push @no_read_write, $in_struct; } + elsif ($attr =~ /^nodetag_number\((\d+)\)$/) + { + $manual_nodetag_number{$in_struct} = $1; + } else { die @@ -475,14 +540,31 @@ open my $nt, '>', 'nodetags.h' . $tmpext or die $!; printf $nt $header_comment, 'nodetags.h'; -my $i = 1; +my $tagno = 0; +my $last_tag = undef; foreach my $n (@node_types, @extra_tags) { next if elem $n, @abstract_types; - print $nt "\tT_${n} = $i,\n"; - $i++; + if (defined $manual_nodetag_number{$n}) + { + # do not change $tagno or $last_tag + print $nt "\tT_${n} = $manual_nodetag_number{$n},\n"; + } + else + { + $tagno++; + $last_tag = $n; + print $nt "\tT_${n} = $tagno,\n"; + } } +# verify that last auto-assigned nodetag stays stable +die "ABI stability break: last nodetag is $last_tag not $last_nodetag\n" + if (defined $last_nodetag && $last_nodetag ne $last_tag); +die + "ABI stability break: last nodetag number is $tagno not $last_nodetag_no\n" + if (defined $last_nodetag_no && $last_nodetag_no != $tagno); + close $nt; diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index c8ed4e05524..cdd6debfa0e 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -66,6 +66,11 @@ typedef enum NodeTag * * - special_read_write: Has special treatment in outNode() and nodeRead(). * + * - nodetag_number(VALUE): assign the specified nodetag number instead of + * an auto-generated number. Typically this would only be used in stable + * branches, to give a newly-added node type a number without breaking ABI + * by changing the numbers of existing node types. + * * Node types can be supertypes of other types whether or not they are marked * abstract: if a node struct appears as the first field of another struct * type, then it is the supertype of that type. The no_copy, no_equal, and diff --git a/src/tools/RELEASE_CHANGES b/src/tools/RELEASE_CHANGES index e8de724fcd8..73b02fa2a40 100644 --- a/src/tools/RELEASE_CHANGES +++ b/src/tools/RELEASE_CHANGES @@ -107,6 +107,10 @@ Starting a New Development Cycle placeholder), "git rm" the previous one, and update release.sgml and filelist.sgml to match. +* In the newly-made branch, change src/backend/nodes/gen_node_support.pl + to enforce ABI stability of the NodeTag list (see "ARM ABI STABILITY + CHECK HERE" therein). + * Notify the private committers email list, to ensure all committers are aware of the new branch even if they're not paying close attention to pgsql-hackers.