diff --git a/doc/src/sgml/plpgsql.sgml b/doc/src/sgml/plpgsql.sgml index 59ec377ea9de6f7a0b5ca601ea0474eeedc04d47..24bcf52a4f747f615bbf5476aa585697d7d0c7be 100644 --- a/doc/src/sgml/plpgsql.sgml +++ b/doc/src/sgml/plpgsql.sgml @@ -1,4 +1,4 @@ -<!-- $PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.114 2007/07/15 00:45:16 tgl Exp $ --> +<!-- $PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.115 2007/07/16 17:01:10 tgl Exp $ --> <chapter id="plpgsql"> <title><application>PL/pgSQL</application> - <acronym>SQL</acronym> Procedural Language</title> @@ -249,10 +249,23 @@ $$ LANGUAGE plpgsql; </programlisting> </para> + <note> + <para> + There is actually a hidden <quote>outer block</> surrounding the body + of any <application>PL/pgSQL</> function. This block provides the + declarations of the function's parameters (if any), as well as some + special variables such as <literal>FOUND</literal> (see + <xref linkend="plpgsql-statements-diagnostics">). The outer block is + labeled with the function's name, meaning that parameters and special + variables can be qualified with the function's name. + </para> + </note> + <para> It is important not to confuse the use of <command>BEGIN</>/<command>END</> for grouping statements in - <application>PL/pgSQL</> with the database commands for transaction + <application>PL/pgSQL</> with the similarly-named SQL commands + for transaction control. <application>PL/pgSQL</>'s <command>BEGIN</>/<command>END</> are only for grouping; they do not start or end a transaction. Functions and trigger procedures are always executed within a transaction @@ -370,6 +383,19 @@ BEGIN END; $$ LANGUAGE plpgsql; </programlisting> + </para> + + <note> + <para> + These two examples are not perfectly equivalent. In the first case, + <literal>subtotal</> could be referenced as + <literal>sales_tax.subtotal</>, but in the second case it could not. + (Had we attached a label to the block, <literal>subtotal</> could + be qualified with that label, instead.) + </para> + </note> + + <para> Some more examples: <programlisting> CREATE FUNCTION instr(varchar, integer) RETURNS integer AS $$ @@ -3618,12 +3644,12 @@ a_output := a_output || $$ if v_$$ || referrer_keys.kind || $$ like '$$ <listitem> <para> - You cannot use parameter names that are the same as columns - that are referenced in the function. Oracle allows you to do this - if you qualify the parameter name using - <literal>function_name.parameter_name</>. - In <application>PL/pgSQL</>, you can instead avoid a conflict by - qualifying the column or table name. + If a name used in a SQL command could be either a column name of a + table or a reference to a variable of the function, + <application>PL/SQL</> treats it as a column name, while + <application>PL/pgSQL</> treats it as a variable name. It's best + to avoid such ambiguities in the first place, but if necessary you + can fix them by properly qualifying the ambiguous name. (See <xref linkend="plpgsql-var-subst">.) </para> </listitem> diff --git a/src/pl/plpgsql/src/gram.y b/src/pl/plpgsql/src/gram.y index 0f41c999724b5726fad8710064ecd7df0d74cf56..795d3c4c4bd49acec5a19736d49f085893b1c5e0 100644 --- a/src/pl/plpgsql/src/gram.y +++ b/src/pl/plpgsql/src/gram.y @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.103 2007/07/15 02:15:04 tgl Exp $ + * $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.104 2007/07/16 17:01:10 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -366,7 +366,7 @@ decl_statement : decl_varname decl_const decl_datatype decl_notnull decl_defval plpgsql_ns_rename($2, $4); } | decl_varname opt_scrollable K_CURSOR - { plpgsql_ns_push(NULL); } + { plpgsql_ns_push($1.name); } decl_cursor_args decl_is_for decl_cursor_query { PLpgSQL_var *new; diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c index 0293bc3fe072ff0e3b615a1ab49afb9b48da0ab6..db150632f0555d3e8e80543220e25504540b98bc 100644 --- a/src/pl/plpgsql/src/pl_comp.c +++ b/src/pl/plpgsql/src/pl_comp.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.116 2007/06/26 16:48:09 alvherre Exp $ + * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.117 2007/07/16 17:01:10 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -306,10 +306,12 @@ do_compile(FunctionCallInfo fcinfo, error_context_stack = &plerrcontext; /* - * Initialize the compiler + * Initialize the compiler, particularly the namespace stack. The + * outermost namespace contains function parameters and other special + * variables (such as FOUND), and is named after the function itself. */ plpgsql_ns_init(); - plpgsql_ns_push(NULL); + plpgsql_ns_push(NameStr(procStruct->proname)); plpgsql_DumpExecTree = false; datums_alloc = 128; diff --git a/src/pl/plpgsql/src/pl_funcs.c b/src/pl/plpgsql/src/pl_funcs.c index 4d5eba73e3b43e7c3c96f74e6844cff37d7b1f3c..da128daa7f7f39ae0c22216a3541fadf322947c4 100644 --- a/src/pl/plpgsql/src/pl_funcs.c +++ b/src/pl/plpgsql/src/pl_funcs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.60 2007/07/15 02:15:04 tgl Exp $ + * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.61 2007/07/16 17:01:11 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -147,15 +147,14 @@ plpgsql_ns_setlocal(bool flag) * ---------- */ void -plpgsql_ns_push(char *label) +plpgsql_ns_push(const char *label) { PLpgSQL_ns *new; if (label == NULL) label = ""; - new = palloc(sizeof(PLpgSQL_ns)); - memset(new, 0, sizeof(PLpgSQL_ns)); + new = palloc0(sizeof(PLpgSQL_ns)); new->upper = ns_current; ns_current = new; @@ -224,7 +223,7 @@ plpgsql_ns_additem(int itemtype, int itemno, const char *name) * ---------- */ PLpgSQL_nsitem * -plpgsql_ns_lookup(char *name, char *label) +plpgsql_ns_lookup(const char *name, const char *label) { PLpgSQL_ns *ns; int i; @@ -236,11 +235,11 @@ plpgsql_ns_lookup(char *name, char *label) { for (ns = ns_current; ns != NULL; ns = ns->upper) { - if (!strcmp(ns->items[0]->name, label)) + if (strcmp(ns->items[0]->name, label) == 0) { for (i = 1; i < ns->items_used; i++) { - if (!strcmp(ns->items[i]->name, name)) + if (strcmp(ns->items[i]->name, name) == 0) return ns->items[i]; } return NULL; /* name not found in specified label */ @@ -254,7 +253,7 @@ plpgsql_ns_lookup(char *name, char *label) */ for (ns = ns_current; ns != NULL; ns = ns->upper) { - if (!strcmp(ns->items[0]->name, name)) + if (strcmp(ns->items[0]->name, name) == 0) return ns->items[0]; } @@ -265,7 +264,7 @@ plpgsql_ns_lookup(char *name, char *label) { for (i = 1; i < ns->items_used; i++) { - if (!strcmp(ns->items[i]->name, name)) + if (strcmp(ns->items[i]->name, name) == 0) return ns->items[i]; } if (ns_localmode) @@ -288,14 +287,13 @@ plpgsql_ns_rename(char *oldname, char *newname) int i; /* - * Lookup name in the namestack; do the lookup in the current namespace - * only. + * Lookup name in the namestack */ for (ns = ns_current; ns != NULL; ns = ns->upper) { for (i = 1; i < ns->items_used; i++) { - if (!strcmp(ns->items[i]->name, oldname)) + if (strcmp(ns->items[i]->name, oldname) == 0) { newitem = palloc(sizeof(PLpgSQL_nsitem) + strlen(newname)); newitem->itemtype = ns->items[i]->itemtype; diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h index dce7be2b1bd29fe3f09c71d45812ed6b08e13dca..6ffb8b419584faaf55dd6692d7a9bb3d7dcb0cf6 100644 --- a/src/pl/plpgsql/src/plpgsql.h +++ b/src/pl/plpgsql/src/plpgsql.h @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.89 2007/07/15 02:15:04 tgl Exp $ + * $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.90 2007/07/16 17:01:11 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -770,10 +770,10 @@ extern char *plpgsql_dstring_get(PLpgSQL_dstring *ds); */ extern void plpgsql_ns_init(void); extern bool plpgsql_ns_setlocal(bool flag); -extern void plpgsql_ns_push(char *label); +extern void plpgsql_ns_push(const char *label); extern void plpgsql_ns_pop(void); extern void plpgsql_ns_additem(int itemtype, int itemno, const char *name); -extern PLpgSQL_nsitem *plpgsql_ns_lookup(char *name, char *nsname); +extern PLpgSQL_nsitem *plpgsql_ns_lookup(const char *name, const char *nsname); extern void plpgsql_ns_rename(char *oldname, char *newname); /* ---------- diff --git a/src/test/regress/expected/plpgsql.out b/src/test/regress/expected/plpgsql.out index ddb44a72527d960033065293b05aa5127df168b5..6d97b63e5c93806a2e8bcb61b919efcf0138bcae 100644 --- a/src/test/regress/expected/plpgsql.out +++ b/src/test/regress/expected/plpgsql.out @@ -3051,3 +3051,31 @@ select * from sc_test(); (3 rows) drop function sc_test(); +-- test qualified variable names +create function pl_qual_names (param1 int) returns void as $$ +<<outerblock>> +declare + param1 int := 1; +begin + <<innerblock>> + declare + param1 int := 2; + begin + raise notice 'param1 = %', param1; + raise notice 'pl_qual_names.param1 = %', pl_qual_names.param1; + raise notice 'outerblock.param1 = %', outerblock.param1; + raise notice 'innerblock.param1 = %', innerblock.param1; + end; +end; +$$ language plpgsql; +select pl_qual_names(42); +NOTICE: param1 = 2 +NOTICE: pl_qual_names.param1 = 42 +NOTICE: outerblock.param1 = 1 +NOTICE: innerblock.param1 = 2 + pl_qual_names +--------------- + +(1 row) + +drop function pl_qual_names(int); diff --git a/src/test/regress/sql/plpgsql.sql b/src/test/regress/sql/plpgsql.sql index ee9de0a5838e142ae2057eb9132b7f0420f33d44..d1c715e8d4cc299b1aeeee8e0e553bd51614f350 100644 --- a/src/test/regress/sql/plpgsql.sql +++ b/src/test/regress/sql/plpgsql.sql @@ -2535,3 +2535,25 @@ select * from sc_test(); drop function sc_test(); +-- test qualified variable names + +create function pl_qual_names (param1 int) returns void as $$ +<<outerblock>> +declare + param1 int := 1; +begin + <<innerblock>> + declare + param1 int := 2; + begin + raise notice 'param1 = %', param1; + raise notice 'pl_qual_names.param1 = %', pl_qual_names.param1; + raise notice 'outerblock.param1 = %', outerblock.param1; + raise notice 'innerblock.param1 = %', innerblock.param1; + end; +end; +$$ language plpgsql; + +select pl_qual_names(42); + +drop function pl_qual_names(int);