Commit 188293f5 authored by Davi Arnaut's avatar Davi Arnaut

Merge Bug#28323 to mysql-5.1.29-rc

parents ac1c5f50 b0d673fc
...@@ -55,3 +55,22 @@ select * from t1; ...@@ -55,3 +55,22 @@ select * from t1;
a a
20 20
drop table t1; drop table t1;
drop table if exists t1;
create table t1(a int, b int, c varchar(20), primary key(a)) engine = innodb;
insert into t1 values(1, 1, 'a');
insert into t1 values(2, 2, 'b');
xa start 'a','b';
update t1 set c = 'aa' where a = 1;
xa start 'a','c';
update t1 set c = 'bb' where a = 2;
update t1 set c = 'bb' where a = 2;
update t1 set c = 'aa' where a = 1;
ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
select count(*) from t1;
count(*)
2
xa end 'a','c';
ERROR XA102: XA_RBDEADLOCK: Transaction branch was rolled back: deadlock was detected
xa rollback 'a','c';
xa start 'a','c';
End of 5.0 tests
...@@ -74,3 +74,47 @@ xa start 'zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz'; ...@@ -74,3 +74,47 @@ xa start 'zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz';
select * from t1; select * from t1;
drop table t1; drop table t1;
disconnect con1;
#
# Bug#28323: Server crashed in xid cache operations
#
--disable_warnings
drop table if exists t1;
--enable_warnings
create table t1(a int, b int, c varchar(20), primary key(a)) engine = innodb;
insert into t1 values(1, 1, 'a');
insert into t1 values(2, 2, 'b');
connect (con1,localhost,root,,);
connect (con2,localhost,root,,);
--connection con1
xa start 'a','b';
update t1 set c = 'aa' where a = 1;
--connection con2
xa start 'a','c';
update t1 set c = 'bb' where a = 2;
--connection con1
--send update t1 set c = 'bb' where a = 2
--connection con2
--sleep 1
--error ER_LOCK_DEADLOCK
update t1 set c = 'aa' where a = 1;
select count(*) from t1;
--error ER_XA_RBDEADLOCK
xa end 'a','c';
xa rollback 'a','c';
--disconnect con2
connect (con3,localhost,root,,);
--connection con3
xa start 'a','c';
--disconnect con1
--disconnect con3
--connection default
--echo End of 5.0 tests
...@@ -1278,7 +1278,12 @@ int ha_rollback_trans(THD *thd, bool all) ...@@ -1278,7 +1278,12 @@ int ha_rollback_trans(THD *thd, bool all)
trans->ha_list= 0; trans->ha_list= 0;
trans->no_2pc=0; trans->no_2pc=0;
if (is_real_trans) if (is_real_trans)
{
if (thd->transaction_rollback_request)
thd->transaction.xid_state.rm_error= thd->main_da.sql_errno();
else
thd->transaction.xid_state.xid.null(); thd->transaction.xid_state.xid.null();
}
if (all) if (all)
{ {
thd->variables.tx_isolation=thd->session_tx_isolation; thd->variables.tx_isolation=thd->session_tx_isolation;
......
...@@ -6128,6 +6128,12 @@ ER_LOAD_DATA_INVALID_COLUMN ...@@ -6128,6 +6128,12 @@ ER_LOAD_DATA_INVALID_COLUMN
ER_LOG_PURGE_NO_FILE ER_LOG_PURGE_NO_FILE
eng "Being purged log %s was not found" eng "Being purged log %s was not found"
ER_XA_RBTIMEOUT XA106
eng "XA_RBTIMEOUT: Transaction branch was rolled back: took too long"
ER_XA_RBDEADLOCK XA102
eng "XA_RBDEADLOCK: Transaction branch was rolled back: deadlock was detected"
ER_NEED_REPREPARE ER_NEED_REPREPARE
eng "Prepared statement needs to be re-prepared" eng "Prepared statement needs to be re-prepared"
......
...@@ -739,7 +739,7 @@ struct st_savepoint { ...@@ -739,7 +739,7 @@ struct st_savepoint {
Ha_trx_info *ha_list; Ha_trx_info *ha_list;
}; };
enum xa_states {XA_NOTR=0, XA_ACTIVE, XA_IDLE, XA_PREPARED}; enum xa_states {XA_NOTR=0, XA_ACTIVE, XA_IDLE, XA_PREPARED, XA_ROLLBACK_ONLY};
extern const char *xa_state_names[]; extern const char *xa_state_names[];
typedef struct st_xid_state { typedef struct st_xid_state {
...@@ -747,6 +747,8 @@ typedef struct st_xid_state { ...@@ -747,6 +747,8 @@ typedef struct st_xid_state {
XID xid; // transaction identifier XID xid; // transaction identifier
enum xa_states xa_state; // used by external XA only enum xa_states xa_state; // used by external XA only
bool in_thd; bool in_thd;
/* Error reported by the Resource Manager (RM) to the Transaction Manager. */
uint rm_error;
} XID_STATE; } XID_STATE;
extern pthread_mutex_t LOCK_xid_cache; extern pthread_mutex_t LOCK_xid_cache;
......
...@@ -83,9 +83,57 @@ const LEX_STRING command_name[]={ ...@@ -83,9 +83,57 @@ const LEX_STRING command_name[]={
}; };
const char *xa_state_names[]={ const char *xa_state_names[]={
"NON-EXISTING", "ACTIVE", "IDLE", "PREPARED" "NON-EXISTING", "ACTIVE", "IDLE", "PREPARED", "ROLLBACK ONLY"
}; };
/**
Mark a XA transaction as rollback-only if the RM unilaterally
rolled back the transaction branch.
@note If a rollback was requested by the RM, this function sets
the appropriate rollback error code and transits the state
to XA_ROLLBACK_ONLY.
@return TRUE if transaction was rolled back or if the transaction
state is XA_ROLLBACK_ONLY. FALSE otherwise.
*/
static bool xa_trans_rolled_back(XID_STATE *xid_state)
{
if (xid_state->rm_error)
{
switch (xid_state->rm_error) {
case ER_LOCK_WAIT_TIMEOUT:
my_error(ER_XA_RBTIMEOUT, MYF(0));
break;
case ER_LOCK_DEADLOCK:
my_error(ER_XA_RBDEADLOCK, MYF(0));
break;
default:
my_error(ER_XA_RBROLLBACK, MYF(0));
}
xid_state->xa_state= XA_ROLLBACK_ONLY;
}
return (xid_state->xa_state == XA_ROLLBACK_ONLY);
}
/**
Rollback work done on behalf of at ransaction branch.
*/
static bool xa_trans_rollback(THD *thd)
{
bool status= test(ha_rollback(thd));
thd->options&= ~(ulong) OPTION_BEGIN;
thd->transaction.all.modified_non_trans_table= FALSE;
thd->server_status&= ~SERVER_STATUS_IN_TRANS;
xid_cache_delete(&thd->transaction.xid_state);
thd->transaction.xid_state.xa_state= XA_NOTR;
thd->transaction.xid_state.rm_error= 0;
return status;
}
static void unlock_locked_tables(THD *thd) static void unlock_locked_tables(THD *thd)
{ {
if (thd->locked_tables) if (thd->locked_tables)
...@@ -4505,6 +4553,7 @@ mysql_execute_command(THD *thd) ...@@ -4505,6 +4553,7 @@ mysql_execute_command(THD *thd)
} }
DBUG_ASSERT(thd->transaction.xid_state.xid.is_null()); DBUG_ASSERT(thd->transaction.xid_state.xid.is_null());
thd->transaction.xid_state.xa_state=XA_ACTIVE; thd->transaction.xid_state.xa_state=XA_ACTIVE;
thd->transaction.xid_state.rm_error= 0;
thd->transaction.xid_state.xid.set(thd->lex->xid); thd->transaction.xid_state.xid.set(thd->lex->xid);
xid_cache_insert(&thd->transaction.xid_state); xid_cache_insert(&thd->transaction.xid_state);
thd->transaction.all.modified_non_trans_table= FALSE; thd->transaction.all.modified_non_trans_table= FALSE;
...@@ -4530,6 +4579,8 @@ mysql_execute_command(THD *thd) ...@@ -4530,6 +4579,8 @@ mysql_execute_command(THD *thd)
my_error(ER_XAER_NOTA, MYF(0)); my_error(ER_XAER_NOTA, MYF(0));
break; break;
} }
if (xa_trans_rolled_back(&thd->transaction.xid_state))
break;
thd->transaction.xid_state.xa_state=XA_IDLE; thd->transaction.xid_state.xa_state=XA_IDLE;
my_ok(thd); my_ok(thd);
break; break;
...@@ -4561,6 +4612,12 @@ mysql_execute_command(THD *thd) ...@@ -4561,6 +4612,12 @@ mysql_execute_command(THD *thd)
XID_STATE *xs=xid_cache_search(thd->lex->xid); XID_STATE *xs=xid_cache_search(thd->lex->xid);
if (!xs || xs->in_thd) if (!xs || xs->in_thd)
my_error(ER_XAER_NOTA, MYF(0)); my_error(ER_XAER_NOTA, MYF(0));
else if (xa_trans_rolled_back(xs))
{
ha_commit_or_rollback_by_xid(thd->lex->xid, 0);
xid_cache_delete(xs);
break;
}
else else
{ {
ha_commit_or_rollback_by_xid(thd->lex->xid, 1); ha_commit_or_rollback_by_xid(thd->lex->xid, 1);
...@@ -4569,6 +4626,11 @@ mysql_execute_command(THD *thd) ...@@ -4569,6 +4626,11 @@ mysql_execute_command(THD *thd)
} }
break; break;
} }
if (xa_trans_rolled_back(&thd->transaction.xid_state))
{
xa_trans_rollback(thd);
break;
}
if (thd->transaction.xid_state.xa_state == XA_IDLE && if (thd->transaction.xid_state.xa_state == XA_IDLE &&
thd->lex->xa_opt == XA_ONE_PHASE) thd->lex->xa_opt == XA_ONE_PHASE)
{ {
...@@ -4615,28 +4677,26 @@ mysql_execute_command(THD *thd) ...@@ -4615,28 +4677,26 @@ mysql_execute_command(THD *thd)
my_error(ER_XAER_NOTA, MYF(0)); my_error(ER_XAER_NOTA, MYF(0));
else else
{ {
bool ok= !xa_trans_rolled_back(xs);
ha_commit_or_rollback_by_xid(thd->lex->xid, 0); ha_commit_or_rollback_by_xid(thd->lex->xid, 0);
xid_cache_delete(xs); xid_cache_delete(xs);
if (ok)
my_ok(thd); my_ok(thd);
} }
break; break;
} }
if (thd->transaction.xid_state.xa_state != XA_IDLE && if (thd->transaction.xid_state.xa_state != XA_IDLE &&
thd->transaction.xid_state.xa_state != XA_PREPARED) thd->transaction.xid_state.xa_state != XA_PREPARED &&
thd->transaction.xid_state.xa_state != XA_ROLLBACK_ONLY)
{ {
my_error(ER_XAER_RMFAIL, MYF(0), my_error(ER_XAER_RMFAIL, MYF(0),
xa_state_names[thd->transaction.xid_state.xa_state]); xa_state_names[thd->transaction.xid_state.xa_state]);
break; break;
} }
if (ha_rollback(thd)) if (xa_trans_rollback(thd))
my_error(ER_XAER_RMERR, MYF(0)); my_error(ER_XAER_RMERR, MYF(0));
else else
my_ok(thd); my_ok(thd);
thd->options&= ~(OPTION_BEGIN | OPTION_KEEP_LOG);
thd->transaction.all.modified_non_trans_table= FALSE;
thd->server_status&= ~SERVER_STATUS_IN_TRANS;
xid_cache_delete(&thd->transaction.xid_state);
thd->transaction.xid_state.xa_state=XA_NOTR;
break; break;
case SQLCOM_XA_RECOVER: case SQLCOM_XA_RECOVER:
res= mysql_xa_recover(thd); res= mysql_xa_recover(thd);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment