summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mysql-test/include/handler.inc5
-rw-r--r--mysql-test/r/handler_innodb.result11
-rw-r--r--mysql-test/r/handler_myisam.result11
-rw-r--r--mysql-test/r/mdl_sync.result30
-rw-r--r--mysql-test/t/mdl_sync.test56
-rw-r--r--sql/ha_ndbcluster_binlog.cc8
-rw-r--r--sql/handler.cc12
-rw-r--r--sql/log_event.cc12
-rw-r--r--sql/log_event_old.cc5
-rw-r--r--sql/rpl_rli.cc11
-rw-r--r--sql/sp_head.cc34
-rw-r--r--sql/sql_admin.cc16
-rw-r--r--sql/sql_base.cc38
-rw-r--r--sql/sql_base.h6
-rw-r--r--sql/sql_cache.cc9
-rw-r--r--sql/sql_handler.cc3
-rw-r--r--sql/sql_parse.cc39
-rw-r--r--sql/sql_prepare.cc19
-rw-r--r--sql/sql_table.cc28
-rw-r--r--sql/transaction.cc45
-rw-r--r--sql/transaction.h1
21 files changed, 257 insertions, 142 deletions
diff --git a/mysql-test/include/handler.inc b/mysql-test/include/handler.inc
index 57d368960bf..2a015ca9c80 100644
--- a/mysql-test/include/handler.inc
+++ b/mysql-test/include/handler.inc
@@ -1087,8 +1087,7 @@ connection con1;
connection default;
--echo #
--echo # Demonstrate that HANDLER locks and transaction locks
---echo # reside in the same context, and we don't back-off
---echo # when have transaction or handler locks.
+--echo # reside in the same context.
--echo #
create table t1 (a int, key a (a));
insert into t1 (a) values (1), (2), (3), (4), (5);
@@ -1109,9 +1108,9 @@ let $wait_condition=select count(*)=1 from information_schema.processlist
--source include/wait_condition.inc
--echo # --> connection default
connection default;
+--echo # We back-off on hitting deadlock condition.
--error ER_LOCK_DEADLOCK
handler t0 open;
---error ER_LOCK_DEADLOCK
select * from t0;
handler t1 open;
commit;
diff --git a/mysql-test/r/handler_innodb.result b/mysql-test/r/handler_innodb.result
index dd4cac669c8..8f75ba7ff03 100644
--- a/mysql-test/r/handler_innodb.result
+++ b/mysql-test/r/handler_innodb.result
@@ -1104,8 +1104,7 @@ handler t1 close;
# --> connection default
#
# Demonstrate that HANDLER locks and transaction locks
-# reside in the same context, and we don't back-off
-# when have transaction or handler locks.
+# reside in the same context.
#
create table t1 (a int, key a (a));
insert into t1 (a) values (1), (2), (3), (4), (5);
@@ -1125,10 +1124,16 @@ rename table t0 to t3, t1 to t0, t3 to t1;
# --> connection con1
# Waiting for 'rename table ...' to get blocked...
# --> connection default
+# We back-off on hitting deadlock condition.
handler t0 open;
ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
select * from t0;
-ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
+a
+1
+2
+3
+4
+5
handler t1 open;
commit;
handler t1 close;
diff --git a/mysql-test/r/handler_myisam.result b/mysql-test/r/handler_myisam.result
index 69d791b8263..2b328a9f02c 100644
--- a/mysql-test/r/handler_myisam.result
+++ b/mysql-test/r/handler_myisam.result
@@ -1100,8 +1100,7 @@ handler t1 close;
# --> connection default
#
# Demonstrate that HANDLER locks and transaction locks
-# reside in the same context, and we don't back-off
-# when have transaction or handler locks.
+# reside in the same context.
#
create table t1 (a int, key a (a));
insert into t1 (a) values (1), (2), (3), (4), (5);
@@ -1121,10 +1120,16 @@ rename table t0 to t3, t1 to t0, t3 to t1;
# --> connection con1
# Waiting for 'rename table ...' to get blocked...
# --> connection default
+# We back-off on hitting deadlock condition.
handler t0 open;
ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
select * from t0;
-ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
+a
+1
+2
+3
+4
+5
handler t1 open;
commit;
handler t1 close;
diff --git a/mysql-test/r/mdl_sync.result b/mysql-test/r/mdl_sync.result
index b2e71faf741..4f929a5760f 100644
--- a/mysql-test/r/mdl_sync.result
+++ b/mysql-test/r/mdl_sync.result
@@ -1843,15 +1843,11 @@ rename table t2 to t0, t1 to t2, t0 to t1;;
# for 'deadlock_con1' which holds shared metadata lock on 't2'.
#
# The below statement should not wait as doing so will cause deadlock.
-# Instead it should fail and emit ER_LOCK_DEADLOCK statement.
+# Instead it should fail and emit ER_LOCK_DEADLOCK statement and
+# transaction should be rolled back.
select * from t1;
ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
#
-# Let us check that failure of the above statement has not released
-# metadata lock on table 't1', i.e. that RENAME TABLE is still blocked.
-# Commit transaction to unblock RENAME TABLE.
-commit;
-#
# Switching to connection 'default'.
# Reap RENAME TABLE.
#
@@ -1888,16 +1884,10 @@ unlock tables;
# Switching to connection 'deadlock_con1'.
# Since the latest RENAME TABLE entered in deadlock with SELECT
# statement the latter should be aborted and emit ER_LOCK_DEADLOCK
-# error.
+# error and transaction should be rolled back.
# Reap SELECT * FROM t1.
ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
#
-# Again let us check that failure of the SELECT statement has not
-# released metadata lock on table 't2', i.e. that the latest RENAME
-# is blocked.
-# Commit transaction to unblock this RENAME TABLE.
-commit;
-#
# Switching to connection 'deadlock_con2'.
# Reap RENAME TABLE ... .
#
@@ -1931,14 +1921,10 @@ alter table t1 add column j int, rename to t2;;
# metadata lock on 't2' and starts waiting for connection
# 'deadlock_con1' which holds shared lock on 't1'.
# The below statement should not wait as it will cause deadlock.
-# An appropriate error should be reported instead.
+# An appropriate error should be reported instead and transaction
+# should be rolled back.
select * from t2;
ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
-# Again let us check that failure of the above statement has not
-# released all metadata locks in connection 'deadlock_con1' and
-# so ALTER TABLE ... RENAME is still blocked.
-# Commit transaction to unblock ALTER TABLE ... RENAME.
-commit;
#
# Switching to connection 'default'.
# Reap ALTER TABLE ... RENAME.
@@ -2426,12 +2412,6 @@ set debug_sync='mdl_acquire_lock_wait SIGNAL alter_go';
update t1 set c3=c3+1 where c2 = 3;
ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
#
-# Let us check that failure of the above statement has not released
-# metadata lock on table 't1', i.e. that ALTER TABLE is still blocked.
-# Unblock ALTER TABLE by commiting transaction and thus releasing
-# metadata lock on 't1'.
-commit;
-#
# Switching to connection 'con46273'.
# Reap ALTER TABLE.
#
diff --git a/mysql-test/t/mdl_sync.test b/mysql-test/t/mdl_sync.test
index 197cad536e4..f6caa299fd4 100644
--- a/mysql-test/t/mdl_sync.test
+++ b/mysql-test/t/mdl_sync.test
@@ -2619,22 +2619,12 @@ let $wait_condition=
--source include/wait_condition.inc
--echo #
--echo # The below statement should not wait as doing so will cause deadlock.
---echo # Instead it should fail and emit ER_LOCK_DEADLOCK statement.
+--echo # Instead it should fail and emit ER_LOCK_DEADLOCK statement and
+--echo # transaction should be rolled back.
--error ER_LOCK_DEADLOCK
select * from t1;
--echo #
---echo # Let us check that failure of the above statement has not released
---echo # metadata lock on table 't1', i.e. that RENAME TABLE is still blocked.
-let $wait_condition=
- select count(*) = 1 from information_schema.processlist
- where state = "Waiting for table metadata lock" and
- info = "rename table t2 to t0, t1 to t2, t0 to t1";
---source include/wait_condition.inc
---echo # Commit transaction to unblock RENAME TABLE.
-commit;
-
---echo #
--echo # Switching to connection 'default'.
connection default;
--echo # Reap RENAME TABLE.
@@ -2696,24 +2686,12 @@ unlock tables;
connection deadlock_con1;
--echo # Since the latest RENAME TABLE entered in deadlock with SELECT
--echo # statement the latter should be aborted and emit ER_LOCK_DEADLOCK
---echo # error.
+--echo # error and transaction should be rolled back.
--echo # Reap SELECT * FROM t1.
--error ER_LOCK_DEADLOCK
--reap
--echo #
---echo # Again let us check that failure of the SELECT statement has not
---echo # released metadata lock on table 't2', i.e. that the latest RENAME
---echo # is blocked.
-let $wait_condition=
- select count(*) = 1 from information_schema.processlist
- where state = "Waiting for table metadata lock" and
- info = "rename table t1 to t0, t2 to t1, t0 to t2";
---source include/wait_condition.inc
---echo # Commit transaction to unblock this RENAME TABLE.
-commit;
-
---echo #
--echo # Switching to connection 'deadlock_con2'.
connection deadlock_con2;
--echo # Reap RENAME TABLE ... .
@@ -2761,22 +2739,11 @@ let $wait_condition=
--source include/wait_condition.inc
--echo # The below statement should not wait as it will cause deadlock.
---echo # An appropriate error should be reported instead.
+--echo # An appropriate error should be reported instead and transaction
+--echo # should be rolled back.
--error ER_LOCK_DEADLOCK
select * from t2;
---echo # Again let us check that failure of the above statement has not
---echo # released all metadata locks in connection 'deadlock_con1' and
---echo # so ALTER TABLE ... RENAME is still blocked.
-let $wait_condition=
- select count(*) = 1 from information_schema.processlist
- where state = "Waiting for table metadata lock" and
- info = "alter table t1 add column j int, rename to t2";
---source include/wait_condition.inc
-
---echo # Commit transaction to unblock ALTER TABLE ... RENAME.
-commit;
-
--echo #
--echo # Switching to connection 'default'.
connection default;
@@ -3578,19 +3545,6 @@ set debug_sync='mdl_acquire_lock_wait SIGNAL alter_go';
update t1 set c3=c3+1 where c2 = 3;
--echo #
---echo # Let us check that failure of the above statement has not released
---echo # metadata lock on table 't1', i.e. that ALTER TABLE is still blocked.
-let $wait_condition=
- select count(*) = 1 from information_schema.processlist
- where state = "Waiting for table metadata lock" and
- info = "alter table t1 add column e int, rename to t2";
---source include/wait_condition.inc
-
---echo # Unblock ALTER TABLE by commiting transaction and thus releasing
---echo # metadata lock on 't1'.
-commit;
-
---echo #
--echo # Switching to connection 'con46273'.
connection con46273;
--echo # Reap ALTER TABLE.
diff --git a/sql/ha_ndbcluster_binlog.cc b/sql/ha_ndbcluster_binlog.cc
index 48edca0a705..15e0b068826 100644
--- a/sql/ha_ndbcluster_binlog.cc
+++ b/sql/ha_ndbcluster_binlog.cc
@@ -1,5 +1,5 @@
/*
- Copyright (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved.
+ Copyright (c) 2006, 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
@@ -2422,6 +2422,12 @@ add_ndb_binlog_index_err:
thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd);
thd->stmt_da->can_overwrite_status= FALSE;
close_thread_tables(thd);
+ /*
+ There should be no need for rolling back transaction due to deadlock
+ (since ndb_binlog_index is non transactional).
+ */
+ DBUG_ASSERT(! thd->transaction_rollback_request);
+
thd->mdl_context.release_transactional_locks();
ndb_binlog_index= 0;
thd->variables.option_bits= saved_options;
diff --git a/sql/handler.cc b/sql/handler.cc
index 98d6e8fb103..c3ad1cade99 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -1480,10 +1480,16 @@ int ha_rollback_trans(THD *thd, bool all)
}
trans->ha_list= 0;
trans->no_2pc=0;
- if (is_real_trans && thd->transaction_rollback_request &&
- thd->transaction.xid_state.xa_state != XA_NOTR)
- thd->transaction.xid_state.rm_error= thd->stmt_da->sql_errno();
}
+
+ /*
+ Thanks to possibility of MDL deadlock rollback request can come even if
+ transaction hasn't been started in any transactional storage engine.
+ */
+ if (is_real_trans && thd->transaction_rollback_request &&
+ thd->transaction.xid_state.xa_state != XA_NOTR)
+ thd->transaction.xid_state.rm_error= thd->stmt_da->sql_errno();
+
/* Always cleanup. Even if nht==0. There may be savepoints. */
if (is_real_trans)
thd->transaction.cleanup();
diff --git a/sql/log_event.cc b/sql/log_event.cc
index d2682a3eb9e..31ee950dbe4 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -5104,6 +5104,8 @@ error:
thd->stmt_da->can_overwrite_status= FALSE;
close_thread_tables(thd);
/*
+ - If transaction rollback was requested due to deadlock
+ perform it and release metadata locks.
- If inside a multi-statement transaction,
defer the release of metadata locks until the current
transaction is either committed or rolled back. This prevents
@@ -5113,7 +5115,12 @@ error:
- If in autocommit mode, or outside a transactional context,
automatically release metadata locks of the current statement.
*/
- if (! thd->in_multi_stmt_transaction_mode())
+ if (thd->transaction_rollback_request)
+ {
+ trans_rollback_implicit(thd);
+ thd->mdl_context.release_transactional_locks();
+ }
+ else if (! thd->in_multi_stmt_transaction_mode())
thd->mdl_context.release_transactional_locks();
else
thd->mdl_context.release_statement_locks();
@@ -8197,7 +8204,10 @@ static int rows_event_stmt_cleanup(Relay_log_info const *rli, THD * thd)
Xid_log_event will come next which will, if some transactional engines
are involved, commit the transaction and flush the pending event to the
binlog.
+ If there was a deadlock the transaction should have been rolled back
+ already. So there should be no need to rollback the transaction.
*/
+ DBUG_ASSERT(! thd->transaction_rollback_request);
error|= (error ? trans_rollback_stmt(thd) : trans_commit_stmt(thd));
/*
diff --git a/sql/log_event_old.cc b/sql/log_event_old.cc
index 55d8d72ec5d..1462b08e993 100644
--- a/sql/log_event_old.cc
+++ b/sql/log_event_old.cc
@@ -1,4 +1,4 @@
-/* Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved.
+/* Copyright (c) 2007, 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
@@ -1808,7 +1808,10 @@ int Old_rows_log_event::do_apply_event(Relay_log_info const *rli)
Xid_log_event will come next which will, if some transactional engines
are involved, commit the transaction and flush the pending event to the
binlog.
+ If there was a deadlock the transaction should have been rolled back
+ already. So there should be no need to rollback the transaction.
*/
+ DBUG_ASSERT(! thd->transaction_rollback_request);
if ((error= (binlog_error ? trans_rollback_stmt(thd) : trans_commit_stmt(thd))))
rli->report(ERROR_LEVEL, error,
"Error in %s event: commit of row events failed, "
diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc
index 2d302011b44..e4f2e4fd382 100644
--- a/sql/rpl_rli.cc
+++ b/sql/rpl_rli.cc
@@ -1,4 +1,4 @@
-/* Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved.
+/* Copyright (c) 2006, 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
@@ -1317,6 +1317,8 @@ void Relay_log_info::slave_close_thread_tables(THD *thd)
close_thread_tables(thd);
/*
+ - If transaction rollback was requested due to deadlock
+ perform it and release metadata locks.
- If inside a multi-statement transaction,
defer the release of metadata locks until the current
transaction is either committed or rolled back. This prevents
@@ -1326,7 +1328,12 @@ void Relay_log_info::slave_close_thread_tables(THD *thd)
- If in autocommit mode, or outside a transactional context,
automatically release metadata locks of the current statement.
*/
- if (! thd->in_multi_stmt_transaction_mode())
+ if (thd->transaction_rollback_request)
+ {
+ trans_rollback_implicit(thd);
+ thd->mdl_context.release_transactional_locks();
+ }
+ else if (! thd->in_multi_stmt_transaction_mode())
thd->mdl_context.release_transactional_locks();
else
thd->mdl_context.release_statement_locks();
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index 552a44c7a96..13d1b310599 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -1,5 +1,5 @@
/*
- Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved.
+ Copyright (c) 2002, 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
@@ -2155,10 +2155,18 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
close_thread_tables(thd);
thd_proc_info(thd, 0);
- if (! thd->in_sub_stmt && ! thd->in_multi_stmt_transaction_mode())
- thd->mdl_context.release_transactional_locks();
- else if (! thd->in_sub_stmt)
- thd->mdl_context.release_statement_locks();
+ if (! thd->in_sub_stmt)
+ {
+ if (thd->transaction_rollback_request)
+ {
+ trans_rollback_implicit(thd);
+ thd->mdl_context.release_transactional_locks();
+ }
+ else if (! thd->in_multi_stmt_transaction_mode())
+ thd->mdl_context.release_transactional_locks();
+ else
+ thd->mdl_context.release_statement_locks();
+ }
thd->rollback_item_tree_changes();
@@ -3008,10 +3016,18 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp,
close_thread_tables(thd);
thd_proc_info(thd, 0);
- if (! thd->in_sub_stmt && ! thd->in_multi_stmt_transaction_mode())
- thd->mdl_context.release_transactional_locks();
- else if (! thd->in_sub_stmt)
- thd->mdl_context.release_statement_locks();
+ if (! thd->in_sub_stmt)
+ {
+ if (thd->transaction_rollback_request)
+ {
+ trans_rollback_implicit(thd);
+ thd->mdl_context.release_transactional_locks();
+ }
+ else if (! thd->in_multi_stmt_transaction_mode())
+ thd->mdl_context.release_transactional_locks();
+ else
+ thd->mdl_context.release_statement_locks();
+ }
}
if (m_lex->query_tables_own_last)
diff --git a/sql/sql_admin.cc b/sql/sql_admin.cc
index 16fcf120128..f07a8089853 100644
--- a/sql/sql_admin.cc
+++ b/sql/sql_admin.cc
@@ -859,8 +859,20 @@ send_result_message:
}
}
/* Error path, a admin command failed. */
- trans_commit_stmt(thd);
- trans_commit_implicit(thd);
+ if (thd->transaction_rollback_request)
+ {
+ /*
+ Unlikely, but transaction rollback was requested by one of storage
+ engines (e.g. due to deadlock). Perform it.
+ */
+ if (trans_rollback_stmt(thd) || trans_rollback_implicit(thd))
+ goto err;
+ }
+ else
+ {
+ if (trans_commit_stmt(thd) || trans_commit_implicit(thd))
+ goto err;
+ }
close_thread_tables(thd);
thd->mdl_context.release_transactional_locks();
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index a8279ec0347..040c0425577 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -1,4 +1,4 @@
-/* Copyright (c) 2000, 2012, 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
@@ -3941,7 +3941,8 @@ end_unlock:
/** Open_table_context */
Open_table_context::Open_table_context(THD *thd, uint flags)
- :m_failed_table(NULL),
+ :m_thd(thd),
+ m_failed_table(NULL),
m_start_of_statement_svp(thd->mdl_context.mdl_savepoint()),
m_timeout(flags & MYSQL_LOCK_IGNORE_TIMEOUT ?
LONG_TIMEOUT : thd->variables.lock_wait_timeout),
@@ -4018,6 +4019,7 @@ request_backoff_action(enum_open_table_action action_arg,
if (action_arg != OT_REOPEN_TABLES && m_has_locks)
{
my_error(ER_LOCK_DEADLOCK, MYF(0));
+ mark_transaction_to_rollback(m_thd, true);
return TRUE;
}
/*
@@ -4027,7 +4029,7 @@ request_backoff_action(enum_open_table_action action_arg,
if (table)
{
DBUG_ASSERT(action_arg == OT_DISCOVER || action_arg == OT_REPAIR);
- m_failed_table= (TABLE_LIST*) current_thd->alloc(sizeof(TABLE_LIST));
+ m_failed_table= (TABLE_LIST*) m_thd->alloc(sizeof(TABLE_LIST));
if (m_failed_table == NULL)
return TRUE;
m_failed_table->init_one_table(table->db, table->db_length,
@@ -4044,8 +4046,6 @@ request_backoff_action(enum_open_table_action action_arg,
/**
Recover from failed attempt of open table by performing requested action.
- @param thd Thread context
-
@pre This function should be called only with "action" != OT_NO_ACTION
and after having called @sa close_tables_for_reopen().
@@ -4055,7 +4055,7 @@ request_backoff_action(enum_open_table_action action_arg,
bool
Open_table_context::
-recover_from_failed_open(THD *thd)
+recover_from_failed_open()
{
bool result= FALSE;
/* Execute the action. */
@@ -4067,33 +4067,33 @@ recover_from_failed_open(THD *thd)
break;
case OT_DISCOVER:
{
- if ((result= lock_table_names(thd, m_failed_table, NULL,
+ if ((result= lock_table_names(m_thd, m_failed_table, NULL,
get_timeout(),
MYSQL_OPEN_SKIP_TEMPORARY)))
break;
- tdc_remove_table(thd, TDC_RT_REMOVE_ALL, m_failed_table->db,
+ tdc_remove_table(m_thd, TDC_RT_REMOVE_ALL, m_failed_table->db,
m_failed_table->table_name, FALSE);
- ha_create_table_from_engine(thd, m_failed_table->db,
+ ha_create_table_from_engine(m_thd, m_failed_table->db,
m_failed_table->table_name);
- thd->warning_info->clear_warning_info(thd->query_id);
- thd->clear_error(); // Clear error message
- thd->mdl_context.release_transactional_locks();
+ m_thd->warning_info->clear_warning_info(m_thd->query_id);
+ m_thd->clear_error(); // Clear error message
+ m_thd->mdl_context.release_transactional_locks();
break;
}
case OT_REPAIR:
{
- if ((result= lock_table_names(thd, m_failed_table, NULL,
+ if ((result= lock_table_names(m_thd, m_failed_table, NULL,
get_timeout(),
MYSQL_OPEN_SKIP_TEMPORARY)))
break;
- tdc_remove_table(thd, TDC_RT_REMOVE_ALL, m_failed_table->db,
+ tdc_remove_table(m_thd, TDC_RT_REMOVE_ALL, m_failed_table->db,
m_failed_table->table_name, FALSE);
- result= auto_repair_table(thd, m_failed_table);
- thd->mdl_context.release_transactional_locks();
+ result= auto_repair_table(m_thd, m_failed_table);
+ m_thd->mdl_context.release_transactional_locks();
break;
}
default:
@@ -4926,7 +4926,7 @@ restart:
TABLE_LIST element. Altough currently this assumption is valid
it may change in future.
*/
- if (ot_ctx.recover_from_failed_open(thd))
+ if (ot_ctx.recover_from_failed_open())
goto err;
error= FALSE;
@@ -4979,7 +4979,7 @@ restart:
{
close_tables_for_reopen(thd, start,
ot_ctx.start_of_statement_svp());
- if (ot_ctx.recover_from_failed_open(thd))
+ if (ot_ctx.recover_from_failed_open())
goto err;
error= FALSE;
@@ -5417,7 +5417,7 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type,
*/
thd->mdl_context.rollback_to_savepoint(ot_ctx.start_of_statement_svp());
table_list->mdl_request.ticket= 0;
- if (ot_ctx.recover_from_failed_open(thd))
+ if (ot_ctx.recover_from_failed_open())
break;
}
diff --git a/sql/sql_base.h b/sql/sql_base.h
index 96ca569dd1f..b118c93ac28 100644
--- a/sql/sql_base.h
+++ b/sql/sql_base.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved.
+/* Copyright (c) 2010, 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
@@ -519,7 +519,7 @@ public:
};
Open_table_context(THD *thd, uint flags);
- bool recover_from_failed_open(THD *thd);
+ bool recover_from_failed_open();
bool request_backoff_action(enum_open_table_action action_arg,
TABLE_LIST *table);
@@ -559,6 +559,8 @@ public:
}
private:
+ /* THD for which tables are opened. */
+ THD *m_thd;
/**
For OT_DISCOVER and OT_REPAIR actions, the table list element for
the table which definition should be re-discovered or which
diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc
index 34a61e99007..ca3db3b48c0 100644
--- a/sql/sql_cache.cc
+++ b/sql/sql_cache.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
@@ -1730,7 +1730,12 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d",
}
else
thd->lex->safe_to_cache_query= 0; // Don't try to cache this
- /* End the statement transaction potentially started by engine. */
+ /*
+ End the statement transaction potentially started by engine.
+ Currently our engines do not request rollback from callbacks.
+ If this is going to change code needs to be reworked.
+ */
+ DBUG_ASSERT(! thd->transaction_rollback_request);
trans_rollback_stmt(thd);
goto err_unlock; // Parse query
}
diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc
index e20ee243b79..b179d54eadf 100644
--- a/sql/sql_handler.cc
+++ b/sql/sql_handler.cc
@@ -589,7 +589,10 @@ retry:
/*
Always close statement transaction explicitly,
so that the engine doesn't have to count locks.
+ There should be no need to perform transaction
+ rollback due to deadlock.
*/
+ DBUG_ASSERT(! thd->transaction_rollback_request);
trans_rollback_stmt(thd);
mysql_ha_close_table(thd, hash_tables);
goto retry;
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index ef3454ec9c9..dac42457a87 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -1,4 +1,4 @@
-/* Copyright (c) 2000, 2012, 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
@@ -1183,6 +1183,18 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
close_thread_tables(thd);
thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
+ if (thd->transaction_rollback_request)
+ {
+ /*
+ Transaction rollback was requested since MDL deadlock was
+ discovered while trying to open tables. Rollback transaction
+ in all storage engines including binary log and release all
+ locks.
+ */
+ trans_rollback_implicit(thd);
+ thd->mdl_context.release_transactional_locks();
+ }
+
thd->cleanup_after_query();
break;
}
@@ -1833,7 +1845,7 @@ err:
can free its locks if LOCK TABLES locked some tables before finding
that it can't lock a table in its list
*/
- trans_commit_implicit(thd);
+ trans_rollback(thd);
/* Close tables and release metadata locks. */
close_thread_tables(thd);
DBUG_ASSERT(!thd->locked_tables_mode);
@@ -1885,6 +1897,13 @@ mysql_execute_command(THD *thd)
DBUG_ASSERT(thd->transaction.stmt.is_empty() || thd->in_sub_stmt);
/*
+ Each statement or replication event which might produce deadlock
+ should handle transaction rollback on its own. So by the start of
+ the next statement transaction rollback request should be fulfilled
+ already.
+ */
+ DBUG_ASSERT(! thd->transaction_rollback_request || thd->in_sub_stmt);
+ /*
In many cases first table of main SELECT_LEX have special meaning =>
check that it is first table in global list and relink it first in
queries_tables list if it is necessary (we need such relinking only
@@ -2070,8 +2089,8 @@ mysql_execute_command(THD *thd)
or triggers as all such statements prohibited there.
*/
DBUG_ASSERT(! thd->in_sub_stmt);
- /* Commit or rollback the statement transaction. */
- thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd);
+ /* Statement transaction still should not be started. */
+ DBUG_ASSERT(thd->transaction.stmt.is_empty());
/* Commit the normal transaction if one is active. */
if (trans_commit_implicit(thd))
goto error;
@@ -4503,7 +4522,17 @@ finish:
DEBUG_SYNC(thd, "execute_command_after_close_tables");
#endif
- if (stmt_causes_implicit_commit(thd, CF_IMPLICIT_COMMIT_END))
+ if (! thd->in_sub_stmt && thd->transaction_rollback_request)
+ {
+ /*
+ We are not in sub-statement and transaction rollback was requested by
+ one of storage engines (e.g. due to deadlock). Rollback transaction in
+ all storage engines including binary log.
+ */
+ trans_rollback_implicit(thd);
+ thd->mdl_context.release_transactional_locks();
+ }
+ else if (stmt_causes_implicit_commit(thd, CF_IMPLICIT_COMMIT_END))
{
/* No transaction control allowed in sub-statements. */
DBUG_ASSERT(! thd->in_sub_stmt);
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index f3deafc6035..74279c5539d 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -1,4 +1,4 @@
-/* Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved.
+/* Copyright (c) 2002, 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
@@ -113,6 +113,7 @@ When one supplies long data for a placeholder:
#include <mysql_com.h>
#endif
#include "lock.h" // MYSQL_OPEN_FORCE_SHARED_MDL
+#include "transaction.h" // trans_rollback_implicit
/**
A result class used to send cursor rows using the binary protocol.
@@ -3297,6 +3298,22 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len)
close_thread_tables(thd);
thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
+
+ /*
+ Transaction rollback was requested since MDL deadlock was discovered
+ while trying to open tables. Rollback transaction in all storage
+ engines including binary log and release all locks.
+
+ Once dynamic SQL is allowed as substatements the below if-statement
+ has to be adjusted to not do rollback in substatement.
+ */
+ DBUG_ASSERT(! thd->in_sub_stmt);
+ if (thd->transaction_rollback_request)
+ {
+ trans_rollback_implicit(thd);
+ thd->mdl_context.release_transactional_locks();
+ }
+
lex_end(lex);
cleanup_stmt();
thd->restore_backup_statement(this, &stmt_backup);
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index ac121572ab2..bfed754a90c 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -4797,7 +4797,6 @@ mysql_discard_or_import_tablespace(THD *thd,
error= write_bin_log(thd, FALSE, thd->query(), thd->query_length());
err:
- trans_rollback_stmt(thd);
thd->tablespace_op=FALSE;
if (error == 0)
@@ -7331,6 +7330,12 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables,
Protocol *protocol= thd->protocol;
DBUG_ENTER("mysql_checksum_table");
+ /*
+ CHECKSUM TABLE returns results and rollbacks statement transaction,
+ so it should not be used in stored function or trigger.
+ */
+ DBUG_ASSERT(! thd->in_sub_stmt);
+
field_list.push_back(item = new Item_empty_string("Table", NAME_LEN*2));
item->maybe_null= 1;
field_list.push_back(item= new Item_int("Checksum", (longlong) 1,
@@ -7349,7 +7354,6 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables,
strxmov(table_name, table->db ,".", table->table_name, NullS);
t= table->table= open_n_lock_single_table(thd, table, TL_READ, 0);
- thd->clear_error(); // these errors shouldn't get client
protocol->prepare_for_resend();
protocol->store(table_name, system_charset_info);
@@ -7358,7 +7362,6 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables,
{
/* Table didn't exist */
protocol->store_null();
- thd->clear_error();
}
else
{
@@ -7443,9 +7446,7 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables,
t->file->ha_rnd_end();
}
}
- thd->clear_error();
- if (! thd->in_sub_stmt)
- trans_rollback_stmt(thd);
+ trans_rollback_stmt(thd);
close_thread_tables(thd);
/*
Don't release metadata locks, this will be done at
@@ -7453,6 +7454,21 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables,
*/
table->table=0; // For query cache
}
+
+ if (thd->transaction_rollback_request)
+ {
+ /*
+ If transaction rollback was requested we honor it. To do this we
+ abort statement and return error as not only CHECKSUM TABLE is
+ rolled back but the whole transaction in which it was used.
+ */
+ thd->protocol->remove_last_row();
+ goto err;
+ }
+
+ /* Hide errors from client. Return NULL for problematic tables instead. */
+ thd->clear_error();
+
if (protocol->write())
goto err;
}
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;
}
diff --git a/sql/transaction.h b/sql/transaction.h
index e002cd4a9dc..abe7823cf9b 100644
--- a/sql/transaction.h
+++ b/sql/transaction.h
@@ -30,6 +30,7 @@ bool trans_begin(THD *thd, uint flags= 0);
bool trans_commit(THD *thd);
bool trans_commit_implicit(THD *thd);
bool trans_rollback(THD *thd);
+bool trans_rollback_implicit(THD *thd);
bool trans_commit_stmt(THD *thd);
bool trans_rollback_stmt(THD *thd);