summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorAlexander Nozdrin <alik@sun.com>2010-07-30 19:28:36 +0400
committerAlexander Nozdrin <alik@sun.com>2010-07-30 19:28:36 +0400
commita0ab253fbd622429beab6027cd532e3f203188be (patch)
treeeaf5cc36760b4d96b253469ba54007facdbf1bf1 /sql
parent727da39fcd32ce2668efb3cac78237a51931c0cf (diff)
downloadmariadb-git-a0ab253fbd622429beab6027cd532e3f203188be.tar.gz
Auto-merge from mysql-trunk-bugfixing.
****** This patch fixes the following bugs: - Bug#5889: Exit handler for a warning doesn't hide the warning in trigger - Bug#9857: Stored procedures: handler for sqlwarning ignored - Bug#23032: Handlers declared in a SP do not handle warnings generated in sub-SP - Bug#36185: Incorrect precedence for warning and exception handlers The problem was in the way warnings/errors during stored routine execution were handled. Prior to this patch the logic was as follows: - when a warning/an error happens: if we're executing a stored routine, and there is a handler for that warning/error, remember the handler, ignore the warning/error and continue execution. - after a stored routine instruction is executed: check for a remembered handler and activate one (if any). This logic caused several problems: - if one instruction generates several warnings (errors) it's impossible to choose the right handler -- a handler for the first generated condition was chosen and remembered for activation. - mess with handling conditions in scopes different from the current one. - not putting generated warnings/errors into Warning Info (Diagnostic Area) is against The Standard. The patch changes the logic as follows: - Diagnostic Area is cleared on the beginning of each statement that either is able to generate warnings, or is able to work with tables. - at the end of a stored routine instruction, Diagnostic Area is left intact. - Diagnostic Area is checked after each stored routine instruction. If an instruction generates several condition, it's now possible to take a look at all of them and determine an appropriate handler. mysql-test/r/signal.result: Update result file: 1. handled conditions are not cleared any more; 2. reflect changes in signal.test mysql-test/r/signal_demo3.result: Update result file: handled conditions are not cleared any more. Due to playing with max_error_count, resulting warning lists have changed. mysql-test/r/sp-big.result: Update result file: handled conditions are not cleared any more. mysql-test/r/sp-bugs.result: Update result file: handled conditions are not cleared any more. mysql-test/r/sp-code.result: Update result file: 1. handled conditions are not cleared any more. 2. add result for a new test case in sp-code.test. mysql-test/r/sp-error.result: Update result file: 1. handled conditions are not cleared any more. 2. add result for a new test case in sp-error.test. mysql-test/r/sp.result: Update result file: handled conditions are not cleared any more. mysql-test/r/sp_trans.result: Update result file: handled conditions are not cleared any more. mysql-test/r/strict.result: Update result file: handled conditions are not cleared any more. mysql-test/r/view.result: Update result file: handled conditions are not cleared any more. mysql-test/suite/funcs_1/r/innodb_storedproc_02.result: Update result file: handled conditions are not cleared any more. mysql-test/suite/funcs_1/r/memory_storedproc_02.result: Update result file: handled conditions are not cleared any more. mysql-test/suite/funcs_1/r/myisam_storedproc_02.result: Update result file: handled conditions are not cleared any more. mysql-test/suite/funcs_1/r/storedproc.result: Update result file: handled conditions are not cleared any more. mysql-test/suite/rpl/r/rpl_row_sp005.result: Update result file: handled conditions are not cleared any more. mysql-test/suite/rpl/r/rpl_row_sp006_InnoDB.result: Update result file: handled conditions are not cleared any more. mysql-test/suite/rpl/r/rpl_row_trig003.result: Update result file: handled conditions are not cleared any more. mysql-test/t/signal.test: Make a test case more readable in the result file. mysql-test/t/sp-code.test: Add a test case for Bug#23032 checking that No Data takes precedence on Warning. mysql-test/t/sp-error.test: Adding test cases for: - Bug#23032 - Bug#36185 - Bug#5889 - Bug#9857 mysql-test/t/sp.test: Fixing test case to reflect behavioral changes made by the patch. sql/sp_head.cc: Reset the per-statement warning count before executing a stored procedure instruction. Move to a separate function code which checks the completion status of the executed statement and searches for a handler. Remove redundant code now that search for a handler is done after execution, errors are always pushed. sql/sp_pcontext.h: Remove unused code. sql/sp_rcontext.cc: - Polish sp_rcontext::find_handler(): use sp_rcontext::m_hfound instead of an extra local variable; - Remove sp_rcontext::handle_condition(); - Introduce sp_rcontext::activate_handler(), which prepares previously found handler for execution. - Move sp_rcontext::enter_handler() code into activate_handler(), because enter_handler() is used only from there; - Cleanups; - Introduce DBUG_EXECUTE_IF() for a test case in sp-code.test sql/sp_rcontext.h: - Remove unused code - Cleanups sql/sql_class.cc: Merge THD::raise_condition_no_handler() into THD::raise_condition(). After the patch raise_condition_no_handler() was called in raise_condition() only. sql/sql_class.h: Remove raise_condition_no_handler(). sql/sql_error.cc: Remove Warning_info::reserve_space() -- handled conditions are not cleared any more, so there is no need for RESIGNAL to re-push them. sql/sql_error.h: Remove Warning_info::reserve_space(). sql/sql_signal.cc: Handled conditions are not cleared any more, so there is no need for RESIGNAL to re-push them.
Diffstat (limited to 'sql')
-rw-r--r--sql/sp_head.cc193
-rw-r--r--sql/sp_pcontext.h7
-rw-r--r--sql/sp_rcontext.cc239
-rw-r--r--sql/sp_rcontext.h69
-rw-r--r--sql/sql_class.cc30
-rw-r--r--sql/sql_class.h17
-rw-r--r--sql/sql_error.cc8
-rw-r--r--sql/sql_error.h16
-rw-r--r--sql/sql_signal.cc12
9 files changed, 277 insertions, 314 deletions
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index 11f138e67be..2d3a32c7f7f 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -1076,6 +1076,104 @@ void sp_head::recursion_level_error(THD *thd)
/**
+ Find an SQL handler for any condition (warning or error) after execution
+ of a stored routine instruction. Basically, this function looks for an
+ appropriate SQL handler in RT-contexts. If an SQL handler is found, it is
+ remembered in the RT-context for future activation (the context can be
+ inactive at the moment).
+
+ If there is no pending condition, the function just returns.
+
+ If there was an error during the execution, an SQL handler for it will be
+ searched within the current and outer scopes.
+
+ There might be several errors in the Warning Info (that's possible by using
+ SIGNAL/RESIGNAL in nested scopes) -- the function is looking for an SQL
+ handler for the latest (current) error only.
+
+ If there was a warning during the execution, an SQL handler for it will be
+ searched within the current scope only.
+
+ If several warnings were thrown during the execution and there are different
+ SQL handlers for them, it is not determined which SQL handler will be chosen.
+ Only one SQL handler will be executed.
+
+ If warnings and errors were thrown during the execution, the error takes
+ precedence. I.e. error handler will be executed. If there is no handler
+ for that error, condition will remain unhandled.
+
+ Once a warning or an error has been handled it is not removed from
+ Warning Info.
+
+ According to The Standard (quoting PeterG):
+
+ An SQL procedure statement works like this ...
+ SQL/Foundation 13.5 <SQL procedure statement>
+ (General Rules) (greatly summarized) says:
+ (1) Empty diagnostics area, thus clearing the condition.
+ (2) Execute statement.
+ During execution, if Exception Condition occurs,
+ set Condition Area = Exception Condition and stop
+ statement.
+ During execution, if No Data occurs,
+ set Condition Area = No Data Condition and continue
+ statement.
+ During execution, if Warning occurs,
+ and Condition Area is not already full due to
+ an earlier No Data condition, set Condition Area
+ = Warning and continue statement.
+ (3) Finish statement.
+ At end of execution, if Condition Area is not
+ already full due to an earlier No Data or Warning,
+ set Condition Area = Successful Completion.
+ In effect, this system means there is a precedence:
+ Exception trumps No Data, No Data trumps Warning,
+ Warning trumps Successful Completion.
+
+ NB: "Procedure statements" include any DDL or DML or
+ control statements. So CREATE and DELETE and WHILE
+ and CALL and RETURN are procedure statements. But
+ DECLARE and END are not procedure statements.
+
+ @param thd thread handle
+ @param ctx runtime context of the stored routine
+*/
+
+static void
+find_handler_after_execution(THD *thd, sp_rcontext *ctx)
+{
+ if (thd->is_error())
+ {
+ ctx->find_handler(thd,
+ thd->stmt_da->sql_errno(),
+ thd->stmt_da->get_sqlstate(),
+ MYSQL_ERROR::WARN_LEVEL_ERROR,
+ thd->stmt_da->message());
+ }
+ else if (thd->warning_info->statement_warn_count())
+ {
+ List_iterator<MYSQL_ERROR> it(thd->warning_info->warn_list());
+ MYSQL_ERROR *err;
+ while ((err= it++))
+ {
+ if (err->get_level() != MYSQL_ERROR::WARN_LEVEL_WARN &&
+ err->get_level() != MYSQL_ERROR::WARN_LEVEL_NOTE)
+ continue;
+
+ if (ctx->find_handler(thd,
+ err->get_sql_errno(),
+ err->get_sqlstate(),
+ err->get_level(),
+ err->get_message_text()))
+ {
+ break;
+ }
+ }
+ }
+}
+
+
+/**
Execute the routine. The main instruction jump loop is there.
Assume the parameters already set.
@todo
@@ -1096,7 +1194,7 @@ sp_head::execute(THD *thd)
LEX_STRING saved_cur_db_name=
{ saved_cur_db_name_buf, sizeof(saved_cur_db_name_buf) };
bool cur_db_changed= FALSE;
- sp_rcontext *ctx;
+ sp_rcontext *ctx= thd->spcont;
bool err_status= FALSE;
uint ip= 0;
ulong save_sql_mode;
@@ -1157,8 +1255,6 @@ sp_head::execute(THD *thd)
goto done;
}
- if ((ctx= thd->spcont))
- ctx->clear_handler();
thd->is_slave_error= 0;
old_arena= thd->stmt_arena;
@@ -1243,7 +1339,6 @@ sp_head::execute(THD *thd)
do
{
sp_instr *i;
- uint hip;
#if defined(ENABLED_PROFILING)
/*
@@ -1265,6 +1360,9 @@ sp_head::execute(THD *thd)
break;
}
+ /* Reset number of warnings for this query. */
+ thd->warning_info->reset_for_next_command();
+
DBUG_PRINT("execute", ("Instruction %u", ip));
/*
@@ -1309,40 +1407,28 @@ sp_head::execute(THD *thd)
free_root(&execute_mem_root, MYF(0));
/*
- Check if an exception has occurred and a handler has been found
- Note: We have to check even if err_status == FALSE, since warnings (and
- some errors) don't return a non-zero value. We also have to check even
- if thd->killed != 0, since some errors return with this even when a
- handler has been found (e.g. "bad data").
+ Find and process SQL handlers unless it is a fatal error (fatal
+ errors are not catchable by SQL handlers) or the connection has been
+ killed during execution.
*/
- if (ctx)
+ if (!thd->is_fatal_error && !thd->killed_errno())
{
- uint handler_index;
+ /*
+ Find SQL handler in the appropriate RT-contexts:
+ - warnings can be handled by SQL handlers within
+ the current scope only;
+ - errors can be handled by any SQL handler from outer scope.
+ */
+ find_handler_after_execution(thd, ctx);
- switch (ctx->found_handler(& hip, & handler_index)) {
- case SP_HANDLER_NONE:
- break;
- case SP_HANDLER_CONTINUE:
- thd->restore_active_arena(&execute_arena, &backup_arena);
- thd->set_n_backup_active_arena(&execute_arena, &backup_arena);
- ctx->push_hstack(i->get_cont_dest());
- /* Fall through */
- default:
- if (ctx->end_partial_result_set)
- thd->protocol->end_partial_result_set(thd);
- ip= hip;
+ /* If found, activate handler for the current scope. */
+ if (ctx->activate_handler(thd, &ip, i, &execute_arena, &backup_arena))
err_status= FALSE;
- ctx->clear_handler();
- ctx->enter_handler(hip, handler_index);
- thd->clear_error();
- thd->is_fatal_error= 0;
- thd->killed= THD::NOT_KILLED;
- thd->mysys_var->abort= 0;
- continue;
- }
-
- ctx->end_partial_result_set= FALSE;
}
+
+ /* Reset sp_rcontext::end_partial_result_set flag. */
+ ctx->end_partial_result_set= FALSE;
+
} while (!err_status && !thd->killed && !thd->is_fatal_error);
#if defined(ENABLED_PROFILING)
@@ -3037,23 +3123,14 @@ sp_instr_set::exec_core(THD *thd, uint *nextp)
{
int res= thd->spcont->set_variable(thd, m_offset, &m_value);
- if (res && thd->spcont->found_handler_here())
+ if (res)
{
- /*
- Failed to evaluate the value, and a handler has been found. Reset the
- variable to NULL.
- */
+ /* Failed to evaluate the value. Reset the variable to NULL. */
if (thd->spcont->set_variable(thd, m_offset, 0))
{
/* If this also failed, let's abort. */
-
- sp_rcontext *spcont= thd->spcont;
-
- thd->spcont= NULL; /* Avoid handlers */
- my_error(ER_OUT_OF_RESOURCES, MYF(0));
- spcont->clear_handler();
- thd->spcont= spcont;
+ my_error(ER_OUT_OF_RESOURCES, MYF(ME_FATALERROR));
}
}
@@ -3586,18 +3663,6 @@ sp_instr_copen::execute(THD *thd, uint *nextp)
if (thd->stmt_arena->free_list)
cleanup_items(thd->stmt_arena->free_list);
thd->stmt_arena= old_arena;
- /*
- Work around the fact that errors in selects are not returned properly
- (but instead converted into a warning), so if a condition handler
- caught, we have lost the result code.
- */
- if (!res)
- {
- uint dummy1, dummy2;
-
- if (thd->spcont->found_handler(&dummy1, &dummy2))
- res= -1;
- }
/* TODO: Assert here that we either have an error or a cursor */
}
DBUG_RETURN(res);
@@ -3773,13 +3838,11 @@ sp_instr_set_case_expr::exec_core(THD *thd, uint *nextp)
{
int res= thd->spcont->set_case_expr(thd, m_case_expr_id, &m_case_expr);
- if (res &&
- !thd->spcont->get_case_expr(m_case_expr_id) &&
- thd->spcont->found_handler_here())
+ if (res && !thd->spcont->get_case_expr(m_case_expr_id))
{
/*
Failed to evaluate the value, the case expression is still not
- initialized, and a handler has been found. Set to NULL so we can continue.
+ initialized. Set to NULL so we can continue.
*/
Item *null_item= new Item_null();
@@ -3788,13 +3851,7 @@ sp_instr_set_case_expr::exec_core(THD *thd, uint *nextp)
thd->spcont->set_case_expr(thd, m_case_expr_id, &null_item))
{
/* If this also failed, we have to abort. */
-
- sp_rcontext *spcont= thd->spcont;
-
- thd->spcont= NULL; /* Avoid handlers */
- my_error(ER_OUT_OF_RESOURCES, MYF(0));
- spcont->clear_handler();
- thd->spcont= spcont;
+ my_error(ER_OUT_OF_RESOURCES, MYF(ME_FATALERROR));
}
}
else
diff --git a/sql/sp_pcontext.h b/sql/sp_pcontext.h
index b12d5362c6b..c27c7d22da2 100644
--- a/sql/sp_pcontext.h
+++ b/sql/sp_pcontext.h
@@ -332,13 +332,6 @@ public:
int
push_cond(LEX_STRING *name, sp_cond_type_t *val);
- inline void
- pop_cond(uint num)
- {
- while (num--)
- pop_dynamic(&m_conds);
- }
-
sp_cond_type_t *
find_cond(LEX_STRING *name, my_bool scoped=0);
diff --git a/sql/sp_rcontext.cc b/sql/sp_rcontext.cc
index b08f8008b59..e76a5e9ebde 100644
--- a/sql/sp_rcontext.cc
+++ b/sql/sp_rcontext.cc
@@ -171,48 +171,50 @@ sp_rcontext::set_return_value(THD *thd, Item **return_value_item)
#define IS_NOT_FOUND_CONDITION(S) ((S)[0] == '0' && (S)[1] == '2')
#define IS_EXCEPTION_CONDITION(S) ((S)[0] != '0' || (S)[1] > '2')
-/*
- Find a handler for the given errno.
- This is called from all error message functions (e.g. push_warning,
- net_send_error, et al) when a sp_rcontext is in effect. If a handler
- is found, no error is sent, and the the SP execution loop will instead
- invoke the found handler.
- This might be called several times before we get back to the execution
- loop, so m_hfound can be >= 0 if a handler has already been found.
- (In which case we don't search again - the first found handler will
- be used.)
- Handlers are pushed on the stack m_handler, with the latest/innermost
+/**
+ Find an SQL handler for the given error.
+
+ SQL handlers are pushed on the stack m_handler, with the latest/innermost
one on the top; we then search for matching handlers from the top and
down.
+
We search through all the handlers, looking for the most specific one
(sql_errno more specific than sqlstate more specific than the rest).
Note that mysql error code handlers is a MySQL extension, not part of
the standard.
- SYNOPSIS
- sql_errno The error code
- level Warning level
+ SQL handlers for warnings are searched in the current scope only.
- RETURN
- 1 if a handler was found, m_hfound is set to its index (>= 0)
- 0 if not found, m_hfound is -1
+ SQL handlers for errors are searched in the current and in outer scopes.
+ That's why finding and activation of handler must be separated: an errror
+ handler might be located in the outer scope, which is not active at the
+ moment. Before such handler can be activated, execution flow should
+ unwind to that scope.
+
+ Found SQL handler is remembered in m_hfound for future activation.
+ If no handler is found, m_hfound is -1.
+
+ @param thd Thread handle
+ @param sql_errno The error code
+ @param sqlstate The error SQL state
+ @param level The error level
+ @param msg The error message
+
+ @retval TRUE if an SQL handler was found
+ @retval FALSE otherwise
*/
bool
sp_rcontext::find_handler(THD *thd,
uint sql_errno,
- const char* sqlstate,
+ const char *sqlstate,
MYSQL_ERROR::enum_warning_level level,
- const char* msg,
- MYSQL_ERROR ** cond_hdl)
+ const char *msg)
{
- if (m_hfound >= 0)
- {
- *cond_hdl= NULL;
- return TRUE; // Already got one
- }
+ int i= m_hcount;
- int i= m_hcount, found= -1;
+ /* Reset previously found handler. */
+ m_hfound= -1;
/*
If this is a fatal sub-statement error, and this runtime
@@ -240,105 +242,56 @@ sp_rcontext::find_handler(THD *thd,
{
case sp_cond_type_t::number:
if (sql_errno == cond->mysqlerr &&
- (found < 0 || m_handler[found].cond->type > sp_cond_type_t::number))
- found= i; // Always the most specific
+ (m_hfound < 0 || m_handler[m_hfound].cond->type > sp_cond_type_t::number))
+ m_hfound= i; // Always the most specific
break;
case sp_cond_type_t::state:
if (strcmp(sqlstate, cond->sqlstate) == 0 &&
- (found < 0 || m_handler[found].cond->type > sp_cond_type_t::state))
- found= i;
+ (m_hfound < 0 || m_handler[m_hfound].cond->type > sp_cond_type_t::state))
+ m_hfound= i;
break;
case sp_cond_type_t::warning:
if ((IS_WARNING_CONDITION(sqlstate) ||
level == MYSQL_ERROR::WARN_LEVEL_WARN) &&
- found < 0)
- found= i;
+ m_hfound < 0)
+ m_hfound= i;
break;
case sp_cond_type_t::notfound:
- if (IS_NOT_FOUND_CONDITION(sqlstate) && found < 0)
- found= i;
+ if (IS_NOT_FOUND_CONDITION(sqlstate) && m_hfound < 0)
+ m_hfound= i;
break;
case sp_cond_type_t::exception:
if (IS_EXCEPTION_CONDITION(sqlstate) &&
level == MYSQL_ERROR::WARN_LEVEL_ERROR &&
- found < 0)
- found= i;
+ m_hfound < 0)
+ m_hfound= i;
break;
}
}
- if (found < 0)
- {
- /*
- Only "exception conditions" are propagated to handlers in calling
- contexts. If no handler is found locally for a "completion condition"
- (warning or "not found") we will simply resume execution.
- */
- if (m_prev_runtime_ctx && IS_EXCEPTION_CONDITION(sqlstate) &&
- level == MYSQL_ERROR::WARN_LEVEL_ERROR)
- return m_prev_runtime_ctx->find_handler(thd,
- sql_errno,
- sqlstate,
- level,
- msg,
- cond_hdl);
- *cond_hdl= NULL;
- return FALSE;
- }
-
- m_hfound= found;
- MYSQL_ERROR *raised= NULL;
- DBUG_ASSERT(m_hfound >= 0);
- DBUG_ASSERT((uint) m_hfound < m_root_parsing_ctx->max_handler_index());
- raised= & m_raised_conditions[m_hfound];
- raised->clear();
- raised->set(sql_errno, sqlstate, level, msg);
-
- *cond_hdl= raised;
- return TRUE;
-}
-
-/*
- Handle the error for a given errno.
- The severity of the error is adjusted depending of the current sql_mode.
- If an handler is present for the error (see find_handler()),
- this function will return true.
- If a handler is found and if the severity of the error indicate
- that the current instruction executed should abort,
- the flag thd->net.report_error is also set.
- This will cause the execution of the current instruction in a
- sp_instr* to fail, and give control to the handler code itself
- in the sp_head::execute() loop.
-
- SYNOPSIS
- sql_errno The error code
- level Warning level
- thd The current thread
+ if (m_hfound >= 0)
+ {
+ DBUG_ASSERT((uint) m_hfound < m_root_parsing_ctx->max_handler_index());
- RETURN
- TRUE if a handler was found.
- FALSE if no handler was found.
-*/
-bool
-sp_rcontext::handle_condition(THD *thd,
- uint sql_errno,
- const char* sqlstate,
- MYSQL_ERROR::enum_warning_level level,
- const char* msg,
- MYSQL_ERROR ** cond_hdl)
-{
- MYSQL_ERROR::enum_warning_level elevated_level= level;
+ m_raised_conditions[m_hfound].clear();
+ m_raised_conditions[m_hfound].set(sql_errno, sqlstate, level, msg);
+ return TRUE;
+ }
- /* Depending on the sql_mode of execution,
- warnings may be considered errors */
- if ((level == MYSQL_ERROR::WARN_LEVEL_WARN) &&
- thd->really_abort_on_warning())
+ /*
+ Only "exception conditions" are propagated to handlers in calling
+ contexts. If no handler is found locally for a "completion condition"
+ (warning or "not found") we will simply resume execution.
+ */
+ if (m_prev_runtime_ctx && IS_EXCEPTION_CONDITION(sqlstate) &&
+ level == MYSQL_ERROR::WARN_LEVEL_ERROR)
{
- elevated_level= MYSQL_ERROR::WARN_LEVEL_ERROR;
+ return m_prev_runtime_ctx->find_handler(thd, sql_errno, sqlstate,
+ level, msg);
}
- return find_handler(thd, sql_errno, sqlstate, elevated_level, msg, cond_hdl);
+ return FALSE;
}
void
@@ -384,7 +337,9 @@ sp_rcontext::pop_handlers(uint count)
{
DBUG_ENTER("sp_rcontext::pop_handlers");
DBUG_ASSERT(m_hcount >= count);
+
m_hcount-= count;
+
DBUG_PRINT("info", ("m_hcount: %d", m_hcount));
DBUG_VOID_RETURN;
}
@@ -394,7 +349,9 @@ sp_rcontext::push_hstack(uint h)
{
DBUG_ENTER("sp_rcontext::push_hstack");
DBUG_ASSERT(m_hsp < m_root_parsing_ctx->max_handler_index());
+
m_hstack[m_hsp++]= h;
+
DBUG_PRINT("info", ("m_hsp: %d", m_hsp));
DBUG_VOID_RETURN;
}
@@ -405,21 +362,74 @@ sp_rcontext::pop_hstack()
uint handler;
DBUG_ENTER("sp_rcontext::pop_hstack");
DBUG_ASSERT(m_hsp);
+
handler= m_hstack[--m_hsp];
+
DBUG_PRINT("info", ("m_hsp: %d", m_hsp));
DBUG_RETURN(handler);
}
-void
-sp_rcontext::enter_handler(uint hip, uint hindex)
+/**
+ Prepare found handler to be executed.
+
+ @retval TRUE if an SQL handler is activated (was found) and IP of the
+ first handler instruction.
+ @retval FALSE if there is no active handler
+*/
+
+bool
+sp_rcontext::activate_handler(THD *thd,
+ uint *ip,
+ sp_instr *instr,
+ Query_arena *execute_arena,
+ Query_arena *backup_arena)
{
- DBUG_ENTER("sp_rcontext::enter_handler");
- DBUG_ASSERT(m_ihsp < m_root_parsing_ctx->max_handler_index());
- m_in_handler[m_ihsp].ip= hip;
- m_in_handler[m_ihsp].index= hindex;
- m_ihsp++;
- DBUG_PRINT("info", ("m_ihsp: %d", m_ihsp));
- DBUG_VOID_RETURN;
+ if (m_hfound < 0)
+ return FALSE;
+
+ switch (m_handler[m_hfound].type) {
+ case SP_HANDLER_NONE:
+ break;
+
+ case SP_HANDLER_CONTINUE:
+ thd->restore_active_arena(execute_arena, backup_arena);
+ thd->set_n_backup_active_arena(execute_arena, backup_arena);
+ push_hstack(instr->get_cont_dest());
+
+ /* Fall through */
+
+ default:
+ /* End aborted result set. */
+
+ if (end_partial_result_set)
+ thd->protocol->end_partial_result_set(thd);
+
+ /* Enter handler. */
+
+ DBUG_ASSERT(m_ihsp < m_root_parsing_ctx->max_handler_index());
+ DBUG_ASSERT(m_hfound >= 0);
+
+ m_in_handler[m_ihsp].ip= m_handler[m_hfound].handler;
+ m_in_handler[m_ihsp].index= m_hfound;
+ m_ihsp++;
+
+ DBUG_PRINT("info", ("Entering handler..."));
+ DBUG_PRINT("info", ("m_ihsp: %d", m_ihsp));
+
+ /* Reset error state. */
+
+ thd->clear_error();
+ thd->killed= THD::NOT_KILLED; // Some errors set thd->killed
+ // (e.g. "bad data").
+
+ /* Return IP of the activated SQL handler. */
+ *ip= m_handler[m_hfound].handler;
+
+ /* Reset found handler. */
+ m_hfound= -1;
+ }
+
+ return TRUE;
}
void
@@ -427,9 +437,11 @@ sp_rcontext::exit_handler()
{
DBUG_ENTER("sp_rcontext::exit_handler");
DBUG_ASSERT(m_ihsp);
+
uint hindex= m_in_handler[m_ihsp-1].index;
m_raised_conditions[hindex].clear();
m_ihsp-= 1;
+
DBUG_PRINT("info", ("m_ihsp: %d", m_ihsp));
DBUG_VOID_RETURN;
}
@@ -567,6 +579,11 @@ sp_cursor::fetch(THD *thd, List<struct sp_variable> *vars)
return -1;
}
+ DBUG_EXECUTE_IF("bug23032_emit_warning",
+ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_UNKNOWN_ERROR,
+ ER(ER_UNKNOWN_ERROR)););
+
result.set_spvar_list(vars);
/* Attempt to fetch one row */
diff --git a/sql/sp_rcontext.h b/sql/sp_rcontext.h
index fad253706cb..1af758ed0af 100644
--- a/sql/sp_rcontext.h
+++ b/sql/sp_rcontext.h
@@ -131,67 +131,40 @@ class sp_rcontext : public Sql_alloc
return m_return_value_set;
}
+ /*
+ SQL handlers support.
+ */
+
void push_handler(struct sp_cond_type *cond, uint h, int type);
void pop_handlers(uint count);
- // Returns 1 if a handler was found, 0 otherwise.
bool
find_handler(THD *thd,
uint sql_errno,
- const char* sqlstate,
+ const char *sqlstate,
MYSQL_ERROR::enum_warning_level level,
- const char* msg,
- MYSQL_ERROR ** cond_hdl);
+ const char *msg);
- // If there is an error handler for this error, handle it and return TRUE.
- bool
- handle_condition(THD *thd,
- uint sql_errno,
- const char* sqlstate,
- MYSQL_ERROR::enum_warning_level level,
- const char* msg,
- MYSQL_ERROR ** cond_hdl);
-
- // Returns handler type and sets *ip to location if one was found
- inline int
- found_handler(uint *ip, uint *index)
- {
- if (m_hfound < 0)
- return SP_HANDLER_NONE;
- *ip= m_handler[m_hfound].handler;
- *index= m_hfound;
- return m_handler[m_hfound].type;
- }
-
- MYSQL_ERROR* raised_condition() const;
-
- // Returns true if we found a handler in this context
- inline bool
- found_handler_here()
- {
- return (m_hfound >= 0);
- }
+ MYSQL_ERROR *
+ raised_condition() const;
- // Clears the handler find state
- inline void
- clear_handler()
- {
- m_hfound= -1;
- }
+ void
+ push_hstack(uint h);
- void push_hstack(uint h);
+ uint
+ pop_hstack();
- uint pop_hstack();
+ bool
+ activate_handler(THD *thd,
+ uint *ip,
+ sp_instr *instr,
+ Query_arena *execute_arena,
+ Query_arena *backup_arena);
- /**
- Enter a SQL exception handler.
- @param hip the handler instruction pointer
- @param index the handler index
- */
- void enter_handler(uint hip, uint index);
- void exit_handler();
+ void
+ exit_handler();
void
push_cursor(sp_lex_keeper *lex_keeper, sp_instr_cpush *i);
@@ -199,7 +172,7 @@ class sp_rcontext : public Sql_alloc
void
pop_cursors(uint count);
- void
+ inline void
pop_all_cursors()
{
pop_cursors(m_ccount);
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 7eb4911c744..60a871e9e88 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -845,35 +845,6 @@ MYSQL_ERROR* THD::raise_condition(uint sql_errno,
}
}
- /*
- If a continue handler is found, the error message will be cleared
- by the stored procedures code.
- */
- if (!is_fatal_error && spcont &&
- spcont->handle_condition(this, sql_errno, sqlstate, level, msg, &cond))
- {
- /*
- Do not push any warnings, a handled error must be completely
- silenced.
- */
- DBUG_RETURN(cond);
- }
-
- /* Un-handled conditions */
-
- cond= raise_condition_no_handler(sql_errno, sqlstate, level, msg);
- DBUG_RETURN(cond);
-}
-
-MYSQL_ERROR*
-THD::raise_condition_no_handler(uint sql_errno,
- const char* sqlstate,
- MYSQL_ERROR::enum_warning_level level,
- const char* msg)
-{
- MYSQL_ERROR *cond= NULL;
- DBUG_ENTER("THD::raise_condition_no_handler");
-
query_cache_abort(&query_cache_tls);
/* FIXME: broken special case */
@@ -886,6 +857,7 @@ THD::raise_condition_no_handler(uint sql_errno,
cond= warning_info->push_warning(this, sql_errno, sqlstate, level, msg);
DBUG_RETURN(cond);
}
+
extern "C"
void *thd_alloc(MYSQL_THD thd, unsigned int size)
{
diff --git a/sql/sql_class.h b/sql/sql_class.h
index c719f5a09ad..b23b65dae2f 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -2800,23 +2800,6 @@ private:
MYSQL_ERROR::enum_warning_level level,
const char* msg);
- /**
- Raise a generic SQL condition, without activation any SQL condition
- handlers.
- This method is necessary to support the RESIGNAL statement,
- which is allowed to bypass SQL exception handlers.
- @param sql_errno the condition error number
- @param sqlstate the condition SQLSTATE
- @param level the condition level
- @param msg the condition message text
- @return The condition raised, or NULL
- */
- MYSQL_ERROR*
- raise_condition_no_handler(uint sql_errno,
- const char* sqlstate,
- MYSQL_ERROR::enum_warning_level level,
- const char* msg);
-
public:
/** Overloaded to guard query/query_length fields */
virtual void set_statement(Statement *stmt);
diff --git a/sql/sql_error.cc b/sql/sql_error.cc
index e5d0f79b2d7..8c038e10a1f 100644
--- a/sql/sql_error.cc
+++ b/sql/sql_error.cc
@@ -494,14 +494,6 @@ void Warning_info::clear_warning_info(ulonglong warn_id_arg)
m_current_row_for_warning= 1; /* Start counting from the first row */
}
-void Warning_info::reserve_space(THD *thd, uint count)
-{
- /* Make room for count conditions */
- while ((m_warn_list.elements > 0) &&
- ((m_warn_list.elements + count) > thd->variables.max_error_count))
- m_warn_list.pop();
-}
-
/**
Append warnings only if the original contents of the routine
warning info was replaced.
diff --git a/sql/sql_error.h b/sql/sql_error.h
index 9e649a004df..87e98e27673 100644
--- a/sql/sql_error.h
+++ b/sql/sql_error.h
@@ -153,8 +153,8 @@ private:
Representation of a SQL condition.
A SQL condition can be a completion condition (note, warning),
or an exception condition (error, not found).
- @note This class is named MYSQL_ERROR instead of SQL_condition for historical reasons,
- to facilitate merging code with previous releases.
+ @note This class is named MYSQL_ERROR instead of SQL_condition for
+ historical reasons, to facilitate merging code with previous releases.
*/
class MYSQL_ERROR : public Sql_alloc
{
@@ -471,18 +471,6 @@ public:
ulong statement_warn_count() const { return m_statement_warn_count; }
- /**
- Reserve some space in the condition area.
- This is a privileged operation, reserved for the RESIGNAL implementation,
- as only the RESIGNAL statement is allowed to remove conditions from
- the condition area.
- For other statements, new conditions are not added to the condition
- area once the condition area is full.
- @param thd The current thread
- @param count The number of slots to reserve
- */
- void reserve_space(THD *thd, uint count);
-
/** Add a new condition to the current list. */
MYSQL_ERROR *push_warning(THD *thd,
uint sql_errno, const char* sqlstate,
diff --git a/sql/sql_signal.cc b/sql/sql_signal.cc
index f340da373e8..09e9a828fa1 100644
--- a/sql/sql_signal.cc
+++ b/sql/sql_signal.cc
@@ -499,18 +499,6 @@ bool Resignal_statement::execute(THD *thd)
}
/* RESIGNAL with signal_value */
-
- /* Make room for 2 conditions */
- thd->warning_info->reserve_space(thd, 2);
-
- MYSQL_ERROR *raised= NULL;
- raised= thd->raise_condition_no_handler(signaled->get_sql_errno(),
- signaled->get_sqlstate(),
- signaled->get_level(),
- signaled->get_message_text());
- if (raised)
- raised->copy_opt_attributes(signaled);
-
result= raise_condition(thd, signaled);
DBUG_RETURN(result);