declare continue handler if then error
This commit is contained in:
@ -113,8 +113,8 @@ typedef struct SimpleEcontextStackEntry {
|
||||
************************************************************/
|
||||
static void plpgsql_exec_error_callback(void* arg);
|
||||
|
||||
static int exec_stmt_block(PLpgSQL_execstate* estate, PLpgSQL_stmt_block* block);
|
||||
static int exec_stmt_block_b_exception(PLpgSQL_execstate* estate, PLpgSQL_stmt_block* block);
|
||||
static int exec_stmt_block(PLpgSQL_execstate* estate, PLpgSQL_stmt_block* block, List* block_ptr_stack=NULL);
|
||||
static int exec_stmt_block_b_exception(PLpgSQL_execstate* estate, PLpgSQL_stmt_block* block, List* block_ptr_stack);
|
||||
static int exec_stmts(PLpgSQL_execstate* estate, List* stmts);
|
||||
static int exec_stmt(PLpgSQL_execstate* estate, PLpgSQL_stmt* stmt);
|
||||
static int exec_stmt_assign(PLpgSQL_execstate* estate, PLpgSQL_stmt_assign* stmt);
|
||||
@ -194,6 +194,12 @@ static void exec_set_sql_cursor_found(PLpgSQL_execstate* estate, PLpgSQL_state s
|
||||
static void exec_set_sql_notfound(PLpgSQL_execstate* estate, PLpgSQL_state state);
|
||||
static void exec_set_sql_isopen(PLpgSQL_execstate* estate, bool state);
|
||||
static void exec_set_sql_rowcount(PLpgSQL_execstate* estate, int rowcount);
|
||||
static int expand_stmts(PLpgSQL_execstate* estate, List* stmts, bool& exception_flag, List* block_ptr_stack);
|
||||
static int expand_stmt_block(PLpgSQL_execstate* estate, PLpgSQL_stmt_block* stmt, bool& exception_flag, List* block_ptr_stack);
|
||||
static int expand_stmt_case(PLpgSQL_execstate* estate, PLpgSQL_stmt_case* stmt, bool& exception_flag, List* block_ptr_stack);
|
||||
static int expand_stmt_if(PLpgSQL_execstate* estate, PLpgSQL_stmt_if* stmt, bool& exception_flag, List* block_ptr_stack);
|
||||
static int expand_stmt_while(PLpgSQL_execstate* estate, PLpgSQL_stmt_while* stmt, bool& exception_flag, List* block_ptr_stack);
|
||||
static int expand_stmt_loop(PLpgSQL_execstate* estate, PLpgSQL_stmt_loop* stmt, bool& exception_flag, List* lock_ptr_stack);
|
||||
static void plpgsql_create_econtext(PLpgSQL_execstate *estate, MemoryContext saveCxt = NULL);
|
||||
static void free_var(PLpgSQL_var* var);
|
||||
static PreparedParamsData* exec_eval_using_params(PLpgSQL_execstate* estate, List* params);
|
||||
@ -2858,7 +2864,7 @@ static int exec_exception_handler(PLpgSQL_execstate* estate, PLpgSQL_stmt_block*
|
||||
* exec_stmt_block Execute a block of statements
|
||||
* ----------
|
||||
*/
|
||||
static int exec_stmt_block(PLpgSQL_execstate* estate, PLpgSQL_stmt_block* block)
|
||||
static int exec_stmt_block(PLpgSQL_execstate* estate, PLpgSQL_stmt_block* block, List* block_ptr_stack)
|
||||
{
|
||||
volatile int rc = -1;
|
||||
int i;
|
||||
@ -2987,7 +2993,13 @@ static int exec_stmt_block(PLpgSQL_execstate* estate, PLpgSQL_stmt_block* block)
|
||||
|
||||
if (block->exceptions != NULL) {
|
||||
if (block->isDeclareHandlerStmt == true/* mysql_style_exception */) {
|
||||
rc = exec_stmt_block_b_exception(estate, block);
|
||||
if (block_ptr_stack == NIL) {
|
||||
block_ptr_stack = list_make1(block);
|
||||
} else {
|
||||
block_ptr_stack = lappend(block_ptr_stack, block);
|
||||
}
|
||||
rc = exec_stmt_block_b_exception(estate, block, block_ptr_stack);
|
||||
list_delete_ptr(block_ptr_stack, block);
|
||||
} else {
|
||||
estate->err_text = gettext_noop("during statement block entry");
|
||||
|
||||
@ -3153,130 +3165,406 @@ static int exec_stmt_block(PLpgSQL_execstate* estate, PLpgSQL_stmt_block* block)
|
||||
* exec_stmt_block_b_exception Execute a block of statements like mysql's exception handling style
|
||||
* ----------
|
||||
*/
|
||||
static int exec_stmt_block_b_exception(PLpgSQL_execstate* estate, PLpgSQL_stmt_block* block)
|
||||
static int exec_stmt_block_b_exception(PLpgSQL_execstate* estate, PLpgSQL_stmt_block* block, List* block_ptr_stack)
|
||||
{
|
||||
estate->err_text = gettext_noop("during statement block entry");
|
||||
bool savedisAllowCommitRollback = u_sess->SPI_cxt.is_allow_commit_rollback;
|
||||
bool savedIsSTP = u_sess->SPI_cxt.is_stp;
|
||||
bool savedProConfigIsSet = u_sess->SPI_cxt.is_proconfig_set;
|
||||
estate->err_text = gettext_noop("during statement block entry");
|
||||
int rc = -1;
|
||||
|
||||
ExceptionContext excptContext;
|
||||
Cursor_Data* saved_cursor_data = estate->cursor_return_data;
|
||||
int saved_cursor_numbers = estate->cursor_return_numbers;
|
||||
int rc = -1;
|
||||
List* stmts = block->body;
|
||||
|
||||
List* stmts = block->body;
|
||||
if (stmts == NIL) {
|
||||
/*
|
||||
* Ensure we do a CHECK_FOR_INTERRUPTS() even though there is no
|
||||
* statement. This prevents hangup in a tight loop if, for instance,
|
||||
* there is a LOOP construct with an empty body.
|
||||
*/
|
||||
CHECK_FOR_INTERRUPTS();
|
||||
return PLPGSQL_RC_OK;
|
||||
}
|
||||
int num_stmts = list_length(stmts);
|
||||
int stmtid = 0;
|
||||
if (stmtid >= num_stmts) {
|
||||
estate->block_level --;
|
||||
return PLPGSQL_RC_OK;
|
||||
}
|
||||
|
||||
int num_stmts = list_length(stmts);
|
||||
int stmtid = 0;
|
||||
bool exception_flag = false;
|
||||
for (;stmtid < num_stmts;) {
|
||||
PLpgSQL_stmt* stmt = (PLpgSQL_stmt*)list_nth(stmts, stmtid);
|
||||
stmtid++;
|
||||
exception_flag = false;
|
||||
exec_exception_begin(estate, &excptContext);
|
||||
PG_TRY();
|
||||
{
|
||||
/*
|
||||
* We need to run the block's statements with a new eval_econtext
|
||||
* that belongs to the current subtransaction; if we try to use
|
||||
* the outer econtext then ExprContext shutdown callbacks will be
|
||||
* called at the wrong times.
|
||||
*/
|
||||
plpgsql_create_econtext(estate);
|
||||
estate->err_text = NULL;
|
||||
/* check condition value */
|
||||
/* Run the block's statements */
|
||||
rc = exec_stmt(estate, stmt);
|
||||
#ifdef ENABLE_MOT
|
||||
// throws ereport
|
||||
MOTCheckTransactionAborted();
|
||||
#endif
|
||||
estate->err_text = gettext_noop("during statement block exit");
|
||||
|
||||
/*
|
||||
* If the block ended with RETURN, we may need to copy the return
|
||||
* value out of the subtransaction eval_context. This is
|
||||
* currently only needed for scalar result types --- rowtype
|
||||
* values will always exist in the function's own memory context.
|
||||
*/
|
||||
if (rc == PLPGSQL_RC_RETURN && !estate->retisset && !estate->retisnull && estate->rettupdesc == NULL) {
|
||||
int16 resTypLen;
|
||||
bool resTypByVal = false;
|
||||
|
||||
get_typlenbyval(estate->rettype, &resTypLen, &resTypByVal);
|
||||
estate->retval = datumCopy(estate->retval, resTypByVal, resTypLen);
|
||||
}
|
||||
|
||||
exec_exception_end(estate, &excptContext);
|
||||
stp_retore_old_xact_stmt_state(savedisAllowCommitRollback);
|
||||
}
|
||||
PG_CATCH();
|
||||
{
|
||||
exception_flag = true;
|
||||
if (estate->func->debug) {
|
||||
PlDebuggerComm* debug_comm = &g_instance.pldebug_cxt.debug_comm[estate->func->debug->comm->comm_idx];
|
||||
/* client has error and debug on inner funciton, throw current error */
|
||||
if (debug_comm->hasClientErrorOccured) {
|
||||
ereport(ERROR, (errmodule(MOD_PLDEBUGGER),
|
||||
errmsg("Debug client has some error occured.")));
|
||||
}
|
||||
}
|
||||
stp_retore_old_xact_stmt_state(savedisAllowCommitRollback);
|
||||
u_sess->SPI_cxt.is_stp = savedIsSTP;
|
||||
u_sess->SPI_cxt.is_proconfig_set = savedProConfigIsSet;
|
||||
|
||||
estate->cursor_return_data = saved_cursor_data;
|
||||
estate->cursor_return_numbers = saved_cursor_numbers;
|
||||
|
||||
/* reset stream for-loop flag */
|
||||
u_sess->SPI_cxt.has_stream_in_cursor_or_forloop_sql = false;
|
||||
|
||||
if (u_sess->plsql_cxt.b_warning_handler) {
|
||||
u_sess->plsql_cxt.b_warning_handler = false;
|
||||
}
|
||||
|
||||
/* gs_signal_handle maybe block sigusr2 when accept SIGINT */
|
||||
gs_signal_unblock_sigusr2();
|
||||
|
||||
estate->err_text = gettext_noop("during exception cleanup");
|
||||
|
||||
exec_exception_cleanup(estate, &excptContext);
|
||||
#ifndef ENABLE_MULTIPLE_NODES
|
||||
AutoDopControl dopControl;
|
||||
dopControl.CloseSmp();
|
||||
#endif
|
||||
estate->err_text = gettext_noop("during exec exception handler");
|
||||
rc = exec_exception_handler(estate, block, &excptContext);
|
||||
|
||||
stp_retore_old_xact_stmt_state(savedisAllowCommitRollback);
|
||||
u_sess->SPI_cxt.is_stp = savedIsSTP;
|
||||
u_sess->SPI_cxt.is_proconfig_set = savedProConfigIsSet;
|
||||
}
|
||||
PG_END_TRY();
|
||||
AssertEreport(excptContext.old_edata == estate->cur_error,
|
||||
MOD_PLSQL,
|
||||
"save current error should be same error as estate current error.");
|
||||
// means exception throw from PG_TRY();
|
||||
if (exception_flag) {
|
||||
if (excptContext.handler_type == PLpgSQL_declare_handler::DECLARE_HANDLER_EXIT) {
|
||||
break;
|
||||
} else if (excptContext.handler_type == PLpgSQL_declare_handler::DECLARE_HANDLER_CONTINUE) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
bool exception_flag = false;
|
||||
rc = expand_stmts(estate, stmts, exception_flag, block_ptr_stack);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int expand_stmts(PLpgSQL_execstate* estate, List* stmts, bool& exception_flag, List* block_ptr_stack)
|
||||
{
|
||||
int rc = -1;
|
||||
int num_stmts = list_length(stmts);
|
||||
int stmtid = 0;
|
||||
bool early_exit = false;
|
||||
if (stmtid >= num_stmts) {
|
||||
estate->block_level --;
|
||||
return PLPGSQL_RC_OK;
|
||||
}
|
||||
|
||||
while(stmtid < num_stmts && !early_exit) {
|
||||
if (exception_flag) {
|
||||
rc = PLPGSQL_RC_EXIT;
|
||||
break;
|
||||
}
|
||||
PLpgSQL_stmt* stmt = (PLpgSQL_stmt*)list_nth(stmts, stmtid);
|
||||
ExceptionContext excptContext;
|
||||
stmtid++;
|
||||
PLpgSQL_stmt_block* block = (PLpgSQL_stmt_block*)lfirst(list_head(block_ptr_stack));
|
||||
switch((enum PLpgSQL_stmt_types)stmt->cmd_type) {
|
||||
case PLPGSQL_STMT_BLOCK:
|
||||
rc = expand_stmt_block(estate, (PLpgSQL_stmt_block*)stmt, exception_flag, block_ptr_stack);
|
||||
break;
|
||||
|
||||
case PLPGSQL_STMT_IF:
|
||||
rc = expand_stmt_if(estate, (PLpgSQL_stmt_if*)stmt, exception_flag, block_ptr_stack);
|
||||
break;
|
||||
|
||||
case PLPGSQL_STMT_CASE:
|
||||
rc = expand_stmt_case(estate, (PLpgSQL_stmt_case*)stmt, exception_flag, block_ptr_stack);
|
||||
break;
|
||||
|
||||
case PLPGSQL_STMT_LOOP:
|
||||
rc = expand_stmt_loop(estate, (PLpgSQL_stmt_loop*)stmt, exception_flag, block_ptr_stack);
|
||||
break;
|
||||
|
||||
case PLPGSQL_STMT_WHILE:
|
||||
rc = expand_stmt_while(estate, (PLpgSQL_stmt_while*)stmt, exception_flag, block_ptr_stack);
|
||||
break;
|
||||
|
||||
default: {
|
||||
bool savedisAllowCommitRollback = u_sess->SPI_cxt.is_allow_commit_rollback;
|
||||
bool savedIsSTP = u_sess->SPI_cxt.is_stp;
|
||||
bool savedProConfigIsSet = u_sess->SPI_cxt.is_proconfig_set;
|
||||
|
||||
Cursor_Data* saved_cursor_data = estate->cursor_return_data;
|
||||
int saved_cursor_numbers = estate->cursor_return_numbers;
|
||||
exec_exception_begin(estate, &excptContext);
|
||||
PG_TRY();
|
||||
{
|
||||
/*
|
||||
* We need to run the block's statements with a new eval_econtext
|
||||
* that belongs to the current subtransaction; if we try to use
|
||||
* the outer econtext then ExprContext shutdown callbacks will be
|
||||
* called at the wrong times.
|
||||
*/
|
||||
plpgsql_create_econtext(estate);
|
||||
|
||||
estate->err_text = NULL;
|
||||
|
||||
/* Run the block's statements */
|
||||
rc = exec_stmt(estate, stmt);
|
||||
if (stmt->cmd_type == PLPGSQL_STMT_EXIT && rc == PLPGSQL_RC_EXIT) {
|
||||
early_exit = true;
|
||||
}
|
||||
estate->err_text = gettext_noop("during statement block exit");
|
||||
|
||||
/*
|
||||
* If the block ended with RETURN, we may need to copy the return
|
||||
* value out of the subtransaction eval_context. This is
|
||||
* currently only needed for scalar result types --- rowtype
|
||||
* values will always exist in the function's own memory context.
|
||||
*/
|
||||
if (rc == PLPGSQL_RC_RETURN && !estate->retisset && !estate->retisnull && estate->rettupdesc == NULL) {
|
||||
int16 resTypLen;
|
||||
bool resTypByVal = false;
|
||||
|
||||
get_typlenbyval(estate->rettype, &resTypLen, &resTypByVal);
|
||||
estate->retval = datumCopy(estate->retval, resTypByVal, resTypLen);
|
||||
}
|
||||
|
||||
exec_exception_end(estate, &excptContext);
|
||||
stp_retore_old_xact_stmt_state(savedisAllowCommitRollback);
|
||||
}
|
||||
PG_CATCH();
|
||||
{
|
||||
exception_flag = true;
|
||||
if (estate->func->debug) {
|
||||
PlDebuggerComm* debug_comm = &g_instance.pldebug_cxt.debug_comm[estate->func->debug->comm->comm_idx];
|
||||
/* client has error and debug on inner funciton, throw current error */
|
||||
if (debug_comm->hasClientErrorOccured) {
|
||||
ereport(ERROR, (errmodule(MOD_PLDEBUGGER),
|
||||
errmsg("Debug client has some error occured.")));
|
||||
}
|
||||
}
|
||||
stp_retore_old_xact_stmt_state(savedisAllowCommitRollback);
|
||||
u_sess->SPI_cxt.is_stp = savedIsSTP;
|
||||
u_sess->SPI_cxt.is_proconfig_set = savedProConfigIsSet;
|
||||
|
||||
estate->cursor_return_data = saved_cursor_data;
|
||||
estate->cursor_return_numbers = saved_cursor_numbers;
|
||||
|
||||
/* reset stream for-loop flag */
|
||||
u_sess->SPI_cxt.has_stream_in_cursor_or_forloop_sql = false;
|
||||
|
||||
/* gs_signal_handle maybe block sigusr2 when accept SIGINT */
|
||||
gs_signal_unblock_sigusr2();
|
||||
|
||||
estate->err_text = gettext_noop("during exception cleanup");
|
||||
|
||||
exec_exception_cleanup(estate, &excptContext);
|
||||
#ifndef ENABLE_MULTIPLE_NODES
|
||||
AutoDopControl dopControl;
|
||||
dopControl.CloseSmp();
|
||||
#endif
|
||||
estate->err_text = gettext_noop("during exec exception handler");
|
||||
rc = exec_exception_handler(estate, block, &excptContext);
|
||||
|
||||
stp_retore_old_xact_stmt_state(savedisAllowCommitRollback);
|
||||
u_sess->SPI_cxt.is_stp = savedIsSTP;
|
||||
u_sess->SPI_cxt.is_proconfig_set = savedProConfigIsSet;
|
||||
}
|
||||
PG_END_TRY();
|
||||
|
||||
AssertEreport(excptContext.old_edata == estate->cur_error,
|
||||
MOD_PLSQL,
|
||||
"save current error should be same error as estate current error.");
|
||||
}
|
||||
}
|
||||
if (exception_flag) {
|
||||
if(block->isDeclareHandlerStmt == false) {
|
||||
exception_flag = false;
|
||||
break;
|
||||
}
|
||||
if (excptContext.handler_type == PLpgSQL_declare_handler::DECLARE_HANDLER_EXIT) {
|
||||
break;
|
||||
} else if (excptContext.handler_type == PLpgSQL_declare_handler::DECLARE_HANDLER_CONTINUE) {
|
||||
exception_flag = false;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int expand_stmt_block(PLpgSQL_execstate* estate, PLpgSQL_stmt_block* stmt, bool& exception_flag, List* block_ptr_stack)
|
||||
{
|
||||
int rc = -1;
|
||||
if (stmt->exceptions != NULL) {
|
||||
stmt->exceptions->exc_list = list_concat(stmt->exceptions->exc_list,
|
||||
((PLpgSQL_stmt_block*)lfirst(block_ptr_stack->head))->exceptions->exc_list);
|
||||
lcons(stmt,block_ptr_stack);
|
||||
}
|
||||
|
||||
rc = expand_stmts(estate, stmt->body, exception_flag, block_ptr_stack);
|
||||
if (stmt->exceptions != NULL) {
|
||||
list_delete_ptr(block_ptr_stack, stmt);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int expand_stmt_case(PLpgSQL_execstate* estate, PLpgSQL_stmt_case* stmt, bool& exception_flag, List* block_ptr_stack)
|
||||
{
|
||||
PLpgSQL_var* t_var = NULL;
|
||||
bool isnull = false;
|
||||
ListCell* l = NULL;
|
||||
|
||||
if (stmt->t_expr != NULL) {
|
||||
/* simple case */
|
||||
Datum t_val;
|
||||
Oid t_oid;
|
||||
|
||||
t_val = exec_eval_expr(estate, stmt->t_expr, &isnull, &t_oid);
|
||||
|
||||
t_var = (PLpgSQL_var*)estate->datums[stmt->t_varno];
|
||||
|
||||
/*
|
||||
* When expected datatype is different from real, change it. Note that
|
||||
* what we're modifying here is an execution copy of the datum, so
|
||||
* this doesn't affect the originally stored function parse tree.
|
||||
*/
|
||||
if (t_var->datatype->typoid != t_oid) {
|
||||
t_var->datatype = plpgsql_build_datatype(t_oid, -1, estate->func->fn_input_collation);
|
||||
}
|
||||
|
||||
/* now we can assign to the variable */
|
||||
exec_assign_value(estate, (PLpgSQL_datum*)t_var, t_val, t_oid, &isnull, NULL, NULL);
|
||||
|
||||
exec_eval_cleanup(estate);
|
||||
}
|
||||
|
||||
/* Now search for a successful WHEN clause */
|
||||
foreach (l, stmt->case_when_list) {
|
||||
PLpgSQL_case_when* cwt = (PLpgSQL_case_when*)lfirst(l);
|
||||
bool value = false;
|
||||
|
||||
value = exec_eval_boolean(estate, cwt->expr, &isnull);
|
||||
exec_eval_cleanup(estate);
|
||||
if (!isnull && value) {
|
||||
/* Found it */
|
||||
|
||||
/* We can now discard any value we had for the temp variable */
|
||||
if (t_var != NULL) {
|
||||
free_var(t_var);
|
||||
t_var->value = (Datum)0;
|
||||
t_var->isnull = true;
|
||||
}
|
||||
|
||||
/* Evaluate the statement(s), and we're done */
|
||||
return expand_stmts(estate, cwt->stmts, exception_flag, block_ptr_stack);
|
||||
}
|
||||
}
|
||||
|
||||
/* We can now discard any value we had for the temp variable */
|
||||
if (t_var != NULL) {
|
||||
free_var(t_var);
|
||||
t_var->value = (Datum)0;
|
||||
t_var->isnull = true;
|
||||
}
|
||||
|
||||
/* SQL2003 mandates this error if there was no ELSE clause */
|
||||
if (!stmt->have_else) {
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_CASE_NOT_FOUND),
|
||||
errmodule(MOD_PLSQL),
|
||||
errmsg("case not found"),
|
||||
errhint("CASE statement is missing ELSE part.")));
|
||||
}
|
||||
|
||||
/* Evaluate the ELSE statements, and we're done */
|
||||
return expand_stmts(estate, stmt->else_stmts, exception_flag, block_ptr_stack);
|
||||
}
|
||||
|
||||
static int expand_stmt_if(PLpgSQL_execstate* estate, PLpgSQL_stmt_if* stmt, bool& exception_flag, List* block_ptr_stack)
|
||||
{
|
||||
bool value = false;
|
||||
bool isnull = false;
|
||||
ListCell* lc = NULL;
|
||||
|
||||
value = exec_eval_boolean(estate, stmt->cond, &isnull);
|
||||
exec_eval_cleanup(estate);
|
||||
if (!isnull && value) {
|
||||
return expand_stmts(estate, stmt->then_body, exception_flag, block_ptr_stack);
|
||||
}
|
||||
|
||||
foreach (lc, stmt->elsif_list) {
|
||||
PLpgSQL_if_elsif* elif = (PLpgSQL_if_elsif*)lfirst(lc);
|
||||
|
||||
value = exec_eval_boolean(estate, elif->cond, &isnull);
|
||||
exec_eval_cleanup(estate);
|
||||
if (!isnull && value) {
|
||||
return expand_stmts(estate, elif->stmts, exception_flag, block_ptr_stack);
|
||||
}
|
||||
}
|
||||
|
||||
return expand_stmts(estate, stmt->else_body, exception_flag, block_ptr_stack);
|
||||
}
|
||||
|
||||
static int expand_stmt_while(PLpgSQL_execstate* estate, PLpgSQL_stmt_while* stmt, bool& exception_flag, List* block_ptr_stack)
|
||||
{
|
||||
bool condition = stmt->condition;
|
||||
for (;;) {
|
||||
int rc;
|
||||
bool value = false;
|
||||
bool isnull = false;
|
||||
|
||||
value = exec_eval_boolean(estate, stmt->cond, &isnull);
|
||||
exec_eval_cleanup(estate);
|
||||
|
||||
if (isnull || (!value && condition) || (value && !condition)) {
|
||||
break;
|
||||
}
|
||||
|
||||
rc = expand_stmts(estate, stmt->body, exception_flag, block_ptr_stack);
|
||||
|
||||
switch (rc) {
|
||||
case PLPGSQL_RC_OK:
|
||||
break;
|
||||
|
||||
case PLPGSQL_RC_EXIT:
|
||||
if (estate->exitlabel == NULL) {
|
||||
return PLPGSQL_RC_OK;
|
||||
}
|
||||
if (stmt->label == NULL) {
|
||||
return PLPGSQL_RC_EXIT;
|
||||
}
|
||||
if (strcmp(stmt->label, estate->exitlabel) != 0) {
|
||||
return PLPGSQL_RC_EXIT;
|
||||
}
|
||||
estate->exitlabel = NULL;
|
||||
return PLPGSQL_RC_OK;
|
||||
|
||||
case PLPGSQL_RC_CONTINUE:
|
||||
if (estate->exitlabel == NULL) {
|
||||
/* anonymous continue, so re-run loop */
|
||||
break;
|
||||
} else if (stmt->label != NULL && strcmp(stmt->label, estate->exitlabel) == 0) {
|
||||
/* label matches named continue, so re-run loop */
|
||||
estate->exitlabel = NULL;
|
||||
} else {
|
||||
/* label doesn't match named continue, propagate upward */
|
||||
return PLPGSQL_RC_CONTINUE;
|
||||
}
|
||||
break;
|
||||
|
||||
case PLPGSQL_RC_RETURN:
|
||||
/*
|
||||
* GOTO target is not in loop_body just return "PLPGSQL_RC_GOTO_UNRESOLVED",
|
||||
* to let the caller to goto-resolve
|
||||
*/
|
||||
case PLPGSQL_RC_GOTO_UNRESOLVED:
|
||||
return rc;
|
||||
|
||||
default:
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE),
|
||||
errmodule(MOD_PLSQL),
|
||||
errmsg("unrecognized return code: %d in PLSQL WHILE statement.", rc)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return PLPGSQL_RC_OK;
|
||||
}
|
||||
|
||||
static int expand_stmt_loop(PLpgSQL_execstate* estate, PLpgSQL_stmt_loop* stmt, bool& exception_flag, List* block_ptr_stack)
|
||||
{
|
||||
for(;;){
|
||||
int rc = expand_stmts(estate, stmt->body, exception_flag, block_ptr_stack);
|
||||
|
||||
switch (rc) {
|
||||
case PLPGSQL_RC_OK:
|
||||
break;
|
||||
|
||||
case PLPGSQL_RC_EXIT:
|
||||
if (estate->exitlabel == NULL) {
|
||||
return PLPGSQL_RC_OK;
|
||||
}
|
||||
if (stmt->label == NULL) {
|
||||
return PLPGSQL_RC_EXIT;
|
||||
}
|
||||
if (strcmp(stmt->label, estate->exitlabel) != 0) {
|
||||
return PLPGSQL_RC_EXIT;
|
||||
}
|
||||
estate->exitlabel = NULL;
|
||||
return PLPGSQL_RC_OK;
|
||||
|
||||
case PLPGSQL_RC_CONTINUE:
|
||||
if (estate->exitlabel == NULL) {
|
||||
/* anonymous continue, so re-run the loop */
|
||||
break;
|
||||
} else if (stmt->label != NULL && strcmp(stmt->label, estate->exitlabel) == 0) {
|
||||
/* label matches named continue, so re-run loop */
|
||||
estate->exitlabel = NULL;
|
||||
} else {
|
||||
/* label doesn't match named continue, so propagate upward */
|
||||
return PLPGSQL_RC_CONTINUE;
|
||||
}
|
||||
break;
|
||||
|
||||
case PLPGSQL_RC_RETURN:
|
||||
/*
|
||||
* GOTO target is not in loop_body just return "PLPGSQL_RC_GOTO_UNRESOLVED",
|
||||
* to let the caller to goto-resolve
|
||||
*/
|
||||
case PLPGSQL_RC_GOTO_UNRESOLVED:
|
||||
return rc;
|
||||
|
||||
default:
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE),
|
||||
errmodule(MOD_PLSQL),
|
||||
errmsg("unrecognized return code: %d in PLSQL LOOP statement.", rc)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
return PLPGSQL_RC_OK;
|
||||
}
|
||||
|
||||
/* ----------
|
||||
* search_goto_target_global search goto target statement in global
|
||||
* return the searched PLpgSQL statement.
|
||||
|
||||
@ -1660,7 +1660,6 @@ typedef struct knl_u_plpgsql_context {
|
||||
bool pragma_autonomous; /* save autonomous flag */
|
||||
char* debug_query_string;
|
||||
bool is_insert_gs_source; /* is doing insert gs_source? */
|
||||
bool b_warning_handler;
|
||||
} knl_u_plpgsql_context;
|
||||
|
||||
//this is used to define functions in package
|
||||
|
||||
@ -309,6 +309,198 @@ call proc_null();
|
||||
|
||||
(1 row)
|
||||
|
||||
CREATE TABLE tb1(
|
||||
col1 INT PRIMARY KEY,
|
||||
col2 text
|
||||
);
|
||||
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "tb1_pkey" for table "tb1"
|
||||
CREATE OR REPLACE PROCEDURE proc1(IN col1 INT, IN col2 text) AS
|
||||
DECLARE result VARCHAR;
|
||||
declare pragma autonomous_transaction;
|
||||
BEGIN
|
||||
DECLARE CONTINUE HANDLER FOR 23505
|
||||
begin
|
||||
RAISE NOTICE 'SQLSTATE = %',SQLSTATE;
|
||||
end;
|
||||
if col1>10 then
|
||||
INSERT INTO tb1 VALUES(col1,'lili');
|
||||
END IF;
|
||||
IF col1 <= 10 THEN
|
||||
INSERT INTO tb1(col1,col2) VALUES(col1,col2);
|
||||
commit;
|
||||
ELSE
|
||||
INSERT INTO tb1(col1,col2) VALUES(col1,col2);
|
||||
rollback;
|
||||
END IF;
|
||||
END;
|
||||
/
|
||||
call proc1(1, 1);
|
||||
proc1
|
||||
-------
|
||||
|
||||
(1 row)
|
||||
|
||||
call proc1(1, 5);
|
||||
NOTICE: SQLSTATE = 23505
|
||||
CONTEXT: referenced column: proc1
|
||||
|
||||
proc1
|
||||
-------
|
||||
|
||||
(1 row)
|
||||
|
||||
call proc1(11, 11);
|
||||
NOTICE: SQLSTATE = 23505
|
||||
CONTEXT: referenced column: proc1
|
||||
|
||||
proc1
|
||||
-------
|
||||
|
||||
(1 row)
|
||||
|
||||
call proc1(11, 5);
|
||||
NOTICE: SQLSTATE = 23505
|
||||
CONTEXT: referenced column: proc1
|
||||
|
||||
proc1
|
||||
-------
|
||||
|
||||
(1 row)
|
||||
|
||||
select * from tb1;
|
||||
col1 | col2
|
||||
------+------
|
||||
1 | 1
|
||||
(1 row)
|
||||
|
||||
CREATE OR REPLACE PROCEDURE proc1(IN a text) AS
|
||||
BEGIN
|
||||
if a='22012' then
|
||||
raise info 'zero error';
|
||||
else
|
||||
raise info 'emmm....';
|
||||
end if;
|
||||
end;
|
||||
/
|
||||
CREATE OR REPLACE PROCEDURE proc2(IN var1 int,var2 int) AS
|
||||
begin
|
||||
DECLARE CONTINUE HANDLER FOR sqlstate'22012'
|
||||
begin
|
||||
RAISE NOTICE 'SQLSTATE = %',SQLSTATE;
|
||||
var1=0;
|
||||
end;
|
||||
var1= var1 / var2;
|
||||
RAISE INFO 'result: %', var1;
|
||||
END;
|
||||
/
|
||||
CREATE OR REPLACE PROCEDURE proc3(a1 int,b1 int) AS
|
||||
BEGIN
|
||||
DECLARE CONTINUE HANDLER FOR sqlstate'22012',sqlstate'0A000'
|
||||
begin
|
||||
RAISE NOTICE 'SQLSTATE = %',SQLSTATE;
|
||||
perform proc1(SQLSTATE);
|
||||
end;
|
||||
a1=a1/b1;
|
||||
IF b1 = 0 THEN
|
||||
raise info 'b1 is zero';
|
||||
create table tb1();
|
||||
perform proc2(b1, a1);
|
||||
END IF;
|
||||
raise info 'END';
|
||||
END;
|
||||
/
|
||||
CALL proc3(1,0);
|
||||
NOTICE: SQLSTATE = 22012
|
||||
INFO: zero error
|
||||
CONTEXT: referenced column: proc1
|
||||
SQL statement "SELECT proc1(SQLSTATE)"
|
||||
PL/pgSQL function proc3(integer,integer) line 6 at PERFORM
|
||||
INFO: b1 is zero
|
||||
NOTICE: SQLSTATE = 0A000
|
||||
INFO: emmm....
|
||||
CONTEXT: referenced column: proc1
|
||||
SQL statement "SELECT proc1(SQLSTATE)"
|
||||
PL/pgSQL function proc3(integer,integer) line 6 at PERFORM
|
||||
INFO: result: 0
|
||||
CONTEXT: referenced column: proc2
|
||||
SQL statement "SELECT proc2(b1, a1)"
|
||||
PL/pgSQL function proc3(integer,integer) line 12 at PERFORM
|
||||
INFO: END
|
||||
proc3
|
||||
-------
|
||||
|
||||
(1 row)
|
||||
|
||||
CALL proc3(0,0);
|
||||
NOTICE: SQLSTATE = 22012
|
||||
INFO: zero error
|
||||
CONTEXT: referenced column: proc1
|
||||
SQL statement "SELECT proc1(SQLSTATE)"
|
||||
PL/pgSQL function proc3(integer,integer) line 6 at PERFORM
|
||||
INFO: b1 is zero
|
||||
NOTICE: SQLSTATE = 0A000
|
||||
INFO: emmm....
|
||||
CONTEXT: referenced column: proc1
|
||||
SQL statement "SELECT proc1(SQLSTATE)"
|
||||
PL/pgSQL function proc3(integer,integer) line 6 at PERFORM
|
||||
NOTICE: SQLSTATE = 22012
|
||||
CONTEXT: referenced column: proc2
|
||||
SQL statement "SELECT proc2(b1, a1)"
|
||||
PL/pgSQL function proc3(integer,integer) line 12 at PERFORM
|
||||
INFO: result: 0
|
||||
CONTEXT: referenced column: proc2
|
||||
SQL statement "SELECT proc2(b1, a1)"
|
||||
PL/pgSQL function proc3(integer,integer) line 12 at PERFORM
|
||||
INFO: END
|
||||
proc3
|
||||
-------
|
||||
|
||||
(1 row)
|
||||
|
||||
create table company(name varchar(100), loc varchar(100), no integer PRIMARY KEY);
|
||||
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "company_pkey" for table "company"
|
||||
insert into company values ('macrosoft', 'usa', 001);
|
||||
insert into company values ('oracle', 'usa', 002);
|
||||
insert into company values ('backberry', 'canada', 003);
|
||||
create or replace procedure test_cursor_handler()
|
||||
as
|
||||
|
||||
declare company_name varchar(100);
|
||||
declare company_loc varchar(100);
|
||||
declare company_no integer;
|
||||
begin
|
||||
DECLARE CONTINUE HANDLER FOR unique_violation
|
||||
begin
|
||||
RAISE NOTICE 'SQLSTATE = %',SQLSTATE;
|
||||
end;
|
||||
declare c1_all cursor is --cursor without args
|
||||
select name, loc, no from company order by 1, 2, 3;
|
||||
if not c1_all%isopen then
|
||||
open c1_all;
|
||||
end if;
|
||||
loop
|
||||
fetch c1_all into company_name, company_loc, company_no;
|
||||
exit when c1_all%notfound;
|
||||
insert into company values (company_name,company_loc,company_no);
|
||||
raise notice '% : % : %',company_name,company_loc,company_no;
|
||||
end loop;
|
||||
if c1_all%isopen then
|
||||
close c1_all;
|
||||
end if;
|
||||
end;
|
||||
/
|
||||
call test_cursor_handler();
|
||||
NOTICE: SQLSTATE = 23505
|
||||
NOTICE: backberry : canada : 3
|
||||
NOTICE: SQLSTATE = 23505
|
||||
NOTICE: macrosoft : usa : 1
|
||||
NOTICE: SQLSTATE = 23505
|
||||
NOTICE: oracle : usa : 2
|
||||
test_cursor_handler
|
||||
---------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
\c regression
|
||||
drop database mysql_test;
|
||||
drop database td_test;
|
||||
|
||||
@ -245,6 +245,106 @@ BEGIN
|
||||
END;
|
||||
/
|
||||
call proc_null();
|
||||
CREATE TABLE tb1(
|
||||
col1 INT PRIMARY KEY,
|
||||
col2 text
|
||||
);
|
||||
CREATE OR REPLACE PROCEDURE proc1(IN col1 INT, IN col2 text) AS
|
||||
DECLARE result VARCHAR;
|
||||
declare pragma autonomous_transaction;
|
||||
BEGIN
|
||||
DECLARE CONTINUE HANDLER FOR 23505
|
||||
begin
|
||||
RAISE NOTICE 'SQLSTATE = %',SQLSTATE;
|
||||
end;
|
||||
if col1>10 then
|
||||
INSERT INTO tb1 VALUES(col1,'lili');
|
||||
END IF;
|
||||
IF col1 <= 10 THEN
|
||||
INSERT INTO tb1(col1,col2) VALUES(col1,col2);
|
||||
commit;
|
||||
ELSE
|
||||
INSERT INTO tb1(col1,col2) VALUES(col1,col2);
|
||||
rollback;
|
||||
END IF;
|
||||
END;
|
||||
/
|
||||
call proc1(1, 1);
|
||||
call proc1(1, 5);
|
||||
call proc1(11, 11);
|
||||
call proc1(11, 5);
|
||||
select * from tb1;
|
||||
|
||||
CREATE OR REPLACE PROCEDURE proc1(IN a text) AS
|
||||
BEGIN
|
||||
if a='22012' then
|
||||
raise info 'zero error';
|
||||
else
|
||||
raise info 'emmm....';
|
||||
end if;
|
||||
end;
|
||||
/
|
||||
CREATE OR REPLACE PROCEDURE proc2(IN var1 int,var2 int) AS
|
||||
begin
|
||||
DECLARE CONTINUE HANDLER FOR sqlstate'22012'
|
||||
begin
|
||||
RAISE NOTICE 'SQLSTATE = %',SQLSTATE;
|
||||
var1=0;
|
||||
end;
|
||||
var1= var1 / var2;
|
||||
RAISE INFO 'result: %', var1;
|
||||
END;
|
||||
/
|
||||
CREATE OR REPLACE PROCEDURE proc3(a1 int,b1 int) AS
|
||||
BEGIN
|
||||
DECLARE CONTINUE HANDLER FOR sqlstate'22012',sqlstate'0A000'
|
||||
begin
|
||||
RAISE NOTICE 'SQLSTATE = %',SQLSTATE;
|
||||
perform proc1(SQLSTATE);
|
||||
end;
|
||||
a1=a1/b1;
|
||||
IF b1 = 0 THEN
|
||||
raise info 'b1 is zero';
|
||||
create table tb1();
|
||||
perform proc2(b1, a1);
|
||||
END IF;
|
||||
raise info 'END';
|
||||
END;
|
||||
/
|
||||
CALL proc3(1,0);
|
||||
CALL proc3(0,0);
|
||||
create table company(name varchar(100), loc varchar(100), no integer PRIMARY KEY);
|
||||
insert into company values ('macrosoft', 'usa', 001);
|
||||
insert into company values ('oracle', 'usa', 002);
|
||||
insert into company values ('backberry', 'canada', 003);
|
||||
create or replace procedure test_cursor_handler()
|
||||
as
|
||||
|
||||
declare company_name varchar(100);
|
||||
declare company_loc varchar(100);
|
||||
declare company_no integer;
|
||||
begin
|
||||
DECLARE CONTINUE HANDLER FOR unique_violation
|
||||
begin
|
||||
RAISE NOTICE 'SQLSTATE = %',SQLSTATE;
|
||||
end;
|
||||
declare c1_all cursor is --cursor without args
|
||||
select name, loc, no from company order by 1, 2, 3;
|
||||
if not c1_all%isopen then
|
||||
open c1_all;
|
||||
end if;
|
||||
loop
|
||||
fetch c1_all into company_name, company_loc, company_no;
|
||||
exit when c1_all%notfound;
|
||||
insert into company values (company_name,company_loc,company_no);
|
||||
raise notice '% : % : %',company_name,company_loc,company_no;
|
||||
end loop;
|
||||
if c1_all%isopen then
|
||||
close c1_all;
|
||||
end if;
|
||||
end;
|
||||
/
|
||||
call test_cursor_handler();
|
||||
\c regression
|
||||
drop database mysql_test;
|
||||
drop database td_test;
|
||||
|
||||
Reference in New Issue
Block a user