-<!-- $PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.156 2010/07/29 19:34:40 petere Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.157 2010/08/09 02:25:05 tgl Exp $ -->
 
 <chapter id="plpgsql">
   <title><application>PL/pgSQL</application> - <acronym>SQL</acronym> Procedural Language</title>
                    || quote_ident(mviews.mv_name) || ' ...');
         EXECUTE 'TRUNCATE TABLE ' || quote_ident(mviews.mv_name);
         EXECUTE 'INSERT INTO '
-                   || quote_ident(mviews.mv_name) || ' ' 
+                   || quote_ident(mviews.mv_name) || ' '
                    || mviews.mv_query;
     END LOOP;
 
        <para>
         An example:
 <programlisting>
-OPEN curs1 FOR EXECUTE 'SELECT * FROM ' || quote_ident(tabname) 
+OPEN curs1 FOR EXECUTE 'SELECT * FROM ' || quote_ident(tabname)
                                         || ' WHERE col1 = $1' USING keyvalue;
 </programlisting>
         In this example, the table name is inserted into the query textually,
     The last variant of <command>RAISE</> has no parameters at all.
     This form can only be used inside a <literal>BEGIN</> block's
     <literal>EXCEPTION</> clause;
-    it causes the error currently being handled to be re-thrown to the
-    next enclosing block.
+    it causes the error currently being handled to be re-thrown.
    </para>
 
+   <note>
+    <para>
+     Before <productname>PostgreSQL</> 9.1, <command>RAISE</> without
+     parameters was interpreted as re-throwing the error from the block
+     containing the active exception handler.  Thus an <literal>EXCEPTION</>
+     clause nested within that handler could not catch it, even if the
+     <command>RAISE</> was within the nested <literal>EXCEPTION</> clause's
+     block. This was deemed surprising as well as being incompatible with
+     Oracle's PL/SQL.
+    </para>
+   </note>
+
    <para>
     If no condition name nor SQLSTATE is specified in a
     <command>RAISE EXCEPTION</command> command, the default is to use
 
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.261 2010/07/06 19:19:01 momjian Exp $
+ *       $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.262 2010/08/09 02:25:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
                        ereport(ERROR,
                                        (errcode(ERRCODE_SYNTAX_ERROR),
                                         errmsg("CONTINUE cannot be used outside a loop")));
-               else if (rc == PLPGSQL_RC_RERAISE)
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_SYNTAX_ERROR),
-                                        errmsg("RAISE without parameters cannot be used outside an exception handler")));
                else
                        ereport(ERROR,
                           (errcode(ERRCODE_S_R_E_FUNCTION_EXECUTED_NO_RETURN_STATEMENT),
                        ereport(ERROR,
                                        (errcode(ERRCODE_SYNTAX_ERROR),
                                         errmsg("CONTINUE cannot be used outside a loop")));
-               else if (rc == PLPGSQL_RC_RERAISE)
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_SYNTAX_ERROR),
-                                        errmsg("RAISE without parameters cannot be used outside an exception handler")));
                else
                        ereport(ERROR,
                           (errcode(ERRCODE_S_R_E_FUNCTION_EXECUTED_NO_RETURN_STATEMENT),
                MemoryContext oldcontext = CurrentMemoryContext;
                ResourceOwner oldowner = CurrentResourceOwner;
                ExprContext *old_eval_econtext = estate->eval_econtext;
+               ErrorData *save_cur_error = estate->cur_error;
 
                estate->err_text = gettext_noop("during statement block entry");
 
                                                                        unpack_sql_state(edata->sqlerrcode));
                                        assign_text_var(errm_var, edata->message);
 
+                                       /*
+                                        * Also set up cur_error so the error data is accessible
+                                        * inside the handler.
+                                        */
+                                       estate->cur_error = edata;
+
                                        estate->err_text = NULL;
 
                                        rc = exec_stmts(estate, exception->action);
                                        errm_var->value = (Datum) 0;
                                        errm_var->isnull = true;
 
-                                       /* re-throw error if requested by handler */
-                                       if (rc == PLPGSQL_RC_RERAISE)
-                                               ReThrowError(edata);
-
                                        break;
                                }
                        }
 
+                       /*
+                        * Restore previous state of cur_error, whether or not we executed
+                        * a handler.  This is needed in case an error got thrown from
+                        * some inner block's exception handler.
+                        */
+                       estate->cur_error = save_cur_error;
+
                        /* If no match found, re-throw the error */
                        if (e == NULL)
                                ReThrowError(edata);
                                FreeErrorData(edata);
                }
                PG_END_TRY();
+
+               Assert(save_cur_error == estate->cur_error);
        }
        else
        {
                case PLPGSQL_RC_OK:
                case PLPGSQL_RC_RETURN:
                case PLPGSQL_RC_CONTINUE:
-               case PLPGSQL_RC_RERAISE:
                        return rc;
 
                case PLPGSQL_RC_EXIT:
                                break;
 
                        case PLPGSQL_RC_RETURN:
-                       case PLPGSQL_RC_RERAISE:
                                return rc;
 
                        default:
                                break;
 
                        case PLPGSQL_RC_RETURN:
-                       case PLPGSQL_RC_RERAISE:
                                return rc;
 
                        default:
                 */
                rc = exec_stmts(estate, stmt->body);
 
-               if (rc == PLPGSQL_RC_RETURN ||
-                       rc == PLPGSQL_RC_RERAISE)
+               if (rc == PLPGSQL_RC_RETURN)
                        break;                          /* break out of the loop */
                else if (rc == PLPGSQL_RC_EXIT)
                {
        /* RAISE with no parameters: re-throw current exception */
        if (stmt->condname == NULL && stmt->message == NULL &&
                stmt->options == NIL)
-               return PLPGSQL_RC_RERAISE;
+       {
+               if (estate->cur_error != NULL)
+                       ReThrowError(estate->cur_error);
+               /* oops, we're not inside a handler */
+               ereport(ERROR,
+                               (errcode(ERRCODE_SYNTAX_ERROR),
+                                errmsg("RAISE without parameters cannot be used outside an exception handler")));
+       }
 
        if (stmt->condname)
        {
 
        estate->rettupdesc = NULL;
        estate->exitlabel = NULL;
+       estate->cur_error = NULL;
 
        estate->tuple_store = NULL;
        if (rsi)
 
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.130 2010/02/26 02:01:35 momjian Exp $
+ *       $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.131 2010/08/09 02:25:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
        PLPGSQL_RC_OK,
        PLPGSQL_RC_EXIT,
        PLPGSQL_RC_RETURN,
-       PLPGSQL_RC_CONTINUE,
-       PLPGSQL_RC_RERAISE
+       PLPGSQL_RC_CONTINUE
 };
 
 /* ----------
        TupleDesc       rettupdesc;
        char       *exitlabel;          /* the "target" label of the current EXIT or
                                                                 * CONTINUE stmt, if any */
+       ErrorData  *cur_error;          /* current exception handler's error */
 
        Tuplestorestate *tuple_store;           /* SRFs accumulate results here */
        MemoryContext tuple_store_cxt;
 
 select raise_test2(10);
 ERROR:  too few parameters specified for RAISE
 CONTEXT:  PL/pgSQL function "raise_test2" line 3 at RAISE
+-- Test re-RAISE inside a nested exception block.  This case is allowed
+-- by Oracle's PL/SQL but was handled differently by PG before 9.1.
+CREATE FUNCTION reraise_test() RETURNS void AS $$
+BEGIN
+   BEGIN
+       RAISE syntax_error;
+   EXCEPTION
+       WHEN syntax_error THEN
+           BEGIN
+               raise notice 'exception % thrown in inner block, reraising', sqlerrm;
+               RAISE;
+           EXCEPTION
+               WHEN OTHERS THEN
+                   raise notice 'RIGHT - exception % caught in inner block', sqlerrm;
+           END;
+   END;
+EXCEPTION
+   WHEN OTHERS THEN
+       raise notice 'WRONG - exception % caught in outer block', sqlerrm;
+END;
+$$ LANGUAGE plpgsql;
+SELECT reraise_test();
+NOTICE:  exception syntax_error thrown in inner block, reraising
+NOTICE:  RIGHT - exception syntax_error caught in inner block
+ reraise_test 
+--------------
+ 
+(1 row)
+
 --
 -- reject function definitions that contain malformed SQL queries at
 -- compile-time, where possible
 $$ language plpgsql;
 select raise_test();
 ERROR:  RAISE without parameters cannot be used outside an exception handler
-CONTEXT:  PL/pgSQL function "raise_test"
+CONTEXT:  PL/pgSQL function "raise_test" line 3 at RAISE
 -- check cases where implicit SQLSTATE variable could be confused with
 -- SQLSTATE as a keyword, cf bug #5524
 create or replace function raise_test() returns void as $$
 
 
 select raise_test2(10);
 
+-- Test re-RAISE inside a nested exception block.  This case is allowed
+-- by Oracle's PL/SQL but was handled differently by PG before 9.1.
+
+CREATE FUNCTION reraise_test() RETURNS void AS $$
+BEGIN
+   BEGIN
+       RAISE syntax_error;
+   EXCEPTION
+       WHEN syntax_error THEN
+           BEGIN
+               raise notice 'exception % thrown in inner block, reraising', sqlerrm;
+               RAISE;
+           EXCEPTION
+               WHEN OTHERS THEN
+                   raise notice 'RIGHT - exception % caught in inner block', sqlerrm;
+           END;
+   END;
+EXCEPTION
+   WHEN OTHERS THEN
+       raise notice 'WRONG - exception % caught in outer block', sqlerrm;
+END;
+$$ LANGUAGE plpgsql;
+
+SELECT reraise_test();
+
 --
 -- reject function definitions that contain malformed SQL queries at
 -- compile-time, where possible