Fix plpgsql's EXIT so that an EXIT without a label only matches a loop,
authorTom Lane <tgl@sss.pgh.pa.us>
Sat, 2 May 2009 17:27:57 +0000 (17:27 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sat, 2 May 2009 17:27:57 +0000 (17:27 +0000)
never a BEGIN block.  This is required for Oracle compatibility and is
also plainly stated to be the behavior by our original documentation
(up until 8.1, in which the docs were adjusted to match the code's behavior;
but actually the old docs said the correct thing and the code was wrong).

Not back-patched because this introduces an incompatibility that could
break working applications.  Requires release note.

doc/src/sgml/plpgsql.sgml
src/pl/plpgsql/src/pl_exec.c

index 30d1c64af5fc39a764b750e04d6946233a41708d..80dbf453275320f06cc123f67ea82fc5a909a05b 100644 (file)
@@ -1904,8 +1904,8 @@ END LOOP <optional> <replaceable>label</replaceable> </optional>;
       indefinitely until terminated by an <literal>EXIT</> or
       <command>RETURN</command> statement.  The optional
       <replaceable>label</replaceable> can be used by <literal>EXIT</>
-      and <literal>CONTINUE</literal> statements in nested loops to
-      specify which loop the statement should be applied to.
+      and <literal>CONTINUE</literal> statements within nested loops to
+      specify which loop those statements refer to.
      </para>
     </sect3>
 
@@ -1939,9 +1939,19 @@ EXIT <optional> <replaceable>label</replaceable> </optional> <optional> WHEN <re
 
        <para>
         <literal>EXIT</> can be used with all types of loops; it is
-        not limited to use with unconditional loops. When used with a
+        not limited to use with unconditional loops.
+       </para>
+
+       <para>
+        When used with a
         <literal>BEGIN</literal> block, <literal>EXIT</literal> passes
         control to the next statement after the end of the block.
+        Note that a label must be used for this purpose; an unlabelled
+        <literal>EXIT</literal> is never considered to match a
+        <literal>BEGIN</literal> block.  (This is a change from
+        pre-8.4 releases of <productname>PostgreSQL</productname>, which
+        would allow an unlabelled <literal>EXIT</literal> to match
+        a <literal>BEGIN</literal> block.)
        </para>
 
        <para>
@@ -1959,11 +1969,13 @@ LOOP
     EXIT WHEN count &gt; 0;  -- same result as previous example
 END LOOP;
 
+&lt;&lt;ablock&gt;&gt;
 BEGIN
     -- some computations
     IF stocks &gt; 100000 THEN
-        EXIT;  -- causes exit from the BEGIN block
+        EXIT ablock;  -- causes exit from the BEGIN block
     END IF;
+    -- computations here will be skipped when stocks &gt; 100000
 END;
 </programlisting>
        </para>
index 1ba342f20dc40e57c9da2ccfa3b9f2c63f50eaea..77d0e8fad4d13034e373b5098abe058b5a43db93 100644 (file)
@@ -1145,11 +1145,15 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block)
                        return rc;
 
                case PLPGSQL_RC_EXIT:
+                       /*
+                        * This is intentionally different from the handling of RC_EXIT
+                        * for loops: to match a block, we require a match by label.
+                        */
                        if (estate->exitlabel == NULL)
-                               return PLPGSQL_RC_OK;
+                               return PLPGSQL_RC_EXIT;
                        if (block->label == NULL)
                                return PLPGSQL_RC_EXIT;
-                       if (strcmp(block->label, estate->exitlabel))
+                       if (strcmp(block->label, estate->exitlabel) != 0)
                                return PLPGSQL_RC_EXIT;
                        estate->exitlabel = NULL;
                        return PLPGSQL_RC_OK;
@@ -1604,7 +1608,7 @@ exec_stmt_while(PLpgSQL_execstate *estate, PLpgSQL_stmt_while *stmt)
                                        return PLPGSQL_RC_OK;
                                if (stmt->label == NULL)
                                        return PLPGSQL_RC_EXIT;
-                               if (strcmp(stmt->label, estate->exitlabel))
+                               if (strcmp(stmt->label, estate->exitlabel) != 0)
                                        return PLPGSQL_RC_EXIT;
                                estate->exitlabel = NULL;
                                return PLPGSQL_RC_OK;