]> granicus.if.org Git - postgresql/commitdiff
Add control knobs for plpgsql's variable resolution behavior, and make the
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 13 Nov 2009 22:43:42 +0000 (22:43 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 13 Nov 2009 22:43:42 +0000 (22:43 +0000)
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.

doc/src/sgml/config.sgml
doc/src/sgml/plpgsql.sgml
src/pl/plpgsql/src/gram.y
src/pl/plpgsql/src/pl_comp.c
src/pl/plpgsql/src/pl_handler.c
src/pl/plpgsql/src/pl_scanner.c
src/pl/plpgsql/src/plpgsql.h
src/test/regress/expected/plpgsql.out
src/test/regress/sql/plpgsql.sql

index 2de93e956c6548acf472127dc052b9f05cf2aa8b..e4a79782ae372a611578d1557a21acbf93dadd19 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $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>
index 72558041461982e5f0e3b2d5bf8bf85244a0a037..6a3c8fa404b32743b33c6c9a2b203a4a01e1f33a 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.146 2009/11/10 14:22:45 momjian Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.147 2009/11/13 22:43:39 tgl Exp $ -->
 
 <chapter id="plpgsql">
   <title><application>PL/pgSQL</application> - <acronym>SQL</acronym> Procedural Language</title>
@@ -217,11 +217,11 @@ END <optional> <replaceable>label</replaceable> </optional>;
     </para>
 
     <para>
-     There are two types of comments in <application>PL/pgSQL</>. A double
-     dash (<literal>--</literal>) starts a comment that extends to the end of
-     the line. A <literal>/*</literal> starts a block comment that extends to
-     the next occurrence of <literal>*/</literal>.  Block comments nest,
-     just as in ordinary SQL.
+     Comments work the same way in <application>PL/pgSQL</> code as in
+     ordinary SQL.  A double dash (<literal>--</literal>) starts a comment
+     that extends to the end of the line. A <literal>/*</literal> starts a
+     block comment that extends to the matching occurrence of
+     <literal>*/</literal>.  Block comments nest.
     </para>
 
     <para>
@@ -327,8 +327,9 @@ arow RECORD;
       to the variable when the block is entered.  If the <literal>DEFAULT</> clause
       is not given then the variable is initialized to the
       <acronym>SQL</acronym> null value.
-      The <literal>CONSTANT</> option prevents the variable from being assigned to,
-      so that its value remains constant for the duration of the block.
+      The <literal>CONSTANT</> option prevents the variable from being
+      assigned to, so that its value will remain constant for the duration of
+      the block.
       If <literal>NOT NULL</>
       is specified, an assignment of a null value results in a run-time
       error. All variables declared as <literal>NOT NULL</>
@@ -727,7 +728,7 @@ SELECT <replaceable>expression</replaceable>
 <programlisting>
 IF x &lt; y THEN ...
 </programlisting>
-     what happens behind the scenes is
+     what happens behind the scenes is equivalent to
 <programlisting>
 PREPARE <replaceable>statement_name</>(integer, integer) AS SELECT $1 &lt; $2;
 </programlisting>
@@ -761,13 +762,17 @@ PREPARE <replaceable>statement_name</>(integer, integer) AS SELECT $1 &lt; $2;
 
     <para>
      An assignment of a value to a <application>PL/pgSQL</application>
-     variable or row/record field is written as:
+     variable is written as:
 <synopsis>
 <replaceable>variable</replaceable> := <replaceable>expression</replaceable>;
 </synopsis>
-     As explained above, the expression in such a statement is evaluated
+     As explained previously, the expression in such a statement is evaluated
      by means of an SQL <command>SELECT</> command sent to the main
-     database engine.  The expression must yield a single value.
+     database engine.  The expression must yield a single value (possibly
+     a row value, if the variable is a row or record variable).  The target
+     variable can be a simple variable (optionally qualified with a block
+     name), a field of a row or record variable, or an element of an array
+     that is a simple variable or field.
     </para>
 
     <para>
@@ -802,38 +807,12 @@ my_record.user_id := 20;
 
     <para>
      Any <application>PL/pgSQL</application> variable name appearing
-     in the command text is replaced by a parameter symbol, and then the
+     in the command text is treated as a parameter, and then the
      current value of the variable is provided as the parameter value
      at run time.  This is exactly like the processing described earlier
      for expressions; for details see <xref linkend="plpgsql-var-subst">.
-     As an example, if you write:
-<programlisting>
-DECLARE
-    key TEXT;
-    delta INTEGER;
-BEGIN
-    ...
-    UPDATE mytab SET val = val + delta WHERE id = key;
-</programlisting>
-      the command text seen by the main SQL engine will look like:
-<programlisting>
-    UPDATE mytab SET val = val + $1 WHERE id = $2;
-</programlisting>
-     Although you don't normally have to think about this, it's helpful
-     to know it when you need to make sense of syntax-error messages.
     </para>
 
-    <caution>
-     <para>
-      <application>PL/pgSQL</application> will substitute for any identifier
-      matching one of the function's declared variables; it is not bright
-      enough to know whether that's what you meant!  Thus, it is a bad idea
-      to use a variable name that is the same as any table, column, or
-      function name that you need to reference in commands within the
-      function.  For more discussion see <xref linkend="plpgsql-var-subst">.
-     </para>
-    </caution>
-
     <para>
      When executing a SQL command in this way,
      <application>PL/pgSQL</application> plans the command just once
@@ -940,7 +919,7 @@ DELETE ... RETURNING <replaceable>expressions</replaceable> INTO <optional>STRIC
     <para>
      If a row or a variable list is used as target, the query's result columns
      must exactly match the structure of the target as to number and data
-     types, or a run-time error
+     types, or else a run-time error
      occurs.  When a record variable is the target, it automatically
      configures itself to the row type of the query result columns.
     </para>
@@ -1089,7 +1068,9 @@ EXECUTE 'SELECT count(*) FROM mytable WHERE inserted_by = $1 AND inserted &lt;=
    INTO c
    USING checked_user, checked_date;
 </programlisting>
+    </para>
 
+    <para>
      Note that parameter symbols can only be used for data values
      &mdash; if you want to use dynamically determined table or column
      names, you must insert them into the command string textually.
@@ -1102,6 +1083,11 @@ EXECUTE 'SELECT count(*) FROM '
    INTO c
    USING checked_user, checked_date;
 </programlisting>
+     Another restriction on parameter symbols is that they only work in
+     <command>SELECT</>, <command>INSERT</>, <command>UPDATE</>, and
+     <command>DELETE</> commands.  In other statement
+     types (generically called utility statements), you must insert
+     values textually even if they are just data values.
     </para>
 
     <para>
@@ -1303,6 +1289,7 @@ GET DIAGNOSTICS integer_var = ROW_COUNT;
      type <type>boolean</type>.  <literal>FOUND</literal> starts out
      false within each <application>PL/pgSQL</application> function call.
      It is set by each of the following types of statements:
+
          <itemizedlist>
           <listitem>
            <para>
@@ -1363,11 +1350,17 @@ GET DIAGNOSTICS integer_var = ROW_COUNT;
           </listitem>
          </itemizedlist>
 
+     Other <application>PL/pgSQL</application> statements do not change
+     the state of <literal>FOUND</literal>.
+     Note in particular that <command>EXECUTE</command>
+     changes the output of <command>GET DIAGNOSTICS</command>, but
+     does not change <literal>FOUND</literal>.
+    </para>
+
+    <para>
      <literal>FOUND</literal> is a local variable within each
      <application>PL/pgSQL</application> function; any changes to it
-     affect only the current function.   <literal>EXECUTE</literal>
-     changes the output of <command>GET DIAGNOSTICS</command>, but
-     does not change the state of <literal>FOUND</literal>.
+     affect only the current function.
     </para>
 
    </sect2>
@@ -1450,7 +1443,7 @@ RETURN <replaceable>expression</replaceable>;
       <command>RETURN</command> with an expression terminates the
       function and returns the value of
       <replaceable>expression</replaceable> to the caller.  This form
-      is to be used for <application>PL/pgSQL</> functions that do
+      is used for <application>PL/pgSQL</> functions that do
       not return a set.
      </para>
 
@@ -2699,7 +2692,7 @@ DELETE FROM <replaceable>table</replaceable> WHERE CURRENT OF <replaceable>curso
         or deleted using the cursor to identify the row.  There are
         restrictions on what the cursor's query can be (in particular,
         no grouping) and it's best to use <literal>FOR UPDATE</> in the
-        cursor.  For additional information see the
+        cursor.  For more information see the
         <xref linkend="sql-declare" endterm="sql-declare-title">
         reference page.
        </para>
@@ -3173,9 +3166,10 @@ RAISE unique_violation USING MESSAGE = 'Duplicate user ID: ' || user_id;
      <listitem>
       <para>
        Data type array of <type>text</type>; the arguments from
-              the <command>CREATE TRIGGER</command> statement.
+       the <command>CREATE TRIGGER</command> statement.
        The index counts from 0. Invalid
-       indices (less than 0 or greater than or equal to <varname>tg_nargs</>) result in a null value.
+       indexes (less than 0 or greater than or equal to <varname>tg_nargs</>)
+       result in a null value.
       </para>
      </listitem>
     </varlistentry>
@@ -3485,139 +3479,170 @@ SELECT * FROM sales_summary_bytime;
    <title>Variable Substitution</title>
 
    <para>
-    When <application>PL/pgSQL</> prepares a SQL statement or expression
-    for execution, any <application>PL/pgSQL</application> variable name
-    appearing in the statement or expression is replaced by a parameter symbol,
-    <literal>$<replaceable>n</replaceable></literal>.  The current value
-    of the variable is then provided as the value for the parameter whenever
-    the statement or expression is executed.  As an example, consider the
-    function
+    SQL statements and expressions within a <application>PL/pgSQL</> function
+    can refer to variables and parameters of the function.  Behind the scenes,
+    <application>PL/pgSQL</> substitutes query parameters for such references.
+    Parameters will only be substituted in places where a parameter or
+    column reference is syntactically allowed.  As an extreme case, consider
+    this example of poor programming style:
 <programlisting>
-CREATE FUNCTION logfunc(logtxt text) RETURNS void AS $$
-    DECLARE
-        curtime timestamp := now();
-    BEGIN
-        INSERT INTO logtable VALUES (logtxt, curtime);
-    END;
-$$ LANGUAGE plpgsql;
-</programlisting>
-    The <command>INSERT</> statement will effectively be processed as
-<programlisting>
-PREPARE <replaceable>statement_name</>(text, timestamp) AS
-  INSERT INTO logtable VALUES ($1, $2);
+        INSERT INTO foo (foo) VALUES (foo);
 </programlisting>
-    followed on each execution by <command>EXECUTE</> with the current
-    actual values of the two variables.  (Note: here we are speaking of
-    the main SQL engine's
-    <xref linkend="sql-execute" endterm="sql-execute-title"> command,
-    not <application>PL/pgSQL</application>'s <command>EXECUTE</>.)
+    The first occurrence of <literal>foo</> must syntactically be a table
+    name, so it will not be substituted, even if the function has a variable
+    named <literal>foo</>.  The second occurrence must be the name of a
+    column of the table, so it will not be substituted either.  Only the
+    third occurrence is a candidate to be a reference to the function's
+    variable.
    </para>
 
+   <note>
+    <para>
+     <productname>PostgreSQL</productname> versions before 8.5 would try
+     to substitute the variable in all three cases, leading to syntax errors.
+    </para>
+   </note>
+
    <para>
-    <emphasis>The substitution mechanism will replace any token that matches a
-    known variable's name.</>  This poses various traps for the unwary.
-    For example, it is a bad idea
-    to use a variable name that is the same as any table or column name
-    that you need to reference in queries within the function, because
-    what you think is a table or column name will still get replaced.
-    In the above example, suppose that <structname>logtable</> has
-    column names <structfield>logtxt</> and <structfield>logtime</>,
-    and we try to write the <command>INSERT</> as
-<programlisting>
-        INSERT INTO logtable (logtxt, logtime) VALUES (logtxt, curtime);
-</programlisting>
-    This will be fed to the main SQL parser as
+    Since the names of variables are syntactically no different from the names
+    of table columns, there can be ambiguity in statements that also refer to
+    tables: is a given name meant to refer to a table column, or a variable?
+    Let's change the previous example to
 <programlisting>
-        INSERT INTO logtable ($1, logtime) VALUES ($1, $2);
+        INSERT INTO dest (col) SELECT foo + bar FROM src;
 </programlisting>
-    resulting in a syntax error like this:
-<screen>
-ERROR:  syntax error at or near "$1"
-LINE 1: INSERT INTO logtable ( $1 , logtime) VALUES ( $1 ,  $2 )
-                               ^
-QUERY:  INSERT INTO logtable ( $1 , logtime) VALUES ( $1 ,  $2 )
-CONTEXT:  SQL statement in PL/PgSQL function "logfunc2" near line 5
-</screen>
+    Here, <literal>dest</> and <literal>src</> must be table names, and
+    <literal>col</> must be a column of <literal>dest</>, but <literal>foo</>
+    and <literal>bar</> might reasonably be either variables of the function
+    or columns of <literal>src</>.
    </para>
 
    <para>
-    This example is fairly easy to diagnose, since it leads to an
-    obvious syntax error.  Much nastier are cases where the substitution
-    is syntactically permissible, since the only symptom may be misbehavior
-    of the function.  In one case, a user wrote something like this:
-<programlisting>
-    DECLARE
-        val text;
-        search_key integer;
-    BEGIN
-        ...
-        FOR val IN SELECT val FROM table WHERE key = search_key LOOP ...
-</programlisting>
-    and wondered why all his table entries seemed to be NULL.  Of course
-    what happened here was that the query became
-<programlisting>
-        SELECT $1 FROM table WHERE key = $2
-</programlisting>
-    and thus it was just an expensive way of assigning <literal>val</>'s
-    current value back to itself for each row.
+    By default, <application>PL/pgSQL</> will report an error if a name
+    in a SQL statement could refer to either a variable or a table column.
+    You can fix such a problem by renaming the variable or column,
+    or by qualifying the ambiguous reference, or by telling
+    <application>PL/pgSQL</> which interpretation to prefer.
    </para>
 
    <para>
-    A commonly used coding rule for avoiding such traps is to use a
+    The simplest solution is to rename the variable or column.
+    A common coding rule is to use a
     different naming convention for <application>PL/pgSQL</application>
-    variables than you use for table and column names.  For example,
-    if all your variables are named
+    variables than you use for column names.  For example,
+    if you consistently name function variables
     <literal>v_<replaceable>something</></literal> while none of your
-    table or column names start with <literal>v_</>, you're pretty safe.
+    column names start with <literal>v_</>, no conflicts will occur.
    </para>
 
    <para>
-    Another workaround is to use qualified (dotted) names for SQL entities.
-    For instance we could safely have written the above example as
+    Alternatively you can qualify ambiguous references to make them clear.
+    In the above example, <literal>src.foo</> would be an unambiguous reference
+    to the table column.  To create an unambiguous reference to a variable,
+    declare it in a labeled block and use the block's label
+    (see <xref linkend="plpgsql-structure">).  For example,
 <programlisting>
-        FOR val IN SELECT table.val FROM table WHERE key = search_key LOOP ...
+    &lt;&lt;block&gt;&gt;
+    DECLARE
+        foo int;
+    BEGIN
+        foo := ...;
+        INSERT INTO dest (col) SELECT block.foo + bar FROM src;
 </programlisting>
-    because <application>PL/pgSQL</application> will not substitute a
-    variable for a trailing component of a qualified name.
-    However this solution does not work in every case &mdash; you can't
-    qualify a name in an <command>INSERT</>'s column name list, for instance.
-    Another point is that record and row variable names will be matched to
-    the first components of qualified names, so a qualified SQL name is
-    still vulnerable in some cases.
-    In such cases choosing a non-conflicting variable name is the only way.
+    Here <literal>block.foo</> means the variable even if there is a column
+    <literal>foo</> in <literal>src</>.  Function parameters, as well as
+    special variables such as <literal>FOUND</>, can be qualified by the
+    function's name, because they are implicitly declared in an outer block
+    labeled with the function's name.
    </para>
 
    <para>
-    Another technique you can use is to attach a label to the block in
-    which your variables are declared, and then qualify the variable names
-    in your SQL commands (see <xref linkend="plpgsql-structure">).
-    For example,
+    Sometimes it is impractical to fix all the ambiguous references in a
+    large body of <application>PL/pgSQL</> code.  In such cases you can
+    specify that <application>PL/pgSQL</> should resolve ambiguous references
+    as the variable (which is compatible with <application>PL/pgSQL</>'s
+    behavior before <productname>PostgreSQL</productname> 8.5), or as the
+    table column (which is compatible with some other systems such as
+    <productname>Oracle</productname>).
+   </para>
+
+   <indexterm>
+     <primary><varname>plpgsql.variable_conflict</> configuration parameter</primary>
+   </indexterm>
+
+   <para>
+    To change this behavior on a system-wide basis, set the configuration
+    parameter <literal>plpgsql.variable_conflict</> to one of
+    <literal>error</>, <literal>use_variable</>, or
+    <literal>use_column</> (where <literal>error</> is the factory default).
+    This parameter affects subsequent compilations
+    of statements in <application>PL/pgSQL</> functions, but not statements
+    already compiled in the current session.  To set the parameter before
+    <application>PL/pgSQL</> has been loaded, it is necessary to have added
+    <quote><literal>plpgsql</></> to the <xref
+    linkend="guc-custom-variable-classes"> list in
+    <filename>postgresql.conf</filename>.  Because changing this setting
+    can cause unexpected changes in the behavior of <application>PL/pgSQL</>
+    functions, it can only be changed by a superuser.
+   </para>
+
+   <para>
+    You can also set the behavior on a function-by-function basis, by
+    inserting one of these special commands at the start of the function
+    text:
+<programlisting>
+#variable_conflict error
+#variable_conflict use_variable
+#variable_conflict use_column
+</programlisting>
+    These commands affect only the function they are written in, and override
+    the setting of <literal>plpgsql.variable_conflict</>.  An example is
 <programlisting>
-    &lt;&lt;pl&gt;&gt;
+CREATE FUNCTION stamp_user(id int, comment text) RETURNS void AS $$
+    #variable_conflict use_variable
     DECLARE
-        val text;
+        curtime timestamp := now();
     BEGIN
-        ...
-        UPDATE table SET col = pl.val WHERE ...
+        UPDATE users SET last_modified = curtime, comment = comment
+          WHERE users.id = id;
+    END;
+$$ LANGUAGE plpgsql;
+</programlisting>
+    In the <literal>UPDATE</> command, <literal>curtime</>, <literal>comment</>,
+    and <literal>id</> will refer to the function's variable and parameters
+    whether or not <literal>users</> has columns of those names.  Notice
+    that we had to qualify the reference to <literal>users.id</> in the
+    <literal>WHERE</> clause to make it refer to the table column.
+    But we did not have to qualify the reference to <literal>comment</>
+    as a target in the <literal>UPDATE</> list, because syntactically
+    that must be a column of <literal>users</>.  We could write the same
+    function without depending on the <literal>variable_conflict</> setting
+    in this way:
+<programlisting>
+CREATE FUNCTION stamp_user(id int, comment text) RETURNS void AS $$
+    &lt;&lt;fn&gt;&gt;
+    DECLARE
+        curtime timestamp := now();
+    BEGIN
+        UPDATE users SET last_modified = fn.curtime, comment = stamp_user.comment
+          WHERE users.id = stamp_user.id;
+    END;
+$$ LANGUAGE plpgsql;
 </programlisting>
-    This is not in itself a solution to the problem of conflicts,
-    since an unqualified name in a SQL command is still at risk of being
-    interpreted the <quote>wrong</> way.  But it is useful for clarifying
-    the intent of potentially-ambiguous code.
    </para>
 
    <para>
     Variable substitution does not happen in the command string given
     to <command>EXECUTE</> or one of its variants.  If you need to
     insert a varying value into such a command, do so as part of
-    constructing the string value, as illustrated in
+    constructing the string value, or use <literal>USING</>, as illustrated in
     <xref linkend="plpgsql-statements-executing-dyn">.
    </para>
 
    <para>
     Variable substitution currently works only in <command>SELECT</>,
     <command>INSERT</>, <command>UPDATE</>, and <command>DELETE</> commands,
-    because the main SQL engine allows parameter symbols only in these
+    because the main SQL engine allows query parameters only in these
     commands.  To use a non-constant name or value in other statement
     types (generically called utility statements), you must construct
     the utility statement as a string and <command>EXECUTE</> it.
@@ -3660,47 +3685,17 @@ CONTEXT:  SQL statement in PL/PgSQL function "logfunc2" near line 5
    </para>
 
    <para>
-    Once <application>PL/pgSQL</> has made an execution plan for a particular
-    command in a function, it will reuse that plan for the life of the
-    database connection.  This is usually a win for performance, but it
-    can cause some problems if you dynamically
-    alter your database schema. For example:
-
-<programlisting>
-CREATE FUNCTION populate() RETURNS integer AS $$
-DECLARE
-    -- declarations
-BEGIN
-    PERFORM my_function();
-END;
-$$ LANGUAGE plpgsql;
-</programlisting>
-
-    If you execute the above function, it will reference the OID for
-    <function>my_function()</function> in the execution plan produced for
-    the <command>PERFORM</command> statement. Later, if you
-    drop and recreate <function>my_function()</function>, then
-    <function>populate()</function> will not be able to find
-    <function>my_function()</function> anymore. You would then have to
-    start a new database session so that <function>populate()</function>
-    will be compiled afresh, before it will work again. You can avoid
-    this problem by using <command>CREATE OR REPLACE FUNCTION</command>
-    when updating the definition of
-    <function>my_function</function>, since when a function is
-    <quote>replaced</quote>, its OID is not changed.
+    A saved plan will be re-planned automatically if there is any schema
+    change to any table used in the query, or if any user-defined function
+    used in the query is redefined.  This makes the re-use of prepared plans
+    transparent in most cases, but there are corner cases where a stale plan
+    might be re-used.  An example is that dropping and re-creating a
+    user-defined operator won't affect already-cached plans; they'll continue
+    to call the original operator's underlying function, if that has not been
+    changed.  When necessary, the cache can be flushed by starting a fresh
+    database session.
    </para>
 
-   <note>
-    <para>
-     In <productname>PostgreSQL</productname> 8.3 and later, saved plans
-     will be replaced whenever any schema changes have occurred to any
-     tables they reference.  This eliminates one of the major disadvantages
-     of saved plans.  However, there is no such mechanism for function
-     references, and thus the above example involving a reference to a
-     deleted function is still valid.
-    </para>
-   </note>
-
    <para>
     Because <application>PL/pgSQL</application> saves execution plans
     in this way, SQL commands that appear directly in a
@@ -4046,28 +4041,19 @@ a_output := a_output || $$ if v_$$ || referrer_keys.kind || $$ like '$$
    <application>PL/pgSQL</application> are:
 
     <itemizedlist>
-     <listitem>
-      <para>
-       There are no default values for parameters in <productname>PostgreSQL</>.
-      </para>
-     </listitem>
-
-     <listitem>
-      <para>
-       You can overload function names in <productname>PostgreSQL</>. This is
-       often used to work around the lack of default parameters.
-      </para>
-     </listitem>
-
      <listitem>
       <para>
        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">.)
+       <application>PL/SQL</> treats it as a column name.  This corresponds
+       to <application>PL/pgSQL</>'s
+       <literal>plpgsql.variable_conflict</> = <literal>use_column</>
+       behavior, which is not the default,
+       as explained in <xref linkend="plpgsql-var-subst">.
+       It's often best to avoid such ambiguities in the first place,
+       but if you have to port a large amount of code that depends on
+       this behavior, setting <literal>variable_conflict</> may be the
+       best solution.
       </para>
      </listitem>
 
@@ -4537,7 +4523,8 @@ $$ LANGUAGE plpgsql;
        The exception names supported by <application>PL/pgSQL</> are
        different from Oracle's.  The set of built-in exception names
        is much larger (see <xref linkend="errcodes-appendix">).  There
-       is not currently a way to declare user-defined exception names.
+       is not currently a way to declare user-defined exception names,
+       although you can throw user-chosen SQLSTATE values instead.
       </para>
      </callout>
     </calloutlist>
index 6edd01c4d8d3c9fe3a6938c130d6b0c1a73d0b85..08d7b9162d321b45f16b8078d3ef337aa5989e61 100644 (file)
@@ -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
                                ;
 
index bc0cde6c7674009a98eb1dc7aee9d7b98bb26cb8..6cfc5195facd71d1526eba67201921bd7ebb66d9 100644 (file)
@@ -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);
index 7741308f28748730328bf120a0b96f5da690d843..4bd032986867a47154cc9b603aa4a7045d148cbc 100644 (file)
@@ -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 $
  *
  *-------------------------------------------------------------------------
  */
 
 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);
index 523a0952cb154963653ca8206562c41f2545799d..fb4abc1454f4cd5ed5909235e17521187f602694 100644 (file)
@@ -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)
 };
 
index 1c05f5d4c2f152797e748beb7244c1ee09c90df9..25d1c0369990f54154ec70768159e88ed04de0ce 100644 (file)
@@ -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;
index 877ed329a675951ae3ce4a39c671e2f04a7bebd4..e126f616fe17a962c3f81966dedfe50922a4e2cd 100644 (file)
@@ -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 $$
index f0a77469d92abe24ef60ee9ad9104d756759c866..79756f6a01cdb94bc985c83535e5700deb45d556 100644 (file)
@@ -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