Skip to content

Commit 09b9845

Browse files
author
Dmitry Shulga
committed
Bug#23264552 -- XA: ASSERT `M_STATUS == DA_ERROR' IN MYSQL_ERRNO:SQL_ERROR.H:385
In case a storage engine failed to prepare a branch of an XA transaction the method handlerton::prepare() returns nonzero value in the function ha_prepare(). To handle error condition the function ha_rollback_trans() is called to rollback current transaction. While doing rollback the following sequence of functions is called ha_rollback_trans --> TC_LOG::rollback --> ha_rollback_low --> XID_STATE::set_error --> Diagnostics_area::mysql_errno. It results in firing of the assert DBUG_ASSERT(m_status == DA_ERROR) since no error has been set in Diagnostics_area. That leads to abnormal server termination for the server built in DEBUG mode. To fix it extra condition added in the function ha_rollback_low() to call XID_STATE::set_error() only in case an error is set in Diagnostics_area. (cherry picked from commit f28c4b9a41ec6929e15f174ef162a667c888f611)
1 parent 531a7fd commit 09b9845

File tree

5 files changed

+66
-2
lines changed

5 files changed

+66
-2
lines changed

mysql-test/r/xa_debug.result

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,3 +89,18 @@ XA ROLLBACK 'xid1';
8989
ERROR XAE04: XAER_NOTA: Unknown XID
9090
XA START 'trx_another_one';
9191
DROP TABLE t1;
92+
#
93+
# Bug#23264552 - XA: ASSERT `m_status == da_error' IN mysql_errno:sql_error.h:385
94+
#
95+
CREATE TABLE t1 (a INT);
96+
XA START 'xid1';
97+
INSERT INTO t1 VALUES (1);
98+
XA END 'xid1';
99+
SET @@session.debug = '+d,simulate_xa_failure_prepare_in_engine';
100+
XA PREPARE 'xid1';
101+
ERROR XA100: XA_RBROLLBACK: Transaction branch was rolled back
102+
SET @@session.debug = '-d,simulate_xa_failure_prepare_in_engine';
103+
XA ROLLBACK 'xid1';
104+
ERROR XAE04: XAER_NOTA: Unknown XID
105+
XA START 'trx_another_one';
106+
DROP TABLE t1;

mysql-test/t/xa_debug.test

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,3 +183,31 @@ XA START 'trx_another_one';
183183
--disconnect con1
184184
--connection default
185185
DROP TABLE t1;
186+
187+
--echo #
188+
--echo # Bug#23264552 - XA: ASSERT `m_status == da_error' IN mysql_errno:sql_error.h:385
189+
--echo #
190+
--connect (con1, 127.0.0.1,root,,test,$MASTER_MYPORT,)
191+
CREATE TABLE t1 (a INT);
192+
193+
XA START 'xid1';
194+
INSERT INTO t1 VALUES (1);
195+
XA END 'xid1';
196+
SET @@session.debug = '+d,simulate_xa_failure_prepare_in_engine';
197+
--error ER_XA_RBROLLBACK
198+
XA PREPARE 'xid1';
199+
SET @@session.debug = '-d,simulate_xa_failure_prepare_in_engine';
200+
201+
--error ER_XAER_NOTA
202+
XA ROLLBACK 'xid1';
203+
204+
#
205+
# Check that subsequent XA transaction can be initiated.
206+
#
207+
208+
XA START 'trx_another_one';
209+
--disconnect con1
210+
--source include/wait_until_disconnected.inc
211+
--connection default
212+
213+
DROP TABLE t1;

sql/handler.cc

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1986,8 +1986,22 @@ int ha_rollback_low(THD *thd, bool all)
19861986
/*
19871987
Thanks to possibility of MDL deadlock rollback request can come even if
19881988
transaction hasn't been started in any transactional storage engine.
1989+
1990+
It is possible to have a call of ha_rollback_low() while handling
1991+
failure from ha_prepare() and an error in Daignostics_area still
1992+
wasn't set. Therefore it is required to check that an error in
1993+
Diagnostics_area is set before calling the method XID_STATE::set_error().
1994+
1995+
If it wasn't done it would lead to failure of the assertion
1996+
DBUG_ASSERT(m_status == DA_ERROR)
1997+
in the method Diagnostics_area::mysql_errno().
1998+
1999+
In case ha_prepare is failed and an error wasn't set in Diagnostics_area
2000+
the error ER_XA_RBROLLBACK is set in the Diagnostics_area from
2001+
the method Sql_cmd_xa_prepare::trans_xa_prepare() when non-zero result code
2002+
returned by ha_prepare() is handled.
19892003
*/
1990-
if (all && thd->transaction_rollback_request)
2004+
if (all && thd->transaction_rollback_request && thd->is_error())
19912005
trn_ctx->xid_state()->set_error(thd);
19922006

19932007
(void) RUN_HOOK(transaction, after_rollback, (thd, all));

sql/xa.cc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -690,6 +690,12 @@ bool Sql_cmd_xa_prepare::trans_xa_prepare(THD *thd)
690690
DBUG_ASSERT(thd->m_transaction_psi == NULL);
691691
#endif
692692

693+
/*
694+
Reset rm_error in case ha_prepare() returned error,
695+
so thd->transaction.xid structure gets reset
696+
by THD::transaction::cleanup().
697+
*/
698+
thd->get_transaction()->xid_state()->reset_error();
693699
cleanup_trans_state(thd);
694700
xid_state->set_state(XID_STATE::XA_NOTR);
695701
thd->get_transaction()->cleanup();

storage/innobase/handler/ha_innodb.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16896,7 +16896,8 @@ innobase_xa_prepare(
1689616896

1689716897
TrxInInnoDB trx_in_innodb(trx);
1689816898

16899-
if (trx_in_innodb.is_aborted()) {
16899+
if (trx_in_innodb.is_aborted() ||
16900+
DBUG_EVALUATE_IF("simulate_xa_failure_prepare_in_engine", 1, 0)) {
1690016901

1690116902
innobase_rollback(hton, thd, prepare_trx);
1690216903

0 commit comments

Comments
 (0)