Hi all!
It's me again, trying to find a solution to the most common mistakes I
make. This time it's accidental shadowing of variables, especially
input variables. I've wasted several hours banging my head against the
wall while shouting "HOW CAN THIS VARIABLE ALWAYS BE NULL?". I can't
believe I'm the only one. To give you a rough idea on how it works:
=# set plpgsql.warn_shadow to true;
SET
=#* create function test(in1 int, in2 int)
-#* returns table(out1 int, out2 int) as $$
$#* declare
$#* IN1 text;
$#* IN2 text;
$#* out1 text;
$#* begin
$#*
$#* declare
$#* out2 text;
$#* in1 text;
$#* begin
$#* end;
$#* end$$ language plpgsql;
WARNING: variable "in1" shadows a previously defined variable
LINE 4: IN1 text;
^
WARNING: variable "in2" shadows a previously defined variable
LINE 5: IN2 text;
^
WARNING: variable "out1" shadows a previously defined variable
LINE 6: out1 text;
^
WARNING: variable "out2" shadows a previously defined variable
LINE 10: out2 text;
^
WARNING: variable "in1" shadows a previously defined variable
LINE 11: in1 text;
^
CREATE FUNCTION
No behaviour change by default. Even setting the GUC doesn't really
change behaviour, just emits some warnings when a potentially faulty
function is compiled.
Let me know what you think so I can either cry or clean the patch up.
Regards,
Marko Tiikkaja
*** a/src/pl/plpgsql/src/pl_comp.c
--- b/src/pl/plpgsql/src/pl_comp.c
***************
*** 352,357 **** do_compile(FunctionCallInfo fcinfo,
--- 352,358 ----
function->out_param_varno = -1; /* set up for no OUT param */
function->resolve_option = plpgsql_variable_conflict;
function->print_strict_params = plpgsql_print_strict_params;
+ function->warn_shadow = plpgsql_warn_shadow;
if (is_dml_trigger)
function->fn_is_trigger = PLPGSQL_DML_TRIGGER;
*** a/src/pl/plpgsql/src/pl_gram.y
--- b/src/pl/plpgsql/src/pl_gram.y
***************
*** 335,340 **** static List *read_raise_options(void);
--- 335,341 ----
%token <keyword> K_USE_VARIABLE
%token <keyword> K_USING
%token <keyword> K_VARIABLE_CONFLICT
+ %token <keyword> K_WARN_SHADOW
%token <keyword> K_WARNING
%token <keyword> K_WHEN
%token <keyword> K_WHILE
***************
*** 364,369 **** comp_option : '#' K_OPTION K_DUMP
--- 365,379 ----
else
elog(ERROR,
"unrecognized print_strict_params option %s", $3);
}
+ | '#' K_WARN_SHADOW option_value
+ {
+ if (strcmp($3, "on") == 0)
+
plpgsql_curr_compile->warn_shadow = true;
+ else if (strcmp($3, "off") == 0)
+
plpgsql_curr_compile->warn_shadow = false;
+ else
+ elog(ERROR,
"unrecognized warn_shadow option %s", $3);
+ }
| '#' K_VARIABLE_CONFLICT K_ERROR
{
plpgsql_curr_compile->resolve_option = PLPGSQL_RESOLVE_ERROR;
***************
*** 727,732 **** decl_varname : T_WORD
--- 737,754 ----
$1.ident, NULL, NULL,
NULL) != NULL)
yyerror("duplicate
declaration");
+ if
(plpgsql_curr_compile->warn_shadow)
+ {
+ PLpgSQL_nsitem *nsi;
+ nsi =
plpgsql_ns_lookup(plpgsql_ns_top(), false,
+
$1.ident, NULL, NULL, NULL);
+ if (nsi != NULL)
+ ereport(WARNING,
+
(errcode(ERRCODE_DUPLICATE_ALIAS),
+
errmsg("variable \"%s\" shadows a previously defined variable",
+
$1.ident),
+
parser_errposition(@1)));
+ }
}
| unreserved_keyword
{
***************
*** 740,745 **** decl_varname : T_WORD
--- 762,779 ----
$1, NULL, NULL,
NULL) != NULL)
yyerror("duplicate
declaration");
+ if
(plpgsql_curr_compile->warn_shadow)
+ {
+ PLpgSQL_nsitem *nsi;
+ nsi =
plpgsql_ns_lookup(plpgsql_ns_top(), false,
+
$1, NULL, NULL, NULL);
+ if (nsi != NULL)
+ ereport(WARNING,
+
(errcode(ERRCODE_DUPLICATE_ALIAS),
+
errmsg("variable \"%s\" shadows a previously defined variable",
+
$1),
+
parser_errposition(@1)));
+ }
}
;
*** a/src/pl/plpgsql/src/pl_handler.c
--- b/src/pl/plpgsql/src/pl_handler.c
***************
*** 38,43 **** static const struct config_enum_entry
variable_conflict_options[] = {
--- 38,44 ----
int plpgsql_variable_conflict = PLPGSQL_RESOLVE_ERROR;
bool plpgsql_print_strict_params = false;
+ bool plpgsql_warn_shadow = false;
/* Hook for plugins */
PLpgSQL_plugin **plugin_ptr = NULL;
***************
*** 76,81 **** _PG_init(void)
--- 77,90 ----
PGC_USERSET, 0,
NULL, NULL, NULL);
+ DefineCustomBoolVariable("plpgsql.warn_shadow",
+ gettext_noop("Display
a warning when a variable declaration shadows another variable."),
+ NULL,
+ &plpgsql_warn_shadow,
+ false,
+ PGC_USERSET, 0,
+ NULL, NULL, NULL);
+
EmitWarningsOnPlaceholders("plpgsql");
plpgsql_HashTableInit();
*** a/src/pl/plpgsql/src/pl_scanner.c
--- b/src/pl/plpgsql/src/pl_scanner.c
***************
*** 161,166 **** static const ScanKeyword unreserved_keywords[] = {
--- 161,167 ----
PG_KEYWORD("use_column", K_USE_COLUMN, UNRESERVED_KEYWORD)
PG_KEYWORD("use_variable", K_USE_VARIABLE, UNRESERVED_KEYWORD)
PG_KEYWORD("variable_conflict", K_VARIABLE_CONFLICT, UNRESERVED_KEYWORD)
+ PG_KEYWORD("warn_shadow", K_WARN_SHADOW, UNRESERVED_KEYWORD)
PG_KEYWORD("warning", K_WARNING, UNRESERVED_KEYWORD)
};
*** a/src/pl/plpgsql/src/plpgsql.h
--- b/src/pl/plpgsql/src/plpgsql.h
***************
*** 738,743 **** typedef struct PLpgSQL_function
--- 738,744 ----
PLpgSQL_resolve_option resolve_option;
bool print_strict_params;
+ bool warn_shadow;
int ndatums;
PLpgSQL_datum **datums;
***************
*** 880,885 **** extern IdentifierLookup plpgsql_IdentifierLookup;
--- 881,887 ----
extern int plpgsql_variable_conflict;
extern bool plpgsql_print_strict_params;
+ extern bool plpgsql_warn_shadow;
extern bool plpgsql_check_syntax;
extern bool plpgsql_DumpExecTree;
*** a/src/test/regress/sql/plpgsql.sql
--- b/src/test/regress/sql/plpgsql.sql
***************
*** 2689,2694 **** end$$ language plpgsql;
--- 2689,2715 ----
select footest();
+ -- test warnings when shadowing a variable
+
+ set plpgsql.warn_shadow to true;
+
+ create function test(in1 int, in2 int)
+ returns table(out1 int, out2 int) as $$
+ declare
+ IN1 text;
+ IN2 text;
+ out1 text;
+ begin
+
+ declare
+ out2 text;
+ in1 text;
+ begin
+ end;
+ end$$ language plpgsql;
+
+ reset plpgsql.warn_shadow;
+
-- test scrollable cursor support
create function sc_test() returns setof integer as $$
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers