Hi, Please consider the following test case
> CREATE OR REPLACE FUNCTION raisetest() returns void 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; $$ LANGUAGE plpgsql; > select raisetest(); NOTICE: exception thrown in inner block, reraising NOTICE: WRONG - exception caught in outer block block raisetest ----------- (1 row) The output of the above function seems to be wrong. Ideally the Exception should have caught in the inner most block instead of the outer block. Below I am sharing my obsevation while debuging this issue. When we give RAISE without the exception name statement, it is internally returning PLPGSQL_RC_RERAISE instead of jumping to the EXCEPTION block of the current Begin-End Block. This will force engine to eliminate/skip the current block's EXCEPTION block. This is the reason its got caught in the next exception block. To fix this, instead of returning PLPGSQL_RC_RERAISE from the function, we will rethrow the exception if their is no EXCEPTION name given to the RAISE statement. When their is RAISE (without exception name) statement, it is been assume that their must be some exception already raised earlier. We are now storing the 'errordata' into the 'estate' structure, while raising the exception. Now since we are not returning PLPGSQL_RC_RERAISE statement, I have also removed the related redundunt code in the pl_exec.c. The testcase mentioned above is behaving correctly like postgres=# select raisetest(); NOTICE: exception thrown in inner block, reraising NOTICE: RIGHT - exception caught in innermost block raisetest ----------- (1 row) Please find attached patch generated on the current branch to fix the problem. -- Piyush S Newe Principal Engineer EnterpriseDB office: +91 20 3058 9500 www.enterprisedb.com Website: www.enterprisedb.com EnterpriseDB Blog: http://blogs.enterprisedb.com/ Follow us on Twitter: http://www.twitter.com/enterprisedb This e-mail message (and any attachment) is intended for the use of the individual or entity to whom it is addressed. This message contains information from EnterpriseDB Corporation that may be privileged, confidential, or exempt from disclosure under applicable law. If you are not the intended recipient or authorized to receive this for the intended recipient, any use, dissemination, distribution, retention, archiving, or copying of this communication is strictly prohibited. If you have received this e-mail in error, please notify the sender immediately by reply e-mail and delete this message.
Index: src/pl/plpgsql/src/pl_exec.c =================================================================== RCS file: /repositories/postgreshome/cvs/pgsql/src/pl/plpgsql/src/pl_exec.c,v retrieving revision 1.257 diff -c -p -r1.257 pl_exec.c *** src/pl/plpgsql/src/pl_exec.c 14 Apr 2010 23:52:10 -0000 1.257 --- src/pl/plpgsql/src/pl_exec.c 23 Apr 2010 09:51:08 -0000 *************** plpgsql_exec_function(PLpgSQL_function * *** 327,336 **** 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 ---- *************** plpgsql_exec_trigger(PLpgSQL_function *f *** 695,704 **** 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 ---- *************** exec_stmt_block(PLpgSQL_execstate *estat *** 1132,1138 **** --- 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; *************** exec_stmt_block(PLpgSQL_execstate *estat *** 1141,1150 **** 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 ---- *************** exec_stmt_block(PLpgSQL_execstate *estat *** 1177,1183 **** case PLPGSQL_RC_OK: case PLPGSQL_RC_RETURN: case PLPGSQL_RC_CONTINUE: - case PLPGSQL_RC_RERAISE: return rc; case PLPGSQL_RC_EXIT: --- 1171,1176 ---- *************** exec_stmt_loop(PLpgSQL_execstate *estate *** 1599,1605 **** break; case PLPGSQL_RC_RETURN: - case PLPGSQL_RC_RERAISE: return rc; default: --- 1592,1597 ---- *************** exec_stmt_while(PLpgSQL_execstate *estat *** 1663,1669 **** break; case PLPGSQL_RC_RETURN: - case PLPGSQL_RC_RERAISE: return rc; default: --- 1655,1660 ---- *************** exec_stmt_fori(PLpgSQL_execstate *estate *** 1782,1789 **** */ 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) { *************** exec_stmt_raise(PLpgSQL_execstate *estat *** 2435,2441 **** /* 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) { --- 2425,2438 ---- /* 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) { *************** plpgsql_estate_setup(PLpgSQL_execstate * *** 2664,2669 **** --- 2661,2667 ---- estate->err_text = NULL; estate->plugin_info = NULL; + estate->last_caught_error = NULL; /* * Create an EState and ExprContext for evaluation of simple expressions. Index: src/pl/plpgsql/src/plpgsql.h =================================================================== RCS file: /repositories/postgreshome/cvs/pgsql/src/pl/plpgsql/src/plpgsql.h,v retrieving revision 1.130 diff -c -p -r1.130 plpgsql.h *** src/pl/plpgsql/src/plpgsql.h 26 Feb 2010 02:01:35 -0000 1.130 --- src/pl/plpgsql/src/plpgsql.h 23 Apr 2010 09:51:08 -0000 *************** enum *** 115,122 **** 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 }; /* ---------- *************** typedef struct PLpgSQL_execstate *** 721,726 **** --- 720,726 ---- const char *err_text; /* additional state info */ void *plugin_info; /* reserved for use by optional plugin */ + ErrorData *last_caught_error; } PLpgSQL_execstate;
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers