mirror of
https://github.com/MariaDB/server.git
synced 2025-01-29 10:14:19 +01: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 sql/ha_innodb.cc: Auto merged sql/handler.cc: Auto merged sql/sql_class.cc: Auto merged sql/sql_class.h: Auto merged
This commit is contained in:
commit
3887705e09
8 changed files with 250 additions and 15 deletions
|
@ -32,3 +32,69 @@ select sum(id) from t3;
|
|||
sum(id)
|
||||
2199024304128
|
||||
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);
|
||||
select sum(id) from t3;
|
||||
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
|
||||
cached binlog for this transaction */
|
||||
|
||||
if (thd) {
|
||||
ha_rollback(thd);
|
||||
}
|
||||
mark_transaction_to_rollback(thd, TRUE);
|
||||
|
||||
return(HA_ERR_LOCK_DEADLOCK);
|
||||
|
||||
|
@ -467,9 +465,7 @@ convert_error_code_to_mysql(
|
|||
latest SQL statement in a lock wait timeout. Previously, we
|
||||
rolled back the whole transaction. */
|
||||
|
||||
if (thd && row_rollback_on_timeout) {
|
||||
ha_rollback(thd);
|
||||
}
|
||||
mark_transaction_to_rollback(thd, row_rollback_on_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
|
||||
cached binlog for this transaction */
|
||||
|
||||
if (thd) {
|
||||
ha_rollback(thd);
|
||||
}
|
||||
mark_transaction_to_rollback(thd, TRUE);
|
||||
|
||||
return(HA_ERR_LOCK_TABLE_FULL);
|
||||
} else {
|
||||
|
|
|
@ -821,6 +821,9 @@ int ha_rollback_trans(THD *thd, bool all)
|
|||
}
|
||||
}
|
||||
#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
|
||||
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))
|
||||
error=1;
|
||||
}
|
||||
else if (thd->transaction_rollback_request && !thd->in_sub_stmt)
|
||||
(void) ha_rollback(thd);
|
||||
else
|
||||
(void) ha_rollback_stmt(thd);
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@ sp_rcontext::sp_rcontext(sp_pcontext *root_parsing_ctx,
|
|||
m_var_items(0),
|
||||
m_return_value_fld(return_value_fld),
|
||||
m_return_value_set(FALSE),
|
||||
in_sub_stmt(FALSE),
|
||||
m_hcount(0),
|
||||
m_hsp(0),
|
||||
m_ihsp(0),
|
||||
|
@ -67,6 +68,8 @@ sp_rcontext::~sp_rcontext()
|
|||
|
||||
bool sp_rcontext::init(THD *thd)
|
||||
{
|
||||
in_sub_stmt= thd->in_sub_stmt;
|
||||
|
||||
if (init_var_table(thd) || init_var_items())
|
||||
return TRUE;
|
||||
|
||||
|
@ -191,7 +194,7 @@ sp_rcontext::set_return_value(THD *thd, Item **return_value_item)
|
|||
*/
|
||||
|
||||
bool
|
||||
sp_rcontext::find_handler(uint sql_errno,
|
||||
sp_rcontext::find_handler(THD *thd, uint sql_errno,
|
||||
MYSQL_ERROR::enum_warning_level level)
|
||||
{
|
||||
if (m_hfound >= 0)
|
||||
|
@ -200,6 +203,15 @@ sp_rcontext::find_handler(uint sql_errno,
|
|||
const char *sqlstate= mysql_errno_to_sqlstate(sql_errno);
|
||||
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) */
|
||||
while (i--)
|
||||
{
|
||||
|
@ -252,7 +264,7 @@ sp_rcontext::find_handler(uint sql_errno,
|
|||
*/
|
||||
if (m_prev_runtime_ctx && IS_EXCEPTION_CONDITION(sqlstate) &&
|
||||
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;
|
||||
}
|
||||
m_hfound= found;
|
||||
|
@ -298,7 +310,7 @@ sp_rcontext::handle_error(uint sql_errno,
|
|||
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)
|
||||
{
|
||||
|
|
|
@ -125,7 +125,7 @@ class sp_rcontext : public Sql_alloc
|
|||
|
||||
// Returns 1 if a handler was found, 0 otherwise.
|
||||
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.
|
||||
bool
|
||||
|
@ -236,6 +236,10 @@ private:
|
|||
during execution.
|
||||
*/
|
||||
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
|
||||
uint m_hcount; // Stack pointer for m_handler
|
||||
|
|
|
@ -173,6 +173,7 @@ THD::THD()
|
|||
Open_tables_state(refresh_version),
|
||||
lock_id(&main_lock_id),
|
||||
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),
|
||||
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),
|
||||
|
@ -976,7 +977,7 @@ void select_send::abort()
|
|||
{
|
||||
DBUG_ENTER("select_send::abort");
|
||||
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))
|
||||
{
|
||||
/*
|
||||
|
@ -2211,6 +2212,13 @@ void THD::restore_sub_statement_state(Sub_statement_state *backup)
|
|||
limit_found_rows= backup->limit_found_rows;
|
||||
sent_row_count= backup->sent_row_count;
|
||||
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))
|
||||
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
|
||||
***************************************************************************/
|
||||
|
|
|
@ -1421,7 +1421,33 @@ public:
|
|||
bool slave_thread, one_shot_set;
|
||||
bool locked, some_tables_deleted;
|
||||
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;
|
||||
|
||||
/*
|
||||
|
@ -2397,3 +2423,5 @@ public:
|
|||
/* Functions in sql_class.cc */
|
||||
|
||||
void add_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var);
|
||||
void mark_transaction_to_rollback(THD *thd, bool all);
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue