diff options
author | Dmitry Lenev <Dmitry.Lenev@oracle.com> | 2013-08-20 13:12:34 +0400 |
---|---|---|
committer | Dmitry Lenev <Dmitry.Lenev@oracle.com> | 2013-08-20 13:12:34 +0400 |
commit | fc2c66929737594f5d6b31a25c65e1ec4a524664 (patch) | |
tree | be0756ebecacc780efe430d822f5e093c1c5136b /sql/transaction.cc | |
parent | c250dbf38501bfa8be51e7c2b3e0a6d8397b8403 (diff) | |
download | mariadb-git-fc2c66929737594f5d6b31a25c65e1ec4a524664.tar.gz |
Fix for bug#14188793 - "DEADLOCK CAUSED BY ALTER TABLE DOEN'T CLEAR
STATUS OF ROLLBACKED TRANSACTION" and bug #17054007 - "TRANSACTION
IS NOT FULLY ROLLED BACK IN CASE OF INNODB DEADLOCK".
The problem in the first bug report was that although deadlock involving
metadata locks was reported using the same error code and message as InnoDB
deadlock it didn't rollback transaction like the latter. This caused
confusion to users as in some cases after ER_LOCK_DEADLOCK transaction
could have been restarted immediately and in some cases rollback was
required.
The problem in the second bug report was that although InnoDB deadlock
caused transaction rollback in all storage engines it didn't cause release
of metadata locks. So concurrent DDL on the tables used in transaction was
blocked until implicit or explicit COMMIT or ROLLBACK was issued in the
connection which got InnoDB deadlock.
The former issue has stemmed from the fact that when support for detection
and reporting metadata locks deadlocks was added we erroneously assumed
that InnoDB doesn't rollback transaction on deadlock but only last statement
(while this is what happens on InnoDB lock timeout actually) and so didn't
implement rollback of transactions on MDL deadlocks.
The latter issue was caused by the fact that rollback of transaction due
to deadlock is carried out by setting THD::transaction_rollback_request
flag at the point where deadlock is detected and performing rollback
inside of trans_rollback_stmt() call when this flag is set. And
trans_rollback_stmt() is not aware of MDL locks, so no MDL locks are
released.
This patch solves these two problems in the following way:
- In case when MDL deadlock is detect transaction rollback is requested
by setting THD::transaction_rollback_request flag.
- Code performing rollback of transaction if THD::transaction_rollback_request
is moved out from trans_rollback_stmt(). Now we handle rollback request
on the same level as we call trans_rollback_stmt() and release statement/
transaction MDL locks.
Diffstat (limited to 'sql/transaction.cc')
-rw-r--r-- | sql/transaction.cc | 45 |
1 files changed, 42 insertions, 3 deletions
diff --git a/sql/transaction.cc b/sql/transaction.cc index 3b0af4db710..4fd6af39135 100644 --- a/sql/transaction.cc +++ b/sql/transaction.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -268,6 +268,47 @@ bool trans_rollback(THD *thd) /** + Implicitly rollback the current transaction, typically + after deadlock was discovered. + + @param thd Current thread + + @retval False Success + @retval True Failure + + @note ha_rollback_low() which is indirectly called by this + function will mark XA transaction for rollback by + setting appropriate RM error status if there was + transaction rollback request. +*/ + +bool trans_rollback_implicit(THD *thd) +{ + int res; + DBUG_ENTER("trans_rollback_implict"); + + /* + Always commit/rollback statement transaction before manipulating + with the normal one. + Don't perform rollback in the middle of sub-statement, wait till + its end. + */ + DBUG_ASSERT(thd->transaction.stmt.is_empty() && !thd->in_sub_stmt); + + thd->server_status&= ~SERVER_STATUS_IN_TRANS; + DBUG_PRINT("info", ("clearing SERVER_STATUS_IN_TRANS")); + res= ha_rollback_trans(thd, true); + thd->variables.option_bits&= ~(OPTION_BEGIN | OPTION_KEEP_LOG); + thd->transaction.all.modified_non_trans_table= false; + + /* Rollback should clear transaction_rollback_request flag. */ + DBUG_ASSERT(! thd->transaction_rollback_request); + + DBUG_RETURN(test(res)); +} + + +/** Commit the single statement transaction. @note Note that if the autocommit is on, then the following call @@ -339,8 +380,6 @@ bool trans_rollback_stmt(THD *thd) if (thd->transaction.stmt.ha_list) { ha_rollback_trans(thd, FALSE); - if (thd->transaction_rollback_request && !thd->in_sub_stmt) - ha_rollback_trans(thd, TRUE); if (! thd->in_active_multi_stmt_transaction()) thd->tx_isolation= (enum_tx_isolation) thd->variables.tx_isolation; } |