Commit 63176099 authored by Tom Lane's avatar Tom Lane

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.
parent 01038d4a
<!-- $PostgreSQL: pgsql/doc/src/sgml/config.sgml,v 1.232 2009/10/21 20:38:58 tgl Exp $ -->
<!-- $PostgreSQL: pgsql/doc/src/sgml/config.sgml,v 1.233 2009/11/13 22:43:39 tgl Exp $ -->
<chapter Id="runtime-config">
<title>Server Configuration</title>
......@@ -5162,8 +5162,8 @@ dynamic_library_path = 'C:\tools\postgresql;H:\my_project\lib;$libdir'
when using custom variables:
<programlisting>
custom_variable_classes = 'plr,plperl'
plr.path = '/usr/lib/R'
custom_variable_classes = 'plpgsql,plperl'
plpgsql.variable_conflict = use_variable
plperl.use_strict = true
plruby.use_strict = true # generates error: unknown class name
</programlisting>
......
This diff is collapsed.
......@@ -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
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment