mirror of
https://github.com/MariaDB/server.git
synced 2025-07-29 05:21:33 +03:00
Merge epotemkin@bk-internal.mysql.com:/home/bk/mysql-5.0-opt
into moonbone.local:/mnt/gentoo64/work/24989-bug-5.0-opt-mysql
This commit is contained in:
@ -32,3 +32,69 @@ select sum(id) from t3;
|
|||||||
sum(id)
|
sum(id)
|
||||||
2199024304128
|
2199024304128
|
||||||
drop table t1,t2,t3,t4;
|
drop table t1,t2,t3,t4;
|
||||||
|
CREATE TABLE t1 (f1 int NOT NULL) ENGINE=InnoDB;
|
||||||
|
CREATE TABLE t2 (f2 int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT) ENGINE=InnoDB;
|
||||||
|
CREATE TRIGGER t1_bi before INSERT
|
||||||
|
ON t1 FOR EACH ROW
|
||||||
|
BEGIN
|
||||||
|
DECLARE CONTINUE HANDLER FOR SQLSTATE '40001' SET @a:= 'deadlock';
|
||||||
|
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET @a:= 'exception';
|
||||||
|
INSERT INTO t2 (f2) VALUES (1);
|
||||||
|
DELETE FROM t2 WHERE f2 = 1;
|
||||||
|
END;|
|
||||||
|
CREATE PROCEDURE proc24989()
|
||||||
|
BEGIN
|
||||||
|
DECLARE CONTINUE HANDLER FOR SQLSTATE '40001' SET @b:= 'deadlock';
|
||||||
|
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET @a:= 'exception';
|
||||||
|
INSERT INTO t2 (f2) VALUES (1);
|
||||||
|
DELETE FROM t2 WHERE f2 = 1;
|
||||||
|
END;|
|
||||||
|
create procedure proc24989_2()
|
||||||
|
deterministic
|
||||||
|
begin
|
||||||
|
declare continue handler for sqlexception
|
||||||
|
select 'Outer handler' as 'exception';
|
||||||
|
insert into t1 values(1);
|
||||||
|
select "continued";
|
||||||
|
end|
|
||||||
|
start transaction;
|
||||||
|
insert into t1 values(1);
|
||||||
|
start transaction;
|
||||||
|
insert into t2 values(123);
|
||||||
|
insert into t1 values(1);
|
||||||
|
insert into t1 values(1);
|
||||||
|
ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
|
||||||
|
select @a;
|
||||||
|
@a
|
||||||
|
NULL
|
||||||
|
select * from t2;
|
||||||
|
f2
|
||||||
|
commit;
|
||||||
|
start transaction;
|
||||||
|
insert into t1 values(1);
|
||||||
|
start transaction;
|
||||||
|
insert into t2 values(123);
|
||||||
|
call proc24989();
|
||||||
|
insert into t1 values(1);
|
||||||
|
select @a,@b;
|
||||||
|
@a @b
|
||||||
|
exception deadlock
|
||||||
|
select * from t2;
|
||||||
|
f2
|
||||||
|
commit;
|
||||||
|
start transaction;
|
||||||
|
insert into t1 values(1);
|
||||||
|
start transaction;
|
||||||
|
insert into t2 values(123);
|
||||||
|
call proc24989_2();
|
||||||
|
insert into t1 values(1);
|
||||||
|
commit;
|
||||||
|
exception
|
||||||
|
Outer handler
|
||||||
|
continued
|
||||||
|
continued
|
||||||
|
select * from t2;
|
||||||
|
f2
|
||||||
|
drop procedure proc24989;
|
||||||
|
drop procedure proc24989_2;
|
||||||
|
drop table t1,t2;
|
||||||
|
@ -44,3 +44,109 @@ INSERT INTO t3 SELECT concat(id),id from t2 ORDER BY -id;
|
|||||||
INSERT INTO t4 SELECT * from t3 ORDER BY concat(a);
|
INSERT INTO t4 SELECT * from t3 ORDER BY concat(a);
|
||||||
select sum(id) from t3;
|
select sum(id) from t3;
|
||||||
drop table t1,t2,t3,t4;
|
drop table t1,t2,t3,t4;
|
||||||
|
|
||||||
|
#
|
||||||
|
# Bug#24989: The DEADLOCK error is improperly handled by InnoDB.
|
||||||
|
#
|
||||||
|
CREATE TABLE t1 (f1 int NOT NULL) ENGINE=InnoDB;
|
||||||
|
CREATE TABLE t2 (f2 int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT) ENGINE=InnoDB;
|
||||||
|
DELIMITER |;
|
||||||
|
CREATE TRIGGER t1_bi before INSERT
|
||||||
|
ON t1 FOR EACH ROW
|
||||||
|
BEGIN
|
||||||
|
DECLARE CONTINUE HANDLER FOR SQLSTATE '40001' SET @a:= 'deadlock';
|
||||||
|
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET @a:= 'exception';
|
||||||
|
INSERT INTO t2 (f2) VALUES (1);
|
||||||
|
DELETE FROM t2 WHERE f2 = 1;
|
||||||
|
END;|
|
||||||
|
|
||||||
|
CREATE PROCEDURE proc24989()
|
||||||
|
BEGIN
|
||||||
|
DECLARE CONTINUE HANDLER FOR SQLSTATE '40001' SET @b:= 'deadlock';
|
||||||
|
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET @a:= 'exception';
|
||||||
|
INSERT INTO t2 (f2) VALUES (1);
|
||||||
|
DELETE FROM t2 WHERE f2 = 1;
|
||||||
|
END;|
|
||||||
|
|
||||||
|
create procedure proc24989_2()
|
||||||
|
deterministic
|
||||||
|
begin
|
||||||
|
declare continue handler for sqlexception
|
||||||
|
select 'Outer handler' as 'exception';
|
||||||
|
|
||||||
|
insert into t1 values(1);
|
||||||
|
select "continued";
|
||||||
|
end|
|
||||||
|
|
||||||
|
DELIMITER ;|
|
||||||
|
|
||||||
|
connect (con1,localhost,root,,);
|
||||||
|
connect (con2,localhost,root,,);
|
||||||
|
|
||||||
|
connection con1;
|
||||||
|
start transaction;
|
||||||
|
insert into t1 values(1);
|
||||||
|
|
||||||
|
connection con2;
|
||||||
|
start transaction;
|
||||||
|
insert into t2 values(123);
|
||||||
|
send insert into t1 values(1);
|
||||||
|
|
||||||
|
connection con1;
|
||||||
|
--sleep 1
|
||||||
|
insert into t1 values(1);
|
||||||
|
|
||||||
|
connection con2;
|
||||||
|
--error 1213
|
||||||
|
reap;
|
||||||
|
select @a;
|
||||||
|
# check that the whole transaction was rolled back
|
||||||
|
select * from t2;
|
||||||
|
|
||||||
|
connection con1;
|
||||||
|
commit;
|
||||||
|
start transaction;
|
||||||
|
insert into t1 values(1);
|
||||||
|
|
||||||
|
connection con2;
|
||||||
|
start transaction;
|
||||||
|
insert into t2 values(123);
|
||||||
|
send call proc24989();
|
||||||
|
|
||||||
|
connection con1;
|
||||||
|
--sleep 1
|
||||||
|
insert into t1 values(1);
|
||||||
|
|
||||||
|
connection con2;
|
||||||
|
reap;
|
||||||
|
select @a,@b;
|
||||||
|
# check that the whole transaction was rolled back
|
||||||
|
select * from t2;
|
||||||
|
|
||||||
|
connection con1;
|
||||||
|
commit;
|
||||||
|
start transaction;
|
||||||
|
insert into t1 values(1);
|
||||||
|
|
||||||
|
connection con2;
|
||||||
|
start transaction;
|
||||||
|
insert into t2 values(123);
|
||||||
|
send call proc24989_2();
|
||||||
|
|
||||||
|
connection con1;
|
||||||
|
--sleep 1
|
||||||
|
insert into t1 values(1);
|
||||||
|
commit;
|
||||||
|
|
||||||
|
connection con2;
|
||||||
|
reap;
|
||||||
|
# check that the whole transaction was rolled back
|
||||||
|
select * from t2;
|
||||||
|
|
||||||
|
disconnect con1;
|
||||||
|
disconnect con2;
|
||||||
|
connection default;
|
||||||
|
drop procedure proc24989;
|
||||||
|
drop procedure proc24989_2;
|
||||||
|
drop table t1,t2;
|
||||||
|
|
||||||
|
@ -455,9 +455,7 @@ convert_error_code_to_mysql(
|
|||||||
tell it also to MySQL so that MySQL knows to empty the
|
tell it also to MySQL so that MySQL knows to empty the
|
||||||
cached binlog for this transaction */
|
cached binlog for this transaction */
|
||||||
|
|
||||||
if (thd) {
|
mark_transaction_to_rollback(thd, TRUE);
|
||||||
ha_rollback(thd);
|
|
||||||
}
|
|
||||||
|
|
||||||
return(HA_ERR_LOCK_DEADLOCK);
|
return(HA_ERR_LOCK_DEADLOCK);
|
||||||
|
|
||||||
@ -467,9 +465,7 @@ convert_error_code_to_mysql(
|
|||||||
latest SQL statement in a lock wait timeout. Previously, we
|
latest SQL statement in a lock wait timeout. Previously, we
|
||||||
rolled back the whole transaction. */
|
rolled back the whole transaction. */
|
||||||
|
|
||||||
if (thd && row_rollback_on_timeout) {
|
mark_transaction_to_rollback(thd, row_rollback_on_timeout);
|
||||||
ha_rollback(thd);
|
|
||||||
}
|
|
||||||
|
|
||||||
return(HA_ERR_LOCK_WAIT_TIMEOUT);
|
return(HA_ERR_LOCK_WAIT_TIMEOUT);
|
||||||
|
|
||||||
@ -521,9 +517,7 @@ convert_error_code_to_mysql(
|
|||||||
tell it also to MySQL so that MySQL knows to empty the
|
tell it also to MySQL so that MySQL knows to empty the
|
||||||
cached binlog for this transaction */
|
cached binlog for this transaction */
|
||||||
|
|
||||||
if (thd) {
|
mark_transaction_to_rollback(thd, TRUE);
|
||||||
ha_rollback(thd);
|
|
||||||
}
|
|
||||||
|
|
||||||
return(HA_ERR_LOCK_TABLE_FULL);
|
return(HA_ERR_LOCK_TABLE_FULL);
|
||||||
} else {
|
} else {
|
||||||
|
@ -821,6 +821,9 @@ int ha_rollback_trans(THD *thd, bool all)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif /* USING_TRANSACTIONS */
|
#endif /* USING_TRANSACTIONS */
|
||||||
|
if (all)
|
||||||
|
thd->transaction_rollback_request= FALSE;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
If a non-transactional table was updated, warn; don't warn if this is a
|
If a non-transactional table was updated, warn; don't warn if this is a
|
||||||
slave thread (because when a slave thread executes a ROLLBACK, it has
|
slave thread (because when a slave thread executes a ROLLBACK, it has
|
||||||
@ -858,6 +861,8 @@ int ha_autocommit_or_rollback(THD *thd, int error)
|
|||||||
if (ha_commit_stmt(thd))
|
if (ha_commit_stmt(thd))
|
||||||
error=1;
|
error=1;
|
||||||
}
|
}
|
||||||
|
else if (thd->transaction_rollback_request && !thd->in_sub_stmt)
|
||||||
|
(void) ha_rollback(thd);
|
||||||
else
|
else
|
||||||
(void) ha_rollback_stmt(thd);
|
(void) ha_rollback_stmt(thd);
|
||||||
|
|
||||||
|
@ -37,6 +37,7 @@ sp_rcontext::sp_rcontext(sp_pcontext *root_parsing_ctx,
|
|||||||
m_var_items(0),
|
m_var_items(0),
|
||||||
m_return_value_fld(return_value_fld),
|
m_return_value_fld(return_value_fld),
|
||||||
m_return_value_set(FALSE),
|
m_return_value_set(FALSE),
|
||||||
|
in_sub_stmt(FALSE),
|
||||||
m_hcount(0),
|
m_hcount(0),
|
||||||
m_hsp(0),
|
m_hsp(0),
|
||||||
m_ihsp(0),
|
m_ihsp(0),
|
||||||
@ -67,6 +68,8 @@ sp_rcontext::~sp_rcontext()
|
|||||||
|
|
||||||
bool sp_rcontext::init(THD *thd)
|
bool sp_rcontext::init(THD *thd)
|
||||||
{
|
{
|
||||||
|
in_sub_stmt= thd->in_sub_stmt;
|
||||||
|
|
||||||
if (init_var_table(thd) || init_var_items())
|
if (init_var_table(thd) || init_var_items())
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
@ -191,7 +194,7 @@ sp_rcontext::set_return_value(THD *thd, Item **return_value_item)
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
bool
|
bool
|
||||||
sp_rcontext::find_handler(uint sql_errno,
|
sp_rcontext::find_handler(THD *thd, uint sql_errno,
|
||||||
MYSQL_ERROR::enum_warning_level level)
|
MYSQL_ERROR::enum_warning_level level)
|
||||||
{
|
{
|
||||||
if (m_hfound >= 0)
|
if (m_hfound >= 0)
|
||||||
@ -200,6 +203,15 @@ sp_rcontext::find_handler(uint sql_errno,
|
|||||||
const char *sqlstate= mysql_errno_to_sqlstate(sql_errno);
|
const char *sqlstate= mysql_errno_to_sqlstate(sql_errno);
|
||||||
int i= m_hcount, found= -1;
|
int i= m_hcount, found= -1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
If this is a fatal sub-statement error, and this runtime
|
||||||
|
context corresponds to a sub-statement, no CONTINUE/EXIT
|
||||||
|
handlers from this context are applicable: try to locate one
|
||||||
|
in the outer scope.
|
||||||
|
*/
|
||||||
|
if (thd->is_fatal_sub_stmt_error && in_sub_stmt)
|
||||||
|
i= 0;
|
||||||
|
|
||||||
/* Search handlers from the latest (innermost) to the oldest (outermost) */
|
/* Search handlers from the latest (innermost) to the oldest (outermost) */
|
||||||
while (i--)
|
while (i--)
|
||||||
{
|
{
|
||||||
@ -252,7 +264,7 @@ sp_rcontext::find_handler(uint sql_errno,
|
|||||||
*/
|
*/
|
||||||
if (m_prev_runtime_ctx && IS_EXCEPTION_CONDITION(sqlstate) &&
|
if (m_prev_runtime_ctx && IS_EXCEPTION_CONDITION(sqlstate) &&
|
||||||
level == MYSQL_ERROR::WARN_LEVEL_ERROR)
|
level == MYSQL_ERROR::WARN_LEVEL_ERROR)
|
||||||
return m_prev_runtime_ctx->find_handler(sql_errno, level);
|
return m_prev_runtime_ctx->find_handler(thd, sql_errno, level);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
m_hfound= found;
|
m_hfound= found;
|
||||||
@ -298,7 +310,7 @@ sp_rcontext::handle_error(uint sql_errno,
|
|||||||
elevated_level= MYSQL_ERROR::WARN_LEVEL_ERROR;
|
elevated_level= MYSQL_ERROR::WARN_LEVEL_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (find_handler(sql_errno, elevated_level))
|
if (find_handler(thd, sql_errno, elevated_level))
|
||||||
{
|
{
|
||||||
if (elevated_level == MYSQL_ERROR::WARN_LEVEL_ERROR)
|
if (elevated_level == MYSQL_ERROR::WARN_LEVEL_ERROR)
|
||||||
{
|
{
|
||||||
|
@ -125,7 +125,7 @@ class sp_rcontext : public Sql_alloc
|
|||||||
|
|
||||||
// Returns 1 if a handler was found, 0 otherwise.
|
// Returns 1 if a handler was found, 0 otherwise.
|
||||||
bool
|
bool
|
||||||
find_handler(uint sql_errno,MYSQL_ERROR::enum_warning_level level);
|
find_handler(THD *thd, uint sql_errno,MYSQL_ERROR::enum_warning_level level);
|
||||||
|
|
||||||
// If there is an error handler for this error, handle it and return TRUE.
|
// If there is an error handler for this error, handle it and return TRUE.
|
||||||
bool
|
bool
|
||||||
@ -236,6 +236,10 @@ private:
|
|||||||
during execution.
|
during execution.
|
||||||
*/
|
*/
|
||||||
bool m_return_value_set;
|
bool m_return_value_set;
|
||||||
|
/**
|
||||||
|
TRUE if the context is created for a sub-statement.
|
||||||
|
*/
|
||||||
|
bool in_sub_stmt;
|
||||||
|
|
||||||
sp_handler_t *m_handler; // Visible handlers
|
sp_handler_t *m_handler; // Visible handlers
|
||||||
uint m_hcount; // Stack pointer for m_handler
|
uint m_hcount; // Stack pointer for m_handler
|
||||||
|
@ -173,6 +173,7 @@ THD::THD()
|
|||||||
Open_tables_state(refresh_version),
|
Open_tables_state(refresh_version),
|
||||||
lock_id(&main_lock_id),
|
lock_id(&main_lock_id),
|
||||||
user_time(0), in_sub_stmt(0), global_read_lock(0), is_fatal_error(0),
|
user_time(0), in_sub_stmt(0), global_read_lock(0), is_fatal_error(0),
|
||||||
|
transaction_rollback_request(0), is_fatal_sub_stmt_error(0),
|
||||||
rand_used(0), time_zone_used(0),
|
rand_used(0), time_zone_used(0),
|
||||||
last_insert_id_used(0), last_insert_id_used_bin_log(0), insert_id_used(0),
|
last_insert_id_used(0), last_insert_id_used_bin_log(0), insert_id_used(0),
|
||||||
clear_next_insert_id(0), in_lock_tables(0), bootstrap(0),
|
clear_next_insert_id(0), in_lock_tables(0), bootstrap(0),
|
||||||
@ -976,7 +977,7 @@ void select_send::abort()
|
|||||||
{
|
{
|
||||||
DBUG_ENTER("select_send::abort");
|
DBUG_ENTER("select_send::abort");
|
||||||
if (status && thd->spcont &&
|
if (status && thd->spcont &&
|
||||||
thd->spcont->find_handler(thd->net.last_errno,
|
thd->spcont->find_handler(thd, thd->net.last_errno,
|
||||||
MYSQL_ERROR::WARN_LEVEL_ERROR))
|
MYSQL_ERROR::WARN_LEVEL_ERROR))
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
@ -2211,6 +2212,13 @@ void THD::restore_sub_statement_state(Sub_statement_state *backup)
|
|||||||
limit_found_rows= backup->limit_found_rows;
|
limit_found_rows= backup->limit_found_rows;
|
||||||
sent_row_count= backup->sent_row_count;
|
sent_row_count= backup->sent_row_count;
|
||||||
client_capabilities= backup->client_capabilities;
|
client_capabilities= backup->client_capabilities;
|
||||||
|
/*
|
||||||
|
If we've left sub-statement mode, reset the fatal error flag.
|
||||||
|
Otherwise keep the current value, to propagate it up the sub-statement
|
||||||
|
stack.
|
||||||
|
*/
|
||||||
|
if (!in_sub_stmt)
|
||||||
|
is_fatal_sub_stmt_error= FALSE;
|
||||||
|
|
||||||
if ((options & OPTION_BIN_LOG) && is_update_query(lex->sql_command))
|
if ((options & OPTION_BIN_LOG) && is_update_query(lex->sql_command))
|
||||||
mysql_bin_log.stop_union_events(this);
|
mysql_bin_log.stop_union_events(this);
|
||||||
@ -2224,6 +2232,18 @@ void THD::restore_sub_statement_state(Sub_statement_state *backup)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Mark transaction to rollback and mark error as fatal to a sub-statement.
|
||||||
|
|
||||||
|
@param thd Thread handle
|
||||||
|
@param all TRUE <=> rollback main transaction.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void mark_transaction_to_rollback(THD *thd, bool all)
|
||||||
|
{
|
||||||
|
thd->is_fatal_sub_stmt_error= TRUE;
|
||||||
|
thd->transaction_rollback_request= all;
|
||||||
|
}
|
||||||
/***************************************************************************
|
/***************************************************************************
|
||||||
Handling of XA id cacheing
|
Handling of XA id cacheing
|
||||||
***************************************************************************/
|
***************************************************************************/
|
||||||
|
@ -1421,7 +1421,33 @@ public:
|
|||||||
bool slave_thread, one_shot_set;
|
bool slave_thread, one_shot_set;
|
||||||
bool locked, some_tables_deleted;
|
bool locked, some_tables_deleted;
|
||||||
bool last_cuted_field;
|
bool last_cuted_field;
|
||||||
bool no_errors, password, is_fatal_error;
|
bool no_errors, password;
|
||||||
|
/**
|
||||||
|
Set to TRUE if execution of the current compound statement
|
||||||
|
can not continue. In particular, disables activation of
|
||||||
|
CONTINUE or EXIT handlers of stored routines.
|
||||||
|
Reset in the end of processing of the current user request, in
|
||||||
|
@see mysql_reset_thd_for_next_command().
|
||||||
|
*/
|
||||||
|
bool is_fatal_error;
|
||||||
|
/**
|
||||||
|
Set by a storage engine to request the entire
|
||||||
|
transaction (that possibly spans multiple engines) to
|
||||||
|
rollback. Reset in ha_rollback.
|
||||||
|
*/
|
||||||
|
bool transaction_rollback_request;
|
||||||
|
/**
|
||||||
|
TRUE if we are in a sub-statement and the current error can
|
||||||
|
not be safely recovered until we left the sub-statement mode.
|
||||||
|
In particular, disables activation of CONTINUE and EXIT
|
||||||
|
handlers inside sub-statements. E.g. if it is a deadlock
|
||||||
|
error and requires a transaction-wide rollback, this flag is
|
||||||
|
raised (traditionally, MySQL first has to close all the reads
|
||||||
|
via @see handler::ha_index_or_rnd_end() and only then perform
|
||||||
|
the rollback).
|
||||||
|
Reset to FALSE when we leave the sub-statement mode.
|
||||||
|
*/
|
||||||
|
bool is_fatal_sub_stmt_error;
|
||||||
bool query_start_used, rand_used, time_zone_used;
|
bool query_start_used, rand_used, time_zone_used;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -2397,3 +2423,5 @@ public:
|
|||||||
/* Functions in sql_class.cc */
|
/* Functions in sql_class.cc */
|
||||||
|
|
||||||
void add_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var);
|
void add_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var);
|
||||||
|
void mark_transaction_to_rollback(THD *thd, bool all);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user