ecpg: add cross-checks to parse.pl for usage of internal tables.
authorTom Lane <[email protected]>
Mon, 14 Oct 2024 19:59:13 +0000 (15:59 -0400)
committerTom Lane <[email protected]>
Mon, 14 Oct 2024 19:59:29 +0000 (15:59 -0400)
parse.pl contains several constant tables that describe tweaks
to be made to the backend grammar.  In the same spirit as
00b0e7204, add cross-checks that each table entry is used at
least once (or exactly once if that's appropriate).  This should
help catch cases where adjustments to the backend grammar cause
a table entry not to match as expected.

Per suggestion from Michael Paquier.

Discussion: https://postgr.es/m/[email protected]

src/interfaces/ecpg/preproc/parse.pl

index d5798e58ee7258cb46c857ec180dec053ec4059a..91f76e719c01435bbb4d688022c83002b60e6bfd 100644 (file)
@@ -33,7 +33,9 @@ GetOptions(
 
 
 # These hash tables define additional transformations to apply to
-# grammar rules.
+# grammar rules.  For bug-detection purposes, we count usages of
+# each hash table entry in a second hash table, and verify that
+# all the entries get used.
 
 # Substitutions to apply to tokens whenever they are seen in a rule.
 my %replace_token = (
@@ -44,6 +46,8 @@ my %replace_token = (
        'IDENT' => 'ecpg_ident',
        'PARAM' => 'ecpg_param',);
 
+my %replace_token_used;
+
 # This hash can provide a result type to override "void" for nonterminals
 # that need that, or it can specify 'ignore' to cause us to skip the rule
 # for that nonterminal.  (In either case, ecpg.trailer had better provide
@@ -68,6 +72,8 @@ my %replace_types = (
        'plassign_target' => 'ignore',
        'plassign_equals' => 'ignore',);
 
+my %replace_types_used;
+
 # This hash provides an "ignore" option or substitute expansion for any
 # rule or rule alternative.  The hash key is the same "concattokens" tag
 # used for lookup in ecpg.addons.
@@ -111,6 +117,8 @@ my %replace_line = (
          'PREPARE prepared_name prep_type_clause AS PreparableStmt',
        'var_nameColId' => 'ECPGColId');
 
+my %replace_line_used;
+
 
 # Declare assorted state variables.
 
@@ -198,6 +206,30 @@ foreach (keys %addons)
        die "addon rule $_ was matched multiple times\n" if $addons{$_}{used} > 1;
 }
 
+# Likewise cross-check that entries in our internal hash tables match something.
+foreach (keys %replace_token)
+{
+       die "replace_token entry $_ was never used\n"
+         if !defined($replace_token_used{$_});
+       # multiple use of a replace_token entry is fine
+}
+
+foreach (keys %replace_types)
+{
+       die "replace_types entry $_ was never used\n"
+         if !defined($replace_types_used{$_});
+       die "replace_types entry $_ was matched multiple times\n"
+         if $replace_types_used{$_} > 1;
+}
+
+foreach (keys %replace_line)
+{
+       die "replace_line entry $_ was never used\n"
+         if !defined($replace_line_used{$_});
+       die "replace_line entry $_ was matched multiple times\n"
+         if $replace_line_used{$_} > 1;
+}
+
 
 # Read the backend grammar.
 sub main
@@ -399,6 +431,7 @@ sub main
                        # Apply replace_token substitution if we have one.
                        if (exists $replace_token{ $arr[$fieldIndexer] })
                        {
+                               $replace_token_used{ $arr[$fieldIndexer] }++;
                                $arr[$fieldIndexer] = $replace_token{ $arr[$fieldIndexer] };
                        }
 
@@ -424,6 +457,7 @@ sub main
                                        && $replace_types{$non_term_id} eq 'ignore')
                                {
                                        # We'll ignore this nonterminal and rule altogether.
+                                       $replace_types_used{$non_term_id}++;
                                        $copymode = 0;
                                        next line;
                                }
@@ -450,6 +484,7 @@ sub main
                                          . $replace_types{$non_term_id} . ' '
                                          . $non_term_id;
                                        add_to_buffer('types', $tstr);
+                                       $replace_types_used{$non_term_id}++;
                                }
 
                                # Emit the target part of the rule.
@@ -615,8 +650,10 @@ sub emit_rule
 
        # apply replace_line substitution if any
        my $rep = $replace_line{$tag};
-       if ($rep)
+       if (defined $rep)
        {
+               $replace_line_used{$tag}++;
+
                if ($rep eq 'ignore')
                {
                        return 0;