-<!-- $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