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

Reply via email to