Skip to content

Commit 4a2989a

Browse files
committed
Bug#16041903 CONTINUE HANDLER NOT INVOKED IN A STORED
FUNCTION AFTER A LOCK WAIT TIMEOUT Overview -------- When the SQL layer is executing a sub statement (stored function or trigger), some errors cannot be safely recovered until we leave sub statement mode. This is the case for e.g. ER_LOCK_WAIT_TIMEOUT. When this error is caught, the handler does a callback to "mark_transaction_for_rollback()" in the SQL layer, which makes the transaction be rolled back implicitly. In "mark_transaction_for_rollback()", the flag "THD::is_fatal_sub_stmt_error" is set. This flag is used to effectievly disable the execution of a condition handler in a stored function while handling the error. The flag is reset when leaving sub statement mode, in "restore_sub_statement_state()", if leaving the last level of sub statements (i.e., nested sub statements are handled by keeping the value of the flag when going to the previous "stack frame"). The problem here is that the flag is set in "mark_transaction_for_rollback()" even if the SQL layer is not in sub statement mode. If this happens, and the transaction afterwards calls a stored function with a condition handler, the handler will be disabled since the flag is already set when calling the function. Suggested fix ------------- The suggested fix is to set "is_fatal_sub_stmt_error" in "mark_transaction_for_rollback()" only in the case where the SQL layer is currently executing a sub statement ("in_sub_stmt==true"). If not in a sub statement, setting the "is_fatal_sub_stmt_error" flag really has no meaning. Additionally, when the flag is set when the SQL layer is not actually in a sub statement, the flag is never cleared since this happens only in "THD::restore_sub_statement_state()" when leaving sub statement mode.
1 parent a86cea3 commit 4a2989a

File tree

3 files changed

+35
-25
lines changed

3 files changed

+35
-25
lines changed

sql/sql_base.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3949,7 +3949,7 @@ request_backoff_action(enum_open_table_action action_arg,
39493949
if (action_arg != OT_REOPEN_TABLES && m_has_locks)
39503950
{
39513951
my_error(ER_LOCK_DEADLOCK, MYF(0));
3952-
mark_transaction_to_rollback(m_thd, true);
3952+
m_thd->mark_transaction_to_rollback(true);
39533953
return TRUE;
39543954
}
39553955
/*

sql/sql_class.cc

Lines changed: 32 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -861,7 +861,7 @@ THD::THD(bool enable_plugins)
861861
next_to_commit(NULL),
862862
is_fatal_error(0),
863863
transaction_rollback_request(0),
864-
is_fatal_sub_stmt_error(0),
864+
is_fatal_sub_stmt_error(false),
865865
rand_used(0),
866866
time_zone_used(0),
867867
in_lock_tables(0),
@@ -3973,7 +3973,8 @@ extern "C" int thd_binlog_format(const MYSQL_THD thd)
39733973

39743974
extern "C" void thd_mark_transaction_to_rollback(MYSQL_THD thd, bool all)
39753975
{
3976-
mark_transaction_to_rollback(thd, all);
3976+
DBUG_ASSERT(thd);
3977+
thd->mark_transaction_to_rollback(all);
39773978
}
39783979

39793980
extern "C" bool thd_binlog_filter_ok(const MYSQL_THD thd)
@@ -4201,13 +4202,21 @@ void THD::restore_sub_statement_state(Sub_statement_state *backup)
42014202
limit_found_rows= backup->limit_found_rows;
42024203
set_sent_row_count(backup->sent_row_count);
42034204
client_capabilities= backup->client_capabilities;
4205+
42044206
/*
42054207
If we've left sub-statement mode, reset the fatal error flag.
42064208
Otherwise keep the current value, to propagate it up the sub-statement
42074209
stack.
4210+
4211+
NOTE: is_fatal_sub_stmt_error can be set only if we've been in the
4212+
sub-statement mode.
42084213
*/
4214+
4215+
DBUG_ASSERT((is_fatal_sub_stmt_error && !in_sub_stmt) ||
4216+
!is_fatal_sub_stmt_error);
4217+
42094218
if (!in_sub_stmt)
4210-
is_fatal_sub_stmt_error= FALSE;
4219+
is_fatal_sub_stmt_error= false;
42114220

42124221
if ((variables.option_bits & OPTION_BIN_LOG) && is_update_query(lex->sql_command) &&
42134222
!is_current_stmt_binlog_format_row())
@@ -4460,27 +4469,30 @@ void THD::get_definer(LEX_USER *definer)
44604469
/**
44614470
Mark transaction to rollback and mark error as fatal to a sub-statement.
44624471
4463-
@param thd Thread handle
44644472
@param all TRUE <=> rollback main transaction.
44654473
*/
44664474

4467-
void mark_transaction_to_rollback(THD *thd, bool all)
4475+
void THD::mark_transaction_to_rollback(bool all)
44684476
{
4469-
if (thd)
4470-
{
4471-
thd->is_fatal_sub_stmt_error= TRUE;
4472-
thd->transaction_rollback_request= all;
4473-
/*
4474-
Aborted transactions can not be IGNOREd.
4475-
Switch off the IGNORE flag for the current
4476-
SELECT_LEX. This should allow my_error()
4477-
to report the error and abort the execution
4478-
flow, even in presence
4479-
of IGNORE clause.
4480-
*/
4481-
if (thd->lex->current_select())
4482-
thd->lex->current_select()->no_error= FALSE;
4483-
}
4477+
/*
4478+
There is no point in setting is_fatal_sub_stmt_error unless
4479+
we are actually in_sub_stmt.
4480+
*/
4481+
if (in_sub_stmt)
4482+
is_fatal_sub_stmt_error= true;
4483+
4484+
transaction_rollback_request= all;
4485+
4486+
/*
4487+
Aborted transactions can not be IGNOREd.
4488+
Switch off the IGNORE flag for the current
4489+
SELECT_LEX. This should allow my_error()
4490+
to report the error and abort the execution
4491+
flow, even in presence
4492+
of IGNORE clause.
4493+
*/
4494+
if (lex->current_select())
4495+
lex->current_select()->no_error= false;
44844496
}
44854497

44864498

sql/sql_class.h

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -637,9 +637,6 @@ typedef struct system_status_var
637637

638638
#define last_system_status_var questions
639639

640-
void mark_transaction_to_rollback(THD *thd, bool all);
641-
642-
643640
/**
644641
Get collation by name, send error to client on failure.
645642
@param name Collation name
@@ -3913,6 +3910,8 @@ class THD :public MDL_context_owner,
39133910
LEX_STRING get_invoker_host() { return invoker_host; }
39143911
bool has_invoker() { return invoker_user.length > 0; }
39153912

3913+
void mark_transaction_to_rollback(bool all);
3914+
39163915
#ifndef DBUG_OFF
39173916
private:
39183917
int gis_debug; // Storage for "SELECT ST_GIS_DEBUG(param);"
@@ -5252,7 +5251,6 @@ void add_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var);
52525251

52535252
void add_diff_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var,
52545253
STATUS_VAR *dec_var);
5255-
void mark_transaction_to_rollback(THD *thd, bool all);
52565254

52575255
/* Inline functions */
52585256

0 commit comments

Comments
 (0)