*** a/doc/src/sgml/plpgsql.sgml --- b/doc/src/sgml/plpgsql.sgml *************** *** 2374,2379 **** SELECT merge_db(1, 'dennis'); --- 2374,2420 ---- + + Re-Raising Exceptions + + + This example shows how to re-raise an exception, which helps you + write more complex exception-handling in your functions: + + CREATE OR REPLACE FUNCTION raisetest() + RETURNS VOID + LANGUAGE plpgsql + AS $$ + BEGIN + BEGIN + RAISE syntax_error; + EXCEPTION + WHEN syntax_error THEN + BEGIN + raise notice 'exception thrown in inner block, reraising'; + RAISE; + EXCEPTION + WHEN OTHERS THEN + RAISE NOTICE 'RIGHT - exception caught in innermost block'; + END; + END; + EXCEPTION + WHEN OTHERS THEN + RAISE NOTICE 'WRONG - exception caught in outer block'; + END; + $$; + + select raisetest(); + NOTICE: exception thrown in inner block, reraising + NOTICE: RIGHT - exception caught in innermost block + raisetest + ----------- + + (1 row) + + + + *** a/src/pl/plpgsql/src/pl_exec.c --- b/src/pl/plpgsql/src/pl_exec.c *************** *** 327,336 **** plpgsql_exec_function(PLpgSQL_function *func, FunctionCallInfo fcinfo) 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), --- 327,332 ---- *************** *** 695,704 **** plpgsql_exec_trigger(PLpgSQL_function *func, 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), --- 691,696 ---- *************** *** 1132,1138 **** exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block) --- 1124,1136 ---- estate->err_text = NULL; + /* + * Set last_caught_error for the duration of the + * exception handler, so that "RAISE;" can rethrow it. + */ + estate->last_caught_error = edata; rc = exec_stmts(estate, exception->action); + estate->last_caught_error = NULL; free_var(state_var); state_var->value = (Datum) 0; *************** *** 1141,1150 **** exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block) errm_var->value = (Datum) 0; errm_var->isnull = true; - /* re-throw error if requested by handler */ - if (rc == PLPGSQL_RC_RERAISE) - ReThrowError(edata); - break; } } --- 1139,1144 ---- *************** *** 1177,1183 **** exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block) case PLPGSQL_RC_OK: case PLPGSQL_RC_RETURN: case PLPGSQL_RC_CONTINUE: - case PLPGSQL_RC_RERAISE: return rc; case PLPGSQL_RC_EXIT: --- 1171,1176 ---- *************** *** 1599,1605 **** exec_stmt_loop(PLpgSQL_execstate *estate, PLpgSQL_stmt_loop *stmt) break; case PLPGSQL_RC_RETURN: - case PLPGSQL_RC_RERAISE: return rc; default: --- 1592,1597 ---- *************** *** 1663,1669 **** exec_stmt_while(PLpgSQL_execstate *estate, PLpgSQL_stmt_while *stmt) break; case PLPGSQL_RC_RETURN: - case PLPGSQL_RC_RERAISE: return rc; default: --- 1655,1660 ---- *************** *** 1782,1789 **** exec_stmt_fori(PLpgSQL_execstate *estate, PLpgSQL_stmt_fori *stmt) */ rc = exec_stmts(estate, stmt->body); ! if (rc == PLPGSQL_RC_RETURN || ! rc == PLPGSQL_RC_RERAISE) break; /* break out of the loop */ else if (rc == PLPGSQL_RC_EXIT) { --- 1773,1779 ---- */ rc = exec_stmts(estate, stmt->body); ! if (rc == PLPGSQL_RC_RETURN) break; /* break out of the loop */ else if (rc == PLPGSQL_RC_EXIT) { *************** *** 2437,2443 **** exec_stmt_raise(PLpgSQL_execstate *estate, PLpgSQL_stmt_raise *stmt) /* RAISE with no parameters: re-throw current exception */ if (stmt->condname == NULL && stmt->message == NULL && stmt->options == NIL) ! return PLPGSQL_RC_RERAISE; if (stmt->condname) { --- 2427,2440 ---- /* RAISE with no parameters: re-throw current exception */ if (stmt->condname == NULL && stmt->message == NULL && stmt->options == NIL) ! { ! if (estate->last_caught_error == NULL) ! ereport(ERROR, ! (errcode(ERRCODE_SYNTAX_ERROR), ! errmsg("RAISE without parameters cannot be used outside an exception handler"))); ! ! ReThrowError(estate->last_caught_error); ! } if (stmt->condname) { *************** *** 2666,2671 **** plpgsql_estate_setup(PLpgSQL_execstate *estate, --- 2663,2669 ---- estate->err_text = NULL; estate->plugin_info = NULL; + estate->last_caught_error = NULL; /* * Create an EState and ExprContext for evaluation of simple expressions. *** a/src/pl/plpgsql/src/plpgsql.h --- b/src/pl/plpgsql/src/plpgsql.h *************** *** 115,122 **** enum PLPGSQL_RC_OK, PLPGSQL_RC_EXIT, PLPGSQL_RC_RETURN, ! PLPGSQL_RC_CONTINUE, ! PLPGSQL_RC_RERAISE }; /* ---------- --- 115,121 ---- PLPGSQL_RC_OK, PLPGSQL_RC_EXIT, PLPGSQL_RC_RETURN, ! PLPGSQL_RC_CONTINUE }; /* ---------- *************** *** 721,726 **** typedef struct PLpgSQL_execstate --- 720,726 ---- const char *err_text; /* additional state info */ void *plugin_info; /* reserved for use by optional plugin */ + ErrorData *last_caught_error; } PLpgSQL_execstate; *** a/src/test/regress/expected/plpgsql.out --- b/src/test/regress/expected/plpgsql.out *************** *** 3577,3583 **** end; $$ language plpgsql; select raise_test(); ERROR: RAISE without parameters cannot be used outside an exception handler ! CONTEXT: PL/pgSQL function "raise_test" -- 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 $$ --- 3577,3583 ---- $$ language plpgsql; select raise_test(); ERROR: RAISE without parameters cannot be used outside an exception handler ! CONTEXT: PL/pgSQL function "raise_test" line 2 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 $$