Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit7d8f1de

Browse files
Extra warnings and errors for PL/pgSQL
Infrastructure to allow plpgsql.extra_warnings plpgsql.extra_errorsInitial extra checks only for shadowed_variablesMarko Tiikkaja and Petr JelinekReviewed by Simon Riggs and Pavel Stěhule
1 parentf14a6bb commit7d8f1de

File tree

7 files changed

+413
-0
lines changed

7 files changed

+413
-0
lines changed

‎doc/src/sgml/plpgsql.sgml

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4711,6 +4711,56 @@ a_output := a_output || $$ if v_$$ || referrer_keys.kind || $$ like '$$
47114711
</variablelist>
47124712

47134713
</sect2>
4714+
<sect2 id="plpgsql-extra-checks">
4715+
<title>Additional compile-time checks</title>
4716+
4717+
<para>
4718+
To aid the user in finding instances of simple but common problems before
4719+
they cause harm, <application>PL/PgSQL</> provides additional
4720+
<replaceable>checks</>. When enabled, depending on the configuration, they
4721+
can be used to emit either a <literal>WARNING</> or an <literal>ERROR</>
4722+
during the compilation of a function. A function which has received
4723+
a <literal>WARNING</> can be executed without producing further messages,
4724+
so you are advised to test in a separate development environment.
4725+
</para>
4726+
4727+
<para>
4728+
These additional checks are enabled through the configuration variables
4729+
<varname>plpgsql.extra_warnings</> for warnings and
4730+
<varname>plpgsql.extra_errors</> for errors. Both can be set either to
4731+
a comma-separated list of checks, <literal>"none"</> or <literal>"all"</>.
4732+
The default is <literal>"none"</>. Currently the list of available checks
4733+
includes only one:
4734+
<variablelist>
4735+
<varlistentry>
4736+
<term><varname>shadowed_variables</varname></term>
4737+
<listitem>
4738+
<para>
4739+
Checks if a declaration shadows a previously defined variable.
4740+
</para>
4741+
</listitem>
4742+
</varlistentry>
4743+
</variablelist>
4744+
4745+
The following example shows the effect of <varname>plpgsql.extra_warnings</>
4746+
set to <varname>shadowed_variables</>:
4747+
<programlisting>
4748+
SET plpgsql.extra_warnings TO 'shadowed_variables';
4749+
4750+
CREATE FUNCTION foo(f1 int) RETURNS int AS $$
4751+
DECLARE
4752+
f1 int;
4753+
BEGIN
4754+
RETURN f1;
4755+
END
4756+
$$ LANGUAGE plpgsql;
4757+
WARNING: variable "f1" shadows a previously defined variable
4758+
LINE 3: f1 int;
4759+
^
4760+
CREATE FUNCTION
4761+
</programlisting>
4762+
</para>
4763+
</sect2>
47144764
</sect1>
47154765

47164766
<!-- **** Porting from Oracle PL/SQL **** -->

‎src/pl/plpgsql/src/pl_comp.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,9 @@ do_compile(FunctionCallInfo fcinfo,
352352
function->out_param_varno=-1;/* set up for no OUT param */
353353
function->resolve_option=plpgsql_variable_conflict;
354354
function->print_strict_params=plpgsql_print_strict_params;
355+
/* only promote extra warnings and errors at CREATE FUNCTION time */
356+
function->extra_warnings=forValidator ?plpgsql_extra_warnings :0;
357+
function->extra_errors=forValidator ?plpgsql_extra_errors :0;
355358

356359
if (is_dml_trigger)
357360
function->fn_is_trigger=PLPGSQL_DML_TRIGGER;
@@ -849,6 +852,9 @@ plpgsql_compile_inline(char *proc_source)
849852
function->out_param_varno=-1;/* set up for no OUT param */
850853
function->resolve_option=plpgsql_variable_conflict;
851854
function->print_strict_params=plpgsql_print_strict_params;
855+
/* don't do extra validation for inline code as we don't want to add spam at runtime */
856+
function->extra_warnings=0;
857+
function->extra_errors=0;
852858

853859
plpgsql_ns_init();
854860
plpgsql_ns_push(func_name);

‎src/pl/plpgsql/src/pl_gram.y

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -727,6 +727,21 @@ decl_varname: T_WORD
727727
$1.ident,NULL,NULL,
728728
NULL) !=NULL)
729729
yyerror("duplicate declaration");
730+
731+
if (plpgsql_curr_compile->extra_warnings & PLPGSQL_XCHECK_SHADOWVAR ||
732+
plpgsql_curr_compile->extra_errors & PLPGSQL_XCHECK_SHADOWVAR)
733+
{
734+
PLpgSQL_nsitem *nsi;
735+
nsi =plpgsql_ns_lookup(plpgsql_ns_top(),false,
736+
$1.ident,NULL,NULL,NULL);
737+
if (nsi !=NULL)
738+
ereport(plpgsql_curr_compile->extra_errors & PLPGSQL_XCHECK_SHADOWVAR ? ERROR : WARNING,
739+
(errcode(ERRCODE_DUPLICATE_ALIAS),
740+
errmsg("variable\"%s\" shadows a previously defined variable",
741+
$1.ident),
742+
parser_errposition(@1)));
743+
}
744+
730745
}
731746
| unreserved_keyword
732747
{
@@ -740,6 +755,21 @@ decl_varname: T_WORD
740755
$1,NULL,NULL,
741756
NULL) !=NULL)
742757
yyerror("duplicate declaration");
758+
759+
if (plpgsql_curr_compile->extra_warnings & PLPGSQL_XCHECK_SHADOWVAR ||
760+
plpgsql_curr_compile->extra_errors & PLPGSQL_XCHECK_SHADOWVAR)
761+
{
762+
PLpgSQL_nsitem *nsi;
763+
nsi =plpgsql_ns_lookup(plpgsql_ns_top(),false,
764+
$1,NULL,NULL,NULL);
765+
if (nsi !=NULL)
766+
ereport(plpgsql_curr_compile->extra_errors & PLPGSQL_XCHECK_SHADOWVAR ? ERROR : WARNING,
767+
(errcode(ERRCODE_DUPLICATE_ALIAS),
768+
errmsg("variable\"%s\" shadows a previously defined variable",
769+
$1),
770+
parser_errposition(@1)));
771+
}
772+
743773
}
744774
;
745775

‎src/pl/plpgsql/src/pl_handler.c

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@
2525
#include"utils/lsyscache.h"
2626
#include"utils/syscache.h"
2727

28+
29+
staticboolplpgsql_extra_checks_check_hook(char**newvalue,void**extra,GucSourcesource);
30+
staticvoidplpgsql_extra_warnings_assign_hook(constchar*newvalue,void*extra);
31+
staticvoidplpgsql_extra_errors_assign_hook(constchar*newvalue,void*extra);
32+
2833
PG_MODULE_MAGIC;
2934

3035
/* Custom GUC variable */
@@ -39,10 +44,89 @@ intplpgsql_variable_conflict = PLPGSQL_RESOLVE_ERROR;
3944

4045
boolplpgsql_print_strict_params= false;
4146

47+
char*plpgsql_extra_warnings_string=NULL;
48+
char*plpgsql_extra_errors_string=NULL;
49+
intplpgsql_extra_warnings;
50+
intplpgsql_extra_errors;
51+
4252
/* Hook for plugins */
4353
PLpgSQL_plugin**plugin_ptr=NULL;
4454

4555

56+
staticbool
57+
plpgsql_extra_checks_check_hook(char**newvalue,void**extra,GucSourcesource)
58+
{
59+
char*rawstring;
60+
List*elemlist;
61+
ListCell*l;
62+
intextrachecks=0;
63+
int*myextra;
64+
65+
if (pg_strcasecmp(*newvalue,"all")==0)
66+
extrachecks=PLPGSQL_XCHECK_ALL;
67+
elseif (pg_strcasecmp(*newvalue,"none")==0)
68+
extrachecks=PLPGSQL_XCHECK_NONE;
69+
else
70+
{
71+
/* Need a modifiable copy of string */
72+
rawstring=pstrdup(*newvalue);
73+
74+
/* Parse string into list of identifiers */
75+
if (!SplitIdentifierString(rawstring,',',&elemlist))
76+
{
77+
/* syntax error in list */
78+
GUC_check_errdetail("List syntax is invalid.");
79+
pfree(rawstring);
80+
list_free(elemlist);
81+
return false;
82+
}
83+
84+
foreach(l,elemlist)
85+
{
86+
char*tok= (char*)lfirst(l);
87+
88+
if (pg_strcasecmp(tok,"shadowed_variables")==0)
89+
extrachecks |=PLPGSQL_XCHECK_SHADOWVAR;
90+
elseif (pg_strcasecmp(tok,"all")==0||pg_strcasecmp(tok,"none")==0)
91+
{
92+
GUC_check_errdetail("Key word \"%s\" cannot be combined with other key words.",tok);
93+
pfree(rawstring);
94+
list_free(elemlist);
95+
return false;
96+
}
97+
else
98+
{
99+
GUC_check_errdetail("Unrecognized key word: \"%s\".",tok);
100+
pfree(rawstring);
101+
list_free(elemlist);
102+
return false;
103+
}
104+
}
105+
106+
pfree(rawstring);
107+
list_free(elemlist);
108+
}
109+
110+
myextra= (int*)malloc(sizeof(int));
111+
*myextra=extrachecks;
112+
*extra= (void*)myextra;
113+
114+
return true;
115+
}
116+
117+
staticvoid
118+
plpgsql_extra_warnings_assign_hook(constchar*newvalue,void*extra)
119+
{
120+
plpgsql_extra_warnings=*((int*)extra);
121+
}
122+
123+
staticvoid
124+
plpgsql_extra_errors_assign_hook(constchar*newvalue,void*extra)
125+
{
126+
plpgsql_extra_errors=*((int*)extra);
127+
}
128+
129+
46130
/*
47131
* _PG_init()- library load-time initialization
48132
*
@@ -76,6 +160,26 @@ _PG_init(void)
76160
PGC_USERSET,0,
77161
NULL,NULL,NULL);
78162

163+
DefineCustomStringVariable("plpgsql.extra_warnings",
164+
gettext_noop("List of programming constructs which should produce a warning."),
165+
NULL,
166+
&plpgsql_extra_warnings_string,
167+
"none",
168+
PGC_USERSET,GUC_LIST_INPUT,
169+
plpgsql_extra_checks_check_hook,
170+
plpgsql_extra_warnings_assign_hook,
171+
NULL);
172+
173+
DefineCustomStringVariable("plpgsql.extra_errors",
174+
gettext_noop("List of programming constructs which should produce an error."),
175+
NULL,
176+
&plpgsql_extra_errors_string,
177+
"none",
178+
PGC_USERSET,GUC_LIST_INPUT,
179+
plpgsql_extra_checks_check_hook,
180+
plpgsql_extra_errors_assign_hook,
181+
NULL);
182+
79183
EmitWarningsOnPlaceholders("plpgsql");
80184

81185
plpgsql_HashTableInit();

‎src/pl/plpgsql/src/plpgsql.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -739,6 +739,10 @@ typedef struct PLpgSQL_function
739739

740740
boolprint_strict_params;
741741

742+
/* extra checks */
743+
intextra_warnings;
744+
intextra_errors;
745+
742746
intndatums;
743747
PLpgSQL_datum**datums;
744748
PLpgSQL_stmt_block*action;
@@ -881,6 +885,14 @@ extern intplpgsql_variable_conflict;
881885

882886
externboolplpgsql_print_strict_params;
883887

888+
/* extra compile-time checks */
889+
#definePLPGSQL_XCHECK_NONE0
890+
#definePLPGSQL_XCHECK_SHADOWVAR1
891+
#definePLPGSQL_XCHECK_ALL((int) ~0)
892+
893+
externintplpgsql_extra_warnings;
894+
externintplpgsql_extra_errors;
895+
884896
externboolplpgsql_check_syntax;
885897
externboolplpgsql_DumpExecTree;
886898

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp