diff --git a/src/common/pl/plpgsql/src/pl_exec.cpp b/src/common/pl/plpgsql/src/pl_exec.cpp index c667dc203..98f81398d 100644 --- a/src/common/pl/plpgsql/src/pl_exec.cpp +++ b/src/common/pl/plpgsql/src/pl_exec.cpp @@ -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. diff --git a/src/include/knl/knl_session.h b/src/include/knl/knl_session.h index 96bdafee2..e5f01ff38 100644 --- a/src/include/knl/knl_session.h +++ b/src/include/knl/knl_session.h @@ -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 diff --git a/src/test/regress/expected/mysql_condition.out b/src/test/regress/expected/mysql_condition.out index 73ec418ec..fc6efa0f5 100644 --- a/src/test/regress/expected/mysql_condition.out +++ b/src/test/regress/expected/mysql_condition.out @@ -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; diff --git a/src/test/regress/sql/mysql_condition.sql b/src/test/regress/sql/mysql_condition.sql index 94cde14ae..78a628b21 100644 --- a/src/test/regress/sql/mysql_condition.sql +++ b/src/test/regress/sql/mysql_condition.sql @@ -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;