Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit b6bd5de

Browse files
committed
Add some error cross-checks to gen_node_support.pl.
Check that if we generate a call to copy, compare, write, or read a specific node type, that node type does have the appropriate support function. (This doesn't protect against trying to invoke nonexistent code when considering generic field types such as "Node *", but it seems like a useful check anyway.) Check that array_size() refers to a field appearing earlier in the struct. Aside from catching obvious errors like a misspelled field name, this protects against a more subtle mistake: if the size field appears later in the struct than the array field, then compare and read functions would misbehave. There is actually exactly that situation in PlannerInfo, but it's okay since we do not need compare or read functionality for that (today anyway). Discussion: https://postgr.es/m/263413.1669513145@sss.pgh.pa.us
1 parent cabfb82 commit b6bd5de

File tree

1 file changed

+49
-13
lines changed

1 file changed

+49
-13
lines changed

src/backend/nodes/gen_node_support.pl

+49-13
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,8 @@ sub elem
123123
my @no_read;
124124
# node types we don't want read/write support for
125125
my @no_read_write;
126+
# node types that have handmade read/write support
127+
my @special_read_write;
126128
# node types we don't want any support functions for, just node tags
127129
my @nodetag_only;
128130

@@ -152,9 +154,12 @@ sub elem
152154
# since we won't use its internal structure here anyway.
153155
push @node_types, qw(List);
154156
# Lists are specially treated in all four support files, too.
155-
push @no_copy, qw(List);
156-
push @no_equal, qw(List);
157-
push @no_read_write, qw(List);
157+
# (Ideally we'd mark List as "special copy/equal" not "no copy/equal".
158+
# But until there's other use-cases for that, just hot-wire the tests
159+
# that would need to distinguish.)
160+
push @no_copy, qw(List);
161+
push @no_equal, qw(List);
162+
push @special_read_write, qw(List);
158163

159164
# Nodes with custom copy/equal implementations are skipped from
160165
# .funcs.c but need case statements in .switch.c.
@@ -338,16 +343,7 @@ sub elem
338343
}
339344
elsif ($attr eq 'special_read_write')
340345
{
341-
# This attribute is called
342-
# "special_read_write" because there is
343-
# special treatment in outNode() and
344-
# nodeRead() for these nodes. For this
345-
# script, it's the same as
346-
# "no_read_write", but calling the
347-
# attribute that externally would probably
348-
# be confusing, since read/write support
349-
# does in fact exist.
350-
push @no_read_write, $in_struct;
346+
push @special_read_write, $in_struct;
351347
}
352348
elsif ($attr =~ /^nodetag_number\((\d+)\)$/)
353349
{
@@ -681,6 +677,9 @@ sub elem
681677
{
682678
" unless $struct_no_equal;
683679

680+
# track already-processed fields to support field order checks
681+
my %previous_fields;
682+
684683
# print instructions for each field
685684
foreach my $f (@{ $node_type_info{$n}->{fields} })
686685
{
@@ -697,6 +696,10 @@ sub elem
697696
if ($a =~ /^array_size\(([\w.]+)\)$/)
698697
{
699698
$array_size_field = $1;
699+
# insist that we copy or compare the array size first!
700+
die
701+
"array size field $array_size_field for field $n.$f must precede $f\n"
702+
if (!$previous_fields{$array_size_field});
700703
}
701704
elsif ($a =~ /^copy_as\(([\w.]+)\)$/)
702705
{
@@ -786,6 +789,17 @@ sub elem
786789
elsif (($t =~ /^(\w+)\*$/ or $t =~ /^struct\s+(\w+)\*$/)
787790
and elem $1, @node_types)
788791
{
792+
die
793+
"node type \"$1\" lacks copy support, which is required for struct \"$n\" field \"$f\"\n"
794+
if (elem $1, @no_copy or elem $1, @nodetag_only)
795+
and $1 ne 'List'
796+
and !$copy_ignore;
797+
die
798+
"node type \"$1\" lacks equal support, which is required for struct \"$n\" field \"$f\"\n"
799+
if (elem $1, @no_equal or elem $1, @nodetag_only)
800+
and $1 ne 'List'
801+
and !$equal_ignore;
802+
789803
print $cff "\tCOPY_NODE_FIELD($f);\n" unless $copy_ignore;
790804
print $eff "\tCOMPARE_NODE_FIELD($f);\n" unless $equal_ignore;
791805
}
@@ -809,6 +823,8 @@ sub elem
809823
die
810824
"could not handle type \"$t\" in struct \"$n\" field \"$f\"\n";
811825
}
826+
827+
$previous_fields{$f} = 1;
812828
}
813829

814830
print $cff "
@@ -851,6 +867,7 @@ sub elem
851867
next if elem $n, @abstract_types;
852868
next if elem $n, @nodetag_only;
853869
next if elem $n, @no_read_write;
870+
next if elem $n, @special_read_write;
854871

855872
my $no_read = (elem $n, @no_read);
856873

@@ -891,6 +908,11 @@ sub elem
891908
";
892909
}
893910

911+
# track already-processed fields to support field order checks
912+
# (this isn't quite redundant with the previous loop, since
913+
# we may be considering structs that lack copy/equal support)
914+
my %previous_fields;
915+
894916
# print instructions for each field
895917
foreach my $f (@{ $node_type_info{$n}->{fields} })
896918
{
@@ -906,6 +928,10 @@ sub elem
906928
if ($a =~ /^array_size\(([\w.]+)\)$/)
907929
{
908930
$array_size_field = $1;
931+
# insist that we read the array size first!
932+
die
933+
"array size field $array_size_field for field $n.$f must precede $f\n"
934+
if (!$previous_fields{$array_size_field} && !$no_read);
909935
}
910936
elsif ($a =~ /^read_as\(([\w.]+)\)$/)
911937
{
@@ -1083,6 +1109,14 @@ sub elem
10831109
elsif (($t =~ /^(\w+)\*$/ or $t =~ /^struct\s+(\w+)\*$/)
10841110
and elem $1, @node_types)
10851111
{
1112+
die
1113+
"node type \"$1\" lacks write support, which is required for struct \"$n\" field \"$f\"\n"
1114+
if (elem $1, @no_read_write or elem $1, @nodetag_only);
1115+
die
1116+
"node type \"$1\" lacks read support, which is required for struct \"$n\" field \"$f\"\n"
1117+
if (elem $1, @no_read or elem $1, @nodetag_only)
1118+
and !$no_read;
1119+
10861120
print $off "\tWRITE_NODE_FIELD($f);\n";
10871121
print $rff "\tREAD_NODE_FIELD($f);\n" unless $no_read;
10881122
}
@@ -1145,6 +1179,8 @@ sub elem
11451179
{
11461180
print $rff "\tlocal_node->$f = $read_as_field;\n" unless $no_read;
11471181
}
1182+
1183+
$previous_fields{$f} = 1;
11481184
}
11491185

11501186
print $off "}

0 commit comments

Comments
 (0)