ecpg: add cross-checks to parse.pl for usage of internal tables.

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/ZsLVbjsc5x5Saesg@paquier.xyz
This commit is contained in:
Tom Lane 2024-10-14 15:59:13 -04:00
parent 66ac94cdc7
commit d2f41b4621

View 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;