diff options
author | unknown <gshchepa/uchum@gleb.loc> | 2007-07-31 11:16:11 +0500 |
---|---|---|
committer | unknown <gshchepa/uchum@gleb.loc> | 2007-07-31 11:16:11 +0500 |
commit | 465c3ef06085c5f8ec6d5de8ad66a4d0ef560f2b (patch) | |
tree | 5ab79ae113ed24b50127d9779123f8ebcbcc2386 | |
parent | 33fc4ad4e124413ef617a1a073bb50135f6a12af (diff) | |
parent | e1dee88ab103383dba1111a9132fb908fbf6a168 (diff) | |
download | mariadb-git-465c3ef06085c5f8ec6d5de8ad66a4d0ef560f2b.tar.gz |
Merge gleb.loc:/home/uchum/work/bk/5.0-opt-30120
into gleb.loc:/home/uchum/work/bk/5.0-opt
-rw-r--r-- | mysql-test/r/innodb-big.result | 66 | ||||
-rw-r--r-- | mysql-test/t/bigint.test | 2 | ||||
-rw-r--r-- | mysql-test/t/innodb-big.test | 106 | ||||
-rw-r--r-- | sql/ha_innodb.cc | 12 | ||||
-rw-r--r-- | sql/handler.cc | 5 | ||||
-rw-r--r-- | sql/log.cc | 1 | ||||
-rw-r--r-- | sql/sp_rcontext.cc | 18 | ||||
-rw-r--r-- | sql/sp_rcontext.h | 6 | ||||
-rw-r--r-- | sql/sql_class.cc | 22 | ||||
-rw-r--r-- | sql/sql_class.h | 30 |
10 files changed, 252 insertions, 16 deletions
diff --git a/mysql-test/r/innodb-big.result b/mysql-test/r/innodb-big.result index 19204b7cc65..84bb02d445e 100644 --- a/mysql-test/r/innodb-big.result +++ b/mysql-test/r/innodb-big.result @@ -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; diff --git a/mysql-test/t/bigint.test b/mysql-test/t/bigint.test index 4aef7395184..002dfad9981 100644 --- a/mysql-test/t/bigint.test +++ b/mysql-test/t/bigint.test @@ -307,6 +307,6 @@ select -(9223372036854775808); select -((9223372036854775808)); select -(-(9223372036854775808)); --disable_metadata ---endble_ps_protocol +--enable_ps_protocol select --9223372036854775808, ---9223372036854775808, ----9223372036854775808; select -(-9223372036854775808), -(-(-9223372036854775808)); diff --git a/mysql-test/t/innodb-big.test b/mysql-test/t/innodb-big.test index ade69ffdb45..dcb32cd8e71 100644 --- a/mysql-test/t/innodb-big.test +++ b/mysql-test/t/innodb-big.test @@ -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; + diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc index 1c952f490da..585c825c710 100644 --- a/sql/ha_innodb.cc +++ b/sql/ha_innodb.cc @@ -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 { diff --git a/sql/handler.cc b/sql/handler.cc index dcc9fde8b76..2285a190c8e 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -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); diff --git a/sql/log.cc b/sql/log.cc index 744d2a3ca65..6992f6c10ef 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -1833,6 +1833,7 @@ bool MYSQL_LOG::write(THD *thd, IO_CACHE *cache, Log_event *commit_event) /* NULL would represent nothing to replicate after ROLLBACK */ DBUG_ASSERT(commit_event != NULL); + DBUG_ASSERT(is_open()); if (likely(is_open())) // Should always be true { uint length, group, carry, hdr_offs, val; diff --git a/sql/sp_rcontext.cc b/sql/sp_rcontext.cc index e49c4eb1240..ac7c46c9fe5 100644 --- a/sql/sp_rcontext.cc +++ b/sql/sp_rcontext.cc @@ -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) { diff --git a/sql/sp_rcontext.h b/sql/sp_rcontext.h index fbf479f52aa..0104b71a648 100644 --- a/sql/sp_rcontext.h +++ b/sql/sp_rcontext.h @@ -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 diff --git a/sql/sql_class.cc b/sql/sql_class.cc index ee4e1ea149c..0650d4ba8c9 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -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 ***************************************************************************/ diff --git a/sql/sql_class.h b/sql/sql_class.h index 058f130d4e7..ef719cdded7 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -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); + |