1
0
mirror of https://github.com/postgres/postgres.git synced 2025-09-03 15:22:11 +03:00

Extra warnings and errors for PL/pgSQL

Infrastructure to allow
 plpgsql.extra_warnings
 plpgsql.extra_errors

Initial extra checks only for shadowed_variables

Marko Tiikkaja and Petr Jelinek
Reviewed by Simon Riggs and Pavel Stěhule
This commit is contained in:
Simon Riggs
2014-04-06 12:21:51 -04:00
parent f14a6bbedb
commit 7d8f1de1bc
7 changed files with 413 additions and 0 deletions

View File

@@ -352,6 +352,9 @@ do_compile(FunctionCallInfo fcinfo,
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;
/* only promote extra warnings and errors at CREATE FUNCTION time */
function->extra_warnings = forValidator ? plpgsql_extra_warnings : 0;
function->extra_errors = forValidator ? plpgsql_extra_errors : 0;
if (is_dml_trigger)
function->fn_is_trigger = PLPGSQL_DML_TRIGGER;
@@ -849,6 +852,9 @@ plpgsql_compile_inline(char *proc_source)
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;
/* don't do extra validation for inline code as we don't want to add spam at runtime */
function->extra_warnings = 0;
function->extra_errors = 0;
plpgsql_ns_init();
plpgsql_ns_push(func_name);

View File

@@ -727,6 +727,21 @@ decl_varname : T_WORD
$1.ident, NULL, NULL,
NULL) != NULL)
yyerror("duplicate declaration");
if (plpgsql_curr_compile->extra_warnings & PLPGSQL_XCHECK_SHADOWVAR ||
plpgsql_curr_compile->extra_errors & PLPGSQL_XCHECK_SHADOWVAR)
{
PLpgSQL_nsitem *nsi;
nsi = plpgsql_ns_lookup(plpgsql_ns_top(), false,
$1.ident, NULL, NULL, NULL);
if (nsi != NULL)
ereport(plpgsql_curr_compile->extra_errors & PLPGSQL_XCHECK_SHADOWVAR ? ERROR : WARNING,
(errcode(ERRCODE_DUPLICATE_ALIAS),
errmsg("variable \"%s\" shadows a previously defined variable",
$1.ident),
parser_errposition(@1)));
}
}
| unreserved_keyword
{
@@ -740,6 +755,21 @@ decl_varname : T_WORD
$1, NULL, NULL,
NULL) != NULL)
yyerror("duplicate declaration");
if (plpgsql_curr_compile->extra_warnings & PLPGSQL_XCHECK_SHADOWVAR ||
plpgsql_curr_compile->extra_errors & PLPGSQL_XCHECK_SHADOWVAR)
{
PLpgSQL_nsitem *nsi;
nsi = plpgsql_ns_lookup(plpgsql_ns_top(), false,
$1, NULL, NULL, NULL);
if (nsi != NULL)
ereport(plpgsql_curr_compile->extra_errors & PLPGSQL_XCHECK_SHADOWVAR ? ERROR : WARNING,
(errcode(ERRCODE_DUPLICATE_ALIAS),
errmsg("variable \"%s\" shadows a previously defined variable",
$1),
parser_errposition(@1)));
}
}
;

View File

@@ -25,6 +25,11 @@
#include "utils/lsyscache.h"
#include "utils/syscache.h"
static bool plpgsql_extra_checks_check_hook(char **newvalue, void **extra, GucSource source);
static void plpgsql_extra_warnings_assign_hook(const char *newvalue, void *extra);
static void plpgsql_extra_errors_assign_hook(const char *newvalue, void *extra);
PG_MODULE_MAGIC;
/* Custom GUC variable */
@@ -39,10 +44,89 @@ int plpgsql_variable_conflict = PLPGSQL_RESOLVE_ERROR;
bool plpgsql_print_strict_params = false;
char *plpgsql_extra_warnings_string = NULL;
char *plpgsql_extra_errors_string = NULL;
int plpgsql_extra_warnings;
int plpgsql_extra_errors;
/* Hook for plugins */
PLpgSQL_plugin **plugin_ptr = NULL;
static bool
plpgsql_extra_checks_check_hook(char **newvalue, void **extra, GucSource source)
{
char *rawstring;
List *elemlist;
ListCell *l;
int extrachecks = 0;
int *myextra;
if (pg_strcasecmp(*newvalue, "all") == 0)
extrachecks = PLPGSQL_XCHECK_ALL;
else if (pg_strcasecmp(*newvalue, "none") == 0)
extrachecks = PLPGSQL_XCHECK_NONE;
else
{
/* Need a modifiable copy of string */
rawstring = pstrdup(*newvalue);
/* Parse string into list of identifiers */
if (!SplitIdentifierString(rawstring, ',', &elemlist))
{
/* syntax error in list */
GUC_check_errdetail("List syntax is invalid.");
pfree(rawstring);
list_free(elemlist);
return false;
}
foreach(l, elemlist)
{
char *tok = (char *) lfirst(l);
if (pg_strcasecmp(tok, "shadowed_variables") == 0)
extrachecks |= PLPGSQL_XCHECK_SHADOWVAR;
else if (pg_strcasecmp(tok, "all") == 0 || pg_strcasecmp(tok, "none") == 0)
{
GUC_check_errdetail("Key word \"%s\" cannot be combined with other key words.", tok);
pfree(rawstring);
list_free(elemlist);
return false;
}
else
{
GUC_check_errdetail("Unrecognized key word: \"%s\".", tok);
pfree(rawstring);
list_free(elemlist);
return false;
}
}
pfree(rawstring);
list_free(elemlist);
}
myextra = (int *) malloc(sizeof(int));
*myextra = extrachecks;
*extra = (void *) myextra;
return true;
}
static void
plpgsql_extra_warnings_assign_hook(const char *newvalue, void *extra)
{
plpgsql_extra_warnings = *((int *) extra);
}
static void
plpgsql_extra_errors_assign_hook(const char *newvalue, void *extra)
{
plpgsql_extra_errors = *((int *) extra);
}
/*
* _PG_init() - library load-time initialization
*
@@ -76,6 +160,26 @@ _PG_init(void)
PGC_USERSET, 0,
NULL, NULL, NULL);
DefineCustomStringVariable("plpgsql.extra_warnings",
gettext_noop("List of programming constructs which should produce a warning."),
NULL,
&plpgsql_extra_warnings_string,
"none",
PGC_USERSET, GUC_LIST_INPUT,
plpgsql_extra_checks_check_hook,
plpgsql_extra_warnings_assign_hook,
NULL);
DefineCustomStringVariable("plpgsql.extra_errors",
gettext_noop("List of programming constructs which should produce an error."),
NULL,
&plpgsql_extra_errors_string,
"none",
PGC_USERSET, GUC_LIST_INPUT,
plpgsql_extra_checks_check_hook,
plpgsql_extra_errors_assign_hook,
NULL);
EmitWarningsOnPlaceholders("plpgsql");
plpgsql_HashTableInit();

View File

@@ -739,6 +739,10 @@ typedef struct PLpgSQL_function
bool print_strict_params;
/* extra checks */
int extra_warnings;
int extra_errors;
int ndatums;
PLpgSQL_datum **datums;
PLpgSQL_stmt_block *action;
@@ -881,6 +885,14 @@ extern int plpgsql_variable_conflict;
extern bool plpgsql_print_strict_params;
/* extra compile-time checks */
#define PLPGSQL_XCHECK_NONE 0
#define PLPGSQL_XCHECK_SHADOWVAR 1
#define PLPGSQL_XCHECK_ALL ((int) ~0)
extern int plpgsql_extra_warnings;
extern int plpgsql_extra_errors;
extern bool plpgsql_check_syntax;
extern bool plpgsql_DumpExecTree;