mirror of
https://github.com/postgres/postgres.git
synced 2025-07-30 11:03:19 +03:00
Add control knobs for plpgsql's variable resolution behavior, and make the
default be "throw error on conflict", as per discussions. The GUC variable is plpgsql.variable_conflict, with values "error", "use_variable", "use_column". The behavior can also be specified per-function by inserting one of #variable_conflict error #variable_conflict use_variable #variable_conflict use_column at the start of the function body. The 8.5 release notes will need to mention using "use_variable" to retain backward-compatible behavior, although we should encourage people to migrate to the much less mistake-prone "error" setting. Update the plpgsql documentation to match this and other recent changes.
This commit is contained in:
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.135 2009/11/12 00:13:00 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.136 2009/11/13 22:43:40 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -256,6 +256,7 @@ static List *read_raise_options(void);
|
||||
%token <keyword> K_ELSIF
|
||||
%token <keyword> K_END
|
||||
%token <keyword> K_ERRCODE
|
||||
%token <keyword> K_ERROR
|
||||
%token <keyword> K_EXCEPTION
|
||||
%token <keyword> K_EXECUTE
|
||||
%token <keyword> K_EXIT
|
||||
@ -301,7 +302,10 @@ static List *read_raise_options(void);
|
||||
%token <keyword> K_THEN
|
||||
%token <keyword> K_TO
|
||||
%token <keyword> K_TYPE
|
||||
%token <keyword> K_USE_COLUMN
|
||||
%token <keyword> K_USE_VARIABLE
|
||||
%token <keyword> K_USING
|
||||
%token <keyword> K_VARIABLE_CONFLICT
|
||||
%token <keyword> K_WARNING
|
||||
%token <keyword> K_WHEN
|
||||
%token <keyword> K_WHILE
|
||||
@ -322,6 +326,18 @@ comp_option : '#' K_OPTION K_DUMP
|
||||
{
|
||||
plpgsql_DumpExecTree = true;
|
||||
}
|
||||
| '#' K_VARIABLE_CONFLICT K_ERROR
|
||||
{
|
||||
plpgsql_curr_compile->resolve_option = PLPGSQL_RESOLVE_ERROR;
|
||||
}
|
||||
| '#' K_VARIABLE_CONFLICT K_USE_VARIABLE
|
||||
{
|
||||
plpgsql_curr_compile->resolve_option = PLPGSQL_RESOLVE_VARIABLE;
|
||||
}
|
||||
| '#' K_VARIABLE_CONFLICT K_USE_COLUMN
|
||||
{
|
||||
plpgsql_curr_compile->resolve_option = PLPGSQL_RESOLVE_COLUMN;
|
||||
}
|
||||
;
|
||||
|
||||
opt_semi :
|
||||
@ -1969,6 +1985,7 @@ unreserved_keyword :
|
||||
| K_DETAIL
|
||||
| K_DUMP
|
||||
| K_ERRCODE
|
||||
| K_ERROR
|
||||
| K_FIRST
|
||||
| K_FORWARD
|
||||
| K_HINT
|
||||
@ -1991,6 +2008,9 @@ unreserved_keyword :
|
||||
| K_SCROLL
|
||||
| K_SQLSTATE
|
||||
| K_TYPE
|
||||
| K_USE_COLUMN
|
||||
| K_USE_VARIABLE
|
||||
| K_VARIABLE_CONFLICT
|
||||
| K_WARNING
|
||||
;
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.145 2009/11/12 00:13:00 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.146 2009/11/13 22:43:42 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -351,7 +351,7 @@ do_compile(FunctionCallInfo fcinfo,
|
||||
function->fn_is_trigger = is_trigger;
|
||||
function->fn_cxt = func_cxt;
|
||||
function->out_param_varno = -1; /* set up for no OUT param */
|
||||
function->resolve_option = PLPGSQL_RESOLVE_BEFORE;
|
||||
function->resolve_option = plpgsql_variable_conflict;
|
||||
|
||||
/*
|
||||
* Initialize the compiler, particularly the namespace stack. The
|
||||
@ -782,7 +782,7 @@ plpgsql_compile_inline(char *proc_source)
|
||||
function->fn_is_trigger = false;
|
||||
function->fn_cxt = func_cxt;
|
||||
function->out_param_varno = -1; /* set up for no OUT param */
|
||||
function->resolve_option = PLPGSQL_RESOLVE_BEFORE;
|
||||
function->resolve_option = plpgsql_variable_conflict;
|
||||
|
||||
plpgsql_ns_init();
|
||||
plpgsql_ns_push(func_name);
|
||||
@ -948,7 +948,7 @@ plpgsql_pre_column_ref(ParseState *pstate, ColumnRef *cref)
|
||||
{
|
||||
PLpgSQL_expr *expr = (PLpgSQL_expr *) pstate->p_ref_hook_state;
|
||||
|
||||
if (expr->func->resolve_option == PLPGSQL_RESOLVE_BEFORE)
|
||||
if (expr->func->resolve_option == PLPGSQL_RESOLVE_VARIABLE)
|
||||
return resolve_column_ref(expr, cref);
|
||||
else
|
||||
return NULL;
|
||||
@ -963,10 +963,10 @@ plpgsql_post_column_ref(ParseState *pstate, ColumnRef *cref, Node *var)
|
||||
PLpgSQL_expr *expr = (PLpgSQL_expr *) pstate->p_ref_hook_state;
|
||||
Node *myvar;
|
||||
|
||||
if (expr->func->resolve_option == PLPGSQL_RESOLVE_BEFORE)
|
||||
if (expr->func->resolve_option == PLPGSQL_RESOLVE_VARIABLE)
|
||||
return NULL; /* we already found there's no match */
|
||||
|
||||
if (expr->func->resolve_option == PLPGSQL_RESOLVE_AFTER && var != NULL)
|
||||
if (expr->func->resolve_option == PLPGSQL_RESOLVE_COLUMN && var != NULL)
|
||||
return NULL; /* there's a table column, prefer that */
|
||||
|
||||
myvar = resolve_column_ref(expr, cref);
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_handler.c,v 1.47 2009/11/04 22:26:07 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_handler.c,v 1.48 2009/11/13 22:43:42 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -26,6 +26,17 @@
|
||||
|
||||
PG_MODULE_MAGIC;
|
||||
|
||||
/* Custom GUC variable */
|
||||
static const struct config_enum_entry variable_conflict_options[] = {
|
||||
{"error", PLPGSQL_RESOLVE_ERROR, false},
|
||||
{"use_variable", PLPGSQL_RESOLVE_VARIABLE, false},
|
||||
{"use_column", PLPGSQL_RESOLVE_COLUMN, false},
|
||||
{NULL, 0, false}
|
||||
};
|
||||
|
||||
int plpgsql_variable_conflict = PLPGSQL_RESOLVE_ERROR;
|
||||
|
||||
/* Hook for plugins */
|
||||
PLpgSQL_plugin **plugin_ptr = NULL;
|
||||
|
||||
|
||||
@ -45,6 +56,17 @@ _PG_init(void)
|
||||
|
||||
pg_bindtextdomain(TEXTDOMAIN);
|
||||
|
||||
DefineCustomEnumVariable("plpgsql.variable_conflict",
|
||||
gettext_noop("Sets handling of conflicts between PL/pgSQL variable names and table column names."),
|
||||
NULL,
|
||||
&plpgsql_variable_conflict,
|
||||
PLPGSQL_RESOLVE_ERROR,
|
||||
variable_conflict_options,
|
||||
PGC_SUSET, 0,
|
||||
NULL, NULL);
|
||||
|
||||
EmitWarningsOnPlaceholders("plpgsql");
|
||||
|
||||
plpgsql_HashTableInit();
|
||||
RegisterXactCallback(plpgsql_xact_cb, NULL);
|
||||
RegisterSubXactCallback(plpgsql_subxact_cb, NULL);
|
||||
|
@ -9,7 +9,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_scanner.c,v 1.1 2009/11/12 00:13:00 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_scanner.c,v 1.2 2009/11/13 22:43:42 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -113,6 +113,7 @@ static const ScanKeyword unreserved_keywords[] = {
|
||||
PG_KEYWORD("detail", K_DETAIL, UNRESERVED_KEYWORD)
|
||||
PG_KEYWORD("dump", K_DUMP, UNRESERVED_KEYWORD)
|
||||
PG_KEYWORD("errcode", K_ERRCODE, UNRESERVED_KEYWORD)
|
||||
PG_KEYWORD("error", K_ERROR, UNRESERVED_KEYWORD)
|
||||
PG_KEYWORD("first", K_FIRST, UNRESERVED_KEYWORD)
|
||||
PG_KEYWORD("forward", K_FORWARD, UNRESERVED_KEYWORD)
|
||||
PG_KEYWORD("hint", K_HINT, UNRESERVED_KEYWORD)
|
||||
@ -135,6 +136,9 @@ static const ScanKeyword unreserved_keywords[] = {
|
||||
PG_KEYWORD("scroll", K_SCROLL, UNRESERVED_KEYWORD)
|
||||
PG_KEYWORD("sqlstate", K_SQLSTATE, UNRESERVED_KEYWORD)
|
||||
PG_KEYWORD("type", K_TYPE, UNRESERVED_KEYWORD)
|
||||
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("warning", K_WARNING, UNRESERVED_KEYWORD)
|
||||
};
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.124 2009/11/12 00:13:00 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.125 2009/11/13 22:43:42 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -147,9 +147,9 @@ enum
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
PLPGSQL_RESOLVE_BEFORE, /* prefer plpgsql var to table column */
|
||||
PLPGSQL_RESOLVE_AFTER, /* prefer table column to plpgsql var */
|
||||
PLPGSQL_RESOLVE_ERROR /* throw error if ambiguous */
|
||||
PLPGSQL_RESOLVE_ERROR, /* throw error if ambiguous */
|
||||
PLPGSQL_RESOLVE_VARIABLE, /* prefer plpgsql var to table column */
|
||||
PLPGSQL_RESOLVE_COLUMN /* prefer table column to plpgsql var */
|
||||
} PLpgSQL_resolve_option;
|
||||
|
||||
|
||||
@ -794,6 +794,8 @@ typedef struct
|
||||
* Global variable declarations
|
||||
**********************************************************************/
|
||||
|
||||
extern int plpgsql_variable_conflict;
|
||||
|
||||
extern bool plpgsql_check_syntax;
|
||||
extern bool plpgsql_DumpExecTree;
|
||||
extern bool plpgsql_LookupIdentifiers;
|
||||
|
@ -4004,6 +4004,7 @@ select scope_test();
|
||||
|
||||
drop function scope_test();
|
||||
-- Check handling of conflicts between plpgsql vars and table columns.
|
||||
set plpgsql.variable_conflict = error;
|
||||
create function conflict_test() returns setof int8_tbl as $$
|
||||
declare r record;
|
||||
q1 bigint := 42;
|
||||
@ -4013,6 +4014,23 @@ begin
|
||||
end loop;
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
select * from conflict_test();
|
||||
ERROR: column reference "q1" is ambiguous
|
||||
LINE 1: select q1,q2 from int8_tbl
|
||||
^
|
||||
DETAIL: It could refer to either a PL/pgSQL variable or a table column.
|
||||
QUERY: select q1,q2 from int8_tbl
|
||||
CONTEXT: PL/pgSQL function "conflict_test" line 4 at FOR over SELECT rows
|
||||
create or replace function conflict_test() returns setof int8_tbl as $$
|
||||
#variable_conflict use_variable
|
||||
declare r record;
|
||||
q1 bigint := 42;
|
||||
begin
|
||||
for r in select q1,q2 from int8_tbl loop
|
||||
return next r;
|
||||
end loop;
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
select * from conflict_test();
|
||||
q1 | q2
|
||||
----+-------------------
|
||||
@ -4023,6 +4041,26 @@ select * from conflict_test();
|
||||
42 | -4567890123456789
|
||||
(5 rows)
|
||||
|
||||
create or replace function conflict_test() returns setof int8_tbl as $$
|
||||
#variable_conflict use_column
|
||||
declare r record;
|
||||
q1 bigint := 42;
|
||||
begin
|
||||
for r in select q1,q2 from int8_tbl loop
|
||||
return next r;
|
||||
end loop;
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
select * from conflict_test();
|
||||
q1 | q2
|
||||
------------------+-------------------
|
||||
123 | 456
|
||||
123 | 4567890123456789
|
||||
4567890123456789 | 123
|
||||
4567890123456789 | 4567890123456789
|
||||
4567890123456789 | -4567890123456789
|
||||
(5 rows)
|
||||
|
||||
drop function conflict_test();
|
||||
-- Check that an unreserved keyword can be used as a variable name
|
||||
create function unreserved_test() returns int as $$
|
||||
|
@ -3176,6 +3176,8 @@ drop function scope_test();
|
||||
|
||||
-- Check handling of conflicts between plpgsql vars and table columns.
|
||||
|
||||
set plpgsql.variable_conflict = error;
|
||||
|
||||
create function conflict_test() returns setof int8_tbl as $$
|
||||
declare r record;
|
||||
q1 bigint := 42;
|
||||
@ -3188,6 +3190,32 @@ $$ language plpgsql;
|
||||
|
||||
select * from conflict_test();
|
||||
|
||||
create or replace function conflict_test() returns setof int8_tbl as $$
|
||||
#variable_conflict use_variable
|
||||
declare r record;
|
||||
q1 bigint := 42;
|
||||
begin
|
||||
for r in select q1,q2 from int8_tbl loop
|
||||
return next r;
|
||||
end loop;
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
|
||||
select * from conflict_test();
|
||||
|
||||
create or replace function conflict_test() returns setof int8_tbl as $$
|
||||
#variable_conflict use_column
|
||||
declare r record;
|
||||
q1 bigint := 42;
|
||||
begin
|
||||
for r in select q1,q2 from int8_tbl loop
|
||||
return next r;
|
||||
end loop;
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
|
||||
select * from conflict_test();
|
||||
|
||||
drop function conflict_test();
|
||||
|
||||
-- Check that an unreserved keyword can be used as a variable name
|
||||
|
Reference in New Issue
Block a user