reraise_issue_PG_v2.patch

text/plain

Filename: reraise_issue_PG_v2.patch
Type: text/plain
Part: 0
Message: Patch review: make RAISE without arguments work like Oracle

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: context
Series: patch v2
File+
doc/src/sgml/plpgsql.sgml 41 0
src/pl/plpgsql/src/pl_exec.c 16 0
src/pl/plpgsql/src/plpgsql.h 2 0
src/test/regress/expected/plpgsql.out 1 0
*** a/doc/src/sgml/plpgsql.sgml
--- b/doc/src/sgml/plpgsql.sgml
***************
*** 2374,2379 **** SELECT merge_db(1, 'dennis');
--- 2374,2420 ----
  
      </para>
      </example>
+     <example id="plpgsql-reraise-example">
+     <title>Re-Raising Exceptions</title>
+     <para>
+ 
+     This example shows how to re-raise an exception, which helps you
+     write more complex exception-handling in your functions:
+ <programlisting>
+ 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)
+ </programlisting>
+ 
+     </para>
+     </example>
    </sect2>
    </sect1>
  
*** 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 $$