summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Lenev <Dmitry.Lenev@oracle.com>2010-11-11 20:11:05 +0300
committerDmitry Lenev <Dmitry.Lenev@oracle.com>2010-11-11 20:11:05 +0300
commit378cdc58c14afb7c48752f98889073fefe2c7ca7 (patch)
treefebeaaf159151300ea8117a014befaf1abec8c60
parentaee8fce23309b1e90061a38c3c2eacf8a0efeb16 (diff)
downloadmariadb-git-378cdc58c14afb7c48752f98889073fefe2c7ca7.tar.gz
Patch that refactors global read lock implementation and fixes
bug #57006 "Deadlock between HANDLER and FLUSH TABLES WITH READ LOCK" and bug #54673 "It takes too long to get readlock for 'FLUSH TABLES WITH READ LOCK'". The first bug manifested itself as a deadlock which occurred when a connection, which had some table open through HANDLER statement, tried to update some data through DML statement while another connection tried to execute FLUSH TABLES WITH READ LOCK concurrently. What happened was that FTWRL in the second connection managed to perform first step of GRL acquisition and thus blocked all upcoming DML. After that it started to wait for table open through HANDLER statement to be flushed. When the first connection tried to execute DML it has started to wait for GRL/the second connection creating deadlock. The second bug manifested itself as starvation of FLUSH TABLES WITH READ LOCK statements in cases when there was a constant stream of concurrent DML statements (in two or more connections). This has happened because requests for protection against GRL which were acquired by DML statements were ignoring presence of pending GRL and thus the latter was starved. This patch solves both these problems by re-implementing GRL using metadata locks. Similar to the old implementation acquisition of GRL in new implementation is two-step. During the first step we block all concurrent DML and DDL statements by acquiring global S metadata lock (each DML and DDL statement acquires global IX lock for its duration). During the second step we block commits by acquiring global S lock in COMMIT namespace (commit code acquires global IX lock in this namespace). Note that unlike in old implementation acquisition of protection against GRL in DML and DDL is semi-automatic. We assume that any statement which should be blocked by GRL will either open and acquires write-lock on tables or acquires metadata locks on objects it is going to modify. For any such statement global IX metadata lock is automatically acquired for its duration. The first problem is solved because waits for GRL become visible to deadlock detector in metadata locking subsystem and thus deadlocks like one in the first bug become impossible. The second problem is solved because global S locks which are used for GRL implementation are given preference over IX locks which are acquired by concurrent DML (and we can switch to fair scheduling in future if needed). Important change: FTWRL/GRL no longer blocks DML and DDL on temporary tables. Before this patch behavior was not consistent in this respect: in some cases DML/DDL statements on temporary tables were blocked while in others they were not. Since the main use cases for FTWRL are various forms of backups and temporary tables are not preserved during backups we have opted for consistently allowing DML/DDL on temporary tables during FTWRL/GRL. Important change: This patch changes thread state names which are used when DML/DDL of FTWRL is waiting for global read lock. It is now either "Waiting for global read lock" or "Waiting for commit lock" depending on the stage on which FTWRL is. Incompatible change: To solve deadlock in events code which was exposed by this patch we have to replace LOCK_event_metadata mutex with metadata locks on events. As result we have to prohibit DDL on events under LOCK TABLES. This patch also adds extensive test coverage for interaction of DML/DDL and FTWRL. Performance of new and old global read lock implementations in sysbench tests were compared. There were no significant difference between new and old implementations.
-rw-r--r--mysql-test/include/check_ftwrl_compatible.inc158
-rw-r--r--mysql-test/include/check_ftwrl_incompatible.inc155
-rw-r--r--mysql-test/include/handler.inc2
-rw-r--r--mysql-test/include/wait_show_condition.inc2
-rw-r--r--mysql-test/r/delayed.result12
-rw-r--r--mysql-test/r/events_2.result46
-rw-r--r--mysql-test/r/flush.result28
-rw-r--r--mysql-test/r/flush_read_lock.result1683
-rw-r--r--mysql-test/r/flush_read_lock_kill.result36
-rw-r--r--mysql-test/r/handler_innodb.result4
-rw-r--r--mysql-test/r/handler_myisam.result4
-rw-r--r--mysql-test/r/mdl_sync.result11
-rw-r--r--mysql-test/suite/perfschema/r/dml_setup_instruments.result2
-rw-r--r--mysql-test/suite/perfschema/r/func_file_io.result1
-rw-r--r--mysql-test/suite/perfschema/r/func_mutex.result1
-rw-r--r--mysql-test/suite/perfschema/r/global_read_lock.result5
-rw-r--r--mysql-test/suite/perfschema/r/server_init.result12
-rw-r--r--mysql-test/suite/perfschema/t/func_file_io.test3
-rw-r--r--mysql-test/suite/perfschema/t/func_mutex.test2
-rw-r--r--mysql-test/suite/perfschema/t/global_read_lock.test8
-rw-r--r--mysql-test/suite/perfschema/t/server_init.test9
-rw-r--r--mysql-test/suite/rpl/r/rpl_tmp_table_and_DDL.result6
-rw-r--r--mysql-test/t/delayed.test15
-rw-r--r--mysql-test/t/events_2.test46
-rw-r--r--mysql-test/t/flush.test67
-rw-r--r--mysql-test/t/flush_block_commit.test2
-rw-r--r--mysql-test/t/flush_block_commit_notembedded.test2
-rw-r--r--mysql-test/t/flush_read_lock.test2187
-rw-r--r--mysql-test/t/flush_read_lock_kill-master.opt1
-rw-r--r--mysql-test/t/flush_read_lock_kill.test68
-rw-r--r--mysql-test/t/lock_multi.test58
-rw-r--r--mysql-test/t/mdl_sync.test15
-rw-r--r--mysql-test/t/trigger_notembedded.test2
-rw-r--r--sql/event_data_objects.cc45
-rw-r--r--sql/event_data_objects.h7
-rw-r--r--sql/event_db_repository.cc53
-rw-r--r--sql/event_db_repository.h2
-rw-r--r--sql/event_queue.cc20
-rw-r--r--sql/events.cc42
-rw-r--r--sql/events.h4
-rw-r--r--sql/ha_ndbcluster.cc51
-rw-r--r--sql/handler.cc40
-rw-r--r--sql/lock.cc362
-rw-r--r--sql/lock.h6
-rw-r--r--sql/log_event.cc2
-rw-r--r--sql/mdl.cc563
-rw-r--r--sql/mdl.h185
-rw-r--r--sql/mysqld.cc16
-rw-r--r--sql/mysqld.h9
-rw-r--r--sql/rpl_rli.cc3
-rw-r--r--sql/sp.cc23
-rw-r--r--sql/sp_head.cc9
-rw-r--r--sql/sql_admin.cc2
-rw-r--r--sql/sql_base.cc130
-rw-r--r--sql/sql_base.h26
-rw-r--r--sql/sql_class.cc10
-rw-r--r--sql/sql_class.h61
-rw-r--r--sql/sql_db.cc3
-rw-r--r--sql/sql_handler.cc35
-rw-r--r--sql/sql_handler.h2
-rw-r--r--sql/sql_insert.cc85
-rw-r--r--sql/sql_lex.cc1
-rw-r--r--sql/sql_lex.h16
-rw-r--r--sql/sql_parse.cc154
-rw-r--r--sql/sql_prepare.cc3
-rw-r--r--sql/sql_rename.cc7
-rw-r--r--sql/sql_show.cc6
-rw-r--r--sql/sql_table.cc30
-rw-r--r--sql/sql_trigger.cc14
-rw-r--r--sql/sql_update.cc12
-rw-r--r--sql/sql_view.cc10
-rw-r--r--sql/sql_yacc.yy4
-rw-r--r--sql/table.cc3
-rw-r--r--sql/table.h3
-rw-r--r--sql/transaction.cc25
75 files changed, 5493 insertions, 1244 deletions
diff --git a/mysql-test/include/check_ftwrl_compatible.inc b/mysql-test/include/check_ftwrl_compatible.inc
new file mode 100644
index 00000000000..76c1915957c
--- /dev/null
+++ b/mysql-test/include/check_ftwrl_compatible.inc
@@ -0,0 +1,158 @@
+#
+# SUMMARY
+# Check that a statement is compatible with FLUSH TABLES WITH READ LOCK.
+#
+# PARAMETERS
+# $con_aux1 Name of the 1st aux connection to be used by this script.
+# $con_aux2 Name of the 2nd aux connection to be used by this script.
+# $statement The statement to be checked.
+# $cleanup_stmt The statement to be run in order to revert effects of
+# the statement to be checked.
+# $skip_3rd_chk Skip the 3rd stage of checking. The purpose of the third
+# stage is to check that metadata locks taken by this
+# statement are compatible with metadata locks taken
+# by FTWRL.
+#
+# EXAMPLE
+# flush_read_lock.test
+#
+--disable_result_log
+--disable_query_log
+
+# Reset DEBUG_SYNC facility for safety.
+set debug_sync= "RESET";
+
+#
+# First, check that the statement can be run under FTWRL.
+#
+flush tables with read lock;
+--disable_abort_on_error
+--eval $statement
+--enable_abort_on_error
+let $err= $mysql_errno;
+if (!$err)
+{
+--echo Success: Was able to run '$statement' under FTWRL.
+unlock tables;
+if (`SELECT "$cleanup_stmt" <> ""`)
+{
+--eval $cleanup_stmt;
+}
+}
+if ($err)
+{
+--echo Error: Wasn't able to run '$statement' under FTWRL!
+unlock tables;
+}
+
+#
+# Then check that this statement won't be blocked by FTWRL
+# that is active in another connection.
+#
+connection $con_aux1;
+flush tables with read lock;
+
+connection default;
+--send_eval $statement;
+
+connection $con_aux1;
+
+--enable_result_log
+--enable_query_log
+let $wait_condition=
+ select count(*) = 0 from information_schema.processlist
+ where info = "$statement";
+--source include/wait_condition.inc
+--disable_result_log
+--disable_query_log
+
+if ($success)
+{
+--echo Success: Was able to run '$statement' with FTWRL active in another connection.
+
+connection default;
+# Apparently statement was successfully executed and so
+# was not blocked by FTWRL.
+# To be safe against wait_condition.inc succeeding due to
+# races let us first reap the statement being checked to
+# ensure that it has been successfully executed.
+--reap
+
+connection $con_aux1;
+unlock tables;
+
+connection default;
+}
+if (!$success)
+{
+--echo Error: Wasn't able to run '$statement' with FTWRL active in another connection!
+unlock tables;
+connection default;
+--reap
+}
+
+if (`SELECT "$cleanup_stmt" <> ""`)
+{
+--eval $cleanup_stmt;
+}
+
+if (`SELECT "$skip_3rd_check" = ""`)
+{
+#
+# Finally, let us check that FTWRL will succeed if this statement
+# is active but has already closed its tables.
+#
+connection default;
+set debug_sync='execute_command_after_close_tables SIGNAL parked WAIT_FOR go';
+--send_eval $statement;
+
+connection $con_aux1;
+set debug_sync="now WAIT_FOR parked";
+--send flush tables with read lock
+
+connection $con_aux2;
+--enable_result_log
+--enable_query_log
+let $wait_condition=
+ select count(*) = 0 from information_schema.processlist
+ where info = "flush tables with read lock";
+--source include/wait_condition.inc
+--disable_result_log
+--disable_query_log
+
+if ($success)
+{
+--echo Success: Was able to run FTWRL while '$statement' was active in another connection.
+connection $con_aux1;
+# Apparently FTWRL was successfully executed and so was not blocked by
+# the statement being checked. To be safe against wait_condition.inc
+# succeeding due to races let us first reap the FTWRL to ensure that it
+# has been successfully executed.
+--reap
+unlock tables;
+set debug_sync="now SIGNAL go";
+connection default;
+--reap
+}
+if (!$success)
+{
+--echo Error: Wasn't able to run FTWRL while '$statement' was active in another connection!
+set debug_sync="now SIGNAL go";
+connection default;
+--reap
+connection $con_aux1;
+--reap
+unlock tables;
+connection default;
+}
+
+set debug_sync= "RESET";
+if (`SELECT "$cleanup_stmt" <> ""`)
+{
+--eval $cleanup_stmt;
+}
+
+}
+
+--enable_result_log
+--enable_query_log
diff --git a/mysql-test/include/check_ftwrl_incompatible.inc b/mysql-test/include/check_ftwrl_incompatible.inc
new file mode 100644
index 00000000000..56deef8e92f
--- /dev/null
+++ b/mysql-test/include/check_ftwrl_incompatible.inc
@@ -0,0 +1,155 @@
+#
+# SUMMARY
+# Check that a statement is incompatible with FLUSH TABLES WITH READ LOCK.
+#
+# PARAMETERS
+# $con_aux1 Name of the 1st aux connection to be used by this script.
+# $con_aux2 Name of the 2nd aux connection to be used by this script.
+# $statement The statement to be checked.
+# $cleanup_stmt1 The 1st statement to be run in order to revert effects
+# of statement to be checked.
+# $cleanup_stmt2 The 2nd statement to be run in order to revert effects
+# of statement to be checked.
+# $skip_3rd_chk Skip the 3rd stage of checking. The purpose of the third
+# stage is to check that metadata locks taken by this
+# statement are incompatible with metadata locks taken
+# by FTWRL.
+#
+# EXAMPLE
+# flush_read_lock.test
+#
+--disable_result_log
+--disable_query_log
+
+# Reset DEBUG_SYNC facility for safety.
+set debug_sync= "RESET";
+
+#
+# First, check that the statement cannot be run under FTWRL.
+#
+flush tables with read lock;
+--disable_abort_on_error
+--eval $statement
+--enable_abort_on_error
+let $err= $mysql_errno;
+if ($err)
+{
+--echo Success: Was not able to run '$statement' under FTWRL.
+unlock tables;
+}
+if (!$err)
+{
+--echo Error: Was able to run '$statement' under FTWRL!
+unlock tables;
+if (`SELECT "$cleanup_stmt1" <> ""`)
+{
+--eval $cleanup_stmt1;
+}
+if (`SELECT "$cleanup_stmt2" <> ""`)
+{
+--eval $cleanup_stmt2;
+}
+}
+
+
+#
+# Then check that this statement is blocked by FTWRL
+# that is active in another connection.
+#
+connection $con_aux1;
+flush tables with read lock;
+
+connection default;
+--send_eval $statement;
+
+connection $con_aux1;
+
+--enable_result_log
+--enable_query_log
+let $wait_condition=
+ select count(*) = 1 from information_schema.processlist
+ where (state = "Waiting for global read lock" or
+ state = "Waiting for commit lock") and
+ info = "$statement";
+--source include/wait_condition.inc
+--disable_result_log
+--disable_query_log
+
+if ($success)
+{
+--echo Success: '$statement' is blocked by FTWRL active in another connection.
+}
+if (!$success)
+{
+--echo Error: '$statement' wasn't blocked by FTWRL active in another connection!
+}
+unlock tables;
+
+connection default;
+--reap
+
+if (`SELECT "$cleanup_stmt1" <> ""`)
+{
+--eval $cleanup_stmt1;
+}
+if (`SELECT "$cleanup_stmt2" <> ""`)
+{
+--eval $cleanup_stmt2;
+}
+
+if (`SELECT "$skip_3rd_check" = ""`)
+{
+#
+# Finally, let us check that FTWRL will not succeed if this
+# statement is active but has already closed its tables.
+#
+connection default;
+--eval set debug_sync='execute_command_after_close_tables SIGNAL parked WAIT_FOR go';
+--send_eval $statement;
+
+connection $con_aux1;
+set debug_sync="now WAIT_FOR parked";
+--send flush tables with read lock
+
+connection $con_aux2;
+--enable_result_log
+--enable_query_log
+let $wait_condition=
+ select count(*) = 1 from information_schema.processlist
+ where (state = "Waiting for global read lock" or
+ state = "Waiting for commit lock") and
+ info = "flush tables with read lock";
+--source include/wait_condition.inc
+--disable_result_log
+--disable_query_log
+
+if ($success)
+{
+--echo Success: FTWRL is blocked when '$statement' is active in another connection.
+}
+if (!$success)
+{
+--echo Error: FTWRL isn't blocked when '$statement' is active in another connection!
+}
+set debug_sync="now SIGNAL go";
+connection default;
+--reap
+connection $con_aux1;
+--reap
+unlock tables;
+connection default;
+
+set debug_sync= "RESET";
+
+if (`SELECT "$cleanup_stmt1" <> ""`)
+{
+--eval $cleanup_stmt1;
+}
+if (`SELECT "$cleanup_stmt2" <> ""`)
+{
+--eval $cleanup_stmt2;
+}
+}
+
+--enable_result_log
+--enable_query_log
diff --git a/mysql-test/include/handler.inc b/mysql-test/include/handler.inc
index b86d5d9287f..57d368960bf 100644
--- a/mysql-test/include/handler.inc
+++ b/mysql-test/include/handler.inc
@@ -1545,8 +1545,6 @@ lock table not_exists_write read;
--echo # We still have the read lock.
--error ER_CANT_UPDATE_WITH_READLOCK
drop table t1;
-handler t1 read next;
-handler t1 close;
handler t1 open;
select a from t2;
handler t1 read next;
diff --git a/mysql-test/include/wait_show_condition.inc b/mysql-test/include/wait_show_condition.inc
index f683ca7b47b..07bde560f20 100644
--- a/mysql-test/include/wait_show_condition.inc
+++ b/mysql-test/include/wait_show_condition.inc
@@ -101,7 +101,7 @@ if (`SELECT '$wait_for_all' = '1'`)
if (!$found)
{
- echo # Timeout in include/wait_show_condition.inc for $wait_condition;
+ echo # Timeout in include/wait_show_condition.inc for $condition;
echo # show_statement : $show_statement;
echo # field : $field;
echo # condition : $condition;
diff --git a/mysql-test/r/delayed.result b/mysql-test/r/delayed.result
index 77aa0d49407..d9082914d05 100644
--- a/mysql-test/r/delayed.result
+++ b/mysql-test/r/delayed.result
@@ -418,6 +418,18 @@ COMMIT;
UNLOCK TABLES;
# Connection con1
# Reaping: INSERT DELAYED INTO t1 VALUES (5)
+# Connection default
+# Test 5: LOCK TABLES + INSERT DELAYED in one connection.
+# This test has triggered some asserts in metadata locking
+# subsystem at some point in time..
+LOCK TABLE t1 WRITE;
+INSERT DELAYED INTO t2 VALUES (7);
+UNLOCK TABLES;
+SET AUTOCOMMIT= 0;
+LOCK TABLE t1 WRITE;
+INSERT DELAYED INTO t2 VALUES (8);
+UNLOCK TABLES;
+SET AUTOCOMMIT= 1;
# Connection con2
# Connection con1
# Connection default
diff --git a/mysql-test/r/events_2.result b/mysql-test/r/events_2.result
index 530d8559f11..66ec00d7357 100644
--- a/mysql-test/r/events_2.result
+++ b/mysql-test/r/events_2.result
@@ -133,15 +133,15 @@ select event_name from information_schema.events;
event_name
e1
create event e2 on schedule every 10 hour do select 1;
-ERROR HY000: Table 'event' was not locked with LOCK TABLES
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
alter event e2 disable;
-ERROR HY000: Table 'event' was not locked with LOCK TABLES
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
alter event e2 rename to e3;
-ERROR HY000: Table 'event' was not locked with LOCK TABLES
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
drop event e2;
-ERROR HY000: Table 'event' was not locked with LOCK TABLES
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
drop event e1;
-ERROR HY000: Table 'event' was not locked with LOCK TABLES
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
unlock tables;
lock table t1 write;
show create event e1;
@@ -151,15 +151,15 @@ select event_name from information_schema.events;
event_name
e1
create event e2 on schedule every 10 hour do select 1;
-ERROR HY000: Table 'event' was not locked with LOCK TABLES
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
alter event e2 disable;
-ERROR HY000: Table 'event' was not locked with LOCK TABLES
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
alter event e2 rename to e3;
-ERROR HY000: Table 'event' was not locked with LOCK TABLES
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
drop event e2;
-ERROR HY000: Table 'event' was not locked with LOCK TABLES
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
drop event e1;
-ERROR HY000: Table 'event' was not locked with LOCK TABLES
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
unlock tables;
lock table t1 read, mysql.event read;
show create event e1;
@@ -169,15 +169,15 @@ select event_name from information_schema.events;
event_name
e1
create event e2 on schedule every 10 hour do select 1;
-ERROR HY000: Table 'event' was locked with a READ lock and can't be updated
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
alter event e2 disable;
-ERROR HY000: Table 'event' was locked with a READ lock and can't be updated
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
alter event e2 rename to e3;
-ERROR HY000: Table 'event' was locked with a READ lock and can't be updated
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
drop event e2;
-ERROR HY000: Table 'event' was locked with a READ lock and can't be updated
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
drop event e1;
-ERROR HY000: Table 'event' was locked with a READ lock and can't be updated
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
unlock tables;
lock table t1 write, mysql.event read;
show create event e1;
@@ -187,15 +187,15 @@ select event_name from information_schema.events;
event_name
e1
create event e2 on schedule every 10 hour do select 1;
-ERROR HY000: Table 'event' was locked with a READ lock and can't be updated
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
alter event e2 disable;
-ERROR HY000: Table 'event' was locked with a READ lock and can't be updated
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
alter event e2 rename to e3;
-ERROR HY000: Table 'event' was locked with a READ lock and can't be updated
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
drop event e2;
-ERROR HY000: Table 'event' was locked with a READ lock and can't be updated
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
drop event e1;
-ERROR HY000: Table 'event' was locked with a READ lock and can't be updated
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
unlock tables;
lock table t1 read, mysql.event write;
ERROR HY000: You can't combine write-locking of system tables with other tables or lock types
@@ -209,11 +209,17 @@ select event_name from information_schema.events;
event_name
e1
create event e2 on schedule every 10 hour do select 1;
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
alter event e2 disable;
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
alter event e2 rename to e3;
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
drop event e3;
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
drop event e1;
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
unlock tables;
+drop event e1;
Make sure we have left no events
select event_name from information_schema.events;
event_name
diff --git a/mysql-test/r/flush.result b/mysql-test/r/flush.result
index ced8306c3ab..b1e2e48eca8 100644
--- a/mysql-test/r/flush.result
+++ b/mysql-test/r/flush.result
@@ -423,3 +423,31 @@ i
4
unlock tables;
drop tables tm, t1, t2;
+#
+# Test for bug #57006 "Deadlock between HANDLER and
+# FLUSH TABLES WITH READ LOCK".
+#
+drop table if exists t1, t2;
+create table t1 (i int);
+create table t2 (i int);
+handler t1 open;
+# Switching to connection 'con1'.
+# Sending:
+flush tables with read lock;
+# Switching to connection 'con2'.
+# Wait until FTWRL starts waiting for 't1' to be closed.
+# Switching to connection 'default'.
+# The below statement should not cause deadlock.
+# Sending:
+insert into t2 values (1);
+# Switching to connection 'con2'.
+# Wait until INSERT starts to wait for FTWRL to go away.
+# Switching to connection 'con1'.
+# FTWRL should be able to continue now.
+# Reap FTWRL.
+unlock tables;
+# Switching to connection 'default'.
+# Reap INSERT.
+handler t1 close;
+# Cleanup.
+drop tables t1, t2;
diff --git a/mysql-test/r/flush_read_lock.result b/mysql-test/r/flush_read_lock.result
new file mode 100644
index 00000000000..2b1071a92b2
--- /dev/null
+++ b/mysql-test/r/flush_read_lock.result
@@ -0,0 +1,1683 @@
+# FTWRL takes two global metadata locks -- a global shared
+# metadata lock and the commit blocker lock.
+# The first lock prevents DDL from taking place.
+# Let's say that all DDL statements that take metadata
+# locks form class #1 -- incompatible with FTWRL because
+# take incompatible MDL table locks.
+# The first global lock doesn't, however, prevent standalone
+# COMMITs (or implicit COMMITs) from taking place, since a
+# COMMIT doesn't take table locks. It doesn't prevent
+# DDL on temporary tables either, since they don't
+# take any table locks either.
+# Most DDL statements do not perform an implicit commit
+# if operate on a temporary table. Examples are CREATE
+# TEMPORARY TABLE and DROP TEMPORARY TABLE.
+# Thus, these DDL statements can go through in presence
+# of FTWRL. This is class #2 -- compatible because
+# do not take incompatible MDL locks and do not issue
+# implicit commit..
+# (Although these operations do not commit, their effects
+# cannot be rolled back either.)
+# ALTER TABLE, ANALYZE, OPTIMIZE and some others always
+# issue an implicit commit, even if its argument is a
+# temporary table.
+# *Howewer* an implicit commit is a no-op if all engines
+# used since the start of transactiona are non-
+# transactional. Thus, for non-transactional engines,
+# these operations are not blocked by FTWRL.
+# This is class #3 -- compatible because do not take
+# MDL table locks and are non-transactional.
+# On the contrary, for transactional engines, there
+# is always a commit, regardless of whether a table
+# is temporary or not. Thus, for example, ALTER TABLE
+# for a transactional engine will wait for FTWRL,
+# even if the subject table is temporary.
+# Thus ALTER TABLE <temporary> is incompatible
+# with FTWRL. This is class #4 -- incompatible
+# becuase issue implicit COMMIT which is not a no-op.
+# Finally, there are administrative statements (such as
+# RESET SLAVE) that do not take any locks and do not
+# issue COMMIT.
+# This is class #5.
+# The goal of this coverage is to test statements
+# of all classes.
+# @todo: documents the effects of @@autocommit,
+# DML and temporary transactional tables.
+# Use MyISAM engine for the most of the tables
+# used in this test in order to be able to
+# check that DDL statements on temporary tables
+# are compatible with FTRWL.
+drop tables if exists t1_base, t2_base, t3_trans;
+drop tables if exists tm_base, tm_base_temp;
+drop database if exists mysqltest1;
+# We're going to test ALTER DATABASE UPGRADE
+drop database if exists `#mysql50#mysqltest-2`;
+drop procedure if exists p1;
+drop function if exists f1;
+drop view if exists v1;
+drop procedure if exists p2;
+drop function if exists f2_base;
+drop function if exists f2_temp;
+drop event if exists e1;
+drop event if exists e2;
+create table t1_base(i int) engine=myisam;
+create table t2_base(j int) engine=myisam;
+create table t3_trans(i int) engine=innodb;
+create temporary table t1_temp(i int) engine=myisam;
+create temporary table t2_temp(j int) engine=myisam;
+create temporary table t3_temp_trans(i int) engine=innodb;
+create database mysqltest1;
+create database `#mysql50#mysqltest-2`;
+create procedure p1() begin end;
+create function f1() returns int return 0;
+create view v1 as select 1 as i;
+create procedure p2(i int) begin end;
+create function f2_base() returns int
+begin
+insert into t1_base values (1);
+return 0;
+end|
+create function f2_temp() returns int
+begin
+insert into t1_temp values (1);
+return 0;
+end|
+create event e1 on schedule every 1 minute do begin end;
+#
+# Test compatibility of FLUSH TABLES WITH READ LOCK
+# with various statements.
+#
+# These tests don't cover some classes of statements:
+# - Replication-related - CHANGE MASTER TO, START/STOP SLAVE and etc
+# (all compatible with FTWRL).
+# - Plugin-related - INSTALL/UNINSTALL (incompatible with FTWRL,
+# require plugin support).
+#
+# 1) ALTER variants.
+#
+# 1.1) ALTER TABLE
+#
+# 1.1.a) For base table should be incompatible with FTWRL.
+#
+Success: Was not able to run 'alter table t1_base add column c1 int' under FTWRL.
+Success: 'alter table t1_base add column c1 int' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'alter table t1_base add column c1 int' is active in another connection.
+#
+# 1.1.b) For a temporary table should be compatible with FTWRL.
+#
+Success: Was able to run 'alter table t1_temp add column c1 int' under FTWRL.
+Success: Was able to run 'alter table t1_temp add column c1 int' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'alter table t1_temp add column c1 int' was active in another connection.
+#
+# 1.2) ALTER DATABASE should be incompatible with FTWRL.
+#
+Success: Was not able to run 'alter database mysqltest1 default character set utf8' under FTWRL.
+Success: 'alter database mysqltest1 default character set utf8' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'alter database mysqltest1 default character set utf8' is active in another connection.
+#
+# 1.3) ALTER DATABASE UPGRADE DATA DIRECTORY NAME should be
+# incompatible with FTWRL.
+#
+Success: Was not able to run 'alter database `#mysql50#mysqltest-2` upgrade data directory name' under FTWRL.
+Success: 'alter database `#mysql50#mysqltest-2` upgrade data directory name' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'alter database `#mysql50#mysqltest-2` upgrade data directory name' is active in another connection.
+#
+# 1.4) ALTER PROCEDURE should be incompatible with FTWRL.
+#
+Success: Was not able to run 'alter procedure p1 comment 'a'' under FTWRL.
+Success: 'alter procedure p1 comment 'a'' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'alter procedure p1 comment 'a'' is active in another connection.
+#
+# 1.5) ALTER FUNCTION should be incompatible with FTWRL.
+#
+Success: Was not able to run 'alter function f1 comment 'a'' under FTWRL.
+Success: 'alter function f1 comment 'a'' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'alter function f1 comment 'a'' is active in another connection.
+#
+# 1.6) ALTER VIEW should be incompatible with FTWRL.
+#
+Success: Was not able to run 'alter view v1 as select 2 as j' under FTWRL.
+Success: 'alter view v1 as select 2 as j' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'alter view v1 as select 2 as j' is active in another connection.
+#
+# 1.7) ALTER EVENT should be incompatible with FTWRL.
+#
+Success: Was not able to run 'alter event e1 comment 'test'' under FTWRL.
+Success: 'alter event e1 comment 'test'' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'alter event e1 comment 'test'' is active in another connection.
+#
+# 1.x) The rest of ALTER statements (ALTER TABLESPACE,
+# ALTER LOGFILE GROUP and ALTER SERVER) are too
+# special to be tested here.
+#
+#
+# 2) ANALYZE TABLE statement is compatible with FTWRL.
+# See Bug#43336 ANALYZE and OPTIMIZE do not honour
+# --read-only for a discussion why.
+#
+Success: Was able to run 'analyze table t1_base' under FTWRL.
+Success: Was able to run 'analyze table t1_base' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'analyze table t1_base' was active in another connection.
+#
+# 3) BEGIN, ROLLBACK and COMMIT statements.
+# BEGIN and ROLLBACK are compatible with FTWRL.
+# COMMIT is not.
+#
+# We need a special test for these statements as
+# FTWRL commits a transaction and because COMMIT
+# is handled in a special way.
+flush tables with read lock;
+begin;
+# ROLLBACK is allowed under FTWRL although there
+# no much sense in it. FTWRL commits any previous
+# changes and doesn't allows any DML after it.
+# So such a ROLLBACK is always a no-op.
+rollback;
+# Although COMMIT is incompatible with FTWRL in
+# other senses it is still allowed under FTWRL.
+# This fact relied upon by some versions of
+# innobackup tool.
+# Similarly to ROLLBACK it is a no-op in this situation.
+commit;
+unlock tables;
+# Check that BEGIN/ROLLBACK are not blocked and
+# COMMIT is blocked by active FTWRL in another
+# connection.
+#
+# Switching to connection 'con1'.
+flush tables with read lock;
+# Switching to connection 'default'.
+begin;
+# Switching to connection 'con1'.
+unlock tables;
+# Switching to connection 'default'.
+# Do some work so ROLLBACK is not a no-op.
+insert into t3_trans values (1);
+# Switching to connection 'con1'.
+flush tables with read lock;
+# Switching to connection 'default'.
+rollback;
+# Switching to connection 'con1'.
+unlock tables;
+# Switching to connection 'default'.
+begin;
+# Do some work so COMMIT is not a no-op.
+insert into t3_trans values (1);
+# Switching to connection 'con1'.
+flush tables with read lock;
+# Switching to connection 'default'.
+# Send:
+commit;
+# Switching to connection 'con1'.
+# Wait until COMMIT is blocked.
+unlock tables;
+# Switching to connection 'default'.
+# Reap COMMIT.
+delete from t3_trans;
+#
+# Check that COMMIT blocks FTWRL in another connection.
+begin;
+insert into t3_trans values (1);
+set debug_sync='RESET';
+set debug_sync='ha_commit_trans_after_acquire_commit_lock SIGNAL parked WAIT_FOR go';
+commit;
+# Switching to connection 'con1'.
+set debug_sync='now WAIT_FOR parked';
+flush tables with read lock;
+# Switching to connection 'con2'.
+# Wait until FTWRL is blocked.
+set debug_sync='now SIGNAL go';
+# Switching to connection 'default'.
+# Reap COMMIT.
+# Switching to connection 'con1'.
+# Reap FTWRL.
+unlock tables;
+# Switching to connection 'default'.
+delete from t3_trans;
+set debug_sync= "RESET";
+# We don't run similar test for BEGIN and ROLLBACK as
+# they release metadata locks in non-standard place.
+#
+# 4) BINLOG statement should be incompatible with FTWRL.
+#
+#
+# Provide format description BINLOG statement first.
+BINLOG '
+MfmqTA8BAAAAZwAAAGsAAAABAAQANS41LjctbTMtZGVidWctbG9nAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAx+apMEzgNAAgAEgAEBAQEEgAAVAAEGggAAAAICAgCAA==
+';
+# Now test compatibility for BINLOG statement which is
+# equivalent to INSERT INTO t1_base VALUES (1).
+# Skip last part of compatibility testing as this statement
+# releases metadata locks in non-standard place.
+Success: Was not able to run 'BINLOG '
+MfmqTBMBAAAALgAAAN0AAAAAACgAAAAAAAEABHRlc3QAB3QxX2Jhc2UAAQMAAQ==
+MfmqTBcBAAAAIgAAAP8AAAAAACgAAAAAAAEAAf/+AQAAAA==
+'' under FTWRL.
+Success: 'BINLOG '
+MfmqTBMBAAAALgAAAN0AAAAAACgAAAAAAAEABHRlc3QAB3QxX2Jhc2UAAQMAAQ==
+MfmqTBcBAAAAIgAAAP8AAAAAACgAAAAAAAEAAf/+AQAAAA==
+'' is blocked by FTWRL active in another connection.
+#
+# 5) CALL statement. This statement uses resources in two
+# ways: through expressions used as parameters and through
+# sub-statements. This test covers only usage through
+# parameters as sub-statements do locking individually.
+#
+# 5.a) In simple cases a parameter expression should be
+# compatible with FTWRL.
+# Skip last part of compatibility testing as this statement
+# releases metadata locks in non-standard place.
+Success: Was able to run 'call p2((select count(*) from t1_base))' under FTWRL.
+Success: Was able to run 'call p2((select count(*) from t1_base))' with FTWRL active in another connection.
+#
+# 5.b) In case when an expression uses function which updates
+# base tables CALL should be incompatible with FTWRL.
+#
+# Skip last part of compatibility testing as this statement
+# releases metadata locks in non-standard place.
+Success: Was not able to run 'call p2(f2_base())' under FTWRL.
+Success: 'call p2(f2_base())' is blocked by FTWRL active in another connection.
+#
+# 5.c) If function used as argument updates temporary tables
+# CALL statement should be compatible with FTWRL.
+#
+# Skip last part of compatibility testing as this statement
+# releases metadata locks in non-standard place.
+Success: Was able to run 'call p2(f2_temp())' under FTWRL.
+Success: Was able to run 'call p2(f2_temp())' with FTWRL active in another connection.
+#
+# 6) CHECK TABLE statement is compatible with FTWRL.
+#
+Success: Was able to run 'check table t1_base' under FTWRL.
+Success: Was able to run 'check table t1_base' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'check table t1_base' was active in another connection.
+#
+# 7) CHECKSUM TABLE statement is compatible with FTWRL.
+#
+Success: Was able to run 'checksum table t1_base' under FTWRL.
+Success: Was able to run 'checksum table t1_base' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'checksum table t1_base' was active in another connection.
+#
+# 8) CREATE variants.
+#
+# 8.1) CREATE TABLE statement.
+#
+# 8.1.a) CREATE TABLE is incompatible with FTWRL when
+# base table is created.
+Success: Was not able to run 'create table t3_base(i int)' under FTWRL.
+Success: 'create table t3_base(i int)' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'create table t3_base(i int)' is active in another connection.
+# 8.1.b) CREATE TABLE is compatible with FTWRL when
+# temporary table is created.
+Success: Was able to run 'create temporary table t3_temp(i int)' under FTWRL.
+Success: Was able to run 'create temporary table t3_temp(i int)' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'create temporary table t3_temp(i int)' was active in another connection.
+# 8.1.c) CREATE TABLE LIKE is incompatible with FTWRL when
+# base table is created.
+Success: Was not able to run 'create table t3_base like t1_temp' under FTWRL.
+Success: 'create table t3_base like t1_temp' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'create table t3_base like t1_temp' is active in another connection.
+# 8.1.d) CREATE TABLE LIKE is compatible with FTWRL when
+# temporary table is created.
+Success: Was able to run 'create temporary table t3_temp like t1_base' under FTWRL.
+Success: Was able to run 'create temporary table t3_temp like t1_base' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'create temporary table t3_temp like t1_base' was active in another connection.
+# 8.1.e) CREATE TABLE SELECT is incompatible with FTWRL when
+# base table is created.
+Success: Was not able to run 'create table t3_base select 1 as i' under FTWRL.
+Success: 'create table t3_base select 1 as i' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'create table t3_base select 1 as i' is active in another connection.
+# 8.1.f) CREATE TABLE SELECT is compatible with FTWRL when
+# temporary table is created.
+Success: Was able to run 'create temporary table t3_temp select 1 as i' under FTWRL.
+Success: Was able to run 'create temporary table t3_temp select 1 as i' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'create temporary table t3_temp select 1 as i' was active in another connection.
+# 8.2) CREATE INDEX statement.
+#
+# 8.2.a) CREATE INDEX is incompatible with FTWRL when
+# applied to base table.
+Success: Was not able to run 'create index i on t1_base (i)' under FTWRL.
+Success: 'create index i on t1_base (i)' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'create index i on t1_base (i)' is active in another connection.
+# 8.2.b) CREATE INDEX is compatible with FTWRL when
+# applied to temporary table.
+Success: Was able to run 'create index i on t1_temp (i)' under FTWRL.
+Success: Was able to run 'create index i on t1_temp (i)' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'create index i on t1_temp (i)' was active in another connection.
+#
+# 8.3) CREATE DATABASE is incompatible with FTWRL.
+#
+Success: Was not able to run 'create database mysqltest2' under FTWRL.
+Success: 'create database mysqltest2' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'create database mysqltest2' is active in another connection.
+#
+# 8.4) CREATE VIEW is incompatible with FTWRL.
+#
+Success: Was not able to run 'create view v2 as select 1 as j' under FTWRL.
+Success: 'create view v2 as select 1 as j' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'create view v2 as select 1 as j' is active in another connection.
+#
+# 8.5) CREATE TRIGGER is incompatible with FTWRL.
+#
+Success: Was not able to run 'create trigger t1_bi before insert on t1_base for each row begin end' under FTWRL.
+Success: 'create trigger t1_bi before insert on t1_base for each row begin end' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'create trigger t1_bi before insert on t1_base for each row begin end' is active in another connection.
+#
+# 8.6) CREATE FUNCTION is incompatible with FTWRL.
+#
+Success: Was not able to run 'create function f2() returns int return 0' under FTWRL.
+Success: 'create function f2() returns int return 0' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'create function f2() returns int return 0' is active in another connection.
+#
+# 8.7) CREATE PROCEDURE is incompatible with FTWRL.
+#
+Success: Was not able to run 'create procedure p3() begin end' under FTWRL.
+Success: 'create procedure p3() begin end' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'create procedure p3() begin end' is active in another connection.
+#
+# 8.8) CREATE EVENT should be incompatible with FTWRL.
+#
+Success: Was not able to run 'create event e2 on schedule every 1 minute do begin end' under FTWRL.
+Success: 'create event e2 on schedule every 1 minute do begin end' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'create event e2 on schedule every 1 minute do begin end' is active in another connection.
+#
+# 8.9) CREATE USER should be incompatible with FTWRL.
+#
+Success: Was not able to run 'create user mysqltest_u1' under FTWRL.
+Success: 'create user mysqltest_u1' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'create user mysqltest_u1' is active in another connection.
+#
+# 8.x) The rest of CREATE variants (CREATE LOGFILE GROUP,
+# CREATE TABLESPACE and CREATE SERVER) are too special
+# to test here.
+#
+#
+# 9) PREPARE, EXECUTE and DEALLOCATE PREPARE statements.
+#
+# 9.1) PREPARE statement is compatible with FTWRL as it
+# doesn't change any data.
+#
+# 9.1.a) Prepare of simple INSERT statement.
+#
+# Skip last part of compatibility testing as this statement
+# releases metadata locks in non-standard place.
+Success: Was able to run 'prepare stmt1 from 'insert into t1_base values (1)'' under FTWRL.
+Success: Was able to run 'prepare stmt1 from 'insert into t1_base values (1)'' with FTWRL active in another connection.
+#
+# 9.1.b) Prepare of multi-UPDATE. At some point such statements
+# tried to acquire thr_lock.c locks during prepare phase.
+# This no longer happens and thus it is compatible with
+# FTWRL.
+# Skip last part of compatibility testing as this statement
+# releases metadata locks in non-standard place.
+Success: Was able to run 'prepare stmt1 from 'update t1_base, t2_base set t1_base.i= 1 where t1_base.i = t2_base.j'' under FTWRL.
+Success: Was able to run 'prepare stmt1 from 'update t1_base, t2_base set t1_base.i= 1 where t1_base.i = t2_base.j'' with FTWRL active in another connection.
+#
+# 9.1.c) Prepare of multi-DELETE. Again PREPARE of such
+# statement should be compatible with FTWRL.
+# Skip last part of compatibility testing as this statement
+# releases metadata locks in non-standard place.
+Success: Was able to run 'prepare stmt1 from 'delete t1_base from t1_base, t2_base where t1_base.i = t2_base.j'' under FTWRL.
+Success: Was able to run 'prepare stmt1 from 'delete t1_base from t1_base, t2_base where t1_base.i = t2_base.j'' with FTWRL active in another connection.
+#
+# 9.2) Compatibility of EXECUTE statement depends on statement
+# to be executed.
+#
+# 9.2.a) EXECUTE for statement which is itself compatible with
+# FTWRL should be compatible.
+prepare stmt1 from 'select * from t1_base';
+Success: Was able to run 'execute stmt1' under FTWRL.
+Success: Was able to run 'execute stmt1' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'execute stmt1' was active in another connection.
+deallocate prepare stmt1;
+#
+# 9.2.b) EXECUTE for statement which is incompatible with FTWRL
+# should be also incompatible.
+#
+# Check that EXECUTE is not allowed under FTWRL.
+prepare stmt1 from 'insert into t1_base values (1)';
+flush tables with read lock;
+execute stmt1;
+ERROR HY000: Can't execute the query because you have a conflicting read lock
+unlock tables;
+# Check that active FTWRL in another connection
+# blocks EXECUTE which changes data.
+#
+# Switching to connection 'con1'.
+flush tables with read lock;
+# Switching to connection 'default'.
+execute stmt1 ;
+# Switching to connection 'con1'.
+# Check that EXECUTE is blocked.
+unlock tables;
+# Switching to connection 'default'.
+# Reap EXECUTE.
+set debug_sync='RESET';
+set debug_sync='execute_command_after_close_tables SIGNAL parked WAIT_FOR go';
+execute stmt1; ;
+# Switching to connection 'con1'.
+set debug_sync='now WAIT_FOR parked';
+flush tables with read lock;
+# Switching to connection 'con2'.
+# Wait until FTWRL is blocked.
+set debug_sync='now SIGNAL go';
+# Switching to connection 'default'.
+# Reap EXECUTE.
+# Switching to connection 'con1'.
+# Reap FTWRL.
+unlock tables;
+# Switching to connection 'default'.
+set debug_sync= "RESET";
+delete from t1_base;
+deallocate prepare stmt1;
+#
+# 9.3) DEALLOCATE PREPARE is compatible with FTWRL.
+#
+prepare stmt1 from 'insert into t1_base values (1)';
+Success: Was able to run 'deallocate prepare stmt1' under FTWRL.
+Success: Was able to run 'deallocate prepare stmt1' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'deallocate prepare stmt1' was active in another connection.
+deallocate prepare stmt1;
+#
+# 10) DELETE variations.
+#
+# 10.1) Simple DELETE.
+#
+# 10.1.a) Simple DELETE on base table is incompatible with FTWRL.
+Success: Was not able to run 'delete from t1_base' under FTWRL.
+Success: 'delete from t1_base' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'delete from t1_base' is active in another connection.
+#
+# 10.1.b) Simple DELETE on temporary table is compatible with FTWRL.
+Success: Was able to run 'delete from t1_temp' under FTWRL.
+Success: Was able to run 'delete from t1_temp' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'delete from t1_temp' was active in another connection.
+#
+# 10.2) Multi DELETE.
+#
+# 10.2.a) Multi DELETE on base tables is incompatible with FTWRL.
+Success: Was not able to run 'delete t1_base from t1_base, t2_base where t1_base.i = t2_base.j' under FTWRL.
+Success: 'delete t1_base from t1_base, t2_base where t1_base.i = t2_base.j' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'delete t1_base from t1_base, t2_base where t1_base.i = t2_base.j' is active in another connection.
+#
+# 10.2.b) Multi DELETE on temporary tables is compatible with FTWRL.
+Success: Was able to run 'delete t1_temp from t1_temp, t2_temp where t1_temp.i = t2_temp.j' under FTWRL.
+Success: Was able to run 'delete t1_temp from t1_temp, t2_temp where t1_temp.i = t2_temp.j' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'delete t1_temp from t1_temp, t2_temp where t1_temp.i = t2_temp.j' was active in another connection.
+#
+# 11) DESCRIBE should be compatible with FTWRL.
+#
+Success: Was able to run 'describe t1_base' under FTWRL.
+Success: Was able to run 'describe t1_base' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'describe t1_base' was active in another connection.
+#
+# 12) Compatibility of DO statement with FTWRL depends on its
+# expression.
+#
+# 12.a) DO with expression which does not change base table
+# should be compatible with FTWRL.
+Success: Was able to run 'do (select count(*) from t1_base)' under FTWRL.
+Success: Was able to run 'do (select count(*) from t1_base)' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'do (select count(*) from t1_base)' was active in another connection.
+#
+# 12.b) DO which calls SF updating base table should be
+# incompatible with FTWRL.
+Success: Was not able to run 'do f2_base()' under FTWRL.
+Success: 'do f2_base()' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'do f2_base()' is active in another connection.
+#
+# 12.c) DO which calls SF updating temporary table should be
+# compatible with FTWRL.
+Success: Was able to run 'do f2_temp()' under FTWRL.
+Success: Was able to run 'do f2_temp()' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'do f2_temp()' was active in another connection.
+#
+# 13) DROP variants.
+#
+# 13.1) DROP TABLES.
+#
+# 13.1.a) DROP TABLES which affects base tables is incompatible
+# with FTWRL.
+Success: Was not able to run 'drop table t2_base' under FTWRL.
+Success: 'drop table t2_base' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'drop table t2_base' is active in another connection.
+# 13.1.b) DROP TABLES which affects only temporary tables
+# in theory can be compatible with FTWRL.
+# In practice it is not yet.
+Success: Was not able to run 'drop table t2_temp' under FTWRL.
+Success: 'drop table t2_temp' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'drop table t2_temp' is active in another connection.
+#
+# 13.1.c) DROP TEMPORARY TABLES should be compatible with FTWRL.
+Success: Was able to run 'drop temporary table t2_temp' under FTWRL.
+Success: Was able to run 'drop temporary table t2_temp' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'drop temporary table t2_temp' was active in another connection.
+#
+# 13.2) DROP INDEX.
+#
+# 13.2.a) DROP INDEX on a base table is incompatible with FTWRL.
+create index i on t1_base (i);
+Success: Was not able to run 'drop index i on t1_base' under FTWRL.
+Success: 'drop index i on t1_base' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'drop index i on t1_base' is active in another connection.
+drop index i on t1_base;
+#
+# 13.2.b) DROP INDEX on a temporary table is compatible with FTWRL.
+create index i on t1_temp (i);
+Success: Was able to run 'drop index i on t1_temp' under FTWRL.
+Success: Was able to run 'drop index i on t1_temp' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'drop index i on t1_temp' was active in another connection.
+drop index i on t1_temp;
+#
+# 13.3) DROP DATABASE is incompatible with FTWRL
+#
+Success: Was not able to run 'drop database mysqltest1' under FTWRL.
+Success: 'drop database mysqltest1' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'drop database mysqltest1' is active in another connection.
+#
+# 13.4) DROP FUNCTION is incompatible with FTWRL.
+#
+Success: Was not able to run 'drop function f1' under FTWRL.
+Success: 'drop function f1' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'drop function f1' is active in another connection.
+#
+# 13.5) DROP PROCEDURE is incompatible with FTWRL.
+#
+Success: Was not able to run 'drop procedure p1' under FTWRL.
+Success: 'drop procedure p1' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'drop procedure p1' is active in another connection.
+#
+# 13.6) DROP USER should be incompatible with FTWRL.
+#
+create user mysqltest_u1;
+Success: Was not able to run 'drop user mysqltest_u1' under FTWRL.
+Success: 'drop user mysqltest_u1' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'drop user mysqltest_u1' is active in another connection.
+drop user mysqltest_u1;
+#
+# 13.7) DROP VIEW should be incompatible with FTWRL.
+#
+Success: Was not able to run 'drop view v1' under FTWRL.
+Success: 'drop view v1' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'drop view v1' is active in another connection.
+#
+# 13.8) DROP EVENT should be incompatible with FTWRL.
+#
+Success: Was not able to run 'drop event e1' under FTWRL.
+Success: 'drop event e1' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'drop event e1' is active in another connection.
+#
+# 13.9) DROP TRIGGER is incompatible with FTWRL.
+#
+create trigger t1_bi before insert on t1_base for each row begin end;
+Success: Was not able to run 'drop trigger t1_bi' under FTWRL.
+Success: 'drop trigger t1_bi' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'drop trigger t1_bi' is active in another connection.
+drop trigger t1_bi;
+#
+# 13.x) The rest of DROP variants (DROP TABLESPACE, DROP LOGFILE
+# GROUP and DROP SERVER) are too special to test here.
+#
+#
+# 14) FLUSH variants.
+#
+# Test compatibility of _some_ important FLUSH variants with FTWRL.
+#
+# 14.1) FLUSH TABLES WITH READ LOCK is compatible with itself.
+#
+# Check that FTWRL statements can be run while FTWRL
+# is active in another connection.
+#
+# Switching to connection 'con1'.
+flush tables with read lock;
+# The second FTWRL in a row is allowed at the moment.
+# It does not make much sense as it does only flush.
+flush tables with read lock;
+unlock tables;
+# Switching to connection 'con1'.
+flush tables with read lock;
+# Switching to connection 'default'.
+flush tables with read lock;
+unlock tables;
+# Switching to connection 'con1'.
+unlock tables;
+# Switching to connection 'default'.
+#
+# 14.2) FLUSH TABLES <list> WITH READ LOCK is not blocked by
+# active FTWRL. But since the latter keeps tables open
+# FTWRL is blocked by FLUSH TABLES <list> WITH READ LOCK.
+flush tables with read lock;
+# FT <list> WRL is allowed under FTWRL at the moment.
+# It does not make much sense though.
+flush tables t1_base, t2_base with read lock;
+unlock tables;
+# Switching to connection 'con1'.
+flush tables with read lock;
+# Switching to connection 'default'.
+flush tables t1_base, t2_base with read lock;
+unlock tables;
+# Switching to connection 'con1'.
+unlock tables;
+# Switching to connection 'default'.
+flush tables t1_base, t2_base with read lock;
+# Switching to connection 'con1'.
+flush tables with read lock;
+# Switching to connection 'con2'.
+# Wait until FTWRL is blocked.
+# Switching to connection 'default'.
+unlock tables;
+# Switching to connection 'con1'.
+# Reap FTWRL.
+unlock tables;
+# Switching to connection 'default'.
+#
+# 14.3) FLUSH TABLES is compatible with FTWRL.
+Success: Was able to run 'flush tables' under FTWRL.
+Success: Was able to run 'flush tables' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'flush tables' was active in another connection.
+#
+# 14.4) FLUSH TABLES <list> is compatible with FTWRL.
+Success: Was able to run 'flush table t1_base, t2_base' under FTWRL.
+Success: Was able to run 'flush table t1_base, t2_base' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'flush table t1_base, t2_base' was active in another connection.
+#
+# 14.5) FLUSH PRIVILEGES is compatible with FTWRL.
+Success: Was able to run 'flush privileges' under FTWRL.
+Success: Was able to run 'flush privileges' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'flush privileges' was active in another connection.
+#
+# 15) GRANT statement should be incompatible with FTWRL.
+#
+Success: Was not able to run 'grant all privileges on t1_base to mysqltest_u1' under FTWRL.
+Success: 'grant all privileges on t1_base to mysqltest_u1' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'grant all privileges on t1_base to mysqltest_u1' is active in another connection.
+drop user mysqltest_u1;
+#
+# 16) All HANDLER variants are half-compatible with FTWRL.
+# I.e. they are not blocked by active FTWRL. But since open
+# HANDLER means open table instance FTWRL is blocked while
+# HANDLER is not closed.
+#
+# Check that HANDLER statements succeed under FTWRL.
+flush tables with read lock;
+handler t1_base open;
+handler t1_base read first;
+i
+handler t1_base close;
+unlock tables;
+# Check that HANDLER statements can be run while FTWRL
+# is active in another connection.
+#
+# Switching to connection 'con1'.
+flush tables with read lock;
+# Switching to connection 'default'.
+handler t1_base open;
+handler t1_base read first;
+i
+handler t1_base close;
+# Switching to connection 'con1'.
+unlock tables;
+# Switching to connection 'default'.
+#
+# 17) HELP statement is compatible with FTWRL.
+#
+Success: Was able to run 'help no_such_topic' under FTWRL.
+Success: Was able to run 'help no_such_topic' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'help no_such_topic' was active in another connection.
+#
+# 18) INSERT statement.
+#
+# 18.a) Ordinary INSERT into base table is incompatible with FTWRL.
+Success: Was not able to run 'insert into t1_base values (1)' under FTWRL.
+Success: 'insert into t1_base values (1)' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'insert into t1_base values (1)' is active in another connection.
+#
+# 18.b) Ordinary INSERT into temp table is compatible with FTWRL.
+Success: Was able to run 'insert into t1_temp values (1)' under FTWRL.
+Success: Was able to run 'insert into t1_temp values (1)' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'insert into t1_temp values (1)' was active in another connection.
+#
+# 18.c) INSERT DELAYED is incompatible with FTWRL.
+Success: Was not able to run 'insert delayed into t1_base values (1)' under FTWRL.
+Success: 'insert delayed into t1_base values (1)' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'insert delayed into t1_base values (1)' is active in another connection.
+delete from t1_base;
+#
+# 18.d) INSERT SELECT into base table is incompatible with FTWRL.
+Success: Was not able to run 'insert into t1_base select * from t1_temp' under FTWRL.
+Success: 'insert into t1_base select * from t1_temp' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'insert into t1_base select * from t1_temp' is active in another connection.
+#
+# 18.e) INSERT SELECT into temp table is compatible with FTWRL.
+Success: Was able to run 'insert into t1_temp select * from t1_base' under FTWRL.
+Success: Was able to run 'insert into t1_temp select * from t1_base' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'insert into t1_temp select * from t1_base' was active in another connection.
+#
+# 19) KILL statement is compatible with FTWRL.
+#
+# Check that KILL can be run under FTWRL.
+flush tables with read lock;
+set @id:= connection_id();
+kill query @id;
+ERROR 70100: Query execution was interrupted
+unlock tables;
+# Check that KILL statements can be run while FTWRL
+# is active in another connection.
+#
+# Switching to connection 'con1'.
+flush tables with read lock;
+# Switching to connection 'default'.
+kill query @id;
+ERROR 70100: Query execution was interrupted
+# Switching to connection 'con1'.
+unlock tables;
+# Switching to connection 'default'.
+# Finally check that KILL doesn't block FTWRL
+set debug_sync='RESET';
+set debug_sync='execute_command_after_close_tables SIGNAL parked WAIT_FOR go';
+kill query @id;
+# Switching to connection 'con1'.
+set debug_sync='now WAIT_FOR parked';
+flush tables with read lock;
+unlock tables;
+set debug_sync='now SIGNAL go';
+# Switching to connection 'default'.
+# Reap KILL.
+ERROR 70100: Query execution was interrupted
+set debug_sync='RESET';
+#
+# 20) LOAD DATA statement.
+#
+# 20.a) LOAD DATA into base table is incompatible with FTWRL.
+Success: Was not able to run 'load data infile '../../std_data/rpl_loaddata.dat' into table t1_base (@dummy, i)' under FTWRL.
+Success: 'load data infile '../../std_data/rpl_loaddata.dat' into table t1_base (@dummy, i)' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'load data infile '../../std_data/rpl_loaddata.dat' into table t1_base (@dummy, i)' is active in another connection.
+#
+# 20.b) LOAD DATA into temporary table is compatible with FTWRL.
+Success: Was able to run 'load data infile '../../std_data/rpl_loaddata.dat' into table t1_temp (@dummy, i)' under FTWRL.
+Success: Was able to run 'load data infile '../../std_data/rpl_loaddata.dat' into table t1_temp (@dummy, i)' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'load data infile '../../std_data/rpl_loaddata.dat' into table t1_temp (@dummy, i)' was active in another connection.
+#
+# 21) LOCK/UNLOCK TABLES statements.
+#
+# LOCK TABLES statement always (almost) blocks FTWRL as it
+# keeps tables open until UNLOCK TABLES.
+# Active FTWRL on the other hand blocks only those
+# LOCK TABLES which allow updating of base tables.
+#
+# 21.a) LOCK TABLES READ is allowed under FTWRL and
+# is not blocked by active FTWRL.
+flush tables with read lock;
+lock tables t1_base read;
+unlock tables;
+#
+# Switching to connection 'con1'.
+flush tables with read lock;
+# Switching to connection 'default'.
+lock tables t1_base read;
+unlock tables;
+# Switching to connection 'con1'.
+unlock tables;
+# Switching to connection 'default'.
+#
+# 21.b) LOCK TABLES WRITE on a base table is disallowed
+# under FTWRL and should be blocked by active FTWRL.
+flush tables with read lock;
+lock tables t1_base write;
+ERROR HY000: Can't execute the query because you have a conflicting read lock
+unlock tables;
+#
+# Switching to connection 'con1'.
+flush tables with read lock;
+# Switching to connection 'default'.
+lock tables t1_base write ;
+# Switching to connection 'con1'.
+# Check that LOCK TABLES WRITE is blocked.
+unlock tables;
+# Switching to connection 'default'.
+# Reap LOCK TABLES WRITE
+unlock tables;
+#
+# 21.c) LOCK TABLES WRITE on temporary table doesn't
+# make much sense but is allowed under FTWRL
+# and should not be blocked by active FTWRL.
+flush tables with read lock;
+lock tables t1_temp write;
+unlock tables;
+#
+# Switching to connection 'con1'.
+flush tables with read lock;
+# Switching to connection 'default'.
+lock tables t1_temp write;
+unlock tables;
+# Switching to connection 'con1'.
+unlock tables;
+# Switching to connection 'default'.
+#
+# 22) OPTIMIZE TABLE statement.
+#
+# 22.a) OPTIMIZE TABLE of base table is incompatible with FTWRL.
+flush tables with read lock;
+# OPTIMIZE statement returns errors as part of result-set.
+optimize table t1_base;
+Table Op Msg_type Msg_text
+test.t1_base optimize Error Can't execute the query because you have a conflicting read lock
+test.t1_base optimize error Corrupt
+unlock tables;
+#
+# Switching to connection 'con1'.
+flush tables with read lock;
+# Switching to connection 'default'.
+optimize table t1_base;
+# Switching to connection 'con1'.
+# Check that OPTIMIZE TABLE is blocked.
+unlock tables;
+# Switching to connection 'default'.
+# Reap OPTIMIZE TABLE
+Table Op Msg_type Msg_text
+test.t1_base optimize status OK
+# We don't check that active OPTIMIZE TABLE blocks
+# FTWRL as this one of statements releasing metadata
+# locks in non-standard place.
+#
+# 22.b) OPTIMIZE TABLE of temporary table is compatible with FTWRL.
+# Skip last part of compatibility testing as this statement
+# releases metadata locks in non-standard place.
+Success: Was able to run 'optimize table t1_temp' under FTWRL.
+Success: Was able to run 'optimize table t1_temp' with FTWRL active in another connection.
+#
+# 23) CACHE statement is compatible with FTWRL.
+#
+# Skip last part of compatibility testing as this statement
+# releases metadata locks in non-standard place.
+Success: Was able to run 'cache index t1_base in default' under FTWRL.
+Success: Was able to run 'cache index t1_base in default' with FTWRL active in another connection.
+#
+# 24) LOAD INDEX statement is compatible with FTWRL.
+#
+# Skip last part of compatibility testing as this statement
+# releases metadata locks in non-standard place.
+Success: Was able to run 'load index into cache t1_base' under FTWRL.
+Success: Was able to run 'load index into cache t1_base' with FTWRL active in another connection.
+#
+# 25) SAVEPOINT/RELEASE SAVEPOINT/ROLLBACK TO SAVEPOINT are
+# compatible with FTWRL.
+#
+# Since manipulations on savepoint have to be done
+# inside transaction and FTWRL commits transaction we
+# need a special test for these statements.
+flush tables with read lock;
+begin;
+savepoint sv1;
+rollback to savepoint sv1;
+release savepoint sv1;
+unlock tables;
+commit;
+# Check that these statements are not blocked by
+# active FTWRL in another connection.
+#
+# Switching to connection 'con1'.
+flush tables with read lock;
+# Switching to connection 'default'.
+begin;
+# Switching to connection 'con1'.
+unlock tables;
+# Switching to connection 'default'.
+# Do some changes to avoid SAVEPOINT and friends
+# being almost no-ops.
+insert into t3_trans values (1);
+# Switching to connection 'con1'.
+flush tables with read lock;
+# Switching to connection 'default'.
+savepoint sv1;
+# Switching to connection 'con1'.
+unlock tables;
+# Switching to connection 'default'.
+insert into t3_trans values (2);
+# Switching to connection 'con1'.
+flush tables with read lock;
+# Switching to connection 'default'.
+rollback to savepoint sv1;
+release savepoint sv1;
+# Switching to connection 'con1'.
+unlock tables;
+# Switching to connection 'default'.
+rollback;
+# Check that these statements don't block FTWRL in
+# another connection.
+begin;
+# Do some changes to avoid SAVEPOINT and friends
+# being almost no-ops.
+insert into t3_trans values (1);
+set debug_sync='RESET';
+set debug_sync='execute_command_after_close_tables SIGNAL parked WAIT_FOR go';
+savepoint sv1;
+# Switching to connection 'con1'.
+set debug_sync='now WAIT_FOR parked';
+flush tables with read lock;
+unlock tables;
+set debug_sync='now SIGNAL go';
+# Switching to connection 'default'.
+# Reap SAVEPOINT
+insert into t3_trans values (2);
+set debug_sync='execute_command_after_close_tables SIGNAL parked WAIT_FOR go';
+rollback to savepoint sv1;
+# Switching to connection 'con1'.
+set debug_sync='now WAIT_FOR parked';
+flush tables with read lock;
+unlock tables;
+set debug_sync='now SIGNAL go';
+# Switching to connection 'default'.
+# Reap ROLLBACK TO SAVEPOINT
+set debug_sync='execute_command_after_close_tables SIGNAL parked WAIT_FOR go';
+release savepoint sv1;
+# Switching to connection 'con1'.
+set debug_sync='now WAIT_FOR parked';
+flush tables with read lock;
+unlock tables;
+set debug_sync='now SIGNAL go';
+# Switching to connection 'default'.
+# Reap RELEASE SAVEPOINT
+rollback;
+set debug_sync= "RESET";
+#
+# 26) RENAME variants.
+#
+# 26.1) RENAME TABLES is incompatible with FTWRL.
+Success: Was not able to run 'rename table t1_base to t3_base' under FTWRL.
+Success: 'rename table t1_base to t3_base' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'rename table t1_base to t3_base' is active in another connection.
+#
+# 26.2) RENAME USER is incompatible with FTWRL.
+create user mysqltest_u1;
+Success: Was not able to run 'rename user mysqltest_u1 to mysqltest_u2' under FTWRL.
+Success: 'rename user mysqltest_u1 to mysqltest_u2' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'rename user mysqltest_u1 to mysqltest_u2' is active in another connection.
+drop user mysqltest_u1;
+#
+# 27) REPAIR TABLE statement.
+#
+# 27.a) REPAIR TABLE of base table is incompatible with FTWRL.
+flush tables with read lock;
+# REPAIR statement returns errors as part of result-set.
+repair table t1_base;
+Table Op Msg_type Msg_text
+test.t1_base repair Error Can't execute the query because you have a conflicting read lock
+test.t1_base repair error Corrupt
+unlock tables;
+#
+# Switching to connection 'con1'.
+flush tables with read lock;
+# Switching to connection 'default'.
+repair table t1_base;
+# Switching to connection 'con1'.
+# Check that REPAIR TABLE is blocked.
+unlock tables;
+# Switching to connection 'default'.
+# Reap REPAIR TABLE
+Table Op Msg_type Msg_text
+test.t1_base repair status OK
+# We don't check that active REPAIR TABLE blocks
+# FTWRL as this one of statements releasing metadata
+# locks in non-standard place.
+#
+# 27.b) REPAIR TABLE of temporary table is compatible with FTWRL.
+# Skip last part of compatibility testing as this statement
+# releases metadata locks in non-standard place.
+Success: Was able to run 'repair table t1_temp' under FTWRL.
+Success: Was able to run 'repair table t1_temp' with FTWRL active in another connection.
+#
+# 28) REPLACE statement.
+#
+# 28.a) Ordinary REPLACE into base table is incompatible with FTWRL.
+Success: Was not able to run 'replace into t1_base values (1)' under FTWRL.
+Success: 'replace into t1_base values (1)' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'replace into t1_base values (1)' is active in another connection.
+#
+# 28.b) Ordinary REPLACE into temp table is compatible with FTWRL.
+Success: Was able to run 'replace into t1_temp values (1)' under FTWRL.
+Success: Was able to run 'replace into t1_temp values (1)' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'replace into t1_temp values (1)' was active in another connection.
+#
+# 28.c) REPLACE SELECT into base table is incompatible with FTWRL.
+Success: Was not able to run 'replace into t1_base select * from t1_temp' under FTWRL.
+Success: 'replace into t1_base select * from t1_temp' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'replace into t1_base select * from t1_temp' is active in another connection.
+#
+# 28.d) REPLACE SELECT into temp table is compatible with FTWRL.
+Success: Was able to run 'replace into t1_temp select * from t1_base' under FTWRL.
+Success: Was able to run 'replace into t1_temp select * from t1_base' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'replace into t1_temp select * from t1_base' was active in another connection.
+#
+# 29) REVOKE variants.
+#
+# 29.1) REVOKE privileges is incompatible with FTWRL.
+grant all privileges on t1_base to mysqltest_u1;
+Success: Was not able to run 'revoke all privileges on t1_base from mysqltest_u1' under FTWRL.
+Success: 'revoke all privileges on t1_base from mysqltest_u1' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'revoke all privileges on t1_base from mysqltest_u1' is active in another connection.
+#
+# 29.2) REVOKE ALL PRIVILEGES, GRANT OPTION is incompatible with FTWRL.
+Success: Was not able to run 'revoke all privileges, grant option from mysqltest_u1' under FTWRL.
+Success: 'revoke all privileges, grant option from mysqltest_u1' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'revoke all privileges, grant option from mysqltest_u1' is active in another connection.
+drop user mysqltest_u1;
+#
+# 30) Compatibility of SELECT statement with FTWRL depends on
+# locking mode used and on functions being invoked by it.
+#
+# 30.a) Simple SELECT which does not change tables should be
+# compatible with FTWRL.
+Success: Was able to run 'select count(*) from t1_base' under FTWRL.
+Success: Was able to run 'select count(*) from t1_base' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'select count(*) from t1_base' was active in another connection.
+# 30.b) SELECT ... FOR UPDATE is incompatible with FTWRL.
+Success: Was not able to run 'select count(*) from t1_base for update' under FTWRL.
+Success: 'select count(*) from t1_base for update' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'select count(*) from t1_base for update' is active in another connection.
+# 30.c) SELECT ... LOCK IN SHARE MODE is compatible with FTWRL.
+Success: Was able to run 'select count(*) from t1_base lock in share mode' under FTWRL.
+Success: Was able to run 'select count(*) from t1_base lock in share mode' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'select count(*) from t1_base lock in share mode' was active in another connection.
+#
+# 30.d) SELECT which calls SF updating base table should be
+# incompatible with FTWRL.
+Success: Was not able to run 'select f2_base()' under FTWRL.
+Success: 'select f2_base()' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'select f2_base()' is active in another connection.
+#
+# 30.e) SELECT which calls SF updating temporary table should be
+# compatible with FTWRL.
+Success: Was able to run 'select f2_temp()' under FTWRL.
+Success: Was able to run 'select f2_temp()' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'select f2_temp()' was active in another connection.
+#
+# 31) Compatibility of SET statement with FTWRL depends on its
+# expression and on whether it is a special SET statement.
+#
+# 31.a) Ordinary SET with expression which does not
+# changes base table should be compatible with FTWRL.
+# Skip last part of compatibility testing as our helper debug
+# sync-point doesn't work for SET statements.
+Success: Was able to run 'set @a:= (select count(*) from t1_base)' under FTWRL.
+Success: Was able to run 'set @a:= (select count(*) from t1_base)' with FTWRL active in another connection.
+#
+# 31.b) Ordinary SET which calls SF updating base table should
+# be incompatible with FTWRL.
+# Skip last part of compatibility testing as our helper debug
+# sync-point doesn't work for SET statements.
+Success: Was not able to run 'set @a:= f2_base()' under FTWRL.
+Success: 'set @a:= f2_base()' is blocked by FTWRL active in another connection.
+#
+# 31.c) Ordinary SET which calls SF updating temporary table
+# should be compatible with FTWRL.
+# Skip last part of compatibility testing as our helper debug
+# sync-point doesn't work for SET statements.
+Success: Was able to run 'set @a:= f2_temp()' under FTWRL.
+Success: Was able to run 'set @a:= f2_temp()' with FTWRL active in another connection.
+#
+# 31.d) Special SET variants have different compatibility with FTWRL.
+#
+# 31.d.I) SET PASSWORD is incompatible with FTWRL as it changes data.
+create user mysqltest_u1;
+# Skip last part of compatibility testing as our helper debug
+# sync-point doesn't work for SET statements.
+Success: Was not able to run 'set password for 'mysqltest_u1' = password('')' under FTWRL.
+Success: 'set password for 'mysqltest_u1' = password('')' is blocked by FTWRL active in another connection.
+drop user mysqltest_u1;
+#
+# 31.d.II) SET READ_ONLY is compatible with FTWRL (but has no
+# effect when executed under it).
+# Skip last part of compatibility testing as our helper debug
+# sync-point doesn't work for SET statements.
+Success: Was able to run 'set global read_only= 1' under FTWRL.
+Success: Was able to run 'set global read_only= 1' with FTWRL active in another connection.
+#
+# 31.d.III) Situation with SET AUTOCOMMIT is complex.
+# Turning auto-commit off is always compatible with FTWRL.
+# Turning auto-commit on causes implicit commit and so
+# is incompatible with FTWRL if there are changes to be
+# committed.
+flush tables with read lock;
+set autocommit= 0;
+# Turning auto-commit on causes implicit commit so can
+# be incompatible with FTWRL if there is something to
+# commit. But since even in this case we allow commits
+# under active FTWRL such statement should always succeed.
+insert into t3_temp_trans values (1);
+set autocommit= 1;
+unlock tables;
+delete from t3_temp_trans;
+# Check that SET AUTOCOMMIT=0 is not blocked and
+# SET AUTOCOMMIT=1 is blocked by active FTWRL in
+# another connection.
+#
+# Switching to connection 'con1'.
+flush tables with read lock;
+# Switching to connection 'default'.
+set autocommit= 0;
+# Switching to connection 'con1'.
+unlock tables;
+# Switching to connection 'default'.
+# Do some work so implicit commit in SET AUTOCOMMIT=1
+# is not a no-op.
+insert into t3_trans values (1);
+# Switching to connection 'con1'.
+flush tables with read lock;
+# Switching to connection 'default'.
+# Send:
+set autocommit= 1;
+# Switching to connection 'con1'.
+# Wait until SET AUTOCOMMIT=1 is blocked.
+unlock tables;
+# Switching to connection 'default'.
+# Reap SET AUTOCOMMIT=1.
+delete from t3_trans;
+#
+# Check that SET AUTOCOMMIT=1 blocks FTWRL in another connection.
+set autocommit= 0;
+insert into t3_trans values (1);
+set debug_sync='RESET';
+set debug_sync='ha_commit_trans_after_acquire_commit_lock SIGNAL parked WAIT_FOR go';
+set autocommit= 1;
+# Switching to connection 'con1'.
+set debug_sync='now WAIT_FOR parked';
+flush tables with read lock;
+# Switching to connection 'con2'.
+# Wait until FTWRL is blocked.
+set debug_sync='now SIGNAL go';
+# Switching to connection 'default'.
+# Reap SET AUTOCOMMIT=1.
+# Switching to connection 'con1'.
+# Reap FTWRL.
+unlock tables;
+# Switching to connection 'default'.
+delete from t3_trans;
+set debug_sync= "RESET";
+#
+# 32) SHOW statements are compatible with FTWRL.
+# Let us test _some_ of them.
+#
+# 32.1) SHOW TABLES.
+Success: Was able to run 'show tables from test' under FTWRL.
+Success: Was able to run 'show tables from test' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'show tables from test' was active in another connection.
+#
+# 32.1) SHOW TABLES.
+Success: Was able to run 'show tables from test' under FTWRL.
+Success: Was able to run 'show tables from test' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'show tables from test' was active in another connection.
+#
+# 32.2) SHOW EVENTS.
+Success: Was able to run 'show events from test' under FTWRL.
+Success: Was able to run 'show events from test' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'show events from test' was active in another connection.
+#
+# 32.3) SHOW GRANTS.
+create user mysqltest_u1;
+Success: Was able to run 'show grants for mysqltest_u1' under FTWRL.
+Success: Was able to run 'show grants for mysqltest_u1' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'show grants for mysqltest_u1' was active in another connection.
+drop user mysqltest_u1;
+#
+# 32.4) SHOW CREATE TABLE.
+Success: Was able to run 'show create table t1_base' under FTWRL.
+Success: Was able to run 'show create table t1_base' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'show create table t1_base' was active in another connection.
+#
+# 32.5) SHOW CREATE FUNCTION.
+Success: Was able to run 'show create function f1' under FTWRL.
+Success: Was able to run 'show create function f1' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'show create function f1' was active in another connection.
+#
+# 33) SIGNAL statement is compatible with FTWRL.
+#
+# Note that we don't cover RESIGNAL as it requires
+# active handler context.
+Success: Was able to run 'signal sqlstate '01000'' under FTWRL.
+Success: Was able to run 'signal sqlstate '01000'' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'signal sqlstate '01000'' was active in another connection.
+#
+# 34) TRUNCATE TABLE statement.
+#
+# 34.a) TRUNCATE of base table is incompatible with FTWRL.
+Success: Was not able to run 'truncate table t1_base' under FTWRL.
+Success: 'truncate table t1_base' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'truncate table t1_base' is active in another connection.
+#
+# 34.b) TRUNCATE of temporary table is compatible with FTWRL.
+Success: Was able to run 'truncate table t1_temp' under FTWRL.
+Success: Was able to run 'truncate table t1_temp' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'truncate table t1_temp' was active in another connection.
+#
+# 35) UPDATE variants.
+#
+# 35.1) Simple UPDATE.
+#
+# 35.1.a) Simple UPDATE on base table is incompatible with FTWRL.
+Success: Was not able to run 'update t1_base set i= 1 where i = 0' under FTWRL.
+Success: 'update t1_base set i= 1 where i = 0' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'update t1_base set i= 1 where i = 0' is active in another connection.
+#
+# 35.1.b) Simple UPDATE on temporary table is compatible with FTWRL.
+Success: Was able to run 'update t1_temp set i= 1 where i = 0' under FTWRL.
+Success: Was able to run 'update t1_temp set i= 1 where i = 0' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'update t1_temp set i= 1 where i = 0' was active in another connection.
+#
+# 35.2) Multi UPDATE.
+#
+# 35.2.a) Multi UPDATE on base tables is incompatible with FTWRL.
+Success: Was not able to run 'update t1_base, t2_base set t1_base.i= 1 where t1_base.i = t2_base.j' under FTWRL.
+Success: 'update t1_base, t2_base set t1_base.i= 1 where t1_base.i = t2_base.j' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'update t1_base, t2_base set t1_base.i= 1 where t1_base.i = t2_base.j' is active in another connection.
+#
+# 35.2.b) Multi UPDATE on temporary tables is compatible with FTWRL.
+Success: Was able to run 'update t1_temp, t2_temp set t1_temp.i= 1 where t1_temp.i = t2_temp.j' under FTWRL.
+Success: Was able to run 'update t1_temp, t2_temp set t1_temp.i= 1 where t1_temp.i = t2_temp.j' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'update t1_temp, t2_temp set t1_temp.i= 1 where t1_temp.i = t2_temp.j' was active in another connection.
+#
+# 36) USE statement is compatible with FTWRL.
+#
+Success: Was able to run 'use mysqltest1' under FTWRL.
+Success: Was able to run 'use mysqltest1' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'use mysqltest1' was active in another connection.
+#
+# 37) XA statements.
+#
+# XA statements are similar to BEGIN/COMMIT/ROLLBACK.
+#
+# XA BEGIN, END, PREPARE, ROLLBACK and RECOVER are compatible
+# with FTWRL. XA COMMIT is not.
+flush tables with read lock;
+# Although all below statements are allowed under FTWRL they
+# are almost no-ops as FTWRL does commit and does not allows
+# any non-temporary DML under it.
+xa start 'test1';
+xa end 'test1';
+xa prepare 'test1';
+xa rollback 'test1';
+xa start 'test1';
+xa end 'test1';
+xa prepare 'test1';
+xa commit 'test1';
+xa recover;
+unlock tables;
+# Check that XA non-COMMIT statements are not and COMMIT is
+# blocked by active FTWRL in another connection
+#
+# Switching to connection 'con1'.
+flush tables with read lock;
+# Switching to connection 'default'.
+xa start 'test1';
+# Switching to connection 'con1'.
+unlock tables;
+# Switching to connection 'default'.
+insert into t3_trans values (1);
+# Switching to connection 'con1'.
+flush tables with read lock;
+# Switching to connection 'default'.
+xa end 'test1';
+xa prepare 'test1';
+xa rollback 'test1';
+# Switching to connection 'con1'.
+unlock tables;
+# Switching to connection 'default'.
+xa start 'test1';
+insert into t3_trans values (1);
+# Switching to connection 'con1'.
+flush tables with read lock;
+# Switching to connection 'default'.
+xa end 'test1';
+xa prepare 'test1';
+# Send:
+xa commit 'test1';;
+# Switching to connection 'con1'.
+# Wait until XA COMMIT is blocked.
+unlock tables;
+# Switching to connection 'default'.
+# Reap XA COMMIT.
+delete from t3_trans;
+#
+# Check that XA COMMIT blocks FTWRL in another connection.
+xa start 'test1';
+insert into t3_trans values (1);
+xa end 'test1';
+xa prepare 'test1';
+set debug_sync='RESET';
+set debug_sync='trans_xa_commit_after_acquire_commit_lock SIGNAL parked WAIT_FOR go';
+xa commit 'test1';
+# Switching to connection 'con1'.
+set debug_sync='now WAIT_FOR parked';
+flush tables with read lock;
+# Switching to connection 'con2'.
+# Wait until FTWRL is blocked.
+set debug_sync='now SIGNAL go';
+# Switching to connection 'default'.
+# Reap XA COMMIT.
+# Switching to connection 'con1'.
+# Reap FTWRL.
+unlock tables;
+# Switching to connection 'default'.
+delete from t3_trans;
+set debug_sync= "RESET";
+#
+# 38) Test effect of auto-commit mode for DML on transactional
+# temporary tables.
+#
+# 38.1) When auto-commit is on each such a statement ends with commit
+# of changes to temporary tables. But since transactions doing
+# such changes are considered read only [sic!/QQ] this commit
+# is compatible with FTWRL.
+#
+# Let us demostrate this fact for some common DML statements.
+Success: Was able to run 'delete from t3_temp_trans' under FTWRL.
+Success: Was able to run 'delete from t3_temp_trans' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'delete from t3_temp_trans' was active in another connection.
+Success: Was able to run 'insert into t3_temp_trans values (1)' under FTWRL.
+Success: Was able to run 'insert into t3_temp_trans values (1)' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'insert into t3_temp_trans values (1)' was active in another connection.
+Success: Was able to run 'update t3_temp_trans, t2_temp set t3_temp_trans.i= 1 where t3_temp_trans.i = t2_temp.j' under FTWRL.
+Success: Was able to run 'update t3_temp_trans, t2_temp set t3_temp_trans.i= 1 where t3_temp_trans.i = t2_temp.j' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'update t3_temp_trans, t2_temp set t3_temp_trans.i= 1 where t3_temp_trans.i = t2_temp.j' was active in another connection.
+#
+# 38.2) When auto-commit is off DML on transaction temporary tables
+# is compatible with FTWRL.
+#
+set autocommit= 0;
+Success: Was able to run 'delete from t3_temp_trans' under FTWRL.
+Success: Was able to run 'delete from t3_temp_trans' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'delete from t3_temp_trans' was active in another connection.
+Success: Was able to run 'insert into t3_temp_trans values (1)' under FTWRL.
+Success: Was able to run 'insert into t3_temp_trans values (1)' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'insert into t3_temp_trans values (1)' was active in another connection.
+Success: Was able to run 'update t3_temp_trans, t2_temp set t3_temp_trans.i= 1 where t3_temp_trans.i = t2_temp.j' under FTWRL.
+Success: Was able to run 'update t3_temp_trans, t2_temp set t3_temp_trans.i= 1 where t3_temp_trans.i = t2_temp.j' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'update t3_temp_trans, t2_temp set t3_temp_trans.i= 1 where t3_temp_trans.i = t2_temp.j' was active in another connection.
+set autocommit= 1;
+#
+# 39) Test effect of DDL on transactional tables.
+#
+# 39.1) Due to implicit commit at the end of statement some of DDL
+# statements which are compatible with FTWRL in non-transactional
+# case are not compatible in case of transactional tables.
+#
+# 39.1.a) ANALYZE TABLE for transactional table is incompatible with
+# FTWRL.
+flush tables with read lock;
+# Implicit commits are allowed under FTWRL.
+analyze table t3_trans;
+Table Op Msg_type Msg_text
+test.t3_trans analyze status OK
+unlock tables;
+#
+# Switching to connection 'con1'.
+flush tables with read lock;
+# Switching to connection 'default'.
+analyze table t3_trans;
+# Switching to connection 'con1'.
+# Check that ANALYZE TABLE is blocked.
+unlock tables;
+# Switching to connection 'default'.
+# Reap ANALYZE TABLE
+Table Op Msg_type Msg_text
+test.t3_trans analyze status OK
+#
+# 39.1.b) CHECK TABLE for transactional table is compatible with FTWRL.
+# Although it does implicit commit at the end of statement it
+# is considered to be read-only operation.
+# Skip last part of compatibility testing as this statement
+# releases metadata locks in non-standard place.
+Success: Was able to run 'check table t3_trans' under FTWRL.
+Success: Was able to run 'check table t3_trans' with FTWRL active in another connection.
+#
+# 39.2) Situation with DDL on temporary transactional tables is
+# complex.
+#
+# 39.2.a) Some statements compatible with FTWRL since they don't
+# do implicit commit.
+#
+# For example, CREATE TEMPORARY TABLE:
+Success: Was able to run 'create temporary table t4_temp_trans(i int) engine=innodb' under FTWRL.
+Success: Was able to run 'create temporary table t4_temp_trans(i int) engine=innodb' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'create temporary table t4_temp_trans(i int) engine=innodb' was active in another connection.
+#
+# Or DROP TEMPORARY TABLE:
+Success: Was able to run 'drop temporary tables t3_temp_trans' under FTWRL.
+Success: Was able to run 'drop temporary tables t3_temp_trans' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'drop temporary tables t3_temp_trans' was active in another connection.
+#
+# 39.2.b) Some statements do implicit commit but are considered
+# read-only and so are compatible with FTWRL.
+#
+# For example, REPAIR TABLE:
+Success: Was able to run 'repair table t3_temp_trans' under FTWRL.
+Success: Was able to run 'repair table t3_temp_trans' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'repair table t3_temp_trans' was active in another connection.
+#
+# And ANALYZE TABLE:
+Success: Was able to run 'analyze table t3_temp_trans' under FTWRL.
+Success: Was able to run 'analyze table t3_temp_trans' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'analyze table t3_temp_trans' was active in another connection.
+#
+# 39.2.c) Some statements do implicit commit and not
+# considered read-only. As result they are
+# not compatible with FTWRL.
+#
+flush tables with read lock;
+# Implicit commits are allowed under FTWRL.
+alter table t3_temp_trans add column c1 int;
+unlock tables;
+#
+# Switching to connection 'con1'.
+flush tables with read lock;
+# Switching to connection 'default'.
+alter table t3_temp_trans drop column c1;
+# Switching to connection 'con1'.
+# Check that ALTER TABLE is blocked.
+unlock tables;
+# Switching to connection 'default'.
+# Reap ALTER TABLE
+#
+# 40) Test effect of implicit commit for DDL which is otherwise
+# compatible with FTWRL. Implicit commit at the start of DDL
+# statement can make it incompatible with FTWRL if there are
+# some changes to be commited even in case when DDL statement
+# itself is compatible with FTWRL.
+#
+# For example CHECK TABLE for base non-transactional tables and
+# ALTER TABLE for temporary non-transactional tables are affected.
+begin;
+insert into t3_trans values (1);
+#
+# Switching to connection 'con1'.
+flush tables with read lock;
+# Switching to connection 'default'.
+check table t1_base;
+# Switching to connection 'con1'.
+# Check that CHECK TABLE is blocked.
+unlock tables;
+# Switching to connection 'default'.
+# Reap CHECK TABLE
+Table Op Msg_type Msg_text
+test.t1_base check status OK
+begin;
+delete from t3_trans;
+#
+# Switching to connection 'con1'.
+flush tables with read lock;
+# Switching to connection 'default'.
+alter table t1_temp add column c1 int;
+# Switching to connection 'con1'.
+# Check that ALTER TABLE is blocked.
+unlock tables;
+# Switching to connection 'default'.
+# Reap ALTER TABLE
+alter table t1_temp drop column c1;
+#
+# Check that FLUSH TABLES WITH READ LOCK is blocked by individual
+# statements and is not blocked in the presence of transaction which
+# has done some changes earlier but is idle now (or does only reads).
+# This allows to use this statement even on systems which has long
+# running transactions.
+#
+begin;
+insert into t1_base values (1);
+insert into t3_trans values (1);
+# Switching to connection 'con1'.
+# The below FTWRL should not be blocked by transaction in 'default'.
+flush tables with read lock;
+# Switching to connection 'default'.
+# Transaction still is able to read even with FTWRL active in another
+# connection.
+select * from t1_base;
+i
+1
+select * from t2_base;
+j
+select * from t3_trans;
+i
+1
+# Switching to connection 'con1'.
+unlock tables;
+# Switching to connection 'default'.
+commit;
+delete from t1_base;
+delete from t3_trans;
+#
+# Check that impending FTWRL blocks new DML statements and
+# so can't be starved by a constant flow of DML.
+# (a.k.a. test for bug #54673 "It takes too long to get
+# readlock for 'FLUSH TABLES WITH READ LOCK'").
+#
+set debug_sync='RESET';
+set debug_sync='execute_command_after_close_tables SIGNAL parked WAIT_FOR go';
+insert into t1_base values (1);
+# Switching to connection 'con1'.
+set debug_sync='now WAIT_FOR parked';
+flush tables with read lock;
+# Switching to connection 'con2'.
+# Wait until FTWRL is blocked.
+# Try to run another INSERT and see that it is blocked.
+insert into t2_base values (1);;
+# Switching to connection 'con3'.
+# Wait until new INSERT is blocked.
+# Unblock INSERT in the first connection.
+set debug_sync='now SIGNAL go';
+# Switching to connection 'default'.
+# Reap first INSERT.
+# Switching to connection 'con1'.
+# Reap FTWRL.
+unlock tables;
+# Switching to connection 'con2'.
+# Reap second INSERT.
+# Switching to connection 'default'.
+set debug_sync= "RESET";
+delete from t1_base;
+delete from t2_base;
+
+# Check that COMMIT thas is issued after
+# FLUSH TABLES WITH READ LOCK is not blocked by
+# FLUSH TABLES WITH READ LOCK from another connection.
+# This scenario is used in innobackup.pl. The COMMIT goes
+# through because the transaction started by FTWRL does
+# not modify any tables, and the commit blocker lock is
+# only taken when there were such modifications.
+
+flush tables with read lock;
+# Switching to connection 'con1'.
+# The below FTWRL should not be blocked by transaction in 'default'.
+flush tables with read lock;
+# Switching to connection 'default'.
+select * from t1_base;
+i
+select * from t3_trans;
+i
+commit;
+# Switching to connection 'con1'.
+select * from t1_base;
+i
+select * from t3_trans;
+i
+commit;
+unlock tables;
+# Switching to connection 'default'.
+unlock tables;
+#
+# Check how FLUSH TABLE WITH READ LOCK is handled for MERGE tables.
+# As usual there are tricky cases related to this type of tables.
+#
+#
+# 1) Most typical case - base MERGE table with base underlying tables.
+#
+# 1.a) DML statements which change data should be incompatible with FTWRL.
+create table tm_base (i int) engine=merge union=(t1_base) insert_method=last;
+Success: Was not able to run 'insert into tm_base values (1)' under FTWRL.
+Success: 'insert into tm_base values (1)' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'insert into tm_base values (1)' is active in another connection.
+#
+# 1.b) DDL statement on such table should be incompatible with FTWRL as well.
+Success: Was not able to run 'alter table tm_base insert_method=first' under FTWRL.
+Success: 'alter table tm_base insert_method=first' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'alter table tm_base insert_method=first' is active in another connection.
+drop table tm_base;
+#
+# 2) Temporary MERGE table with base underlying tables.
+#
+# 2.a) DML statements which change data should be incompatible with FTWRL
+# as they affect base tables.
+create temporary table tm_temp_base (i int) engine=merge union=(t1_base) insert_method=last;
+Success: Was not able to run 'insert into tm_temp_base values (1)' under FTWRL.
+Success: 'insert into tm_temp_base values (1)' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'insert into tm_temp_base values (1)' is active in another connection.
+#
+# 2.b) Some of DDL statements on such table can be compatible with FTWRL
+# as they don't affect base tables.
+Success: Was able to run 'drop temporary tables tm_temp_base' under FTWRL.
+Success: Was able to run 'drop temporary tables tm_temp_base' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'drop temporary tables tm_temp_base' was active in another connection.
+#
+# 2.c) ALTER statement is incompatible with FTWRL. Even though it does
+# not change data in base table it still acquires strong metadata
+# locks on them.
+Success: Was not able to run 'alter table tm_temp_base insert_method=first' under FTWRL.
+Success: 'alter table tm_temp_base insert_method=first' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'alter table tm_temp_base insert_method=first' is active in another connection.
+drop table tm_temp_base;
+#
+# 3) Temporary MERGE table with temporary underlying tables.
+#
+# 3.a) DML statements should be compatible with FTWRL as
+# no base table is going to be affected.
+create temporary table tm_temp_temp (i int) engine=merge union=(t1_temp) insert_method=last;
+Success: Was able to run 'insert into tm_temp_temp values (1)' under FTWRL.
+Success: Was able to run 'insert into tm_temp_temp values (1)' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'insert into tm_temp_temp values (1)' was active in another connection.
+#
+# 3.b) DDL statements should be compatible with FTWRL as well
+# as no base table is going to be affected too.
+Success: Was able to run 'alter table tm_temp_temp union=(t1_temp) insert_method=first' under FTWRL.
+Success: Was able to run 'alter table tm_temp_temp union=(t1_temp) insert_method=first' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'alter table tm_temp_temp union=(t1_temp) insert_method=first' was active in another connection.
+drop table tm_temp_temp;
+#
+# 4) For the sake of completeness let us check that base MERGE tables
+# with temporary underlying tables are not functional.
+create table tm_base_temp (i int) engine=merge union=(t1_temp) insert_method=last;
+select * from tm_base_temp;
+ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist
+drop table tm_base_temp;
+#
+# Clean-up.
+#
+drop event e1;
+drop function f2_temp;
+drop function f2_base;
+drop procedure p2;
+drop view v1;
+drop function f1;
+drop procedure p1;
+drop database `#mysql50#mysqltest-2`;
+drop database mysqltest1;
+drop temporary tables t1_temp, t2_temp;
+drop tables t1_base, t2_base, t3_trans;
diff --git a/mysql-test/r/flush_read_lock_kill.result b/mysql-test/r/flush_read_lock_kill.result
index b16a8b114b3..8453d26cbea 100644
--- a/mysql-test/r/flush_read_lock_kill.result
+++ b/mysql-test/r/flush_read_lock_kill.result
@@ -1,12 +1,38 @@
-SET @old_concurrent_insert= @@global.concurrent_insert;
-SET @@global.concurrent_insert= 0;
DROP TABLE IF EXISTS t1;
-CREATE TABLE t1 (kill_id INT);
+SET DEBUG_SYNC= 'RESET';
+CREATE TABLE t1 (kill_id INT) engine = InnoDB;
INSERT INTO t1 VALUES(connection_id());
+# Switching to connection 'default'.
+# Start transaction.
+BEGIN;
+INSERT INTO t1 VALUES(connection_id());
+# Ensure that COMMIT will pause once it acquires protection
+# against its global read lock.
+SET DEBUG_SYNC='ha_commit_trans_after_acquire_commit_lock SIGNAL acquired WAIT_FOR go';
+# Sending:
+COMMIT;
+# Switching to 'con1'.
+# Wait till COMMIT acquires protection against global read
+# lock and pauses.
+SET DEBUG_SYNC='now WAIT_FOR acquired';
+# Sending:
FLUSH TABLES WITH READ LOCK;
-SELECT ((@id := kill_id) - kill_id) FROM t1;
+# Switching to 'con2'.
+SELECT ((@id := kill_id) - kill_id) FROM t1 LIMIT 1;
((@id := kill_id) - kill_id)
0
+# Wait till FLUSH TABLES WITH READ LOCK blocks due
+# to active COMMIT
+# Kill connection 'con1'.
KILL CONNECTION @id;
+# Switching to 'con1'.
+# Try to reap FLUSH TABLES WITH READ LOCK,
+# it fail due to killed statement and connection.
+Got one of the listed errors
+# Switching to 'con2'.
+# Resume COMMIT.
+SET DEBUG_SYNC='now SIGNAL go';
+# Switching to 'default'.
+# Reaping COMMIT.
DROP TABLE t1;
-SET @@global.concurrent_insert= @old_concurrent_insert;
+SET DEBUG_SYNC= 'RESET';
diff --git a/mysql-test/r/handler_innodb.result b/mysql-test/r/handler_innodb.result
index 66914285733..dd4cac669c8 100644
--- a/mysql-test/r/handler_innodb.result
+++ b/mysql-test/r/handler_innodb.result
@@ -1485,10 +1485,6 @@ ERROR 42S02: Table 'test.not_exists_write' doesn't exist
# We still have the read lock.
drop table t1;
ERROR HY000: Can't execute the query because you have a conflicting read lock
-handler t1 read next;
-a b
-1 1
-handler t1 close;
handler t1 open;
select a from t2;
a
diff --git a/mysql-test/r/handler_myisam.result b/mysql-test/r/handler_myisam.result
index d5d43ca0717..69d791b8263 100644
--- a/mysql-test/r/handler_myisam.result
+++ b/mysql-test/r/handler_myisam.result
@@ -1481,10 +1481,6 @@ ERROR 42S02: Table 'test.not_exists_write' doesn't exist
# We still have the read lock.
drop table t1;
ERROR HY000: Can't execute the query because you have a conflicting read lock
-handler t1 read next;
-a b
-1 1
-handler t1 close;
handler t1 open;
select a from t2;
a
diff --git a/mysql-test/r/mdl_sync.result b/mysql-test/r/mdl_sync.result
index d2a32c25201..594cf433692 100644
--- a/mysql-test/r/mdl_sync.result
+++ b/mysql-test/r/mdl_sync.result
@@ -2471,7 +2471,7 @@ CREATE PROCEDURE p1() SELECT 1;
SET DEBUG_SYNC= 'now WAIT_FOR table_opened';
# Check that FLUSH must wait to get the GRL
# and let CREATE PROCEDURE continue
-SET DEBUG_SYNC= 'wait_lock_global_read_lock SIGNAL grlwait';
+SET DEBUG_SYNC= 'mdl_acquire_lock_wait SIGNAL grlwait';
FLUSH TABLES WITH READ LOCK;
# Connection 1
# Connection 2
@@ -2486,10 +2486,17 @@ DROP PROCEDURE p1;
SET DEBUG_SYNC= 'now WAIT_FOR table_opened';
# Check that FLUSH must wait to get the GRL
# and let DROP PROCEDURE continue
-SET DEBUG_SYNC= 'wait_lock_global_read_lock SIGNAL grlwait';
+SET DEBUG_SYNC= 'mdl_acquire_lock_wait SIGNAL grlwait';
FLUSH TABLES WITH READ LOCK;
# Connection 1
+# Once FLUSH TABLES WITH READ LOCK starts waiting
+# DROP PROCEDURE will be waked up and will drop
+# procedure. Global read lock will be granted after
+# this statement ends.
+#
+# Reaping DROP PROCEDURE.
# Connection 2
+# Reaping FTWRL.
UNLOCK TABLES;
# Connection 1
SET DEBUG_SYNC= 'RESET';
diff --git a/mysql-test/suite/perfschema/r/dml_setup_instruments.result b/mysql-test/suite/perfschema/r/dml_setup_instruments.result
index 2338252976c..15174ee40bf 100644
--- a/mysql-test/suite/perfschema/r/dml_setup_instruments.result
+++ b/mysql-test/suite/perfschema/r/dml_setup_instruments.result
@@ -37,7 +37,6 @@ where name like 'Wait/Synch/Cond/sql/%'
order by name limit 10;
NAME ENABLED TIMED
wait/synch/cond/sql/COND_flush_thread_cache YES YES
-wait/synch/cond/sql/COND_global_read_lock YES YES
wait/synch/cond/sql/COND_manager YES YES
wait/synch/cond/sql/COND_queue_state YES YES
wait/synch/cond/sql/COND_rpl_status YES YES
@@ -46,6 +45,7 @@ wait/synch/cond/sql/COND_thread_cache YES YES
wait/synch/cond/sql/COND_thread_count YES YES
wait/synch/cond/sql/Delayed_insert::cond YES YES
wait/synch/cond/sql/Delayed_insert::cond_client YES YES
+wait/synch/cond/sql/Event_scheduler::COND_state YES YES
select * from performance_schema.SETUP_INSTRUMENTS
where name='Wait';
select * from performance_schema.SETUP_INSTRUMENTS
diff --git a/mysql-test/suite/perfschema/r/func_file_io.result b/mysql-test/suite/perfschema/r/func_file_io.result
index 655ce1394f9..8314bed94bf 100644
--- a/mysql-test/suite/perfschema/r/func_file_io.result
+++ b/mysql-test/suite/perfschema/r/func_file_io.result
@@ -100,3 +100,4 @@ INNER JOIN performance_schema.THREADS p USING (THREAD_ID)
WHERE p.PROCESSLIST_ID = 1
GROUP BY h.EVENT_NAME
HAVING TOTAL_WAIT > 0;
+UPDATE performance_schema.SETUP_INSTRUMENTS SET enabled = 'YES';
diff --git a/mysql-test/suite/perfschema/r/func_mutex.result b/mysql-test/suite/perfschema/r/func_mutex.result
index e32d7267bb1..93ba4064a90 100644
--- a/mysql-test/suite/perfschema/r/func_mutex.result
+++ b/mysql-test/suite/perfschema/r/func_mutex.result
@@ -110,4 +110,5 @@ WHERE (EVENT_NAME = 'wait/synch/rwlock/sql/LOCK_grant'));
SELECT IF((COALESCE(@after_count, 0) - COALESCE(@before_count, 0)) = 0, 'Success', 'Failure') test_fm2_rw_timed;
test_fm2_rw_timed
Success
+UPDATE performance_schema.SETUP_INSTRUMENTS SET enabled = 'YES';
DROP TABLE t1;
diff --git a/mysql-test/suite/perfschema/r/global_read_lock.result b/mysql-test/suite/perfschema/r/global_read_lock.result
index 93d6adfd049..365ac095e84 100644
--- a/mysql-test/suite/perfschema/r/global_read_lock.result
+++ b/mysql-test/suite/perfschema/r/global_read_lock.result
@@ -1,4 +1,5 @@
use performance_schema;
+update performance_schema.SETUP_INSTRUMENTS set enabled='YES';
grant SELECT, UPDATE, LOCK TABLES on performance_schema.* to pfsuser@localhost;
flush privileges;
connect (con1, localhost, pfsuser, , test);
@@ -21,9 +22,9 @@ select event_name,
left(source, locate(":", source)) as short_source,
timer_end, timer_wait, operation
from performance_schema.EVENTS_WAITS_CURRENT
-where event_name like "wait/synch/cond/sql/COND_global_read_lock";
+where event_name like "wait/synch/cond/sql/MDL_context::COND_wait_status";
event_name short_source timer_end timer_wait operation
-wait/synch/cond/sql/COND_global_read_lock lock.cc: NULL NULL wait
+wait/synch/cond/sql/MDL_context::COND_wait_status mdl.cc: NULL NULL timed_wait
unlock tables;
update performance_schema.SETUP_INSTRUMENTS set enabled='NO';
update performance_schema.SETUP_INSTRUMENTS set enabled='YES';
diff --git a/mysql-test/suite/perfschema/r/server_init.result b/mysql-test/suite/perfschema/r/server_init.result
index 0c1e06d157c..474a481929a 100644
--- a/mysql-test/suite/perfschema/r/server_init.result
+++ b/mysql-test/suite/perfschema/r/server_init.result
@@ -88,10 +88,6 @@ where name like "wait/synch/mutex/sql/LOCK_manager";
count(name)
1
select count(name) from MUTEX_INSTANCES
-where name like "wait/synch/mutex/sql/LOCK_global_read_lock";
-count(name)
-1
-select count(name) from MUTEX_INSTANCES
where name like "wait/synch/mutex/sql/LOCK_global_system_variables";
count(name)
1
@@ -120,10 +116,6 @@ where name like "wait/synch/mutex/sql/Query_cache::structure_guard_mutex";
count(name)
1
select count(name) from MUTEX_INSTANCES
-where name like "wait/synch/mutex/sql/LOCK_event_metadata";
-count(name)
-1
-select count(name) from MUTEX_INSTANCES
where name like "wait/synch/mutex/sql/LOCK_event_queue";
count(name)
1
@@ -184,10 +176,6 @@ where name like "wait/synch/cond/sql/COND_manager";
count(name)
1
select count(name) from COND_INSTANCES
-where name like "wait/synch/cond/sql/COND_global_read_lock";
-count(name)
-1
-select count(name) from COND_INSTANCES
where name like "wait/synch/cond/sql/COND_thread_cache";
count(name)
1
diff --git a/mysql-test/suite/perfschema/t/func_file_io.test b/mysql-test/suite/perfschema/t/func_file_io.test
index dc9a7a09e40..9ba8d061b12 100644
--- a/mysql-test/suite/perfschema/t/func_file_io.test
+++ b/mysql-test/suite/perfschema/t/func_file_io.test
@@ -190,3 +190,6 @@ HAVING TOTAL_WAIT > 0;
## HAVING BYTES > 0
## ORDER BY i.user, h.operation;
## --enable_result_log
+
+# Clean-up.
+UPDATE performance_schema.SETUP_INSTRUMENTS SET enabled = 'YES';
diff --git a/mysql-test/suite/perfschema/t/func_mutex.test b/mysql-test/suite/perfschema/t/func_mutex.test
index 98cb905c67c..624f5734c40 100644
--- a/mysql-test/suite/perfschema/t/func_mutex.test
+++ b/mysql-test/suite/perfschema/t/func_mutex.test
@@ -128,4 +128,6 @@ SET @after_count = (SELECT SUM(TIMER_WAIT)
SELECT IF((COALESCE(@after_count, 0) - COALESCE(@before_count, 0)) = 0, 'Success', 'Failure') test_fm2_rw_timed;
+# Clean-up.
+UPDATE performance_schema.SETUP_INSTRUMENTS SET enabled = 'YES';
DROP TABLE t1;
diff --git a/mysql-test/suite/perfschema/t/global_read_lock.test b/mysql-test/suite/perfschema/t/global_read_lock.test
index b953ea32ce0..9b7c54b1411 100644
--- a/mysql-test/suite/perfschema/t/global_read_lock.test
+++ b/mysql-test/suite/perfschema/t/global_read_lock.test
@@ -22,6 +22,10 @@
use performance_schema;
+# Make test robust against errors in other tests.
+# Ensure that instrumentation is turned on when we create new connection.
+update performance_schema.SETUP_INSTRUMENTS set enabled='YES';
+
grant SELECT, UPDATE, LOCK TABLES on performance_schema.* to pfsuser@localhost;
flush privileges;
@@ -60,7 +64,7 @@ lock tables performance_schema.SETUP_INSTRUMENTS write;
--echo connection default;
connection default;
-let $wait_condition= select 1 from performance_schema.EVENTS_WAITS_CURRENT where event_name like "wait/synch/cond/sql/COND_global_read_lock";
+let $wait_condition= select 1 from performance_schema.EVENTS_WAITS_CURRENT where event_name like "wait/synch/cond/sql/MDL_context::COND_wait_status";
--source include/wait_condition.inc
@@ -69,7 +73,7 @@ select event_name,
left(source, locate(":", source)) as short_source,
timer_end, timer_wait, operation
from performance_schema.EVENTS_WAITS_CURRENT
- where event_name like "wait/synch/cond/sql/COND_global_read_lock";
+ where event_name like "wait/synch/cond/sql/MDL_context::COND_wait_status";
unlock tables;
diff --git a/mysql-test/suite/perfschema/t/server_init.test b/mysql-test/suite/perfschema/t/server_init.test
index cc461374a79..7036464ddb9 100644
--- a/mysql-test/suite/perfschema/t/server_init.test
+++ b/mysql-test/suite/perfschema/t/server_init.test
@@ -101,9 +101,6 @@ select count(name) from MUTEX_INSTANCES
where name like "wait/synch/mutex/sql/LOCK_manager";
select count(name) from MUTEX_INSTANCES
- where name like "wait/synch/mutex/sql/LOCK_global_read_lock";
-
-select count(name) from MUTEX_INSTANCES
where name like "wait/synch/mutex/sql/LOCK_global_system_variables";
select count(name) from MUTEX_INSTANCES
@@ -133,9 +130,6 @@ select count(name) from MUTEX_INSTANCES
# where name like "wait/synch/mutex/sql/Event_scheduler::LOCK_scheduler_state";
select count(name) from MUTEX_INSTANCES
- where name like "wait/synch/mutex/sql/LOCK_event_metadata";
-
-select count(name) from MUTEX_INSTANCES
where name like "wait/synch/mutex/sql/LOCK_event_queue";
select count(name) from MUTEX_INSTANCES
@@ -189,9 +183,6 @@ select count(name) from COND_INSTANCES
where name like "wait/synch/cond/sql/COND_manager";
select count(name) from COND_INSTANCES
- where name like "wait/synch/cond/sql/COND_global_read_lock";
-
-select count(name) from COND_INSTANCES
where name like "wait/synch/cond/sql/COND_thread_cache";
select count(name) from COND_INSTANCES
diff --git a/mysql-test/suite/rpl/r/rpl_tmp_table_and_DDL.result b/mysql-test/suite/rpl/r/rpl_tmp_table_and_DDL.result
index 3136599e5aa..14af782a4d5 100644
--- a/mysql-test/suite/rpl/r/rpl_tmp_table_and_DDL.result
+++ b/mysql-test/suite/rpl/r/rpl_tmp_table_and_DDL.result
@@ -123,16 +123,16 @@ DROP PROCEDURE p2;
ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
INSERT INTO t2 VALUES ("DROP PROCEDURE p2 with table locked");
CREATE EVENT e1 ON SCHEDULE EVERY 10 HOUR DO SELECT 1;
-ERROR HY000: Table 'event' was not locked with LOCK TABLES
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
INSERT INTO t2 VALUES ("CREATE EVENT e1 with table locked");
UNLOCK TABLE;
CREATE EVENT e2 ON SCHEDULE EVERY 10 HOUR DO SELECT 1;
LOCK TABLE t1 WRITE;
ALTER EVENT e2 ON SCHEDULE EVERY 20 HOUR DO SELECT 1;
-ERROR HY000: Table 'event' was not locked with LOCK TABLES
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
INSERT INTO t2 VALUES ("ALTER EVENT e2 with table locked");
DROP EVENT e2;
-ERROR HY000: Table 'event' was not locked with LOCK TABLES
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
INSERT INTO t2 VALUES ("DROP EVENT e2 with table locked");
CREATE DATABASE mysqltest1;
ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
diff --git a/mysql-test/t/delayed.test b/mysql-test/t/delayed.test
index 3a2bc982ad3..c47db78a11b 100644
--- a/mysql-test/t/delayed.test
+++ b/mysql-test/t/delayed.test
@@ -539,6 +539,21 @@ connection con1;
--echo # Reaping: INSERT DELAYED INTO t1 VALUES (5)
--reap
+--echo # Connection default
+connection default;
+
+--echo # Test 5: LOCK TABLES + INSERT DELAYED in one connection.
+--echo # This test has triggered some asserts in metadata locking
+--echo # subsystem at some point in time..
+LOCK TABLE t1 WRITE;
+INSERT DELAYED INTO t2 VALUES (7);
+UNLOCK TABLES;
+SET AUTOCOMMIT= 0;
+LOCK TABLE t1 WRITE;
+INSERT DELAYED INTO t2 VALUES (8);
+UNLOCK TABLES;
+SET AUTOCOMMIT= 1;
+
--echo # Connection con2
connection con2;
disconnect con2;
diff --git a/mysql-test/t/events_2.test b/mysql-test/t/events_2.test
index 08412d2f5b0..3d609654b21 100644
--- a/mysql-test/t/events_2.test
+++ b/mysql-test/t/events_2.test
@@ -212,15 +212,15 @@ lock table t1 read;
--replace_regex /STARTS '[^']+'/STARTS '#'/
show create event e1;
select event_name from information_schema.events;
---error ER_TABLE_NOT_LOCKED
+--error ER_LOCK_OR_ACTIVE_TRANSACTION
create event e2 on schedule every 10 hour do select 1;
---error ER_TABLE_NOT_LOCKED
+--error ER_LOCK_OR_ACTIVE_TRANSACTION
alter event e2 disable;
---error ER_TABLE_NOT_LOCKED
+--error ER_LOCK_OR_ACTIVE_TRANSACTION
alter event e2 rename to e3;
---error ER_TABLE_NOT_LOCKED
+--error ER_LOCK_OR_ACTIVE_TRANSACTION
drop event e2;
---error ER_TABLE_NOT_LOCKED
+--error ER_LOCK_OR_ACTIVE_TRANSACTION
drop event e1;
unlock tables;
#
@@ -229,15 +229,15 @@ lock table t1 write;
--replace_regex /STARTS '[^']+'/STARTS '#'/
show create event e1;
select event_name from information_schema.events;
---error ER_TABLE_NOT_LOCKED
+--error ER_LOCK_OR_ACTIVE_TRANSACTION
create event e2 on schedule every 10 hour do select 1;
---error ER_TABLE_NOT_LOCKED
+--error ER_LOCK_OR_ACTIVE_TRANSACTION
alter event e2 disable;
---error ER_TABLE_NOT_LOCKED
+--error ER_LOCK_OR_ACTIVE_TRANSACTION
alter event e2 rename to e3;
---error ER_TABLE_NOT_LOCKED
+--error ER_LOCK_OR_ACTIVE_TRANSACTION
drop event e2;
---error ER_TABLE_NOT_LOCKED
+--error ER_LOCK_OR_ACTIVE_TRANSACTION
drop event e1;
unlock tables;
#
@@ -246,15 +246,15 @@ lock table t1 read, mysql.event read;
--replace_regex /STARTS '[^']+'/STARTS '#'/
show create event e1;
select event_name from information_schema.events;
---error ER_TABLE_NOT_LOCKED_FOR_WRITE
+--error ER_LOCK_OR_ACTIVE_TRANSACTION
create event e2 on schedule every 10 hour do select 1;
---error ER_TABLE_NOT_LOCKED_FOR_WRITE
+--error ER_LOCK_OR_ACTIVE_TRANSACTION
alter event e2 disable;
---error ER_TABLE_NOT_LOCKED_FOR_WRITE
+--error ER_LOCK_OR_ACTIVE_TRANSACTION
alter event e2 rename to e3;
---error ER_TABLE_NOT_LOCKED_FOR_WRITE
+--error ER_LOCK_OR_ACTIVE_TRANSACTION
drop event e2;
---error ER_TABLE_NOT_LOCKED_FOR_WRITE
+--error ER_LOCK_OR_ACTIVE_TRANSACTION
drop event e1;
unlock tables;
#
@@ -263,15 +263,15 @@ lock table t1 write, mysql.event read;
--replace_regex /STARTS '[^']+'/STARTS '#'/
show create event e1;
select event_name from information_schema.events;
---error ER_TABLE_NOT_LOCKED_FOR_WRITE
+--error ER_LOCK_OR_ACTIVE_TRANSACTION
create event e2 on schedule every 10 hour do select 1;
---error ER_TABLE_NOT_LOCKED_FOR_WRITE
+--error ER_LOCK_OR_ACTIVE_TRANSACTION
alter event e2 disable;
---error ER_TABLE_NOT_LOCKED_FOR_WRITE
+--error ER_LOCK_OR_ACTIVE_TRANSACTION
alter event e2 rename to e3;
---error ER_TABLE_NOT_LOCKED_FOR_WRITE
+--error ER_LOCK_OR_ACTIVE_TRANSACTION
drop event e2;
---error ER_TABLE_NOT_LOCKED_FOR_WRITE
+--error ER_LOCK_OR_ACTIVE_TRANSACTION
drop event e1;
unlock tables;
#
@@ -285,12 +285,18 @@ lock table mysql.event write;
--replace_regex /STARTS '[^']+'/STARTS '#'/
show create event e1;
select event_name from information_schema.events;
+--error ER_LOCK_OR_ACTIVE_TRANSACTION
create event e2 on schedule every 10 hour do select 1;
+--error ER_LOCK_OR_ACTIVE_TRANSACTION
alter event e2 disable;
+--error ER_LOCK_OR_ACTIVE_TRANSACTION
alter event e2 rename to e3;
+--error ER_LOCK_OR_ACTIVE_TRANSACTION
drop event e3;
+--error ER_LOCK_OR_ACTIVE_TRANSACTION
drop event e1;
unlock tables;
+drop event e1;
--echo Make sure we have left no events
select event_name from information_schema.events;
--echo
diff --git a/mysql-test/t/flush.test b/mysql-test/t/flush.test
index 1cafbe3e6bd..944c9c43019 100644
--- a/mysql-test/t/flush.test
+++ b/mysql-test/t/flush.test
@@ -577,3 +577,70 @@ select * from t1;
select * from t2;
unlock tables;
drop tables tm, t1, t2;
+
+
+--echo #
+--echo # Test for bug #57006 "Deadlock between HANDLER and
+--echo # FLUSH TABLES WITH READ LOCK".
+--echo #
+--disable_warnings
+drop table if exists t1, t2;
+--enable_warnings
+connect (con1,localhost,root,,);
+connect (con2,localhost,root,,);
+connection default;
+create table t1 (i int);
+create table t2 (i int);
+handler t1 open;
+
+--echo # Switching to connection 'con1'.
+connection con1;
+--echo # Sending:
+--send flush tables with read lock
+
+--echo # Switching to connection 'con2'.
+connection con2;
+--echo # Wait until FTWRL starts waiting for 't1' to be closed.
+let $wait_condition=
+ select count(*) = 1 from information_schema.processlist
+ where state = "Waiting for table flush"
+ and info = "flush tables with read lock";
+--source include/wait_condition.inc
+
+--echo # Switching to connection 'default'.
+connection default;
+--echo # The below statement should not cause deadlock.
+--echo # Sending:
+--send insert into t2 values (1)
+
+--echo # Switching to connection 'con2'.
+connection con2;
+--echo # Wait until INSERT starts to wait for FTWRL to go away.
+let $wait_condition=
+ select count(*) = 1 from information_schema.processlist
+ where state = "Waiting for global read lock"
+ and info = "insert into t2 values (1)";
+--source include/wait_condition.inc
+
+--echo # Switching to connection 'con1'.
+connection con1;
+--echo # FTWRL should be able to continue now.
+--echo # Reap FTWRL.
+--reap
+unlock tables;
+
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Reap INSERT.
+--reap
+handler t1 close;
+
+--echo # Cleanup.
+connection con1;
+disconnect con1;
+--source include/wait_until_disconnected.inc
+connection con2;
+disconnect con2;
+--source include/wait_until_disconnected.inc
+connection default;
+drop tables t1, t2;
diff --git a/mysql-test/t/flush_block_commit.test b/mysql-test/t/flush_block_commit.test
index 0b3bede1684..90443dc9242 100644
--- a/mysql-test/t/flush_block_commit.test
+++ b/mysql-test/t/flush_block_commit.test
@@ -39,7 +39,7 @@ connection con2;
--echo # Wait until COMMIT gets blocked.
let $wait_condition=
select count(*) = 1 from information_schema.processlist
- where state = "Waiting for release of readlock" and info = "COMMIT";
+ where state = "Waiting for commit lock" and info = "COMMIT";
--source include/wait_condition.inc
--echo # Verify that 'con1' was blocked and data did not move.
SELECT * FROM t1;
diff --git a/mysql-test/t/flush_block_commit_notembedded.test b/mysql-test/t/flush_block_commit_notembedded.test
index 7e3f4838ffb..fe9dbf7c19e 100644
--- a/mysql-test/t/flush_block_commit_notembedded.test
+++ b/mysql-test/t/flush_block_commit_notembedded.test
@@ -53,7 +53,7 @@ begin;
connection con1;
let $wait_condition=
select count(*) = 1 from information_schema.processlist
- where state = "Waiting for release of readlock" and
+ where state = "Waiting for global read lock" and
info = "insert into t1 values (1)";
--source include/wait_condition.inc
unlock tables;
diff --git a/mysql-test/t/flush_read_lock.test b/mysql-test/t/flush_read_lock.test
new file mode 100644
index 00000000000..b234d941e6e
--- /dev/null
+++ b/mysql-test/t/flush_read_lock.test
@@ -0,0 +1,2187 @@
+#
+# Test coverage for various aspects of FLUSH TABLES WITH READ LOCK
+# functionality.
+#
+
+# We need InnoDB for COMMIT/ROLLBACK related tests.
+--source include/have_innodb.inc
+# We need the Debug Sync Facility.
+--source include/have_debug_sync.inc
+# Save the initial number of concurrent sessions.
+--source include/count_sessions.inc
+
+--echo # FTWRL takes two global metadata locks -- a global shared
+--echo # metadata lock and the commit blocker lock.
+--echo # The first lock prevents DDL from taking place.
+--echo # Let's say that all DDL statements that take metadata
+--echo # locks form class #1 -- incompatible with FTWRL because
+--echo # take incompatible MDL table locks.
+--echo # The first global lock doesn't, however, prevent standalone
+--echo # COMMITs (or implicit COMMITs) from taking place, since a
+--echo # COMMIT doesn't take table locks. It doesn't prevent
+--echo # DDL on temporary tables either, since they don't
+--echo # take any table locks either.
+--echo # Most DDL statements do not perform an implicit commit
+--echo # if operate on a temporary table. Examples are CREATE
+--echo # TEMPORARY TABLE and DROP TEMPORARY TABLE.
+--echo # Thus, these DDL statements can go through in presence
+--echo # of FTWRL. This is class #2 -- compatible because
+--echo # do not take incompatible MDL locks and do not issue
+--echo # implicit commit..
+--echo # (Although these operations do not commit, their effects
+--echo # cannot be rolled back either.)
+--echo # ALTER TABLE, ANALYZE, OPTIMIZE and some others always
+--echo # issue an implicit commit, even if its argument is a
+--echo # temporary table.
+--echo # *Howewer* an implicit commit is a no-op if all engines
+--echo # used since the start of transactiona are non-
+--echo # transactional. Thus, for non-transactional engines,
+--echo # these operations are not blocked by FTWRL.
+--echo # This is class #3 -- compatible because do not take
+--echo # MDL table locks and are non-transactional.
+--echo # On the contrary, for transactional engines, there
+--echo # is always a commit, regardless of whether a table
+--echo # is temporary or not. Thus, for example, ALTER TABLE
+--echo # for a transactional engine will wait for FTWRL,
+--echo # even if the subject table is temporary.
+--echo # Thus ALTER TABLE <temporary> is incompatible
+--echo # with FTWRL. This is class #4 -- incompatible
+--echo # becuase issue implicit COMMIT which is not a no-op.
+--echo # Finally, there are administrative statements (such as
+--echo # RESET SLAVE) that do not take any locks and do not
+--echo # issue COMMIT.
+--echo # This is class #5.
+--echo # The goal of this coverage is to test statements
+--echo # of all classes.
+--echo # @todo: documents the effects of @@autocommit,
+--echo # DML and temporary transactional tables.
+
+--echo # Use MyISAM engine for the most of the tables
+--echo # used in this test in order to be able to
+--echo # check that DDL statements on temporary tables
+--echo # are compatible with FTRWL.
+--disable_warnings
+drop tables if exists t1_base, t2_base, t3_trans;
+drop tables if exists tm_base, tm_base_temp;
+drop database if exists mysqltest1;
+--echo # We're going to test ALTER DATABASE UPGRADE
+drop database if exists `#mysql50#mysqltest-2`;
+drop procedure if exists p1;
+drop function if exists f1;
+drop view if exists v1;
+drop procedure if exists p2;
+drop function if exists f2_base;
+drop function if exists f2_temp;
+drop event if exists e1;
+drop event if exists e2;
+--enable_warnings
+create table t1_base(i int) engine=myisam;
+create table t2_base(j int) engine=myisam;
+create table t3_trans(i int) engine=innodb;
+create temporary table t1_temp(i int) engine=myisam;
+create temporary table t2_temp(j int) engine=myisam;
+create temporary table t3_temp_trans(i int) engine=innodb;
+create database mysqltest1;
+create database `#mysql50#mysqltest-2`;
+create procedure p1() begin end;
+create function f1() returns int return 0;
+create view v1 as select 1 as i;
+create procedure p2(i int) begin end;
+delimiter |;
+create function f2_base() returns int
+begin
+ insert into t1_base values (1);
+ return 0;
+end|
+create function f2_temp() returns int
+begin
+ insert into t1_temp values (1);
+ return 0;
+end|
+delimiter ;|
+create event e1 on schedule every 1 minute do begin end;
+
+connect (con1,localhost,root,,);
+connect (con2,localhost,root,,);
+connect (con3,localhost,root,,);
+connection default;
+
+--echo #
+--echo # Test compatibility of FLUSH TABLES WITH READ LOCK
+--echo # with various statements.
+--echo #
+--echo # These tests don't cover some classes of statements:
+--echo # - Replication-related - CHANGE MASTER TO, START/STOP SLAVE and etc
+--echo # (all compatible with FTWRL).
+--echo # - Plugin-related - INSTALL/UNINSTALL (incompatible with FTWRL,
+--echo # require plugin support).
+
+let $con_aux1=con1;
+let $con_aux2=con2;
+let $cleanup_stmt2= ;
+let $skip_3rd_check= ;
+
+--echo #
+--echo # 1) ALTER variants.
+--echo #
+--echo # 1.1) ALTER TABLE
+--echo #
+--echo # 1.1.a) For base table should be incompatible with FTWRL.
+--echo #
+let $statement= alter table t1_base add column c1 int;
+let $cleanup_stmt1= alter table t1_base drop column c1;
+--source include/check_ftwrl_incompatible.inc
+
+--echo #
+--echo # 1.1.b) For a temporary table should be compatible with FTWRL.
+--echo #
+let $statement= alter table t1_temp add column c1 int;
+let $cleanup_stmt= alter table t1_temp drop column c1;
+--source include/check_ftwrl_compatible.inc
+
+--echo #
+--echo # 1.2) ALTER DATABASE should be incompatible with FTWRL.
+--echo #
+let $statement= alter database mysqltest1 default character set utf8;
+let $cleanup_stmt1= alter database mysqltest1 default character set latin1;
+--source include/check_ftwrl_incompatible.inc
+
+--echo #
+--echo # 1.3) ALTER DATABASE UPGRADE DATA DIRECTORY NAME should be
+--echo # incompatible with FTWRL.
+--echo #
+let $statement= alter database `#mysql50#mysqltest-2` upgrade data directory name;
+let $cleanup_stmt1= drop database `mysqltest-2`;
+let $cleanup_stmt2= create database `#mysql50#mysqltest-2`;
+--source include/check_ftwrl_incompatible.inc
+let $cleanup_stmt2= ;
+
+--echo #
+--echo # 1.4) ALTER PROCEDURE should be incompatible with FTWRL.
+--echo #
+let $statement= alter procedure p1 comment 'a';
+let $cleanup_stmt1= alter procedure p1 comment '';
+--source include/check_ftwrl_incompatible.inc
+
+--echo #
+--echo # 1.5) ALTER FUNCTION should be incompatible with FTWRL.
+--echo #
+let $statement= alter function f1 comment 'a';
+let $cleanup_stmt1= alter function f1 comment '';
+--source include/check_ftwrl_incompatible.inc
+
+--echo #
+--echo # 1.6) ALTER VIEW should be incompatible with FTWRL.
+--echo #
+let $statement= alter view v1 as select 2 as j;
+let $cleanup_stmt1= alter view v1 as select 1 as i;
+--source include/check_ftwrl_incompatible.inc
+
+--echo #
+--echo # 1.7) ALTER EVENT should be incompatible with FTWRL.
+--echo #
+let $statement= alter event e1 comment 'test';
+let $cleanup_stmt1= alter event e1 comment '';
+--source include/check_ftwrl_incompatible.inc
+
+--echo #
+--echo # 1.x) The rest of ALTER statements (ALTER TABLESPACE,
+--echo # ALTER LOGFILE GROUP and ALTER SERVER) are too
+--echo # special to be tested here.
+--echo #
+
+
+--echo #
+--echo # 2) ANALYZE TABLE statement is compatible with FTWRL.
+--echo # See Bug#43336 ANALYZE and OPTIMIZE do not honour
+--echo # --read-only for a discussion why.
+--echo #
+let $statement= analyze table t1_base;
+let $cleanup_stmt= ;
+--source include/check_ftwrl_compatible.inc
+
+--echo #
+--echo # 3) BEGIN, ROLLBACK and COMMIT statements.
+--echo # BEGIN and ROLLBACK are compatible with FTWRL.
+--echo # COMMIT is not.
+--echo #
+--echo # We need a special test for these statements as
+--echo # FTWRL commits a transaction and because COMMIT
+--echo # is handled in a special way.
+flush tables with read lock;
+begin;
+--echo # ROLLBACK is allowed under FTWRL although there
+--echo # no much sense in it. FTWRL commits any previous
+--echo # changes and doesn't allows any DML after it.
+--echo # So such a ROLLBACK is always a no-op.
+rollback;
+--echo # Although COMMIT is incompatible with FTWRL in
+--echo # other senses it is still allowed under FTWRL.
+--echo # This fact relied upon by some versions of
+--echo # innobackup tool.
+--echo # Similarly to ROLLBACK it is a no-op in this situation.
+commit;
+unlock tables;
+--echo # Check that BEGIN/ROLLBACK are not blocked and
+--echo # COMMIT is blocked by active FTWRL in another
+--echo # connection.
+--echo #
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+flush tables with read lock;
+--echo # Switching to connection 'default'.
+connection default;
+begin;
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Do some work so ROLLBACK is not a no-op.
+insert into t3_trans values (1);
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+flush tables with read lock;
+--echo # Switching to connection 'default'.
+connection default;
+rollback;
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+begin;
+--echo # Do some work so COMMIT is not a no-op.
+insert into t3_trans values (1);
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+flush tables with read lock;
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Send:
+--send commit
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+--echo # Wait until COMMIT is blocked.
+let $wait_condition=
+ select count(*) = 1 from information_schema.processlist
+ where state = "Waiting for commit lock" and
+ info = "commit";
+--source include/wait_condition.inc
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Reap COMMIT.
+--reap
+delete from t3_trans;
+--echo #
+--echo # Check that COMMIT blocks FTWRL in another connection.
+begin;
+insert into t3_trans values (1);
+set debug_sync='RESET';
+set debug_sync='ha_commit_trans_after_acquire_commit_lock SIGNAL parked WAIT_FOR go';
+--send commit
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+set debug_sync='now WAIT_FOR parked';
+--send flush tables with read lock
+--echo # Switching to connection '$con_aux2'.
+connection $con_aux2;
+--echo # Wait until FTWRL is blocked.
+let $wait_condition=
+ select count(*) = 1 from information_schema.processlist
+ where state = "Waiting for commit lock" and
+ info = "flush tables with read lock";
+--source include/wait_condition.inc
+set debug_sync='now SIGNAL go';
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Reap COMMIT.
+--reap
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+--echo # Reap FTWRL.
+--reap
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+delete from t3_trans;
+set debug_sync= "RESET";
+--echo # We don't run similar test for BEGIN and ROLLBACK as
+--echo # they release metadata locks in non-standard place.
+
+
+--echo #
+--echo # 4) BINLOG statement should be incompatible with FTWRL.
+--echo #
+--echo #
+--echo # Provide format description BINLOG statement first.
+BINLOG '
+MfmqTA8BAAAAZwAAAGsAAAABAAQANS41LjctbTMtZGVidWctbG9nAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAx+apMEzgNAAgAEgAEBAQEEgAAVAAEGggAAAAICAgCAA==
+';
+--echo # Now test compatibility for BINLOG statement which is
+--echo # equivalent to INSERT INTO t1_base VALUES (1).
+let $statement= BINLOG '
+MfmqTBMBAAAALgAAAN0AAAAAACgAAAAAAAEABHRlc3QAB3QxX2Jhc2UAAQMAAQ==
+MfmqTBcBAAAAIgAAAP8AAAAAACgAAAAAAAEAAf/+AQAAAA==
+';
+let $cleanup_stmt1= delete from t1_base where i = 1 limit 1;
+--echo # Skip last part of compatibility testing as this statement
+--echo # releases metadata locks in non-standard place.
+let $skip_3rd_check= 1;
+--source include/check_ftwrl_incompatible.inc
+let $skip_3rd_check= ;
+
+
+--echo #
+--echo # 5) CALL statement. This statement uses resources in two
+--echo # ways: through expressions used as parameters and through
+--echo # sub-statements. This test covers only usage through
+--echo # parameters as sub-statements do locking individually.
+--echo #
+--echo # 5.a) In simple cases a parameter expression should be
+--echo # compatible with FTWRL.
+let $statement= call p2((select count(*) from t1_base));
+let $cleanup_stmt1= ;
+--echo # Skip last part of compatibility testing as this statement
+--echo # releases metadata locks in non-standard place.
+let $skip_3rd_check= 1;
+--source include/check_ftwrl_compatible.inc
+let $skip_3rd_check= ;
+
+--echo #
+--echo # 5.b) In case when an expression uses function which updates
+--echo # base tables CALL should be incompatible with FTWRL.
+--echo #
+let $statement= call p2(f2_base());
+let $cleanup_stmt1= ;
+--echo # Skip last part of compatibility testing as this statement
+--echo # releases metadata locks in non-standard place.
+let $skip_3rd_check= 1;
+--source include/check_ftwrl_incompatible.inc
+let $skip_3rd_check= ;
+
+--echo #
+--echo # 5.c) If function used as argument updates temporary tables
+--echo # CALL statement should be compatible with FTWRL.
+--echo #
+let $statement= call p2(f2_temp());
+let $cleanup_stmt1= ;
+--echo # Skip last part of compatibility testing as this statement
+--echo # releases metadata locks in non-standard place.
+let $skip_3rd_check= 1;
+--source include/check_ftwrl_compatible.inc
+let $skip_3rd_check= ;
+
+
+--echo #
+--echo # 6) CHECK TABLE statement is compatible with FTWRL.
+--echo #
+let $statement= check table t1_base;
+let $cleanup_stmt= ;
+--source include/check_ftwrl_compatible.inc
+
+
+--echo #
+--echo # 7) CHECKSUM TABLE statement is compatible with FTWRL.
+--echo #
+let $statement= checksum table t1_base;
+let $cleanup_stmt= ;
+--source include/check_ftwrl_compatible.inc
+
+
+--echo #
+--echo # 8) CREATE variants.
+--echo #
+--echo # 8.1) CREATE TABLE statement.
+--echo #
+--echo # 8.1.a) CREATE TABLE is incompatible with FTWRL when
+--echo # base table is created.
+let $statement= create table t3_base(i int);
+let $cleanup_stmt1= drop table t3_base;
+--source include/check_ftwrl_incompatible.inc
+
+--echo # 8.1.b) CREATE TABLE is compatible with FTWRL when
+--echo # temporary table is created.
+let $statement= create temporary table t3_temp(i int);
+let $cleanup_stmt= drop temporary tables t3_temp;
+--source include/check_ftwrl_compatible.inc
+
+--echo # 8.1.c) CREATE TABLE LIKE is incompatible with FTWRL when
+--echo # base table is created.
+let $statement= create table t3_base like t1_temp;
+let $cleanup_stmt1= drop table t3_base;
+--source include/check_ftwrl_incompatible.inc
+
+--echo # 8.1.d) CREATE TABLE LIKE is compatible with FTWRL when
+--echo # temporary table is created.
+let $statement= create temporary table t3_temp like t1_base;
+let $cleanup_stmt= drop temporary table t3_temp;
+--source include/check_ftwrl_compatible.inc
+
+--echo # 8.1.e) CREATE TABLE SELECT is incompatible with FTWRL when
+--echo # base table is created.
+let $statement= create table t3_base select 1 as i;
+let $cleanup_stmt1= drop table t3_base;
+--source include/check_ftwrl_incompatible.inc
+
+--echo # 8.1.f) CREATE TABLE SELECT is compatible with FTWRL when
+--echo # temporary table is created.
+let $statement= create temporary table t3_temp select 1 as i;
+let $cleanup_stmt= drop temporary table t3_temp;
+--source include/check_ftwrl_compatible.inc
+
+--echo # 8.2) CREATE INDEX statement.
+--echo #
+--echo # 8.2.a) CREATE INDEX is incompatible with FTWRL when
+--echo # applied to base table.
+let $statement= create index i on t1_base (i);
+let $cleanup_stmt1= drop index i on t1_base;
+--source include/check_ftwrl_incompatible.inc
+
+--echo # 8.2.b) CREATE INDEX is compatible with FTWRL when
+--echo # applied to temporary table.
+let $statement= create index i on t1_temp (i);
+let $cleanup_stmt= drop index i on t1_temp;
+--source include/check_ftwrl_compatible.inc
+
+--echo #
+--echo # 8.3) CREATE DATABASE is incompatible with FTWRL.
+--echo #
+let $statement= create database mysqltest2;
+let $cleanup_stmt1= drop database mysqltest2;
+--source include/check_ftwrl_incompatible.inc
+
+--echo #
+--echo # 8.4) CREATE VIEW is incompatible with FTWRL.
+--echo #
+let $statement= create view v2 as select 1 as j;
+let $cleanup_stmt1= drop view v2;
+--source include/check_ftwrl_incompatible.inc
+
+--echo #
+--echo # 8.5) CREATE TRIGGER is incompatible with FTWRL.
+--echo #
+let $statement= create trigger t1_bi before insert on t1_base for each row begin end;
+let $cleanup_stmt1= drop trigger t1_bi;
+--source include/check_ftwrl_incompatible.inc
+
+--echo #
+--echo # 8.6) CREATE FUNCTION is incompatible with FTWRL.
+--echo #
+let $statement= create function f2() returns int return 0;
+let $cleanup_stmt1= drop function f2;
+--source include/check_ftwrl_incompatible.inc
+
+--echo #
+--echo # 8.7) CREATE PROCEDURE is incompatible with FTWRL.
+--echo #
+let $statement= create procedure p3() begin end;
+let $cleanup_stmt1= drop procedure p3;
+--source include/check_ftwrl_incompatible.inc
+
+--echo #
+--echo # 8.8) CREATE EVENT should be incompatible with FTWRL.
+--echo #
+let $statement= create event e2 on schedule every 1 minute do begin end;
+let $cleanup_stmt1= drop event e2;
+--source include/check_ftwrl_incompatible.inc
+
+--echo #
+--echo # 8.9) CREATE USER should be incompatible with FTWRL.
+--echo #
+let $statement= create user mysqltest_u1;
+let $cleanup_stmt1= drop user mysqltest_u1;
+--source include/check_ftwrl_incompatible.inc
+
+--echo #
+--echo # 8.x) The rest of CREATE variants (CREATE LOGFILE GROUP,
+--echo # CREATE TABLESPACE and CREATE SERVER) are too special
+--echo # to test here.
+--echo #
+
+
+--echo #
+--echo # 9) PREPARE, EXECUTE and DEALLOCATE PREPARE statements.
+--echo #
+--echo # 9.1) PREPARE statement is compatible with FTWRL as it
+--echo # doesn't change any data.
+--echo #
+--echo # 9.1.a) Prepare of simple INSERT statement.
+--echo #
+let $statement= prepare stmt1 from 'insert into t1_base values (1)';
+let $cleanup_stmt= deallocate prepare stmt1;
+--echo # Skip last part of compatibility testing as this statement
+--echo # releases metadata locks in non-standard place.
+let $skip_3rd_check= 1;
+--source include/check_ftwrl_compatible.inc
+let $skip_3rd_check= ;
+
+--echo #
+--echo # 9.1.b) Prepare of multi-UPDATE. At some point such statements
+--echo # tried to acquire thr_lock.c locks during prepare phase.
+--echo # This no longer happens and thus it is compatible with
+--echo # FTWRL.
+let $statement= prepare stmt1 from 'update t1_base, t2_base set t1_base.i= 1 where t1_base.i = t2_base.j';
+let $cleanup_stmt= deallocate prepare stmt1;
+--echo # Skip last part of compatibility testing as this statement
+--echo # releases metadata locks in non-standard place.
+let $skip_3rd_check= 1;
+--source include/check_ftwrl_compatible.inc
+let $skip_3rd_check= ;
+
+--echo #
+--echo # 9.1.c) Prepare of multi-DELETE. Again PREPARE of such
+--echo # statement should be compatible with FTWRL.
+let $statement= prepare stmt1 from 'delete t1_base from t1_base, t2_base where t1_base.i = t2_base.j';
+let $cleanup_stmt= deallocate prepare stmt1;
+--echo # Skip last part of compatibility testing as this statement
+--echo # releases metadata locks in non-standard place.
+let $skip_3rd_check= 1;
+--source include/check_ftwrl_compatible.inc
+let $skip_3rd_check= ;
+
+--echo #
+--echo # 9.2) Compatibility of EXECUTE statement depends on statement
+--echo # to be executed.
+--echo #
+--echo # 9.2.a) EXECUTE for statement which is itself compatible with
+--echo # FTWRL should be compatible.
+prepare stmt1 from 'select * from t1_base';
+let $statement= execute stmt1;
+let $cleanup_stmt= ;
+--source include/check_ftwrl_compatible.inc
+deallocate prepare stmt1;
+
+--echo #
+--echo # 9.2.b) EXECUTE for statement which is incompatible with FTWRL
+--echo # should be also incompatible.
+--echo #
+--echo # Check that EXECUTE is not allowed under FTWRL.
+prepare stmt1 from 'insert into t1_base values (1)';
+flush tables with read lock;
+--error ER_CANT_UPDATE_WITH_READLOCK
+execute stmt1;
+unlock tables;
+--echo # Check that active FTWRL in another connection
+--echo # blocks EXECUTE which changes data.
+--echo #
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+flush tables with read lock;
+--echo # Switching to connection 'default'.
+connection default;
+--send execute stmt1
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+--echo # Check that EXECUTE is blocked.
+let $wait_condition=
+ select count(*) = 1 from information_schema.processlist
+ where state = "Waiting for global read lock" and
+ info = "insert into t1_base values (1)";
+--source include/wait_condition.inc
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Reap EXECUTE.
+--reap
+set debug_sync='RESET';
+set debug_sync='execute_command_after_close_tables SIGNAL parked WAIT_FOR go';
+--send execute stmt1;
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+set debug_sync='now WAIT_FOR parked';
+--send flush tables with read lock
+--echo # Switching to connection '$con_aux2'.
+connection $con_aux2;
+--echo # Wait until FTWRL is blocked.
+let $wait_condition=
+ select count(*) = 1 from information_schema.processlist
+ where state = "Waiting for global read lock" and
+ info = "flush tables with read lock";
+--source include/wait_condition.inc
+set debug_sync='now SIGNAL go';
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Reap EXECUTE.
+--reap
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+--echo # Reap FTWRL.
+--reap
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+set debug_sync= "RESET";
+delete from t1_base;
+deallocate prepare stmt1;
+
+--echo #
+--echo # 9.3) DEALLOCATE PREPARE is compatible with FTWRL.
+--echo #
+prepare stmt1 from 'insert into t1_base values (1)';
+let $statement= deallocate prepare stmt1;
+let $cleanup_stmt= prepare stmt1 from 'insert into t1_base values (1)';
+--source include/check_ftwrl_compatible.inc
+deallocate prepare stmt1;
+
+
+--echo #
+--echo # 10) DELETE variations.
+--echo #
+--echo # 10.1) Simple DELETE.
+--echo #
+--echo # 10.1.a) Simple DELETE on base table is incompatible with FTWRL.
+let $statement= delete from t1_base;
+let $cleanup_stmt1= ;
+--source include/check_ftwrl_incompatible.inc
+
+--echo #
+--echo # 10.1.b) Simple DELETE on temporary table is compatible with FTWRL.
+let $statement= delete from t1_temp;
+let $cleanup_stmt= ;
+--source include/check_ftwrl_compatible.inc
+
+--echo #
+--echo # 10.2) Multi DELETE.
+--echo #
+--echo # 10.2.a) Multi DELETE on base tables is incompatible with FTWRL.
+let $statement= delete t1_base from t1_base, t2_base where t1_base.i = t2_base.j;
+let $cleanup_stmt1= ;
+--source include/check_ftwrl_incompatible.inc
+
+--echo #
+--echo # 10.2.b) Multi DELETE on temporary tables is compatible with FTWRL.
+let $statement= delete t1_temp from t1_temp, t2_temp where t1_temp.i = t2_temp.j;
+let $cleanup_stmt= ;
+--source include/check_ftwrl_compatible.inc
+
+
+--echo #
+--echo # 11) DESCRIBE should be compatible with FTWRL.
+--echo #
+let $statement= describe t1_base;
+let $cleanup_stmt= ;
+--source include/check_ftwrl_compatible.inc
+
+
+--echo #
+--echo # 12) Compatibility of DO statement with FTWRL depends on its
+--echo # expression.
+--echo #
+--echo # 12.a) DO with expression which does not change base table
+--echo # should be compatible with FTWRL.
+let $statement= do (select count(*) from t1_base);
+let $cleanup_stmt= ;
+--source include/check_ftwrl_compatible.inc
+
+--echo #
+--echo # 12.b) DO which calls SF updating base table should be
+--echo # incompatible with FTWRL.
+let $statement= do f2_base();
+let $cleanup_stmt1= delete from t1_base limit 1;
+--source include/check_ftwrl_incompatible.inc
+
+--echo #
+--echo # 12.c) DO which calls SF updating temporary table should be
+--echo # compatible with FTWRL.
+let $statement= do f2_temp();
+let $cleanup_stmt= delete from t1_temp limit 1;
+--source include/check_ftwrl_compatible.inc
+
+
+--echo #
+--echo # 13) DROP variants.
+--echo #
+--echo # 13.1) DROP TABLES.
+--echo #
+--echo # 13.1.a) DROP TABLES which affects base tables is incompatible
+--echo # with FTWRL.
+let $statement= drop table t2_base;
+let $cleanup_stmt1= create table t2_base(j int);
+--source include/check_ftwrl_incompatible.inc
+
+--echo # 13.1.b) DROP TABLES which affects only temporary tables
+--echo # in theory can be compatible with FTWRL.
+--echo # In practice it is not yet.
+let $statement= drop table t2_temp;
+let $cleanup_stmt1= create temporary table t2_temp(j int);
+--source include/check_ftwrl_incompatible.inc
+
+--echo #
+--echo # 13.1.c) DROP TEMPORARY TABLES should be compatible with FTWRL.
+let $statement= drop temporary table t2_temp;
+let $cleanup_stmt= create temporary table t2_temp(j int);
+--source include/check_ftwrl_compatible.inc
+
+--echo #
+--echo # 13.2) DROP INDEX.
+--echo #
+--echo # 13.2.a) DROP INDEX on a base table is incompatible with FTWRL.
+create index i on t1_base (i);
+let $statement= drop index i on t1_base;
+let $cleanup_stmt1= create index i on t1_base (i);
+--source include/check_ftwrl_incompatible.inc
+drop index i on t1_base;
+
+--echo #
+--echo # 13.2.b) DROP INDEX on a temporary table is compatible with FTWRL.
+create index i on t1_temp (i);
+let $statement= drop index i on t1_temp;
+let $cleanup_stmt= create index i on t1_temp (i);
+--source include/check_ftwrl_compatible.inc
+drop index i on t1_temp;
+
+--echo #
+--echo # 13.3) DROP DATABASE is incompatible with FTWRL
+--echo #
+let $statement= drop database mysqltest1;
+let $cleanup_stmt1= create database mysqltest1;
+--source include/check_ftwrl_incompatible.inc
+
+--echo #
+--echo # 13.4) DROP FUNCTION is incompatible with FTWRL.
+--echo #
+let $statement= drop function f1;
+let $cleanup_stmt1= create function f1() returns int return 0;
+--source include/check_ftwrl_incompatible.inc
+
+--echo #
+--echo # 13.5) DROP PROCEDURE is incompatible with FTWRL.
+--echo #
+let $statement= drop procedure p1;
+let $cleanup_stmt1= create procedure p1() begin end;
+--source include/check_ftwrl_incompatible.inc
+
+--echo #
+--echo # 13.6) DROP USER should be incompatible with FTWRL.
+--echo #
+create user mysqltest_u1;
+let $statement= drop user mysqltest_u1;
+let $cleanup_stmt1= create user mysqltest_u1;
+--source include/check_ftwrl_incompatible.inc
+drop user mysqltest_u1;
+
+--echo #
+--echo # 13.7) DROP VIEW should be incompatible with FTWRL.
+--echo #
+let $statement= drop view v1;
+let $cleanup_stmt1= create view v1 as select 1 as i;
+--source include/check_ftwrl_incompatible.inc
+
+--echo #
+--echo # 13.8) DROP EVENT should be incompatible with FTWRL.
+--echo #
+let $statement= drop event e1;
+let $cleanup_stmt1= create event e1 on schedule every 1 minute do begin end;
+--source include/check_ftwrl_incompatible.inc
+
+--echo #
+--echo # 13.9) DROP TRIGGER is incompatible with FTWRL.
+--echo #
+create trigger t1_bi before insert on t1_base for each row begin end;
+let $statement= drop trigger t1_bi;
+let $cleanup_stmt1= create trigger t1_bi before insert on t1_base for each row begin end;
+--source include/check_ftwrl_incompatible.inc
+drop trigger t1_bi;
+
+--echo #
+--echo # 13.x) The rest of DROP variants (DROP TABLESPACE, DROP LOGFILE
+--echo # GROUP and DROP SERVER) are too special to test here.
+--echo #
+
+
+--echo #
+--echo # 14) FLUSH variants.
+--echo #
+--echo # Test compatibility of _some_ important FLUSH variants with FTWRL.
+--echo #
+--echo # 14.1) FLUSH TABLES WITH READ LOCK is compatible with itself.
+--echo #
+--echo # Check that FTWRL statements can be run while FTWRL
+--echo # is active in another connection.
+--echo #
+--echo # Switching to connection '$con_aux1'.
+flush tables with read lock;
+--echo # The second FTWRL in a row is allowed at the moment.
+--echo # It does not make much sense as it does only flush.
+flush tables with read lock;
+unlock tables;
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+flush tables with read lock;
+--echo # Switching to connection 'default'.
+connection default;
+flush tables with read lock;
+unlock tables;
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+
+--echo #
+--echo # 14.2) FLUSH TABLES <list> WITH READ LOCK is not blocked by
+--echo # active FTWRL. But since the latter keeps tables open
+--echo # FTWRL is blocked by FLUSH TABLES <list> WITH READ LOCK.
+flush tables with read lock;
+--echo # FT <list> WRL is allowed under FTWRL at the moment.
+--echo # It does not make much sense though.
+flush tables t1_base, t2_base with read lock;
+unlock tables;
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+flush tables with read lock;
+--echo # Switching to connection 'default'.
+connection default;
+flush tables t1_base, t2_base with read lock;
+unlock tables;
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+flush tables t1_base, t2_base with read lock;
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+--send flush tables with read lock
+--echo # Switching to connection '$con_aux2'.
+connection $con_aux2;
+--echo # Wait until FTWRL is blocked.
+let $wait_condition=
+ select count(*) = 1 from information_schema.processlist
+ where state = "Waiting for table flush" and
+ info = "flush tables with read lock";
+--source include/wait_condition.inc
+--echo # Switching to connection 'default'.
+connection default;
+unlock tables;
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+--echo # Reap FTWRL.
+--reap
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+
+
+--echo #
+--echo # 14.3) FLUSH TABLES is compatible with FTWRL.
+let $statement= flush tables;
+let $cleanup_stmt= ;
+--source include/check_ftwrl_compatible.inc
+
+--echo #
+--echo # 14.4) FLUSH TABLES <list> is compatible with FTWRL.
+let $statement= flush table t1_base, t2_base;
+let $cleanup_stmt= ;
+--source include/check_ftwrl_compatible.inc
+
+--echo #
+--echo # 14.5) FLUSH PRIVILEGES is compatible with FTWRL.
+let $statement= flush privileges;
+let $cleanup_stmt= ;
+--source include/check_ftwrl_compatible.inc
+
+
+--echo #
+--echo # 15) GRANT statement should be incompatible with FTWRL.
+--echo #
+let $statement= grant all privileges on t1_base to mysqltest_u1;
+let $cleanup_stmt1= revoke all privileges on t1_base from mysqltest_u1;
+--source include/check_ftwrl_incompatible.inc
+drop user mysqltest_u1;
+
+
+--echo #
+--echo # 16) All HANDLER variants are half-compatible with FTWRL.
+--echo # I.e. they are not blocked by active FTWRL. But since open
+--echo # HANDLER means open table instance FTWRL is blocked while
+--echo # HANDLER is not closed.
+--echo #
+--echo # Check that HANDLER statements succeed under FTWRL.
+flush tables with read lock;
+handler t1_base open;
+handler t1_base read first;
+handler t1_base close;
+unlock tables;
+--echo # Check that HANDLER statements can be run while FTWRL
+--echo # is active in another connection.
+--echo #
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+flush tables with read lock;
+--echo # Switching to connection 'default'.
+connection default;
+handler t1_base open;
+handler t1_base read first;
+handler t1_base close;
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+
+
+--echo #
+--echo # 17) HELP statement is compatible with FTWRL.
+--echo #
+let $statement= help no_such_topic;
+let $cleanup_stmt= ;
+--source include/check_ftwrl_compatible.inc
+
+
+--echo #
+--echo # 18) INSERT statement.
+--echo #
+--echo # 18.a) Ordinary INSERT into base table is incompatible with FTWRL.
+let $statement= insert into t1_base values (1);
+let $cleanup_stmt1= delete from t1_base limit 1;
+--source include/check_ftwrl_incompatible.inc
+
+--echo #
+--echo # 18.b) Ordinary INSERT into temp table is compatible with FTWRL.
+let $statement= insert into t1_temp values (1);
+let $cleanup_stmt= delete from t1_temp limit 1;
+--source include/check_ftwrl_compatible.inc
+
+--echo #
+--echo # 18.c) INSERT DELAYED is incompatible with FTWRL.
+let $statement= insert delayed into t1_base values (1);
+let $cleanup_stmt1= ;
+--source include/check_ftwrl_incompatible.inc
+delete from t1_base;
+
+--echo #
+--echo # 18.d) INSERT SELECT into base table is incompatible with FTWRL.
+let $statement= insert into t1_base select * from t1_temp;
+let $cleanup_stmt1= ;
+--source include/check_ftwrl_incompatible.inc
+
+--echo #
+--echo # 18.e) INSERT SELECT into temp table is compatible with FTWRL.
+let $statement= insert into t1_temp select * from t1_base;
+let $cleanup_stmt= ;
+--source include/check_ftwrl_compatible.inc
+
+
+--echo #
+--echo # 19) KILL statement is compatible with FTWRL.
+--echo #
+--echo # Check that KILL can be run under FTWRL.
+flush tables with read lock;
+set @id:= connection_id();
+--error ER_QUERY_INTERRUPTED
+kill query @id;
+unlock tables;
+--echo # Check that KILL statements can be run while FTWRL
+--echo # is active in another connection.
+--echo #
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+flush tables with read lock;
+--echo # Switching to connection 'default'.
+connection default;
+--error ER_QUERY_INTERRUPTED
+kill query @id;
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Finally check that KILL doesn't block FTWRL
+set debug_sync='RESET';
+set debug_sync='execute_command_after_close_tables SIGNAL parked WAIT_FOR go';
+--send kill query @id
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+set debug_sync='now WAIT_FOR parked';
+flush tables with read lock;
+unlock tables;
+set debug_sync='now SIGNAL go';
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Reap KILL.
+--error ER_QUERY_INTERRUPTED
+--reap
+set debug_sync='RESET';
+
+
+--echo #
+--echo # 20) LOAD DATA statement.
+--echo #
+--echo # 20.a) LOAD DATA into base table is incompatible with FTWRL.
+let $statement= load data infile '../../std_data/rpl_loaddata.dat' into table t1_base (@dummy, i);
+let $cleanup_stmt1= delete from t1_base;
+--source include/check_ftwrl_incompatible.inc
+
+--echo #
+--echo # 20.b) LOAD DATA into temporary table is compatible with FTWRL.
+let $statement= load data infile '../../std_data/rpl_loaddata.dat' into table t1_temp (@dummy, i);
+let $cleanup_stmt= delete from t1_temp;
+--source include/check_ftwrl_compatible.inc
+
+
+--echo #
+--echo # 21) LOCK/UNLOCK TABLES statements.
+--echo #
+--echo # LOCK TABLES statement always (almost) blocks FTWRL as it
+--echo # keeps tables open until UNLOCK TABLES.
+--echo # Active FTWRL on the other hand blocks only those
+--echo # LOCK TABLES which allow updating of base tables.
+--echo #
+--echo # 21.a) LOCK TABLES READ is allowed under FTWRL and
+--echo # is not blocked by active FTWRL.
+flush tables with read lock;
+lock tables t1_base read;
+unlock tables;
+--echo #
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+flush tables with read lock;
+--echo # Switching to connection 'default'.
+connection default;
+lock tables t1_base read;
+unlock tables;
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+
+--echo #
+--echo # 21.b) LOCK TABLES WRITE on a base table is disallowed
+--echo # under FTWRL and should be blocked by active FTWRL.
+flush tables with read lock;
+--error ER_CANT_UPDATE_WITH_READLOCK
+lock tables t1_base write;
+unlock tables;
+--echo #
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+flush tables with read lock;
+--echo # Switching to connection 'default'.
+connection default;
+--send lock tables t1_base write
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+--echo # Check that LOCK TABLES WRITE is blocked.
+let $wait_condition=
+ select count(*) = 1 from information_schema.processlist
+ where state = "Waiting for global read lock" and
+ info = "lock tables t1_base write";
+--source include/wait_condition.inc
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Reap LOCK TABLES WRITE
+--reap
+unlock tables;
+
+--echo #
+--echo # 21.c) LOCK TABLES WRITE on temporary table doesn't
+--echo # make much sense but is allowed under FTWRL
+--echo # and should not be blocked by active FTWRL.
+flush tables with read lock;
+lock tables t1_temp write;
+unlock tables;
+--echo #
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+flush tables with read lock;
+--echo # Switching to connection 'default'.
+connection default;
+lock tables t1_temp write;
+unlock tables;
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+
+
+--echo #
+--echo # 22) OPTIMIZE TABLE statement.
+--echo #
+--echo # 22.a) OPTIMIZE TABLE of base table is incompatible with FTWRL.
+flush tables with read lock;
+--echo # OPTIMIZE statement returns errors as part of result-set.
+optimize table t1_base;
+unlock tables;
+--echo #
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+flush tables with read lock;
+--echo # Switching to connection 'default'.
+connection default;
+--send optimize table t1_base
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+--echo # Check that OPTIMIZE TABLE is blocked.
+let $wait_condition=
+ select count(*) = 1 from information_schema.processlist
+ where state = "Waiting for global read lock" and
+ info = "optimize table t1_base";
+--source include/wait_condition.inc
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Reap OPTIMIZE TABLE
+--reap
+--echo # We don't check that active OPTIMIZE TABLE blocks
+--echo # FTWRL as this one of statements releasing metadata
+--echo # locks in non-standard place.
+
+--echo #
+--echo # 22.b) OPTIMIZE TABLE of temporary table is compatible with FTWRL.
+let $statement= optimize table t1_temp;
+let $cleanup_stmt= ;
+--echo # Skip last part of compatibility testing as this statement
+--echo # releases metadata locks in non-standard place.
+let $skip_3rd_check= 1;
+--source include/check_ftwrl_compatible.inc
+let $skip_3rd_check= ;
+
+
+--echo #
+--echo # 23) CACHE statement is compatible with FTWRL.
+--echo #
+let $statement= cache index t1_base in default;
+let $cleanup_stmt= ;
+--echo # Skip last part of compatibility testing as this statement
+--echo # releases metadata locks in non-standard place.
+let $skip_3rd_check= 1;
+--source include/check_ftwrl_compatible.inc
+let $skip_3rd_check= ;
+
+
+--echo #
+--echo # 24) LOAD INDEX statement is compatible with FTWRL.
+--echo #
+let $statement= load index into cache t1_base;
+let $cleanup_stmt= ;
+--echo # Skip last part of compatibility testing as this statement
+--echo # releases metadata locks in non-standard place.
+let $skip_3rd_check= 1;
+--source include/check_ftwrl_compatible.inc
+let $skip_3rd_check= ;
+
+
+--echo #
+--echo # 25) SAVEPOINT/RELEASE SAVEPOINT/ROLLBACK TO SAVEPOINT are
+--echo # compatible with FTWRL.
+--echo #
+--echo # Since manipulations on savepoint have to be done
+--echo # inside transaction and FTWRL commits transaction we
+--echo # need a special test for these statements.
+flush tables with read lock;
+begin;
+savepoint sv1;
+rollback to savepoint sv1;
+release savepoint sv1;
+unlock tables;
+commit;
+--echo # Check that these statements are not blocked by
+--echo # active FTWRL in another connection.
+--echo #
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+flush tables with read lock;
+--echo # Switching to connection 'default'.
+connection default;
+begin;
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Do some changes to avoid SAVEPOINT and friends
+--echo # being almost no-ops.
+insert into t3_trans values (1);
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+flush tables with read lock;
+--echo # Switching to connection 'default'.
+connection default;
+savepoint sv1;
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+insert into t3_trans values (2);
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+flush tables with read lock;
+--echo # Switching to connection 'default'.
+connection default;
+rollback to savepoint sv1;
+release savepoint sv1;
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+rollback;
+--echo # Check that these statements don't block FTWRL in
+--echo # another connection.
+begin;
+--echo # Do some changes to avoid SAVEPOINT and friends
+--echo # being almost no-ops.
+insert into t3_trans values (1);
+set debug_sync='RESET';
+set debug_sync='execute_command_after_close_tables SIGNAL parked WAIT_FOR go';
+--send savepoint sv1
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+set debug_sync='now WAIT_FOR parked';
+flush tables with read lock;
+unlock tables;
+set debug_sync='now SIGNAL go';
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Reap SAVEPOINT
+--reap
+insert into t3_trans values (2);
+set debug_sync='execute_command_after_close_tables SIGNAL parked WAIT_FOR go';
+--send rollback to savepoint sv1
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+set debug_sync='now WAIT_FOR parked';
+flush tables with read lock;
+unlock tables;
+set debug_sync='now SIGNAL go';
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Reap ROLLBACK TO SAVEPOINT
+--reap
+set debug_sync='execute_command_after_close_tables SIGNAL parked WAIT_FOR go';
+--send release savepoint sv1
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+set debug_sync='now WAIT_FOR parked';
+flush tables with read lock;
+unlock tables;
+set debug_sync='now SIGNAL go';
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Reap RELEASE SAVEPOINT
+--reap
+rollback;
+set debug_sync= "RESET";
+
+
+--echo #
+--echo # 26) RENAME variants.
+--echo #
+--echo # 26.1) RENAME TABLES is incompatible with FTWRL.
+let $statement= rename table t1_base to t3_base;
+let $cleanup_stmt1= rename table t3_base to t1_base;
+--source include/check_ftwrl_incompatible.inc
+
+--echo #
+--echo # 26.2) RENAME USER is incompatible with FTWRL.
+create user mysqltest_u1;
+let $statement= rename user mysqltest_u1 to mysqltest_u2;
+let $cleanup_stmt1= rename user mysqltest_u2 to mysqltest_u1;
+--source include/check_ftwrl_incompatible.inc
+drop user mysqltest_u1;
+
+
+--echo #
+--echo # 27) REPAIR TABLE statement.
+--echo #
+--echo # 27.a) REPAIR TABLE of base table is incompatible with FTWRL.
+flush tables with read lock;
+--echo # REPAIR statement returns errors as part of result-set.
+repair table t1_base;
+unlock tables;
+--echo #
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+flush tables with read lock;
+--echo # Switching to connection 'default'.
+connection default;
+--send repair table t1_base
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+--echo # Check that REPAIR TABLE is blocked.
+let $wait_condition=
+ select count(*) = 1 from information_schema.processlist
+ where state = "Waiting for global read lock" and
+ info = "repair table t1_base";
+--source include/wait_condition.inc
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Reap REPAIR TABLE
+--reap
+--echo # We don't check that active REPAIR TABLE blocks
+--echo # FTWRL as this one of statements releasing metadata
+--echo # locks in non-standard place.
+
+--echo #
+--echo # 27.b) REPAIR TABLE of temporary table is compatible with FTWRL.
+let $statement= repair table t1_temp;
+let $cleanup_stmt= ;
+--echo # Skip last part of compatibility testing as this statement
+--echo # releases metadata locks in non-standard place.
+let $skip_3rd_check= 1;
+--source include/check_ftwrl_compatible.inc
+let $skip_3rd_check= ;
+
+
+--echo #
+--echo # 28) REPLACE statement.
+--echo #
+--echo # 28.a) Ordinary REPLACE into base table is incompatible with FTWRL.
+let $statement= replace into t1_base values (1);
+let $cleanup_stmt1= delete from t1_base limit 1;
+--source include/check_ftwrl_incompatible.inc
+
+--echo #
+--echo # 28.b) Ordinary REPLACE into temp table is compatible with FTWRL.
+let $statement= replace into t1_temp values (1);
+let $cleanup_stmt= delete from t1_temp limit 1;
+--source include/check_ftwrl_compatible.inc
+
+--echo #
+--echo # 28.c) REPLACE SELECT into base table is incompatible with FTWRL.
+let $statement= replace into t1_base select * from t1_temp;
+let $cleanup_stmt1= ;
+--source include/check_ftwrl_incompatible.inc
+
+--echo #
+--echo # 28.d) REPLACE SELECT into temp table is compatible with FTWRL.
+let $statement= replace into t1_temp select * from t1_base;
+let $cleanup_stmt= ;
+--source include/check_ftwrl_compatible.inc
+
+
+--echo #
+--echo # 29) REVOKE variants.
+--echo #
+--echo # 29.1) REVOKE privileges is incompatible with FTWRL.
+grant all privileges on t1_base to mysqltest_u1;
+let $statement= revoke all privileges on t1_base from mysqltest_u1;
+let $cleanup_stmt1= grant all privileges on t1_base to mysqltest_u1;
+--source include/check_ftwrl_incompatible.inc
+
+--echo #
+--echo # 29.2) REVOKE ALL PRIVILEGES, GRANT OPTION is incompatible with FTWRL.
+let $statement= revoke all privileges, grant option from mysqltest_u1;
+let $cleanup_stmt1= grant all privileges on t1_base to mysqltest_u1;
+--source include/check_ftwrl_incompatible.inc
+drop user mysqltest_u1;
+
+
+--echo #
+--echo # 30) Compatibility of SELECT statement with FTWRL depends on
+--echo # locking mode used and on functions being invoked by it.
+--echo #
+--echo # 30.a) Simple SELECT which does not change tables should be
+--echo # compatible with FTWRL.
+let $statement= select count(*) from t1_base;
+let $cleanup_stmt= ;
+--source include/check_ftwrl_compatible.inc
+
+--echo # 30.b) SELECT ... FOR UPDATE is incompatible with FTWRL.
+let $statement= select count(*) from t1_base for update;
+let $cleanup_stmt1= ;
+--source include/check_ftwrl_incompatible.inc
+
+--echo # 30.c) SELECT ... LOCK IN SHARE MODE is compatible with FTWRL.
+let $statement= select count(*) from t1_base lock in share mode;
+let $cleanup_stmt= ;
+--source include/check_ftwrl_compatible.inc
+
+--echo #
+--echo # 30.d) SELECT which calls SF updating base table should be
+--echo # incompatible with FTWRL.
+let $statement= select f2_base();
+let $cleanup_stmt1= delete from t1_base limit 1;
+--source include/check_ftwrl_incompatible.inc
+
+--echo #
+--echo # 30.e) SELECT which calls SF updating temporary table should be
+--echo # compatible with FTWRL.
+let $statement= select f2_temp();
+let $cleanup_stmt= delete from t1_temp limit 1;
+--source include/check_ftwrl_compatible.inc
+
+
+--echo #
+--echo # 31) Compatibility of SET statement with FTWRL depends on its
+--echo # expression and on whether it is a special SET statement.
+--echo #
+--echo # 31.a) Ordinary SET with expression which does not
+--echo # changes base table should be compatible with FTWRL.
+let $statement= set @a:= (select count(*) from t1_base);
+let $cleanup_stmt= ;
+--echo # Skip last part of compatibility testing as our helper debug
+--echo # sync-point doesn't work for SET statements.
+let $skip_3rd_check= 1;
+--source include/check_ftwrl_compatible.inc
+let $skip_3rd_check= ;
+
+--echo #
+--echo # 31.b) Ordinary SET which calls SF updating base table should
+--echo # be incompatible with FTWRL.
+let $statement= set @a:= f2_base();
+let $cleanup_stmt1= delete from t1_base limit 1;
+--echo # Skip last part of compatibility testing as our helper debug
+--echo # sync-point doesn't work for SET statements.
+let $skip_3rd_check= 1;
+--source include/check_ftwrl_incompatible.inc
+let $skip_3rd_check= ;
+
+--echo #
+--echo # 31.c) Ordinary SET which calls SF updating temporary table
+--echo # should be compatible with FTWRL.
+let $statement= set @a:= f2_temp();
+let $cleanup_stmt= delete from t1_temp limit 1;
+--echo # Skip last part of compatibility testing as our helper debug
+--echo # sync-point doesn't work for SET statements.
+let $skip_3rd_check= 1;
+--source include/check_ftwrl_compatible.inc
+let $skip_3rd_check= ;
+
+--echo #
+--echo # 31.d) Special SET variants have different compatibility with FTWRL.
+--echo #
+--echo # 31.d.I) SET PASSWORD is incompatible with FTWRL as it changes data.
+create user mysqltest_u1;
+let $statement= set password for 'mysqltest_u1' = password('');
+let $cleanup_stmt1= ;
+--echo # Skip last part of compatibility testing as our helper debug
+--echo # sync-point doesn't work for SET statements.
+let $skip_3rd_check= 1;
+--source include/check_ftwrl_incompatible.inc
+let $skip_3rd_check= ;
+drop user mysqltest_u1;
+--echo #
+--echo # 31.d.II) SET READ_ONLY is compatible with FTWRL (but has no
+--echo # effect when executed under it).
+let $statement= set global read_only= 1;
+let $cleanup_stmt= set global read_only= 0;
+--echo # Skip last part of compatibility testing as our helper debug
+--echo # sync-point doesn't work for SET statements.
+let $skip_3rd_check= 1;
+--source include/check_ftwrl_compatible.inc
+let $skip_3rd_check= ;
+--echo #
+--echo # 31.d.III) Situation with SET AUTOCOMMIT is complex.
+--echo # Turning auto-commit off is always compatible with FTWRL.
+--echo # Turning auto-commit on causes implicit commit and so
+--echo # is incompatible with FTWRL if there are changes to be
+--echo # committed.
+flush tables with read lock;
+set autocommit= 0;
+--echo # Turning auto-commit on causes implicit commit so can
+--echo # be incompatible with FTWRL if there is something to
+--echo # commit. But since even in this case we allow commits
+--echo # under active FTWRL such statement should always succeed.
+insert into t3_temp_trans values (1);
+set autocommit= 1;
+unlock tables;
+delete from t3_temp_trans;
+--echo # Check that SET AUTOCOMMIT=0 is not blocked and
+--echo # SET AUTOCOMMIT=1 is blocked by active FTWRL in
+--echo # another connection.
+--echo #
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+flush tables with read lock;
+--echo # Switching to connection 'default'.
+connection default;
+set autocommit= 0;
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Do some work so implicit commit in SET AUTOCOMMIT=1
+--echo # is not a no-op.
+insert into t3_trans values (1);
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+flush tables with read lock;
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Send:
+--send set autocommit= 1
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+--echo # Wait until SET AUTOCOMMIT=1 is blocked.
+let $wait_condition=
+ select count(*) = 1 from information_schema.processlist
+ where state = "Waiting for commit lock" and
+ info = "set autocommit= 1";
+--source include/wait_condition.inc
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Reap SET AUTOCOMMIT=1.
+--reap
+delete from t3_trans;
+--echo #
+--echo # Check that SET AUTOCOMMIT=1 blocks FTWRL in another connection.
+set autocommit= 0;
+insert into t3_trans values (1);
+set debug_sync='RESET';
+set debug_sync='ha_commit_trans_after_acquire_commit_lock SIGNAL parked WAIT_FOR go';
+--send set autocommit= 1
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+set debug_sync='now WAIT_FOR parked';
+--send flush tables with read lock
+--echo # Switching to connection '$con_aux2'.
+connection $con_aux2;
+--echo # Wait until FTWRL is blocked.
+let $wait_condition=
+ select count(*) = 1 from information_schema.processlist
+ where state = "Waiting for commit lock" and
+ info = "flush tables with read lock";
+--source include/wait_condition.inc
+set debug_sync='now SIGNAL go';
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Reap SET AUTOCOMMIT=1.
+--reap
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+--echo # Reap FTWRL.
+--reap
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+delete from t3_trans;
+set debug_sync= "RESET";
+
+
+--echo #
+--echo # 32) SHOW statements are compatible with FTWRL.
+--echo # Let us test _some_ of them.
+--echo #
+--echo # 32.1) SHOW TABLES.
+let $statement= show tables from test;
+let $cleanup_stmt= ;
+--source include/check_ftwrl_compatible.inc
+
+--echo #
+--echo # 32.1) SHOW TABLES.
+let $statement= show tables from test;
+let $cleanup_stmt= ;
+--source include/check_ftwrl_compatible.inc
+
+--echo #
+--echo # 32.2) SHOW EVENTS.
+let $statement= show events from test;
+let $cleanup_stmt= ;
+--source include/check_ftwrl_compatible.inc
+
+--echo #
+--echo # 32.3) SHOW GRANTS.
+create user mysqltest_u1;
+let $statement= show grants for mysqltest_u1;
+let $cleanup_stmt= ;
+--source include/check_ftwrl_compatible.inc
+drop user mysqltest_u1;
+
+--echo #
+--echo # 32.4) SHOW CREATE TABLE.
+let $statement= show create table t1_base;
+let $cleanup_stmt= ;
+--source include/check_ftwrl_compatible.inc
+
+--echo #
+--echo # 32.5) SHOW CREATE FUNCTION.
+let $statement= show create function f1;
+let $cleanup_stmt= ;
+--source include/check_ftwrl_compatible.inc
+
+
+--echo #
+--echo # 33) SIGNAL statement is compatible with FTWRL.
+--echo #
+--echo # Note that we don't cover RESIGNAL as it requires
+--echo # active handler context.
+let $statement= signal sqlstate '01000';
+let $cleanup_stmt= ;
+--source include/check_ftwrl_compatible.inc
+
+
+--echo #
+--echo # 34) TRUNCATE TABLE statement.
+--echo #
+--echo # 34.a) TRUNCATE of base table is incompatible with FTWRL.
+let $statement= truncate table t1_base;
+let $cleanup_stmt1= ;
+--source include/check_ftwrl_incompatible.inc
+
+--echo #
+--echo # 34.b) TRUNCATE of temporary table is compatible with FTWRL.
+let $statement= truncate table t1_temp;
+let $cleanup_stmt= ;
+--source include/check_ftwrl_compatible.inc
+
+
+--echo #
+--echo # 35) UPDATE variants.
+--echo #
+--echo # 35.1) Simple UPDATE.
+--echo #
+--echo # 35.1.a) Simple UPDATE on base table is incompatible with FTWRL.
+let $statement= update t1_base set i= 1 where i = 0;
+let $cleanup_stmt1= ;
+--source include/check_ftwrl_incompatible.inc
+
+--echo #
+--echo # 35.1.b) Simple UPDATE on temporary table is compatible with FTWRL.
+let $statement= update t1_temp set i= 1 where i = 0;
+let $cleanup_stmt= ;
+--source include/check_ftwrl_compatible.inc
+
+--echo #
+--echo # 35.2) Multi UPDATE.
+--echo #
+--echo # 35.2.a) Multi UPDATE on base tables is incompatible with FTWRL.
+let $statement= update t1_base, t2_base set t1_base.i= 1 where t1_base.i = t2_base.j;
+let $cleanup_stmt1= ;
+--source include/check_ftwrl_incompatible.inc
+
+--echo #
+--echo # 35.2.b) Multi UPDATE on temporary tables is compatible with FTWRL.
+let $statement= update t1_temp, t2_temp set t1_temp.i= 1 where t1_temp.i = t2_temp.j;
+let $cleanup_stmt= ;
+--source include/check_ftwrl_compatible.inc
+
+
+--echo #
+--echo # 36) USE statement is compatible with FTWRL.
+--echo #
+let $statement= use mysqltest1;
+let $cleanup_stmt= use test;
+--source include/check_ftwrl_compatible.inc
+
+
+--echo #
+--echo # 37) XA statements.
+--echo #
+--echo # XA statements are similar to BEGIN/COMMIT/ROLLBACK.
+--echo #
+--echo # XA BEGIN, END, PREPARE, ROLLBACK and RECOVER are compatible
+--echo # with FTWRL. XA COMMIT is not.
+flush tables with read lock;
+--echo # Although all below statements are allowed under FTWRL they
+--echo # are almost no-ops as FTWRL does commit and does not allows
+--echo # any non-temporary DML under it.
+xa start 'test1';
+xa end 'test1';
+xa prepare 'test1';
+xa rollback 'test1';
+xa start 'test1';
+xa end 'test1';
+xa prepare 'test1';
+xa commit 'test1';
+--disable_result_log
+xa recover;
+--enable_result_log
+unlock tables;
+--echo # Check that XA non-COMMIT statements are not and COMMIT is
+--echo # blocked by active FTWRL in another connection
+--echo #
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+flush tables with read lock;
+--echo # Switching to connection 'default'.
+connection default;
+xa start 'test1';
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+insert into t3_trans values (1);
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+flush tables with read lock;
+--echo # Switching to connection 'default'.
+connection default;
+xa end 'test1';
+xa prepare 'test1';
+xa rollback 'test1';
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+xa start 'test1';
+insert into t3_trans values (1);
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+flush tables with read lock;
+--echo # Switching to connection 'default'.
+connection default;
+connection default;
+xa end 'test1';
+xa prepare 'test1';
+--echo # Send:
+--send xa commit 'test1';
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+--echo # Wait until XA COMMIT is blocked.
+let $wait_condition=
+ select count(*) = 1 from information_schema.processlist
+ where state = "Waiting for commit lock" and
+ info = "xa commit 'test1'";
+--source include/wait_condition.inc
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Reap XA COMMIT.
+--reap
+delete from t3_trans;
+--echo #
+--echo # Check that XA COMMIT blocks FTWRL in another connection.
+xa start 'test1';
+insert into t3_trans values (1);
+xa end 'test1';
+xa prepare 'test1';
+set debug_sync='RESET';
+set debug_sync='trans_xa_commit_after_acquire_commit_lock SIGNAL parked WAIT_FOR go';
+--send xa commit 'test1'
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+set debug_sync='now WAIT_FOR parked';
+--send flush tables with read lock
+--echo # Switching to connection '$con_aux2'.
+connection $con_aux2;
+--echo # Wait until FTWRL is blocked.
+let $wait_condition=
+ select count(*) = 1 from information_schema.processlist
+ where state = "Waiting for commit lock" and
+ info = "flush tables with read lock";
+--source include/wait_condition.inc
+set debug_sync='now SIGNAL go';
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Reap XA COMMIT.
+--reap
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+--echo # Reap FTWRL.
+--reap
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+delete from t3_trans;
+set debug_sync= "RESET";
+
+
+--echo #
+--echo # 38) Test effect of auto-commit mode for DML on transactional
+--echo # temporary tables.
+--echo #
+--echo # 38.1) When auto-commit is on each such a statement ends with commit
+--echo # of changes to temporary tables. But since transactions doing
+--echo # such changes are considered read only [sic!/QQ] this commit
+--echo # is compatible with FTWRL.
+--echo #
+--echo # Let us demostrate this fact for some common DML statements.
+let $statement= delete from t3_temp_trans;
+let $cleanup_stmt= ;
+--source include/check_ftwrl_compatible.inc
+
+let $statement= insert into t3_temp_trans values (1);
+let $cleanup_stmt= delete from t3_temp_trans limit 1;
+--source include/check_ftwrl_compatible.inc
+
+let $statement= update t3_temp_trans, t2_temp set t3_temp_trans.i= 1 where t3_temp_trans.i = t2_temp.j;
+let $cleanup_stmt= ;
+--source include/check_ftwrl_compatible.inc
+
+--echo #
+--echo # 38.2) When auto-commit is off DML on transaction temporary tables
+--echo # is compatible with FTWRL.
+--echo #
+set autocommit= 0;
+let $statement= delete from t3_temp_trans;
+let $cleanup_stmt= ;
+--source include/check_ftwrl_compatible.inc
+
+let $statement= insert into t3_temp_trans values (1);
+let $cleanup_stmt= delete from t3_temp_trans limit 1;
+--source include/check_ftwrl_compatible.inc
+
+let $statement= update t3_temp_trans, t2_temp set t3_temp_trans.i= 1 where t3_temp_trans.i = t2_temp.j;
+let $cleanup_stmt= ;
+--source include/check_ftwrl_compatible.inc
+set autocommit= 1;
+
+
+--echo #
+--echo # 39) Test effect of DDL on transactional tables.
+--echo #
+--echo # 39.1) Due to implicit commit at the end of statement some of DDL
+--echo # statements which are compatible with FTWRL in non-transactional
+--echo # case are not compatible in case of transactional tables.
+--echo #
+--echo # 39.1.a) ANALYZE TABLE for transactional table is incompatible with
+--echo # FTWRL.
+flush tables with read lock;
+--echo # Implicit commits are allowed under FTWRL.
+analyze table t3_trans;
+unlock tables;
+--echo #
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+flush tables with read lock;
+--echo # Switching to connection 'default'.
+connection default;
+--send analyze table t3_trans
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+--echo # Check that ANALYZE TABLE is blocked.
+let $wait_condition=
+ select count(*) = 1 from information_schema.processlist
+ where state = "Waiting for commit lock" and
+ info = "analyze table t3_trans";
+--source include/wait_condition.inc
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Reap ANALYZE TABLE
+--reap
+
+--echo #
+--echo # 39.1.b) CHECK TABLE for transactional table is compatible with FTWRL.
+--echo # Although it does implicit commit at the end of statement it
+--echo # is considered to be read-only operation.
+let $statement= check table t3_trans;
+let $cleanup_stmt= ;
+--echo # Skip last part of compatibility testing as this statement
+--echo # releases metadata locks in non-standard place.
+let $skip_3rd_check= 1;
+--source include/check_ftwrl_compatible.inc
+let $skip_3rd_check= ;
+
+--echo #
+--echo # 39.2) Situation with DDL on temporary transactional tables is
+--echo # complex.
+--echo #
+--echo # 39.2.a) Some statements compatible with FTWRL since they don't
+--echo # do implicit commit.
+--echo #
+--echo # For example, CREATE TEMPORARY TABLE:
+let $statement= create temporary table t4_temp_trans(i int) engine=innodb;
+let $cleanup_stmt= drop temporary tables t4_temp_trans;
+--source include/check_ftwrl_compatible.inc
+--echo #
+--echo # Or DROP TEMPORARY TABLE:
+let $statement= drop temporary tables t3_temp_trans;
+let $cleanup_stmt= create temporary table t3_temp_trans(i int) engine=innodb;
+--source include/check_ftwrl_compatible.inc
+--echo #
+--echo # 39.2.b) Some statements do implicit commit but are considered
+--echo # read-only and so are compatible with FTWRL.
+--echo #
+--echo # For example, REPAIR TABLE:
+let $statement= repair table t3_temp_trans;
+let $cleanup_stmt= ;
+--source include/check_ftwrl_compatible.inc
+--echo #
+--echo # And ANALYZE TABLE:
+let $statement= analyze table t3_temp_trans;
+let $cleanup_stmt= ;
+--source include/check_ftwrl_compatible.inc
+--echo #
+--echo # 39.2.c) Some statements do implicit commit and not
+--echo # considered read-only. As result they are
+--echo # not compatible with FTWRL.
+--echo #
+flush tables with read lock;
+--echo # Implicit commits are allowed under FTWRL.
+alter table t3_temp_trans add column c1 int;
+unlock tables;
+--echo #
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+flush tables with read lock;
+--echo # Switching to connection 'default'.
+connection default;
+--send alter table t3_temp_trans drop column c1
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+--echo # Check that ALTER TABLE is blocked.
+let $wait_condition=
+ select count(*) = 1 from information_schema.processlist
+ where state = "Waiting for commit lock" and
+ info = "alter table t3_temp_trans drop column c1";
+--source include/wait_condition.inc
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Reap ALTER TABLE
+--reap
+
+
+--echo #
+--echo # 40) Test effect of implicit commit for DDL which is otherwise
+--echo # compatible with FTWRL. Implicit commit at the start of DDL
+--echo # statement can make it incompatible with FTWRL if there are
+--echo # some changes to be commited even in case when DDL statement
+--echo # itself is compatible with FTWRL.
+--echo #
+--echo # For example CHECK TABLE for base non-transactional tables and
+--echo # ALTER TABLE for temporary non-transactional tables are affected.
+begin;
+insert into t3_trans values (1);
+--echo #
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+flush tables with read lock;
+--echo # Switching to connection 'default'.
+connection default;
+--send check table t1_base
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+--echo # Check that CHECK TABLE is blocked.
+let $wait_condition=
+ select count(*) = 1 from information_schema.processlist
+ where state = "Waiting for commit lock" and
+ info = "check table t1_base";
+--source include/wait_condition.inc
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Reap CHECK TABLE
+--reap
+begin;
+delete from t3_trans;
+--echo #
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+flush tables with read lock;
+--echo # Switching to connection 'default'.
+connection default;
+--send alter table t1_temp add column c1 int
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+--echo # Check that ALTER TABLE is blocked.
+let $wait_condition=
+ select count(*) = 1 from information_schema.processlist
+ where state = "Waiting for commit lock" and
+ info = "alter table t1_temp add column c1 int";
+--source include/wait_condition.inc
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Reap ALTER TABLE
+--reap
+alter table t1_temp drop column c1;
+
+
+--echo #
+--echo # Check that FLUSH TABLES WITH READ LOCK is blocked by individual
+--echo # statements and is not blocked in the presence of transaction which
+--echo # has done some changes earlier but is idle now (or does only reads).
+--echo # This allows to use this statement even on systems which has long
+--echo # running transactions.
+--echo #
+begin;
+insert into t1_base values (1);
+insert into t3_trans values (1);
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+--echo # The below FTWRL should not be blocked by transaction in 'default'.
+flush tables with read lock;
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Transaction still is able to read even with FTWRL active in another
+--echo # connection.
+select * from t1_base;
+select * from t2_base;
+select * from t3_trans;
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+commit;
+delete from t1_base;
+delete from t3_trans;
+
+
+--echo #
+--echo # Check that impending FTWRL blocks new DML statements and
+--echo # so can't be starved by a constant flow of DML.
+--echo # (a.k.a. test for bug #54673 "It takes too long to get
+--echo # readlock for 'FLUSH TABLES WITH READ LOCK'").
+--echo #
+set debug_sync='RESET';
+set debug_sync='execute_command_after_close_tables SIGNAL parked WAIT_FOR go';
+--send insert into t1_base values (1)
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+set debug_sync='now WAIT_FOR parked';
+--send flush tables with read lock
+--echo # Switching to connection '$con_aux2'.
+connection $con_aux2;
+--echo # Wait until FTWRL is blocked.
+let $wait_condition=
+ select count(*) = 1 from information_schema.processlist
+ where state = "Waiting for global read lock" and
+ info = "flush tables with read lock";
+--source include/wait_condition.inc
+--echo # Try to run another INSERT and see that it is blocked.
+--send insert into t2_base values (1);
+--echo # Switching to connection 'con3'.
+connection con3;
+--echo # Wait until new INSERT is blocked.
+let $wait_condition=
+ select count(*) = 1 from information_schema.processlist
+ where state = "Waiting for global read lock" and
+ info = "insert into t2_base values (1)";
+--echo # Unblock INSERT in the first connection.
+set debug_sync='now SIGNAL go';
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Reap first INSERT.
+--reap
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+--echo # Reap FTWRL.
+--reap
+unlock tables;
+--echo # Switching to connection '$con_aux2'.
+connection $con_aux2;
+--echo # Reap second INSERT.
+--reap
+--echo # Switching to connection 'default'.
+connection default;
+set debug_sync= "RESET";
+delete from t1_base;
+delete from t2_base;
+
+--echo
+--echo # Check that COMMIT thas is issued after
+--echo # FLUSH TABLES WITH READ LOCK is not blocked by
+--echo # FLUSH TABLES WITH READ LOCK from another connection.
+--echo # This scenario is used in innobackup.pl. The COMMIT goes
+--echo # through because the transaction started by FTWRL does
+--echo # not modify any tables, and the commit blocker lock is
+--echo # only taken when there were such modifications.
+--echo
+flush tables with read lock;
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+--echo # The below FTWRL should not be blocked by transaction in 'default'.
+flush tables with read lock;
+--echo # Switching to connection 'default'.
+connection default;
+select * from t1_base;
+select * from t3_trans;
+commit;
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+select * from t1_base;
+select * from t3_trans;
+commit;
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+unlock tables;
+
+
+--echo #
+--echo # Check how FLUSH TABLE WITH READ LOCK is handled for MERGE tables.
+--echo # As usual there are tricky cases related to this type of tables.
+--echo #
+--echo #
+--echo # 1) Most typical case - base MERGE table with base underlying tables.
+--echo #
+--echo # 1.a) DML statements which change data should be incompatible with FTWRL.
+create table tm_base (i int) engine=merge union=(t1_base) insert_method=last;
+let $statement= insert into tm_base values (1);
+let $cleanup_stmt1= delete from tm_base;
+--source include/check_ftwrl_incompatible.inc
+--echo #
+--echo # 1.b) DDL statement on such table should be incompatible with FTWRL as well.
+let $statement= alter table tm_base insert_method=first;
+let $cleanup_stmt1= alter table tm_base insert_method=last;
+--source include/check_ftwrl_incompatible.inc
+drop table tm_base;
+
+--echo #
+--echo # 2) Temporary MERGE table with base underlying tables.
+--echo #
+--echo # 2.a) DML statements which change data should be incompatible with FTWRL
+--echo # as they affect base tables.
+create temporary table tm_temp_base (i int) engine=merge union=(t1_base) insert_method=last;
+let $statement= insert into tm_temp_base values (1);
+let $cleanup_stmt1= delete from tm_temp_base;
+--source include/check_ftwrl_incompatible.inc
+--echo #
+--echo # 2.b) Some of DDL statements on such table can be compatible with FTWRL
+--echo # as they don't affect base tables.
+let $statement= drop temporary tables tm_temp_base;
+let $cleanup_stmt= create temporary table tm_temp_base (i int) engine=merge union=(t1_base) insert_method=last;
+--source include/check_ftwrl_compatible.inc
+--echo #
+--echo # 2.c) ALTER statement is incompatible with FTWRL. Even though it does
+--echo # not change data in base table it still acquires strong metadata
+--echo # locks on them.
+let $statement= alter table tm_temp_base insert_method=first;
+let $cleanup_stmt1= alter table tm_temp_base insert_method=last;
+--source include/check_ftwrl_incompatible.inc
+drop table tm_temp_base;
+
+--echo #
+--echo # 3) Temporary MERGE table with temporary underlying tables.
+--echo #
+--echo # 3.a) DML statements should be compatible with FTWRL as
+--echo # no base table is going to be affected.
+create temporary table tm_temp_temp (i int) engine=merge union=(t1_temp) insert_method=last;
+let $statement= insert into tm_temp_temp values (1);
+let $cleanup_stmt= delete from tm_temp_temp;
+--source include/check_ftwrl_compatible.inc
+--echo #
+--echo # 3.b) DDL statements should be compatible with FTWRL as well
+--echo # as no base table is going to be affected too.
+let $statement= alter table tm_temp_temp union=(t1_temp) insert_method=first;
+let $cleanup_stmt= alter table tm_temp_temp union=(t1_temp) insert_method=last;
+--source include/check_ftwrl_compatible.inc
+drop table tm_temp_temp;
+
+--echo #
+--echo # 4) For the sake of completeness let us check that base MERGE tables
+--echo # with temporary underlying tables are not functional.
+create table tm_base_temp (i int) engine=merge union=(t1_temp) insert_method=last;
+--error ER_WRONG_MRG_TABLE
+select * from tm_base_temp;
+drop table tm_base_temp;
+
+
+--echo #
+--echo # Clean-up.
+--echo #
+drop event e1;
+drop function f2_temp;
+drop function f2_base;
+drop procedure p2;
+drop view v1;
+drop function f1;
+drop procedure p1;
+drop database `#mysql50#mysqltest-2`;
+drop database mysqltest1;
+drop temporary tables t1_temp, t2_temp;
+drop tables t1_base, t2_base, t3_trans;
+disconnect con1;
+disconnect con2;
+disconnect con3;
+
+# Check that all connections opened by test cases in this file are really
+# gone so execution of other tests won't be affected by their presence.
+--source include/wait_until_count_sessions.inc
diff --git a/mysql-test/t/flush_read_lock_kill-master.opt b/mysql-test/t/flush_read_lock_kill-master.opt
deleted file mode 100644
index 61e2b242351..00000000000
--- a/mysql-test/t/flush_read_lock_kill-master.opt
+++ /dev/null
@@ -1 +0,0 @@
---loose-debug=+d,make_global_read_lock_block_commit_loop
diff --git a/mysql-test/t/flush_read_lock_kill.test b/mysql-test/t/flush_read_lock_kill.test
index 2d359383949..a672fa5dfc5 100644
--- a/mysql-test/t/flush_read_lock_kill.test
+++ b/mysql-test/t/flush_read_lock_kill.test
@@ -2,24 +2,19 @@
# for running commits to finish (in the past it could not)
# This will not be a meaningful test on non-debug servers so will be
# skipped.
-# If running mysql-test-run --debug, the --debug added by
-# mysql-test-run to the mysqld command line will override the one of
-# -master.opt. But this test is designed to still pass then (though it
-# won't test anything interesting).
# This also won't work with the embedded server test
--source include/not_embedded.inc
--source include/have_debug.inc
+# This test needs transactional engine as otherwise COMMIT
+# won't block FLUSH TABLES WITH GLOBAL READ LOCK.
+--source include/have_innodb.inc
+
# Save the initial number of concurrent sessions
--source include/count_sessions.inc
-# Disable concurrent inserts to avoid test failures when reading the
-# connection id which was inserted into a table by another thread.
-SET @old_concurrent_insert= @@global.concurrent_insert;
-SET @@global.concurrent_insert= 0;
-
connect (con1,localhost,root,,);
connect (con2,localhost,root,,);
connection con1;
@@ -27,47 +22,64 @@ connection con1;
--disable_warnings
DROP TABLE IF EXISTS t1;
--enable_warnings
-CREATE TABLE t1 (kill_id INT);
+SET DEBUG_SYNC= 'RESET';
+CREATE TABLE t1 (kill_id INT) engine = InnoDB;
+INSERT INTO t1 VALUES(connection_id());
+
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Start transaction.
+BEGIN;
INSERT INTO t1 VALUES(connection_id());
+--echo # Ensure that COMMIT will pause once it acquires protection
+--echo # against its global read lock.
+SET DEBUG_SYNC='ha_commit_trans_after_acquire_commit_lock SIGNAL acquired WAIT_FOR go';
-# Thanks to the parameter we passed to --debug, this FLUSH will
-# block on a debug build running with our --debug=make_global... It
-# will block until killed. In other cases (non-debug build or other
-# --debug) it will succeed immediately
+--echo # Sending:
+--send COMMIT
+--echo # Switching to 'con1'.
connection con1;
+--echo # Wait till COMMIT acquires protection against global read
+--echo # lock and pauses.
+SET DEBUG_SYNC='now WAIT_FOR acquired';
+--echo # Sending:
send FLUSH TABLES WITH READ LOCK;
-# kill con1
+--echo # Switching to 'con2'.
connection con2;
-SELECT ((@id := kill_id) - kill_id) FROM t1;
+SELECT ((@id := kill_id) - kill_id) FROM t1 LIMIT 1;
-# Wait for the debug sync point, test won't run on non-debug
-# builds anyway.
+--echo # Wait till FLUSH TABLES WITH READ LOCK blocks due
+--echo # to active COMMIT
let $wait_condition=
select count(*) = 1 from information_schema.processlist
- where state = "Waiting for all running commits to finish"
+ where state = "Waiting for commit lock"
and info = "flush tables with read lock";
--source include/wait_condition.inc
+--echo # Kill connection 'con1'.
KILL CONNECTION @id;
+--echo # Switching to 'con1'.
connection con1;
-# On debug builds it will be error 1053 (killed); on non-debug, or
-# debug build running without our --debug=make_global..., will be
-# error 0 (no error). The only important thing to test is that on
-# debug builds with our --debug=make_global... we don't hang forever.
---error 0,1317,2013
+--echo # Try to reap FLUSH TABLES WITH READ LOCK,
+--echo # it fail due to killed statement and connection.
+--error 1317,2013
reap;
+--echo # Switching to 'con2'.
connection con2;
-DROP TABLE t1;
+--echo # Resume COMMIT.
+SET DEBUG_SYNC='now SIGNAL go';
+--echo # Switching to 'default'.
connection default;
+--echo # Reaping COMMIT.
+--reap
disconnect con2;
-
-# Restore global concurrent_insert value
-SET @@global.concurrent_insert= @old_concurrent_insert;
+DROP TABLE t1;
+SET DEBUG_SYNC= 'RESET';
# Wait till all disconnects are completed
--source include/wait_until_count_sessions.inc
diff --git a/mysql-test/t/lock_multi.test b/mysql-test/t/lock_multi.test
index 6bdb235903d..5bab5e647ab 100644
--- a/mysql-test/t/lock_multi.test
+++ b/mysql-test/t/lock_multi.test
@@ -228,7 +228,7 @@ connection writer;
# Sleep a bit till the flush of connection locker is in work and hangs
let $wait_condition=
select count(*) = 1 from information_schema.processlist
- where state = "Waiting for global metadata lock" and
+ where state = "Waiting for global read lock" and
info = "FLUSH TABLES WITH READ LOCK";
--source include/wait_condition.inc
# This must not block.
@@ -260,7 +260,7 @@ connection writer;
# Sleep a bit till the flush of connection locker is in work and hangs
let $wait_condition=
select count(*) = 1 from information_schema.processlist
- where state = "Waiting for global metadata lock" and
+ where state = "Waiting for global read lock" and
info = "FLUSH TABLES WITH READ LOCK";
--source include/wait_condition.inc
--error ER_TABLE_NOT_LOCKED
@@ -296,10 +296,11 @@ DROP DATABASE mysqltest_1;
# With bug in place: try to acquire LOCK_mysql_create_table...
# When fixed: Reject dropping db because of the read lock.
connection con1;
-# Wait a bit so that the session con2 is in state "Waiting for release of readlock"
+# Wait a bit so that the session con2 is in state
+# "Waiting for global read lock"
let $wait_condition=
select count(*) = 1 from information_schema.processlist
- where state = "Waiting for release of readlock"
+ where state = "Waiting for global read lock"
and info = "DROP DATABASE mysqltest_1";
--source include/wait_condition.inc
--error ER_CANT_UPDATE_WITH_READLOCK
@@ -376,7 +377,7 @@ connection con5;
--echo # con5
let $wait_condition=
select count(*) = 1 from information_schema.processlist
- where state = "Waiting for global metadata lock" and
+ where state = "Waiting for global read lock" and
info = "flush tables with read lock";
--source include/wait_condition.inc
--echo # global read lock is taken
@@ -384,10 +385,11 @@ connection con3;
--echo # con3
send select * from t2 for update;
connection con5;
-let $show_statement= SHOW PROCESSLIST;
-let $field= State;
-let $condition= = 'Waiting for release of readlock';
---source include/wait_show_condition.inc
+let $wait_condition=
+ select count(*) = 1 from information_schema.processlist
+ where state = "Waiting for global read lock" and
+ info = "select * from t2 for update";
+--source include/wait_condition.inc
--echo # waiting for release of read lock
connection con4;
--echo # con4
@@ -433,10 +435,11 @@ connection con1;
send update t2 set a = 1;
connection default;
--echo # default
-let $show_statement= SHOW PROCESSLIST;
-let $field= State;
-let $condition= = 'Waiting for release of readlock';
---source include/wait_show_condition.inc
+let $wait_condition=
+ select count(*) = 1 from information_schema.processlist
+ where state = "Waiting for global read lock" and
+ info = "update t2 set a = 1";
+--source include/wait_condition.inc
--echo # statement is waiting for release of read lock
connection con2;
--echo # con2
@@ -460,10 +463,11 @@ connection con1;
send lock tables t2 write;
connection default;
--echo # default
-let $show_statement= SHOW PROCESSLIST;
-let $field= State;
-let $condition= = 'Waiting for release of readlock';
---source include/wait_show_condition.inc
+let $wait_condition=
+ select count(*) = 1 from information_schema.processlist
+ where state = "Waiting for global read lock" and
+ info = "lock tables t2 write";
+--source include/wait_condition.inc
--echo # statement is waiting for release of read lock
connection con2;
--echo # con2
@@ -571,7 +575,8 @@ connection default;
--echo connection: default
let $wait_condition=
select count(*) = 1 from information_schema.processlist
- where state = "Waiting for global metadata lock";
+ where state = "Waiting for global read lock" and
+ info = "flush tables with read lock";
--source include/wait_condition.inc
alter table t1 add column j int;
connect (insert,localhost,root,,test,,);
@@ -579,14 +584,16 @@ connection insert;
--echo connection: insert
let $wait_condition=
select count(*) = 1 from information_schema.processlist
- where state = "Waiting for global metadata lock";
+ where state = "Waiting for global read lock" and
+ info = "flush tables with read lock";
--source include/wait_condition.inc
--send insert into t1 values (1,2);
--echo connection: default
connection default;
let $wait_condition=
select count(*) = 1 from information_schema.processlist
- where state = "Waiting for release of readlock";
+ where state = "Waiting for global read lock" and
+ info = "insert into t1 values (1,2)";
--source include/wait_condition.inc
unlock tables;
connection flush;
@@ -594,7 +601,8 @@ connection flush;
--reap
let $wait_condition=
select count(*) = 1 from information_schema.processlist
- where state = "Waiting for release of readlock";
+ where state = "Waiting for global read lock" and
+ info = "insert into t1 values (1,2)";
--source include/wait_condition.inc
select * from t1;
unlock tables;
@@ -629,12 +637,12 @@ connection default;
--echo connection: default
let $wait_condition=
select count(*) = 1 from information_schema.processlist
- where state = "Waiting for global metadata lock";
+ where state = "Waiting for global read lock";
--source include/wait_condition.inc
flush tables;
let $wait_condition=
select count(*) = 1 from information_schema.processlist
- where state = "Waiting for global metadata lock";
+ where state = "Waiting for global read lock";
--source include/wait_condition.inc
unlock tables;
connection flush;
@@ -698,12 +706,12 @@ connection default;
--echo connection: default
let $wait_condition=
select count(*) = 1 from information_schema.processlist
- where state = "Waiting for global metadata lock";
+ where state = "Waiting for global read lock";
--source include/wait_condition.inc
flush tables;
let $wait_condition=
select count(*) = 1 from information_schema.processlist
- where state = "Waiting for global metadata lock";
+ where state = "Waiting for global read lock";
--source include/wait_condition.inc
drop table t1;
connection flush;
diff --git a/mysql-test/t/mdl_sync.test b/mysql-test/t/mdl_sync.test
index 5c4fc20b428..197cad536e4 100644
--- a/mysql-test/t/mdl_sync.test
+++ b/mysql-test/t/mdl_sync.test
@@ -3661,7 +3661,7 @@ connection con2;
SET DEBUG_SYNC= 'now WAIT_FOR table_opened';
--echo # Check that FLUSH must wait to get the GRL
--echo # and let CREATE PROCEDURE continue
-SET DEBUG_SYNC= 'wait_lock_global_read_lock SIGNAL grlwait';
+SET DEBUG_SYNC= 'mdl_acquire_lock_wait SIGNAL grlwait';
--send FLUSH TABLES WITH READ LOCK
--echo # Connection 1
@@ -3689,15 +3689,22 @@ connection con2;
SET DEBUG_SYNC= 'now WAIT_FOR table_opened';
--echo # Check that FLUSH must wait to get the GRL
--echo # and let DROP PROCEDURE continue
-SET DEBUG_SYNC= 'wait_lock_global_read_lock SIGNAL grlwait';
+SET DEBUG_SYNC= 'mdl_acquire_lock_wait SIGNAL grlwait';
--send FLUSH TABLES WITH READ LOCK
--echo # Connection 1
connection default;
+--echo # Once FLUSH TABLES WITH READ LOCK starts waiting
+--echo # DROP PROCEDURE will be waked up and will drop
+--echo # procedure. Global read lock will be granted after
+--echo # this statement ends.
+--echo #
+--echo # Reaping DROP PROCEDURE.
--reap
--echo # Connection 2
connection con2;
+--echo # Reaping FTWRL.
--reap
UNLOCK TABLES;
@@ -4485,7 +4492,7 @@ connection con2;
--echo # Connection default
connection default;
let $wait_condition=SELECT COUNT(*)=1 FROM information_schema.processlist
- WHERE state='Waiting for release of readlock'
+ WHERE state='Waiting for global read lock'
AND info='CREATE TABLE db1.t2(a INT)';
--source include/wait_condition.inc
UNLOCK TABLES;
@@ -4507,7 +4514,7 @@ connection con2;
--echo # Connection default
connection default;
let $wait_condition=SELECT COUNT(*)=1 FROM information_schema.processlist
- WHERE state='Waiting for release of readlock'
+ WHERE state='Waiting for global read lock'
AND info='ALTER DATABASE db1 DEFAULT CHARACTER SET utf8';
--source include/wait_condition.inc
UNLOCK TABLES;
diff --git a/mysql-test/t/trigger_notembedded.test b/mysql-test/t/trigger_notembedded.test
index 89857063ebd..536b00308ab 100644
--- a/mysql-test/t/trigger_notembedded.test
+++ b/mysql-test/t/trigger_notembedded.test
@@ -896,7 +896,7 @@ connection default;
--echo connection: default
let $wait_condition=
select count(*) = 1 from information_schema.processlist
- where state = "Waiting for global metadata lock";
+ where state = "Waiting for global read lock";
--source include/wait_condition.inc
create trigger t1_bi before insert on t1 for each row begin end;
unlock tables;
diff --git a/sql/event_data_objects.cc b/sql/event_data_objects.cc
index 52c509621ac..bdc79dcbcfd 100644
--- a/sql/event_data_objects.cc
+++ b/sql/event_data_objects.cc
@@ -290,7 +290,6 @@ Event_basic::load_time_zone(THD *thd, const LEX_STRING tz_name)
*/
Event_queue_element::Event_queue_element():
- status_changed(FALSE), last_executed_changed(FALSE),
on_completion(Event_parse_data::ON_COMPLETION_DROP),
status(Event_parse_data::ENABLED), expression(0), dropped(FALSE),
execution_count(0)
@@ -539,7 +538,6 @@ Event_queue_element::load_from_row(THD *thd, TABLE *table)
TIME_NO_ZERO_DATE);
last_executed= my_tz_OFFSET0->TIME_to_gmt_sec(&time,&not_used);
}
- last_executed_changed= FALSE;
if ((ptr= get_field(&mem_root, table->field[ET_FIELD_STATUS])) == NullS)
DBUG_RETURN(TRUE);
@@ -935,7 +933,6 @@ Event_queue_element::compute_next_execution_time()
DBUG_PRINT("info",("One-time event will be dropped: %d.", dropped));
status= Event_parse_data::DISABLED;
- status_changed= TRUE;
}
goto ret;
}
@@ -955,7 +952,6 @@ Event_queue_element::compute_next_execution_time()
dropped= TRUE;
DBUG_PRINT("info", ("Dropped: %d", dropped));
status= Event_parse_data::DISABLED;
- status_changed= TRUE;
goto ret;
}
@@ -1018,7 +1014,6 @@ Event_queue_element::compute_next_execution_time()
if (on_completion == Event_parse_data::ON_COMPLETION_DROP)
dropped= TRUE;
status= Event_parse_data::DISABLED;
- status_changed= TRUE;
}
else
{
@@ -1108,7 +1103,6 @@ Event_queue_element::compute_next_execution_time()
execute_at= 0;
execute_at_null= TRUE;
status= Event_parse_data::DISABLED;
- status_changed= TRUE;
if (on_completion == Event_parse_data::ON_COMPLETION_DROP)
dropped= TRUE;
}
@@ -1144,50 +1138,11 @@ void
Event_queue_element::mark_last_executed(THD *thd)
{
last_executed= (my_time_t) thd->query_start();
- last_executed_changed= TRUE;
execution_count++;
}
-/*
- Saves status and last_executed_at to the disk if changed.
-
- SYNOPSIS
- Event_queue_element::update_timing_fields()
- thd - thread context
-
- RETURN VALUE
- FALSE OK
- TRUE Error while opening mysql.event for writing or during
- write on disk
-*/
-
-bool
-Event_queue_element::update_timing_fields(THD *thd)
-{
- Event_db_repository *db_repository= Events::get_db_repository();
- int ret;
-
- DBUG_ENTER("Event_queue_element::update_timing_fields");
-
- DBUG_PRINT("enter", ("name: %*s", (int) name.length, name.str));
-
- /* No need to update if nothing has changed */
- if (!(status_changed || last_executed_changed))
- DBUG_RETURN(0);
-
- ret= db_repository->update_timing_fields_for_event(thd,
- dbname, name,
- last_executed_changed,
- last_executed,
- status_changed,
- (ulonglong) status);
- last_executed_changed= status_changed= FALSE;
- DBUG_RETURN(ret);
-}
-
-
static
void
append_datetime(String *buf, Time_zone *time_zone, my_time_t secs,
diff --git a/sql/event_data_objects.h b/sql/event_data_objects.h
index 9d17213bcb8..46740812d31 100644
--- a/sql/event_data_objects.h
+++ b/sql/event_data_objects.h
@@ -82,10 +82,6 @@ protected:
class Event_queue_element : public Event_basic
{
-protected:
- bool status_changed;
- bool last_executed_changed;
-
public:
int on_completion;
int status;
@@ -117,9 +113,6 @@ public:
void
mark_last_executed(THD *thd);
-
- bool
- update_timing_fields(THD *thd);
};
diff --git a/sql/event_db_repository.cc b/sql/event_db_repository.cc
index db508e4ea41..053558aa0c3 100644
--- a/sql/event_db_repository.cc
+++ b/sql/event_db_repository.cc
@@ -622,6 +622,12 @@ Event_db_repository::create_event(THD *thd, Event_parse_data *parse_data,
TABLE *table= NULL;
sp_head *sp= thd->lex->sphead;
ulong saved_mode= thd->variables.sql_mode;
+ /*
+ Take a savepoint to release only the lock on mysql.event
+ table at the end but keep the global read lock and
+ possible other locks taken by the caller.
+ */
+ MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
DBUG_ENTER("Event_db_repository::create_event");
@@ -699,8 +705,8 @@ Event_db_repository::create_event(THD *thd, Event_parse_data *parse_data,
ret= 0;
end:
- if (table)
- close_mysql_tables(thd);
+ close_thread_tables(thd);
+ thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
thd->variables.sql_mode= saved_mode;
DBUG_RETURN(test(ret));
@@ -734,6 +740,12 @@ Event_db_repository::update_event(THD *thd, Event_parse_data *parse_data,
TABLE *table= NULL;
sp_head *sp= thd->lex->sphead;
ulong saved_mode= thd->variables.sql_mode;
+ /*
+ Take a savepoint to release only the lock on mysql.event
+ table at the end but keep the global read lock and
+ possible other locks taken by the caller.
+ */
+ MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
int ret= 1;
DBUG_ENTER("Event_db_repository::update_event");
@@ -811,8 +823,8 @@ Event_db_repository::update_event(THD *thd, Event_parse_data *parse_data,
ret= 0;
end:
- if (table)
- close_mysql_tables(thd);
+ close_thread_tables(thd);
+ thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
thd->variables.sql_mode= saved_mode;
DBUG_RETURN(test(ret));
@@ -838,6 +850,12 @@ Event_db_repository::drop_event(THD *thd, LEX_STRING db, LEX_STRING name,
bool drop_if_exists)
{
TABLE *table= NULL;
+ /*
+ Take a savepoint to release only the lock on mysql.event
+ table at the end but keep the global read lock and
+ possible other locks taken by the caller.
+ */
+ MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
int ret= 1;
DBUG_ENTER("Event_db_repository::drop_event");
@@ -866,8 +884,8 @@ Event_db_repository::drop_event(THD *thd, LEX_STRING db, LEX_STRING name,
ret= 0;
end:
- if (table)
- close_mysql_tables(thd);
+ close_thread_tables(thd);
+ thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
DBUG_RETURN(test(ret));
}
@@ -940,7 +958,7 @@ Event_db_repository::drop_schema_events(THD *thd, LEX_STRING schema)
TABLE *table= NULL;
READ_RECORD read_record_info;
enum enum_events_table_field field= ET_FIELD_DB;
- MDL_ticket *mdl_savepoint= thd->mdl_context.mdl_savepoint();
+ MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
DBUG_ENTER("Event_db_repository::drop_schema_events");
DBUG_PRINT("enter", ("field=%d schema=%s", field, schema.str));
@@ -1042,15 +1060,14 @@ Event_db_repository::
update_timing_fields_for_event(THD *thd,
LEX_STRING event_db_name,
LEX_STRING event_name,
- bool update_last_executed,
my_time_t last_executed,
- bool update_status,
ulonglong status)
{
TABLE *table= NULL;
Field **fields;
int ret= 1;
bool save_binlog_row_based;
+ MYSQL_TIME time;
DBUG_ENTER("Event_db_repository::update_timing_fields_for_event");
@@ -1075,20 +1092,12 @@ update_timing_fields_for_event(THD *thd,
/* Don't update create on row update. */
table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
- if (update_last_executed)
- {
- MYSQL_TIME time;
- my_tz_OFFSET0->gmt_sec_to_TIME(&time, last_executed);
+ my_tz_OFFSET0->gmt_sec_to_TIME(&time, last_executed);
+ fields[ET_FIELD_LAST_EXECUTED]->set_notnull();
+ fields[ET_FIELD_LAST_EXECUTED]->store_time(&time, MYSQL_TIMESTAMP_DATETIME);
- fields[ET_FIELD_LAST_EXECUTED]->set_notnull();
- fields[ET_FIELD_LAST_EXECUTED]->store_time(&time,
- MYSQL_TIMESTAMP_DATETIME);
- }
- if (update_status)
- {
- fields[ET_FIELD_STATUS]->set_notnull();
- fields[ET_FIELD_STATUS]->store(status, TRUE);
- }
+ fields[ET_FIELD_STATUS]->set_notnull();
+ fields[ET_FIELD_STATUS]->store(status, TRUE);
if ((ret= table->file->ha_update_row(table->record[1], table->record[0])))
{
diff --git a/sql/event_db_repository.h b/sql/event_db_repository.h
index ea7f3bbac0e..58484d17f06 100644
--- a/sql/event_db_repository.h
+++ b/sql/event_db_repository.h
@@ -101,9 +101,7 @@ public:
update_timing_fields_for_event(THD *thd,
LEX_STRING event_db_name,
LEX_STRING event_name,
- bool update_last_executed,
my_time_t last_executed,
- bool update_status,
ulonglong status);
public:
static bool
diff --git a/sql/event_queue.cc b/sql/event_queue.cc
index 458a7c1defd..92b8a2c9071 100644
--- a/sql/event_queue.cc
+++ b/sql/event_queue.cc
@@ -17,6 +17,8 @@
#include "unireg.h"
#include "event_queue.h"
#include "event_data_objects.h"
+#include "event_db_repository.h"
+#include "events.h"
#include "sql_audit.h"
#include "tztime.h" // my_tz_find, my_tz_OFFSET0, struct Time_zone
#include "log.h" // sql_print_error
@@ -444,7 +446,6 @@ Event_queue::recalculate_activation_times(THD *thd)
for (i= 0; i < queue.elements; i++)
{
((Event_queue_element*)queue_element(&queue, i))->compute_next_execution_time();
- ((Event_queue_element*)queue_element(&queue, i))->update_timing_fields(thd);
}
queue_fix(&queue);
/*
@@ -567,6 +568,8 @@ Event_queue::get_top_for_execution_if_time(THD *thd,
{
bool ret= FALSE;
*event_name= NULL;
+ my_time_t last_executed;
+ int status;
DBUG_ENTER("Event_queue::get_top_for_execution_if_time");
LOCK_QUEUE_DATA();
@@ -632,8 +635,14 @@ Event_queue::get_top_for_execution_if_time(THD *thd,
top->execution_count++;
(*event_name)->dropped= top->dropped;
+ /*
+ Save new values of last_executed timestamp and event status on stack
+ in order to be able to update event description in system table once
+ QUEUE_DATA lock is released.
+ */
+ last_executed= top->last_executed;
+ status= top->status;
- top->update_timing_fields(thd);
if (top->status == Event_parse_data::DISABLED)
{
DBUG_PRINT("info", ("removing from the queue"));
@@ -656,9 +665,16 @@ end:
ret, (long) *event_name));
if (*event_name)
+ {
DBUG_PRINT("info", ("db: %s name: %s",
(*event_name)->dbname.str, (*event_name)->name.str));
+ Event_db_repository *db_repository= Events::get_db_repository();
+ (void) db_repository->update_timing_fields_for_event(thd,
+ (*event_name)->dbname, (*event_name)->name,
+ last_executed, (ulonglong) status);
+ }
+
DBUG_RETURN(ret);
}
diff --git a/sql/events.cc b/sql/events.cc
index e7e47801586..23a0dc9eb52 100644
--- a/sql/events.cc
+++ b/sql/events.cc
@@ -30,6 +30,7 @@
#include "event_scheduler.h"
#include "sp_head.h" // for Stored_program_creation_ctx
#include "set_var.h"
+#include "lock.h" // lock_object_name
/**
@addtogroup Event_Scheduler
@@ -77,7 +78,6 @@ Event_queue *Events::event_queue;
Event_scheduler *Events::scheduler;
Event_db_repository *Events::db_repository;
ulong Events::opt_event_scheduler= Events::EVENTS_OFF;
-mysql_mutex_t Events::LOCK_event_metadata;
bool Events::check_system_tables_error= FALSE;
@@ -340,7 +340,9 @@ Events::create_event(THD *thd, Event_parse_data *parse_data,
if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
thd->clear_current_stmt_binlog_format_row();
- mysql_mutex_lock(&LOCK_event_metadata);
+ if (lock_object_name(thd, MDL_key::EVENT,
+ parse_data->dbname.str, parse_data->name.str))
+ DBUG_RETURN(TRUE);
/* On error conditions my_error() is called so no need to handle here */
if (!(ret= db_repository->create_event(thd, parse_data, if_not_exists)))
@@ -388,7 +390,6 @@ Events::create_event(THD *thd, Event_parse_data *parse_data,
}
}
}
- mysql_mutex_unlock(&LOCK_event_metadata);
/* Restore the state of binlog format */
DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
if (save_binlog_row_based)
@@ -472,7 +473,9 @@ Events::update_event(THD *thd, Event_parse_data *parse_data,
if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
thd->clear_current_stmt_binlog_format_row();
- mysql_mutex_lock(&LOCK_event_metadata);
+ if (lock_object_name(thd, MDL_key::EVENT,
+ parse_data->dbname.str, parse_data->name.str))
+ DBUG_RETURN(TRUE);
/* On error conditions my_error() is called so no need to handle here */
if (!(ret= db_repository->update_event(thd, parse_data,
@@ -502,7 +505,6 @@ Events::update_event(THD *thd, Event_parse_data *parse_data,
ret= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
}
}
- mysql_mutex_unlock(&LOCK_event_metadata);
/* Restore the state of binlog format */
DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
if (save_binlog_row_based)
@@ -556,7 +558,9 @@ Events::drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name, bool if_exists)
if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
thd->clear_current_stmt_binlog_format_row();
- mysql_mutex_lock(&LOCK_event_metadata);
+ if (lock_object_name(thd, MDL_key::EVENT,
+ dbname.str, name.str))
+ DBUG_RETURN(TRUE);
/* On error conditions my_error() is called so no need to handle here */
if (!(ret= db_repository->drop_event(thd, dbname, name, if_exists)))
{
@@ -566,7 +570,6 @@ Events::drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name, bool if_exists)
DBUG_ASSERT(thd->query() && thd->query_length());
ret= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
}
- mysql_mutex_unlock(&LOCK_event_metadata);
/* Restore the state of binlog format */
DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
if (save_binlog_row_based)
@@ -595,15 +598,12 @@ Events::drop_schema_events(THD *thd, char *db)
DBUG_PRINT("enter", ("dropping events from %s", db));
/*
- sic: no check if the scheduler is disabled or system tables
+ Sic: no check if the scheduler is disabled or system tables
are damaged, as intended.
*/
-
- mysql_mutex_lock(&LOCK_event_metadata);
if (event_queue)
event_queue->drop_schema_events(thd, db_lex);
db_repository->drop_schema_events(thd, db_lex);
- mysql_mutex_unlock(&LOCK_event_metadata);
DBUG_VOID_RETURN;
}
@@ -915,12 +915,11 @@ Events::deinit()
}
#ifdef HAVE_PSI_INTERFACE
-PSI_mutex_key key_LOCK_event_metadata, key_LOCK_event_queue,
+PSI_mutex_key key_LOCK_event_queue,
key_event_scheduler_LOCK_scheduler_state;
static PSI_mutex_info all_events_mutexes[]=
{
- { &key_LOCK_event_metadata, "LOCK_event_metadata", PSI_FLAG_GLOBAL},
{ &key_LOCK_event_queue, "LOCK_event_queue", PSI_FLAG_GLOBAL},
{ &key_event_scheduler_LOCK_scheduler_state, "Event_scheduler::LOCK_scheduler_state", PSI_FLAG_GLOBAL}
};
@@ -974,23 +973,6 @@ Events::init_mutexes()
#ifdef HAVE_PSI_INTERFACE
init_events_psi_keys();
#endif
-
- mysql_mutex_init(key_LOCK_event_metadata,
- &LOCK_event_metadata, MY_MUTEX_INIT_FAST);
-}
-
-
-/*
- Destroys Events mutexes
-
- SYNOPSIS
- Events::destroy_mutexes()
-*/
-
-void
-Events::destroy_mutexes()
-{
- mysql_mutex_destroy(&LOCK_event_metadata);
}
diff --git a/sql/events.h b/sql/events.h
index f3ebc6da4ad..a337b29049a 100644
--- a/sql/events.h
+++ b/sql/events.h
@@ -26,8 +26,7 @@
*/
#ifdef HAVE_PSI_INTERFACE
-extern PSI_mutex_key key_LOCK_event_metadata,
- key_event_scheduler_LOCK_scheduler_state;
+extern PSI_mutex_key key_event_scheduler_LOCK_scheduler_state;
extern PSI_cond_key key_event_scheduler_COND_state;
extern PSI_thread_key key_thread_event_scheduler, key_thread_event_worker;
#endif /* HAVE_PSI_INTERFACE */
@@ -79,7 +78,6 @@ public:
enum enum_opt_event_scheduler { EVENTS_OFF, EVENTS_ON, EVENTS_DISABLED };
/* Protected using LOCK_global_system_variables only. */
static ulong opt_event_scheduler;
- static mysql_mutex_t LOCK_event_metadata;
static bool check_if_system_tables_error();
static bool start();
static bool stop();
diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc
index 6b07cf76d79..20745db9ce9 100644
--- a/sql/ha_ndbcluster.cc
+++ b/sql/ha_ndbcluster.cc
@@ -7382,38 +7382,35 @@ int ndbcluster_find_files(handlerton *hton, THD *thd,
}
/*
+ Delete old files.
+
ndbcluster_find_files() may be called from I_S code and ndbcluster_binlog
thread in situations when some tables are already open. This means that
code below will try to obtain exclusive metadata lock on some table
- while holding shared meta-data lock on other tables. This might lead to
- a deadlock, and therefore is disallowed by assertions of the metadata
- locking subsystem. This is violation of metadata
- locking protocol which has to be closed ASAP.
+ while holding shared meta-data lock on other tables. This might lead to a
+ deadlock but such a deadlock should be detected by MDL deadlock detector.
+
XXX: the scenario described above is not covered with any test.
*/
- if (!global_read_lock)
- {
- // Delete old files
- List_iterator_fast<char> it3(delete_list);
- while ((file_name_str= it3++))
- {
- DBUG_PRINT("info", ("Remove table %s/%s", db, file_name_str));
- // Delete the table and all related files
- TABLE_LIST table_list;
- table_list.init_one_table(db, strlen(db), file_name_str,
- strlen(file_name_str), file_name_str,
- TL_WRITE);
- table_list.mdl_request.set_type(MDL_EXCLUSIVE);
- (void)mysql_rm_table_part2(thd, &table_list,
- FALSE, /* if_exists */
- FALSE, /* drop_temporary */
- FALSE, /* drop_view */
- TRUE /* dont_log_query*/);
- trans_commit_implicit(thd); /* Safety, should be unnecessary. */
- thd->mdl_context.release_transactional_locks();
- /* Clear error message that is returned when table is deleted */
- thd->clear_error();
- }
+ List_iterator_fast<char> it3(delete_list);
+ while ((file_name_str= it3++))
+ {
+ DBUG_PRINT("info", ("Remove table %s/%s", db, file_name_str));
+ /* Delete the table and all related files. */
+ TABLE_LIST table_list;
+ table_list.init_one_table(db, strlen(db), file_name_str,
+ strlen(file_name_str), file_name_str,
+ TL_WRITE);
+ table_list.mdl_request.set_type(MDL_EXCLUSIVE);
+ (void)mysql_rm_table_part2(thd, &table_list,
+ FALSE, /* if_exists */
+ FALSE, /* drop_temporary */
+ FALSE, /* drop_view */
+ TRUE /* dont_log_query*/);
+ trans_commit_implicit(thd); /* Safety, should be unnecessary. */
+ thd->mdl_context.release_transactional_locks();
+ /* Clear error message that is returned when table is deleted */
+ thd->clear_error();
}
/* Lock mutex before creating .FRM files. */
diff --git a/sql/handler.cc b/sql/handler.cc
index eb060002f48..22bb3069a9f 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -29,8 +29,6 @@
#include "sql_cache.h" // query_cache, query_cache_*
#include "key.h" // key_copy, key_unpack, key_cmp_if_same, key_cmp
#include "sql_table.h" // build_table_filename
-#include "lock.h" // wait_if_global_read_lock,
- // start_waiting_global_read_lock
#include "sql_parse.h" // check_stack_overrun
#include "sql_acl.h" // SUPER_ACL
#include "sql_base.h" // free_io_cache
@@ -41,6 +39,7 @@
#include "transaction.h"
#include <errno.h>
#include "probes_mysql.h"
+#include "debug_sync.h" // DEBUG_SYNC
#ifdef WITH_PARTITION_STORAGE_ENGINE
#include "ha_partition.h"
@@ -1155,6 +1154,7 @@ int ha_commit_trans(THD *thd, bool all)
{
uint rw_ha_count;
bool rw_trans;
+ MDL_request mdl_request;
DBUG_EXECUTE_IF("crash_commit_before", DBUG_SUICIDE(););
@@ -1166,11 +1166,27 @@ int ha_commit_trans(THD *thd, bool all)
/* rw_trans is TRUE when we in a transaction changing data */
rw_trans= is_real_trans && (rw_ha_count > 0);
- if (rw_trans &&
- thd->global_read_lock.wait_if_global_read_lock(thd, FALSE, FALSE))
+ if (rw_trans)
{
- ha_rollback_trans(thd, all);
- DBUG_RETURN(1);
+ /*
+ Acquire a metadata lock which will ensure that COMMIT is blocked
+ by an active FLUSH TABLES WITH READ LOCK (and vice versa:
+ COMMIT in progress blocks FTWRL).
+
+ We allow the owner of FTWRL to COMMIT; we assume that it knows
+ what it does.
+ */
+ mdl_request.init(MDL_key::COMMIT, "", "", MDL_INTENTION_EXCLUSIVE,
+ MDL_EXPLICIT);
+
+ if (thd->mdl_context.acquire_lock(&mdl_request,
+ thd->variables.lock_wait_timeout))
+ {
+ ha_rollback_trans(thd, all);
+ DBUG_RETURN(1);
+ }
+
+ DEBUG_SYNC(thd, "ha_commit_trans_after_acquire_commit_lock");
}
if (rw_trans &&
@@ -1225,8 +1241,16 @@ int ha_commit_trans(THD *thd, bool all)
DBUG_EXECUTE_IF("crash_commit_after", DBUG_SUICIDE(););
RUN_HOOK(transaction, after_commit, (thd, FALSE));
end:
- if (rw_trans)
- thd->global_read_lock.start_waiting_global_read_lock(thd);
+ if (rw_trans && mdl_request.ticket)
+ {
+ /*
+ We do not always immediately release transactional locks
+ after ha_commit_trans() (see uses of ha_enable_transaction()),
+ thus we release the commit blocker lock as soon as it's
+ not needed.
+ */
+ thd->mdl_context.release_lock(mdl_request.ticket);
+ }
}
/* Free resources and perform other cleanup even for 'empty' transactions. */
else if (is_real_trans)
diff --git a/sql/lock.cc b/sql/lock.cc
index 84cc24b3f61..2e141e1c9fb 100644
--- a/sql/lock.cc
+++ b/sql/lock.cc
@@ -777,8 +777,11 @@ bool lock_schema_name(THD *thd, const char *db)
return TRUE;
}
- global_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE);
- mdl_request.init(MDL_key::SCHEMA, db, "", MDL_EXCLUSIVE);
+ if (thd->global_read_lock.can_acquire_protection())
+ return TRUE;
+ global_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE,
+ MDL_STATEMENT);
+ mdl_request.init(MDL_key::SCHEMA, db, "", MDL_EXCLUSIVE, MDL_TRANSACTION);
mdl_requests.push_front(&mdl_request);
mdl_requests.push_front(&global_request);
@@ -793,13 +796,13 @@ bool lock_schema_name(THD *thd, const char *db)
/**
- Obtain an exclusive metadata lock on the stored routine name.
+ Obtain an exclusive metadata lock on an object name.
@param thd Thread handle.
- @param is_function Stored routine type (only functions or procedures
- are name-locked.
- @param db The schema the routine belongs to.
- @param name Routine name.
+ @param mdl_type Object type (currently functions, procedures
+ and events can be name-locked).
+ @param db The schema the object belongs to.
+ @param name Object name in the schema.
This function assumes that no metadata locks were acquired
before calling it. Additionally, it cannot be called while
@@ -815,12 +818,9 @@ bool lock_schema_name(THD *thd, const char *db)
or this connection was killed.
*/
-bool lock_routine_name(THD *thd, bool is_function,
+bool lock_object_name(THD *thd, MDL_key::enum_mdl_namespace mdl_type,
const char *db, const char *name)
{
- MDL_key::enum_mdl_namespace mdl_type= (is_function ?
- MDL_key::FUNCTION :
- MDL_key::PROCEDURE);
MDL_request_list mdl_requests;
MDL_request global_request;
MDL_request schema_request;
@@ -836,9 +836,13 @@ bool lock_routine_name(THD *thd, bool is_function,
DBUG_ASSERT(name);
DEBUG_SYNC(thd, "before_wait_locked_pname");
- global_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE);
- schema_request.init(MDL_key::SCHEMA, db, "", MDL_INTENTION_EXCLUSIVE);
- mdl_request.init(mdl_type, db, name, MDL_EXCLUSIVE);
+ if (thd->global_read_lock.can_acquire_protection())
+ return TRUE;
+ global_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE,
+ MDL_STATEMENT);
+ schema_request.init(MDL_key::SCHEMA, db, "", MDL_INTENTION_EXCLUSIVE,
+ MDL_TRANSACTION);
+ mdl_request.init(mdl_type, db, name, MDL_EXCLUSIVE, MDL_TRANSACTION);
mdl_requests.push_front(&mdl_request);
mdl_requests.push_front(&schema_request);
@@ -888,45 +892,24 @@ static void print_lock_error(int error, const char *table)
/****************************************************************************
Handling of global read locks
+ Global read lock is implemented using metadata lock infrastructure.
+
Taking the global read lock is TWO steps (2nd step is optional; without
it, COMMIT of existing transactions will be allowed):
lock_global_read_lock() THEN make_global_read_lock_block_commit().
- The global locks are handled through the global variables:
- global_read_lock
- count of threads which have the global read lock (i.e. have completed at
- least the first step above)
- global_read_lock_blocks_commit
- count of threads which have the global read lock and block
- commits (i.e. are in or have completed the second step above)
- waiting_for_read_lock
- count of threads which want to take a global read lock but cannot
- protect_against_global_read_lock
- count of threads which have set protection against global read lock.
-
- access to them is protected with a mutex LOCK_global_read_lock
-
- (XXX: one should never take LOCK_open if LOCK_global_read_lock is
- taken, otherwise a deadlock may occur. Other mutexes could be a
- problem too - grep the code for global_read_lock if you want to use
- any other mutex here) Also one must not hold LOCK_open when calling
- wait_if_global_read_lock(). When the thread with the global read lock
- tries to close its tables, it needs to take LOCK_open in
- close_thread_table().
-
How blocking of threads by global read lock is achieved: that's
- advisory. Any piece of code which should be blocked by global read lock must
- be designed like this:
- - call to wait_if_global_read_lock(). When this returns 0, no global read
- lock is owned; if argument abort_on_refresh was 0, none can be obtained.
- - job
- - if abort_on_refresh was 0, call to start_waiting_global_read_lock() to
- allow other threads to get the global read lock. I.e. removal of the
- protection.
- (Note: it's a bit like an implementation of rwlock).
-
- [ I am sorry to mention some SQL syntaxes below I know I shouldn't but found
- no better descriptive way ]
+ semi-automatic. We assume that any statement which should be blocked
+ by global read lock will either open and acquires write-lock on tables
+ or acquires metadata locks on objects it is going to modify. For any
+ such statement global IX metadata lock is automatically acquired for
+ its duration (in case of LOCK TABLES until end of LOCK TABLES mode).
+ And lock_global_read_lock() simply acquires global S metadata lock
+ and thus prohibits execution of statements which modify data (unless
+ they modify only temporary tables). If deadlock happens it is detected
+ by MDL subsystem and resolved in the standard fashion (by backing-off
+ metadata locks acquired so far and restarting open tables process
+ if possible).
Why does FLUSH TABLES WITH READ LOCK need to block COMMIT: because it's used
to read a non-moving SHOW MASTER STATUS, and a COMMIT writes to the binary
@@ -960,11 +943,6 @@ static void print_lock_error(int error, const char *table)
****************************************************************************/
-volatile uint global_read_lock=0;
-volatile uint global_read_lock_blocks_commit=0;
-static volatile uint protect_against_global_read_lock=0;
-static volatile uint waiting_for_read_lock=0;
-
/**
Take global read lock, wait if there is protection against lock.
@@ -985,84 +963,17 @@ bool Global_read_lock::lock_global_read_lock(THD *thd)
if (!m_state)
{
MDL_request mdl_request;
- const char *old_message;
- const char *new_message= "Waiting to get readlock";
- (void) mysql_mutex_lock(&LOCK_global_read_lock);
-
- old_message= thd->enter_cond(&COND_global_read_lock,
- &LOCK_global_read_lock, new_message);
- DBUG_PRINT("info",
- ("waiting_for: %d protect_against: %d",
- waiting_for_read_lock, protect_against_global_read_lock));
-
- waiting_for_read_lock++;
-
-#if defined(ENABLED_DEBUG_SYNC)
- /*
- The below sync point fires if we have to wait for
- protect_against_global_read_lock.
-
- WARNING: Beware to use WAIT_FOR with this sync point. We hold
- LOCK_global_read_lock here.
-
- The sync point is after enter_cond() so that proc_info is
- available immediately after the sync point sends a SIGNAL. This
- can make tests more reliable.
-
- The sync point is before the loop so that it is executed only once.
- */
- if (protect_against_global_read_lock && !thd->killed)
- {
- DEBUG_SYNC(thd, "wait_lock_global_read_lock");
- }
-#endif /* defined(ENABLED_DEBUG_SYNC) */
-
- while (protect_against_global_read_lock && !thd->killed)
- mysql_cond_wait(&COND_global_read_lock, &LOCK_global_read_lock);
- waiting_for_read_lock--;
- if (thd->killed)
- {
- thd->exit_cond(old_message);
- DBUG_RETURN(1);
- }
- m_state= GRL_ACQUIRED;
- global_read_lock++;
- thd->exit_cond(old_message); // this unlocks LOCK_global_read_lock
- /*
- When we perform FLUSH TABLES or ALTER TABLE under LOCK TABLES,
- tables being reopened are protected only by meta-data locks at
- some point. To avoid sneaking in with our global read lock at
- this moment we have to take global shared meta data lock.
-
- TODO: We should change this code to acquire global shared metadata
- lock before acquiring global read lock. But in order to do
- this we have to get rid of all those places in which
- wait_if_global_read_lock() is called before acquiring
- metadata locks first. Also long-term we should get rid of
- redundancy between metadata locks, global read lock and DDL
- blocker (see WL#4399 and WL#4400).
- */
DBUG_ASSERT(! thd->mdl_context.is_lock_owner(MDL_key::GLOBAL, "", "",
MDL_SHARED));
- mdl_request.init(MDL_key::GLOBAL, "", "", MDL_SHARED);
+ mdl_request.init(MDL_key::GLOBAL, "", "", MDL_SHARED, MDL_EXPLICIT);
if (thd->mdl_context.acquire_lock(&mdl_request,
thd->variables.lock_wait_timeout))
- {
- /* Our thread was killed -- return back to initial state. */
- mysql_mutex_lock(&LOCK_global_read_lock);
- if (!(--global_read_lock))
- {
- DBUG_PRINT("signal", ("Broadcasting COND_global_read_lock"));
- mysql_cond_broadcast(&COND_global_read_lock);
- }
- mysql_mutex_unlock(&LOCK_global_read_lock);
- m_state= GRL_NONE;
DBUG_RETURN(1);
- }
- thd->mdl_context.move_ticket_after_trans_sentinel(mdl_request.ticket);
+
m_mdl_global_shared_lock= mdl_request.ticket;
+ m_state= GRL_ACQUIRED;
}
/*
We DON'T set global_read_lock_blocks_commit now, it will be set after
@@ -1088,166 +999,22 @@ bool Global_read_lock::lock_global_read_lock(THD *thd)
void Global_read_lock::unlock_global_read_lock(THD *thd)
{
- uint tmp;
DBUG_ENTER("unlock_global_read_lock");
- DBUG_PRINT("info",
- ("global_read_lock: %u global_read_lock_blocks_commit: %u",
- global_read_lock, global_read_lock_blocks_commit));
DBUG_ASSERT(m_mdl_global_shared_lock && m_state);
- thd->mdl_context.release_lock(m_mdl_global_shared_lock);
- m_mdl_global_shared_lock= NULL;
-
- mysql_mutex_lock(&LOCK_global_read_lock);
- tmp= --global_read_lock;
- if (m_state == GRL_ACQUIRED_AND_BLOCKS_COMMIT)
- --global_read_lock_blocks_commit;
- mysql_mutex_unlock(&LOCK_global_read_lock);
- /* Send the signal outside the mutex to avoid a context switch */
- if (!tmp)
+ if (m_mdl_blocks_commits_lock)
{
- DBUG_PRINT("signal", ("Broadcasting COND_global_read_lock"));
- mysql_cond_broadcast(&COND_global_read_lock);
+ thd->mdl_context.release_lock(m_mdl_blocks_commits_lock);
+ m_mdl_blocks_commits_lock= NULL;
}
+ thd->mdl_context.release_lock(m_mdl_global_shared_lock);
+ m_mdl_global_shared_lock= NULL;
m_state= GRL_NONE;
DBUG_VOID_RETURN;
}
-/**
- Wait if the global read lock is set, and optionally seek protection against
- global read lock.
-
- See also "Handling of global read locks" above.
-
- @param thd Reference to thread.
- @param abort_on_refresh If True, abort waiting if a refresh occurs,
- do NOT seek protection against GRL.
- If False, wait until the GRL is released and seek
- protection against GRL.
- @param is_not_commit If False, called from a commit operation,
- wait only if commit blocking is also enabled.
-
- @retval False Success, protection against global read lock is set
- (if !abort_on_refresh)
- @retval True Failure, wait was aborted or thread was killed.
-*/
-
-#define must_wait (global_read_lock && \
- (is_not_commit || \
- global_read_lock_blocks_commit))
-
-bool Global_read_lock::
-wait_if_global_read_lock(THD *thd, bool abort_on_refresh,
- bool is_not_commit)
-{
- const char *UNINIT_VAR(old_message);
- bool result= 0, need_exit_cond;
- DBUG_ENTER("wait_if_global_read_lock");
-
- /*
- If we already have protection against global read lock,
- just increment the counter.
- */
- if (unlikely(m_protection_count > 0))
- {
- if (!abort_on_refresh)
- m_protection_count++;
- DBUG_RETURN(FALSE);
- }
- /*
- Assert that we do not own LOCK_open. If we would own it, other
- threads could not close their tables. This would make a pretty
- deadlock.
- */
- mysql_mutex_assert_not_owner(&LOCK_open);
-
- mysql_mutex_lock(&LOCK_global_read_lock);
- if ((need_exit_cond= must_wait))
- {
- if (m_state) // This thread had the read locks
- {
- if (is_not_commit)
- my_message(ER_CANT_UPDATE_WITH_READLOCK,
- ER(ER_CANT_UPDATE_WITH_READLOCK), MYF(0));
- mysql_mutex_unlock(&LOCK_global_read_lock);
- /*
- We allow FLUSHer to COMMIT; we assume FLUSHer knows what it does.
- This allowance is needed to not break existing versions of innobackup
- which do a BEGIN; INSERT; FLUSH TABLES WITH READ LOCK; COMMIT.
- */
- DBUG_RETURN(is_not_commit);
- }
- old_message=thd->enter_cond(&COND_global_read_lock, &LOCK_global_read_lock,
- "Waiting for release of readlock");
- while (must_wait && ! thd->killed &&
- (!abort_on_refresh || !thd->open_tables ||
- thd->open_tables->s->version == refresh_version))
- {
- DBUG_PRINT("signal", ("Waiting for COND_global_read_lock"));
- mysql_cond_wait(&COND_global_read_lock, &LOCK_global_read_lock);
- DBUG_PRINT("signal", ("Got COND_global_read_lock"));
- }
- if (thd->killed)
- result=1;
- }
- if (!abort_on_refresh && !result)
- {
- m_protection_count++;
- protect_against_global_read_lock++;
- DBUG_PRINT("sql_lock", ("protect_against_global_read_lock incr: %u",
- protect_against_global_read_lock));
- }
- /*
- The following is only true in case of a global read locks (which is rare)
- and if old_message is set
- */
- if (unlikely(need_exit_cond))
- thd->exit_cond(old_message); // this unlocks LOCK_global_read_lock
- else
- mysql_mutex_unlock(&LOCK_global_read_lock);
- DBUG_RETURN(result);
-}
-
-
-/**
- Release protection against global read lock and restart
- global read lock waiters.
-
- Should only be called if we have protection against global read lock.
-
- See also "Handling of global read locks" above.
-
- @param thd Reference to thread.
-*/
-
-void Global_read_lock::start_waiting_global_read_lock(THD *thd)
-{
- bool tmp;
- DBUG_ENTER("start_waiting_global_read_lock");
- /*
- Ignore request if we do not have protection against global read lock.
- (Note that this is a violation of the interface contract, hence the assert).
- */
- DBUG_ASSERT(m_protection_count > 0);
- if (unlikely(m_protection_count == 0))
- DBUG_VOID_RETURN;
- /* Decrement local read lock protection counter, return if we still have it */
- if (unlikely(--m_protection_count > 0))
- DBUG_VOID_RETURN;
- if (unlikely(m_state))
- DBUG_VOID_RETURN;
- mysql_mutex_lock(&LOCK_global_read_lock);
- DBUG_ASSERT(protect_against_global_read_lock);
- tmp= (!--protect_against_global_read_lock &&
- (waiting_for_read_lock || global_read_lock_blocks_commit));
- mysql_mutex_unlock(&LOCK_global_read_lock);
- if (tmp)
- mysql_cond_broadcast(&COND_global_read_lock);
- DBUG_VOID_RETURN;
-}
-
/**
Make global read lock also block commits.
@@ -1266,8 +1033,7 @@ void Global_read_lock::start_waiting_global_read_lock(THD *thd)
bool Global_read_lock::make_global_read_lock_block_commit(THD *thd)
{
- bool error;
- const char *old_message;
+ MDL_request mdl_request;
DBUG_ENTER("make_global_read_lock_block_commit");
/*
If we didn't succeed lock_global_read_lock(), or if we already suceeded
@@ -1275,42 +1041,32 @@ bool Global_read_lock::make_global_read_lock_block_commit(THD *thd)
*/
if (m_state != GRL_ACQUIRED)
DBUG_RETURN(0);
- mysql_mutex_lock(&LOCK_global_read_lock);
- /* increment this BEFORE waiting on cond (otherwise race cond) */
- global_read_lock_blocks_commit++;
- /* For testing we set up some blocking, to see if we can be killed */
- DBUG_EXECUTE_IF("make_global_read_lock_block_commit_loop",
- protect_against_global_read_lock++;);
- old_message= thd->enter_cond(&COND_global_read_lock, &LOCK_global_read_lock,
- "Waiting for all running commits to finish");
- while (protect_against_global_read_lock && !thd->killed)
- mysql_cond_wait(&COND_global_read_lock, &LOCK_global_read_lock);
- DBUG_EXECUTE_IF("make_global_read_lock_block_commit_loop",
- protect_against_global_read_lock--;);
- if ((error= test(thd->killed)))
- global_read_lock_blocks_commit--; // undo what we did
- else
- m_state= GRL_ACQUIRED_AND_BLOCKS_COMMIT;
- thd->exit_cond(old_message); // this unlocks LOCK_global_read_lock
- DBUG_RETURN(error);
+
+ mdl_request.init(MDL_key::COMMIT, "", "", MDL_SHARED, MDL_EXPLICIT);
+
+ if (thd->mdl_context.acquire_lock(&mdl_request,
+ thd->variables.lock_wait_timeout))
+ DBUG_RETURN(TRUE);
+
+ m_mdl_blocks_commits_lock= mdl_request.ticket;
+ m_state= GRL_ACQUIRED_AND_BLOCKS_COMMIT;
+
+ DBUG_RETURN(FALSE);
}
/**
- Broadcast COND_global_read_lock.
-
- TODO/FIXME: Dmitry thinks that we broadcast on COND_global_read_lock
- when old instance of table is closed to avoid races
- between incrementing refresh_version and
- wait_if_global_read_lock(thd, TRUE, FALSE) call.
- Once global read lock implementation starts using MDL
- infrastructure this will became unnecessary and should
- be removed.
+ Set explicit duration for metadata locks which are used to implement GRL.
+
+ @param thd Reference to thread.
*/
-void broadcast_refresh(void)
+void Global_read_lock::set_explicit_lock_duration(THD *thd)
{
- mysql_cond_broadcast(&COND_global_read_lock);
+ if (m_mdl_global_shared_lock)
+ thd->mdl_context.set_lock_duration(m_mdl_global_shared_lock, MDL_EXPLICIT);
+ if (m_mdl_blocks_commits_lock)
+ thd->mdl_context.set_lock_duration(m_mdl_blocks_commits_lock, MDL_EXPLICIT);
}
/**
diff --git a/sql/lock.h b/sql/lock.h
index c097c8d269e..6f779595af8 100644
--- a/sql/lock.h
+++ b/sql/lock.h
@@ -2,6 +2,7 @@
#define LOCK_INCLUDED
#include "thr_lock.h" /* thr_lock_type */
+#include "mdl.h"
// Forward declarations
struct TABLE;
@@ -18,11 +19,10 @@ void mysql_lock_remove(THD *thd, MYSQL_LOCK *locked,TABLE *table);
void mysql_lock_abort(THD *thd, TABLE *table, bool upgrade_lock);
bool mysql_lock_abort_for_thread(THD *thd, TABLE *table);
MYSQL_LOCK *mysql_lock_merge(MYSQL_LOCK *a,MYSQL_LOCK *b);
-void broadcast_refresh(void);
/* Lock based on name */
bool lock_schema_name(THD *thd, const char *db);
/* Lock based on stored routine name */
-bool lock_routine_name(THD *thd, bool is_function, const char *db,
- const char *name);
+bool lock_object_name(THD *thd, MDL_key::enum_mdl_namespace mdl_type,
+ const char *db, const char *name);
#endif /* LOCK_INCLUDED */
diff --git a/sql/log_event.cc b/sql/log_event.cc
index b4641af07c5..2feb69493a8 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -4961,6 +4961,8 @@ error:
*/
if (! thd->in_multi_stmt_transaction_mode())
thd->mdl_context.release_transactional_locks();
+ else
+ thd->mdl_context.release_statement_locks();
DBUG_EXECUTE_IF("LOAD_DATA_INFILE_has_fatal_error",
thd->is_slave_error= 0; thd->is_fatal_error= 1;);
diff --git a/sql/mdl.cc b/sql/mdl.cc
index d53ddcee0c8..3aed54ce12d 100644
--- a/sql/mdl.cc
+++ b/sql/mdl.cc
@@ -68,8 +68,6 @@ static void init_mdl_psi_keys(void)
}
#endif /* HAVE_PSI_INTERFACE */
-void notify_shared_lock(THD *thd, MDL_ticket *conflicting_ticket);
-
/**
Thread state names to be used in case when we have to wait on resource
@@ -78,12 +76,14 @@ void notify_shared_lock(THD *thd, MDL_ticket *conflicting_ticket);
const char *MDL_key::m_namespace_to_wait_state_name[NAMESPACE_END]=
{
- "Waiting for global metadata lock",
+ "Waiting for global read lock",
"Waiting for schema metadata lock",
"Waiting for table metadata lock",
"Waiting for stored function metadata lock",
"Waiting for stored procedure metadata lock",
- NULL
+ "Waiting for trigger metadata lock",
+ "Waiting for event metadata lock",
+ "Waiting for commit lock"
};
static bool mdl_initialized= 0;
@@ -100,7 +100,6 @@ class MDL_map
public:
void init();
void destroy();
- MDL_lock *find(const MDL_key *key);
MDL_lock *find_or_insert(const MDL_key *key);
void remove(MDL_lock *lock);
private:
@@ -110,6 +109,10 @@ private:
HASH m_locks;
/* Protects access to m_locks hash. */
mysql_mutex_t m_mutex;
+ /** Pre-allocated MDL_lock object for GLOBAL namespace. */
+ MDL_lock *m_global_lock;
+ /** Pre-allocated MDL_lock object for COMMIT namespace. */
+ MDL_lock *m_commit_lock;
};
@@ -354,18 +357,6 @@ public:
inline static MDL_lock *create(const MDL_key *key);
- void notify_shared_locks(MDL_context *ctx)
- {
- Ticket_iterator it(m_granted);
- MDL_ticket *conflicting_ticket;
-
- while ((conflicting_ticket= it++))
- {
- if (conflicting_ticket->get_ctx() != ctx)
- notify_shared_lock(ctx->get_thd(), conflicting_ticket);
- }
- }
-
void reschedule_waiters();
void remove_ticket(Ticket_list MDL_lock::*queue, MDL_ticket *ticket);
@@ -373,6 +364,9 @@ public:
bool visit_subgraph(MDL_ticket *waiting_ticket,
MDL_wait_for_graph_visitor *gvisitor);
+ virtual bool needs_notification(const MDL_ticket *ticket) const = 0;
+ virtual void notify_conflicting_locks(MDL_context *ctx) = 0;
+
/** List of granted tickets for this lock. */
Ticket_list m_granted;
/** Tickets for contexts waiting to acquire a lock. */
@@ -442,6 +436,11 @@ public:
{
return m_waiting_incompatible;
}
+ virtual bool needs_notification(const MDL_ticket *ticket) const
+ {
+ return (ticket->get_type() == MDL_SHARED);
+ }
+ virtual void notify_conflicting_locks(MDL_context *ctx);
private:
static const bitmap_t m_granted_incompatible[MDL_TYPE_END];
@@ -469,6 +468,11 @@ public:
{
return m_waiting_incompatible;
}
+ virtual bool needs_notification(const MDL_ticket *ticket) const
+ {
+ return ticket->is_upgradable_or_exclusive();
+ }
+ virtual void notify_conflicting_locks(MDL_context *ctx);
private:
static const bitmap_t m_granted_incompatible[MDL_TYPE_END];
@@ -536,9 +540,14 @@ void mdl_destroy()
void MDL_map::init()
{
+ MDL_key global_lock_key(MDL_key::GLOBAL, "", "");
+ MDL_key commit_lock_key(MDL_key::COMMIT, "", "");
+
mysql_mutex_init(key_MDL_map_mutex, &m_mutex, NULL);
my_hash_init(&m_locks, &my_charset_bin, 16 /* FIXME */, 0, 0,
mdl_locks_key, 0, 0);
+ m_global_lock= MDL_lock::create(&global_lock_key);
+ m_commit_lock= MDL_lock::create(&commit_lock_key);
}
@@ -552,6 +561,8 @@ void MDL_map::destroy()
DBUG_ASSERT(!m_locks.records);
mysql_mutex_destroy(&m_mutex);
my_hash_free(&m_locks);
+ MDL_lock::destroy(m_global_lock);
+ MDL_lock::destroy(m_commit_lock);
}
@@ -569,43 +580,28 @@ MDL_lock* MDL_map::find_or_insert(const MDL_key *mdl_key)
MDL_lock *lock;
my_hash_value_type hash_value;
- hash_value= my_calc_hash(&m_locks, mdl_key->ptr(), mdl_key->length());
-
-retry:
- mysql_mutex_lock(&m_mutex);
- if (!(lock= (MDL_lock*) my_hash_search_using_hash_value(&m_locks,
- hash_value,
- mdl_key->ptr(),
- mdl_key->length())))
+ if (mdl_key->mdl_namespace() == MDL_key::GLOBAL ||
+ mdl_key->mdl_namespace() == MDL_key::COMMIT)
{
- lock= MDL_lock::create(mdl_key);
- if (!lock || my_hash_insert(&m_locks, (uchar*)lock))
- {
- mysql_mutex_unlock(&m_mutex);
- MDL_lock::destroy(lock);
- return NULL;
- }
- }
-
- if (move_from_hash_to_lock_mutex(lock))
- goto retry;
+ /*
+ Avoid locking m_mutex when lock for GLOBAL or COMMIT namespace is
+ requested. Return pointer to pre-allocated MDL_lock instance instead.
+ Such an optimization allows to save one mutex lock/unlock for any
+ statement changing data.
- return lock;
-}
+ It works since these namespaces contain only one element so keys
+ for them look like '<namespace-id>\0\0'.
+ */
+ DBUG_ASSERT(mdl_key->length() == 3);
+ lock= (mdl_key->mdl_namespace() == MDL_key::GLOBAL) ? m_global_lock :
+ m_commit_lock;
-/**
- Find MDL_lock object corresponding to the key.
+ mysql_prlock_wrlock(&lock->m_rwlock);
- @retval non-NULL - MDL_lock instance for the key with locked
- MDL_lock::m_rwlock.
- @retval NULL - There was no MDL_lock for the key.
-*/
+ return lock;
+ }
-MDL_lock* MDL_map::find(const MDL_key *mdl_key)
-{
- MDL_lock *lock;
- my_hash_value_type hash_value;
hash_value= my_calc_hash(&m_locks, mdl_key->ptr(), mdl_key->length());
@@ -616,8 +612,13 @@ retry:
mdl_key->ptr(),
mdl_key->length())))
{
- mysql_mutex_unlock(&m_mutex);
- return NULL;
+ lock= MDL_lock::create(mdl_key);
+ if (!lock || my_hash_insert(&m_locks, (uchar*)lock))
+ {
+ mysql_mutex_unlock(&m_mutex);
+ MDL_lock::destroy(lock);
+ return NULL;
+ }
}
if (move_from_hash_to_lock_mutex(lock))
@@ -684,6 +685,17 @@ void MDL_map::remove(MDL_lock *lock)
{
uint ref_usage, ref_release;
+ if (lock->key.mdl_namespace() == MDL_key::GLOBAL ||
+ lock->key.mdl_namespace() == MDL_key::COMMIT)
+ {
+ /*
+ Never destroy pre-allocated MDL_lock objects for GLOBAL and
+ COMMIT namespaces.
+ */
+ mysql_prlock_unlock(&lock->m_rwlock);
+ return;
+ }
+
/*
Destroy the MDL_lock object, but ensure that anyone that is
holding a reference to the object is not remaining, if so he
@@ -719,8 +731,7 @@ void MDL_map::remove(MDL_lock *lock)
*/
MDL_context::MDL_context()
- :m_trans_sentinel(NULL),
- m_thd(NULL),
+ : m_thd(NULL),
m_needs_thr_lock_abort(FALSE),
m_waiting_for(NULL)
{
@@ -742,7 +753,9 @@ MDL_context::MDL_context()
void MDL_context::destroy()
{
- DBUG_ASSERT(m_tickets.is_empty());
+ DBUG_ASSERT(m_tickets[MDL_STATEMENT].is_empty() &&
+ m_tickets[MDL_TRANSACTION].is_empty() &&
+ m_tickets[MDL_EXPLICIT].is_empty());
mysql_prlock_destroy(&m_LOCK_waiting_for);
}
@@ -771,10 +784,12 @@ void MDL_context::destroy()
void MDL_request::init(MDL_key::enum_mdl_namespace mdl_namespace,
const char *db_arg,
const char *name_arg,
- enum enum_mdl_type mdl_type_arg)
+ enum_mdl_type mdl_type_arg,
+ enum_mdl_duration mdl_duration_arg)
{
key.mdl_key_init(mdl_namespace, db_arg, name_arg);
type= mdl_type_arg;
+ duration= mdl_duration_arg;
ticket= NULL;
}
@@ -789,49 +804,17 @@ void MDL_request::init(MDL_key::enum_mdl_namespace mdl_namespace,
*/
void MDL_request::init(const MDL_key *key_arg,
- enum enum_mdl_type mdl_type_arg)
+ enum_mdl_type mdl_type_arg,
+ enum_mdl_duration mdl_duration_arg)
{
key.mdl_key_init(key_arg);
type= mdl_type_arg;
+ duration= mdl_duration_arg;
ticket= NULL;
}
/**
- Allocate and initialize one lock request.
-
- Same as mdl_init_lock(), but allocates the lock and the key buffer
- on a memory root. Necessary to lock ad-hoc tables, e.g.
- mysql.* tables of grant and data dictionary subsystems.
-
- @param mdl_namespace Id of namespace of object to be locked
- @param db Name of database to which object belongs
- @param name Name of of object
- @param root MEM_ROOT on which object should be allocated
-
- @note The allocated lock request will have MDL_SHARED type.
-
- @retval 0 Error if out of memory
- @retval non-0 Pointer to an object representing a lock request
-*/
-
-MDL_request *
-MDL_request::create(MDL_key::enum_mdl_namespace mdl_namespace, const char *db,
- const char *name, enum_mdl_type mdl_type,
- MEM_ROOT *root)
-{
- MDL_request *mdl_request;
-
- if (!(mdl_request= (MDL_request*) alloc_root(root, sizeof(MDL_request))))
- return NULL;
-
- mdl_request->init(mdl_namespace, db, name, mdl_type);
-
- return mdl_request;
-}
-
-
-/**
Auxiliary functions needed for creation/destruction of MDL_lock objects.
@note Also chooses an MDL_lock descendant appropriate for object namespace.
@@ -846,6 +829,7 @@ inline MDL_lock *MDL_lock::create(const MDL_key *mdl_key)
{
case MDL_key::GLOBAL:
case MDL_key::SCHEMA:
+ case MDL_key::COMMIT:
return new MDL_scoped_lock(mdl_key);
default:
return new MDL_object_lock(mdl_key);
@@ -867,9 +851,17 @@ void MDL_lock::destroy(MDL_lock *lock)
on memory allocation by reusing released objects.
*/
-MDL_ticket *MDL_ticket::create(MDL_context *ctx_arg, enum_mdl_type type_arg)
+MDL_ticket *MDL_ticket::create(MDL_context *ctx_arg, enum_mdl_type type_arg
+#ifndef DBUG_OFF
+ , enum_mdl_duration duration_arg
+#endif
+ )
{
- return new MDL_ticket(ctx_arg, type_arg);
+ return new MDL_ticket(ctx_arg, type_arg
+#ifndef DBUG_OFF
+ , duration_arg
+#endif
+ );
}
@@ -1438,13 +1430,11 @@ bool MDL_ticket::is_incompatible_when_waiting(enum_mdl_type type) const
/**
Check whether the context already holds a compatible lock ticket
on an object.
- Start searching the transactional locks. If not
- found in the list of transactional locks, look at LOCK TABLES
- and HANDLER locks.
+ Start searching from list of locks for the same duration as lock
+ being requested. If not look at lists for other durations.
@param mdl_request Lock request object for lock to be acquired
- @param[out] is_transactional FALSE if we pass beyond m_trans_sentinel
- while searching for ticket, otherwise TRUE.
+ @param[out] result_duration Duration of lock which was found.
@note Tickets which correspond to lock types "stronger" than one
being requested are also considered compatible.
@@ -1454,24 +1444,28 @@ bool MDL_ticket::is_incompatible_when_waiting(enum_mdl_type type) const
MDL_ticket *
MDL_context::find_ticket(MDL_request *mdl_request,
- bool *is_transactional)
+ enum_mdl_duration *result_duration)
{
MDL_ticket *ticket;
- Ticket_iterator it(m_tickets);
-
- *is_transactional= TRUE;
+ int i;
- while ((ticket= it++))
+ for (i= 0; i < MDL_DURATION_END; i++)
{
- if (ticket == m_trans_sentinel)
- *is_transactional= FALSE;
+ enum_mdl_duration duration= (enum_mdl_duration)((mdl_request->duration+i) %
+ MDL_DURATION_END);
+ Ticket_iterator it(m_tickets[duration]);
- if (mdl_request->key.is_equal(&ticket->m_lock->key) &&
- ticket->has_stronger_or_equal_type(mdl_request->type))
- break;
+ while ((ticket= it++))
+ {
+ if (mdl_request->key.is_equal(&ticket->m_lock->key) &&
+ ticket->has_stronger_or_equal_type(mdl_request->type))
+ {
+ *result_duration= duration;
+ return ticket;
+ }
+ }
}
-
- return ticket;
+ return NULL;
}
@@ -1549,7 +1543,7 @@ MDL_context::try_acquire_lock_impl(MDL_request *mdl_request,
MDL_lock *lock;
MDL_key *key= &mdl_request->key;
MDL_ticket *ticket;
- bool is_transactional;
+ enum_mdl_duration found_duration;
DBUG_ASSERT(mdl_request->type != MDL_EXCLUSIVE ||
is_lock_owner(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE));
@@ -1563,7 +1557,7 @@ MDL_context::try_acquire_lock_impl(MDL_request *mdl_request,
Check whether the context already holds a shared lock on the object,
and if so, grant the request.
*/
- if ((ticket= find_ticket(mdl_request, &is_transactional)))
+ if ((ticket= find_ticket(mdl_request, &found_duration)))
{
DBUG_ASSERT(ticket->m_lock);
DBUG_ASSERT(ticket->has_stronger_or_equal_type(mdl_request->type));
@@ -1585,7 +1579,9 @@ MDL_context::try_acquire_lock_impl(MDL_request *mdl_request,
a different alias.
*/
mdl_request->ticket= ticket;
- if (!is_transactional && clone_ticket(mdl_request))
+ if ((found_duration != mdl_request->duration ||
+ mdl_request->duration == MDL_EXPLICIT) &&
+ clone_ticket(mdl_request))
{
/* Clone failed. */
mdl_request->ticket= NULL;
@@ -1594,7 +1590,11 @@ MDL_context::try_acquire_lock_impl(MDL_request *mdl_request,
return FALSE;
}
- if (!(ticket= MDL_ticket::create(this, mdl_request->type)))
+ if (!(ticket= MDL_ticket::create(this, mdl_request->type
+#ifndef DBUG_OFF
+ , mdl_request->duration
+#endif
+ )))
return TRUE;
/* The below call implicitly locks MDL_lock::m_rwlock on success. */
@@ -1612,7 +1612,7 @@ MDL_context::try_acquire_lock_impl(MDL_request *mdl_request,
mysql_prlock_unlock(&lock->m_rwlock);
- m_tickets.push_front(ticket);
+ m_tickets[mdl_request->duration].push_front(ticket);
mdl_request->ticket= ticket;
}
@@ -1647,7 +1647,11 @@ MDL_context::clone_ticket(MDL_request *mdl_request)
we effectively downgrade the cloned lock to the level of
the request.
*/
- if (!(ticket= MDL_ticket::create(this, mdl_request->type)))
+ if (!(ticket= MDL_ticket::create(this, mdl_request->type
+#ifndef DBUG_OFF
+ , mdl_request->duration
+#endif
+ )))
return TRUE;
/* clone() is not supposed to be used to get a stronger lock. */
@@ -1660,36 +1664,74 @@ MDL_context::clone_ticket(MDL_request *mdl_request)
ticket->m_lock->m_granted.add_ticket(ticket);
mysql_prlock_unlock(&ticket->m_lock->m_rwlock);
- m_tickets.push_front(ticket);
+ m_tickets[mdl_request->duration].push_front(ticket);
return FALSE;
}
/**
- Notify a thread holding a shared metadata lock which
- conflicts with a pending exclusive lock.
+ Notify threads holding a shared metadata locks on object which
+ conflict with a pending X, SNW or SNRW lock.
- @param thd Current thread context
- @param conflicting_ticket Conflicting metadata lock
+ @param ctx MDL_context for current thread.
*/
-void notify_shared_lock(THD *thd, MDL_ticket *conflicting_ticket)
+void MDL_object_lock::notify_conflicting_locks(MDL_context *ctx)
{
- /* Only try to abort locks on which we back off. */
- if (conflicting_ticket->get_type() < MDL_SHARED_NO_WRITE)
+ Ticket_iterator it(m_granted);
+ MDL_ticket *conflicting_ticket;
+
+ while ((conflicting_ticket= it++))
{
- MDL_context *conflicting_ctx= conflicting_ticket->get_ctx();
- THD *conflicting_thd= conflicting_ctx->get_thd();
- DBUG_ASSERT(thd != conflicting_thd); /* Self-deadlock */
+ /* Only try to abort locks on which we back off. */
+ if (conflicting_ticket->get_ctx() != ctx &&
+ conflicting_ticket->get_type() < MDL_SHARED_NO_WRITE)
- /*
- If thread which holds conflicting lock is waiting on table-level
- lock or some other non-MDL resource we might need to wake it up
- by calling code outside of MDL.
- */
- mysql_notify_thread_having_shared_lock(thd, conflicting_thd,
- conflicting_ctx->get_needs_thr_lock_abort());
+ {
+ MDL_context *conflicting_ctx= conflicting_ticket->get_ctx();
+
+ /*
+ If thread which holds conflicting lock is waiting on table-level
+ lock or some other non-MDL resource we might need to wake it up
+ by calling code outside of MDL.
+ */
+ mysql_notify_thread_having_shared_lock(ctx->get_thd(),
+ conflicting_ctx->get_thd(),
+ conflicting_ctx->get_needs_thr_lock_abort());
+ }
+ }
+}
+
+
+/**
+ Notify threads holding scoped IX locks which conflict with a pending S lock.
+
+ @param ctx MDL_context for current thread.
+*/
+
+void MDL_scoped_lock::notify_conflicting_locks(MDL_context *ctx)
+{
+ Ticket_iterator it(m_granted);
+ MDL_ticket *conflicting_ticket;
+
+ while ((conflicting_ticket= it++))
+ {
+ if (conflicting_ticket->get_ctx() != ctx &&
+ conflicting_ticket->get_type() == MDL_INTENTION_EXCLUSIVE)
+
+ {
+ MDL_context *conflicting_ctx= conflicting_ticket->get_ctx();
+
+ /*
+ Thread which holds global IX lock can be a handler thread for
+ insert delayed. We need to kill such threads in order to get
+ global shared lock. We do this my calling code outside of MDL.
+ */
+ mysql_notify_thread_having_shared_lock(ctx->get_thd(),
+ conflicting_ctx->get_thd(),
+ conflicting_ctx->get_needs_thr_lock_abort());
+ }
}
}
@@ -1747,8 +1789,8 @@ MDL_context::acquire_lock(MDL_request *mdl_request, ulong lock_wait_timeout)
*/
m_wait.reset_status();
- if (ticket->is_upgradable_or_exclusive())
- lock->notify_shared_locks(this);
+ if (lock->needs_notification(ticket))
+ lock->notify_conflicting_locks(this);
mysql_prlock_unlock(&lock->m_rwlock);
@@ -1759,7 +1801,7 @@ MDL_context::acquire_lock(MDL_request *mdl_request, ulong lock_wait_timeout)
find_deadlock();
- if (ticket->is_upgradable_or_exclusive())
+ if (lock->needs_notification(ticket))
{
struct timespec abs_shortwait;
set_timespec(abs_shortwait, 1);
@@ -1775,7 +1817,7 @@ MDL_context::acquire_lock(MDL_request *mdl_request, ulong lock_wait_timeout)
break;
mysql_prlock_wrlock(&lock->m_rwlock);
- lock->notify_shared_locks(this);
+ lock->notify_conflicting_locks(this);
mysql_prlock_unlock(&lock->m_rwlock);
set_timespec(abs_shortwait, 1);
}
@@ -1818,7 +1860,7 @@ MDL_context::acquire_lock(MDL_request *mdl_request, ulong lock_wait_timeout)
*/
DBUG_ASSERT(wait_status == MDL_wait::GRANTED);
- m_tickets.push_front(ticket);
+ m_tickets[mdl_request->duration].push_front(ticket);
mdl_request->ticket= ticket;
@@ -1859,7 +1901,7 @@ bool MDL_context::acquire_locks(MDL_request_list *mdl_requests,
{
MDL_request_list::Iterator it(*mdl_requests);
MDL_request **sort_buf, **p_req;
- MDL_ticket *mdl_svp= mdl_savepoint();
+ MDL_savepoint mdl_svp= mdl_savepoint();
ssize_t req_count= static_cast<ssize_t>(mdl_requests->elements());
if (req_count == 0)
@@ -1928,7 +1970,7 @@ MDL_context::upgrade_shared_lock_to_exclusive(MDL_ticket *mdl_ticket,
ulong lock_wait_timeout)
{
MDL_request mdl_xlock_request;
- MDL_ticket *mdl_svp= mdl_savepoint();
+ MDL_savepoint mdl_svp= mdl_savepoint();
bool is_new_ticket;
DBUG_ENTER("MDL_ticket::upgrade_shared_lock_to_exclusive");
@@ -1945,7 +1987,8 @@ MDL_context::upgrade_shared_lock_to_exclusive(MDL_ticket *mdl_ticket,
DBUG_ASSERT(mdl_ticket->m_type == MDL_SHARED_NO_WRITE ||
mdl_ticket->m_type == MDL_SHARED_NO_READ_WRITE);
- mdl_xlock_request.init(&mdl_ticket->m_lock->key, MDL_EXCLUSIVE);
+ mdl_xlock_request.init(&mdl_ticket->m_lock->key, MDL_EXCLUSIVE,
+ MDL_TRANSACTION);
if (acquire_lock(&mdl_xlock_request, lock_wait_timeout))
DBUG_RETURN(TRUE);
@@ -1969,7 +2012,7 @@ MDL_context::upgrade_shared_lock_to_exclusive(MDL_ticket *mdl_ticket,
if (is_new_ticket)
{
- m_tickets.remove(mdl_xlock_request.ticket);
+ m_tickets[MDL_TRANSACTION].remove(mdl_xlock_request.ticket);
MDL_ticket::destroy(mdl_xlock_request.ticket);
}
@@ -2230,10 +2273,12 @@ void MDL_context::find_deadlock()
/**
Release lock.
- @param ticket Ticket for lock to be released.
+ @param duration Lock duration.
+ @param ticket Ticket for lock to be released.
+
*/
-void MDL_context::release_lock(MDL_ticket *ticket)
+void MDL_context::release_lock(enum_mdl_duration duration, MDL_ticket *ticket)
{
MDL_lock *lock= ticket->m_lock;
DBUG_ENTER("MDL_context::release_lock");
@@ -2243,12 +2288,9 @@ void MDL_context::release_lock(MDL_ticket *ticket)
DBUG_ASSERT(this == ticket->get_ctx());
mysql_mutex_assert_not_owner(&LOCK_open);
- if (ticket == m_trans_sentinel)
- m_trans_sentinel= ++Ticket_list::Iterator(m_tickets, ticket);
-
lock->remove_ticket(&MDL_lock::m_granted, ticket);
- m_tickets.remove(ticket);
+ m_tickets[duration].remove(ticket);
MDL_ticket::destroy(ticket);
DBUG_VOID_RETURN;
@@ -2256,50 +2298,56 @@ void MDL_context::release_lock(MDL_ticket *ticket)
/**
+ Release lock with explicit duration.
+
+ @param ticket Ticket for lock to be released.
+
+*/
+
+void MDL_context::release_lock(MDL_ticket *ticket)
+{
+ DBUG_ASSERT(ticket->m_duration == MDL_EXPLICIT);
+
+ release_lock(MDL_EXPLICIT, ticket);
+}
+
+
+/**
Release all locks associated with the context. If the sentinel
is not NULL, do not release locks stored in the list after and
including the sentinel.
- Transactional locks are added to the beginning of the list, i.e.
- stored in reverse temporal order. This allows to employ this
- function to:
+ Statement and transactional locks are added to the beginning of
+ the corresponding lists, i.e. stored in reverse temporal order.
+ This allows to employ this function to:
- back off in case of a lock conflict.
- - release all locks in the end of a transaction
+ - release all locks in the end of a statment or transaction
- rollback to a savepoint.
-
- The sentinel semantics is used to support LOCK TABLES
- mode and HANDLER statements: locks taken by these statements
- survive COMMIT, ROLLBACK, ROLLBACK TO SAVEPOINT.
*/
-void MDL_context::release_locks_stored_before(MDL_ticket *sentinel)
+void MDL_context::release_locks_stored_before(enum_mdl_duration duration,
+ MDL_ticket *sentinel)
{
MDL_ticket *ticket;
- Ticket_iterator it(m_tickets);
+ Ticket_iterator it(m_tickets[duration]);
DBUG_ENTER("MDL_context::release_locks_stored_before");
- if (m_tickets.is_empty())
+ if (m_tickets[duration].is_empty())
DBUG_VOID_RETURN;
while ((ticket= it++) && ticket != sentinel)
{
DBUG_PRINT("info", ("found lock to release ticket=%p", ticket));
- release_lock(ticket);
+ release_lock(duration, ticket);
}
- /*
- If all locks were released, then the sentinel was not present
- in the list. It must never happen because the sentinel was
- bogus, i.e. pointed to a ticket that no longer exists.
- */
- DBUG_ASSERT(! m_tickets.is_empty() || sentinel == NULL);
DBUG_VOID_RETURN;
}
/**
- Release all locks in the context which correspond to the same name/
- object as this lock request.
+ Release all explicit locks in the context which correspond to the
+ same name/object as this lock request.
@param ticket One of the locks for the name/object for which all
locks should be released.
@@ -2312,17 +2360,13 @@ void MDL_context::release_all_locks_for_name(MDL_ticket *name)
/* Remove matching lock tickets from the context. */
MDL_ticket *ticket;
- Ticket_iterator it_ticket(m_tickets);
+ Ticket_iterator it_ticket(m_tickets[MDL_EXPLICIT]);
while ((ticket= it_ticket++))
{
DBUG_ASSERT(ticket->m_lock);
- /*
- We rarely have more than one ticket in this loop,
- let's not bother saving on pthread_cond_broadcast().
- */
if (ticket->m_lock == lock)
- release_lock(ticket);
+ release_lock(MDL_EXPLICIT, ticket);
}
}
@@ -2377,9 +2421,10 @@ MDL_context::is_lock_owner(MDL_key::enum_mdl_namespace mdl_namespace,
enum_mdl_type mdl_type)
{
MDL_request mdl_request;
- bool is_transactional_unused;
- mdl_request.init(mdl_namespace, db, name, mdl_type);
- MDL_ticket *ticket= find_ticket(&mdl_request, &is_transactional_unused);
+ enum_mdl_duration not_unused;
+ /* We don't care about exact duration of lock here. */
+ mdl_request.init(mdl_namespace, db, name, mdl_type, MDL_TRANSACTION);
+ MDL_ticket *ticket= find_ticket(&mdl_request, &not_unused);
DBUG_ASSERT(ticket == NULL || ticket->m_lock);
@@ -2408,18 +2453,15 @@ bool MDL_ticket::has_pending_conflicting_lock() const
@note It's safe to iterate and unlock any locks after taken after this
savepoint because other statements that take other special locks
cause a implicit commit (ie LOCK TABLES).
-
- @param mdl_savepont The last acquired MDL lock when the
- savepoint was set.
*/
-void MDL_context::rollback_to_savepoint(MDL_ticket *mdl_savepoint)
+void MDL_context::rollback_to_savepoint(const MDL_savepoint &mdl_savepoint)
{
DBUG_ENTER("MDL_context::rollback_to_savepoint");
/* If savepoint is NULL, it is from the start of the transaction. */
- release_locks_stored_before(mdl_savepoint ?
- mdl_savepoint : m_trans_sentinel);
+ release_locks_stored_before(MDL_STATEMENT, mdl_savepoint.m_stmt_ticket);
+ release_locks_stored_before(MDL_TRANSACTION, mdl_savepoint.m_trans_ticket);
DBUG_VOID_RETURN;
}
@@ -2437,65 +2479,150 @@ void MDL_context::rollback_to_savepoint(MDL_ticket *mdl_savepoint)
void MDL_context::release_transactional_locks()
{
DBUG_ENTER("MDL_context::release_transactional_locks");
- release_locks_stored_before(m_trans_sentinel);
+ release_locks_stored_before(MDL_STATEMENT, NULL);
+ release_locks_stored_before(MDL_TRANSACTION, NULL);
+ DBUG_VOID_RETURN;
+}
+
+
+void MDL_context::release_statement_locks()
+{
+ DBUG_ENTER("MDL_context::release_transactional_locks");
+ release_locks_stored_before(MDL_STATEMENT, NULL);
DBUG_VOID_RETURN;
}
/**
Does this savepoint have this lock?
-
- @retval TRUE The ticket is older than the savepoint and
- is not LT, HA or GLR ticket. Thus it belongs
- to the savepoint.
- @retval FALSE The ticket is newer than the savepoint
- or is an LT, HA or GLR ticket.
+
+ @retval TRUE The ticket is older than the savepoint or
+ is an LT, HA or GLR ticket. Thus it belongs
+ to the savepoint or has explicit duration.
+ @retval FALSE The ticket is newer than the savepoint.
+ and is not an LT, HA or GLR ticket.
*/
-bool MDL_context::has_lock(MDL_ticket *mdl_savepoint,
+bool MDL_context::has_lock(const MDL_savepoint &mdl_savepoint,
MDL_ticket *mdl_ticket)
{
MDL_ticket *ticket;
/* Start from the beginning, most likely mdl_ticket's been just acquired. */
- MDL_context::Ticket_iterator it(m_tickets);
- bool found_savepoint= FALSE;
+ MDL_context::Ticket_iterator s_it(m_tickets[MDL_STATEMENT]);
+ MDL_context::Ticket_iterator t_it(m_tickets[MDL_TRANSACTION]);
- while ((ticket= it++) && ticket != m_trans_sentinel)
+ while ((ticket= s_it++) && ticket != mdl_savepoint.m_stmt_ticket)
{
- /*
- First met the savepoint. The ticket must be
- somewhere after it.
- */
- if (ticket == mdl_savepoint)
- found_savepoint= TRUE;
- /*
- Met the ticket. If we haven't yet met the savepoint,
- the ticket is newer than the savepoint.
- */
if (ticket == mdl_ticket)
- return found_savepoint;
+ return FALSE;
}
- /* Reached m_trans_sentinel. The ticket must be LT, HA or GRL ticket. */
- return FALSE;
+
+ while ((ticket= t_it++) && ticket != mdl_savepoint.m_trans_ticket)
+ {
+ if (ticket == mdl_ticket)
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+/**
+ Change lock duration for transactional lock.
+
+ @param ticket Ticket representing lock.
+ @param duration Lock duration to be set.
+
+ @note This method only supports changing duration of
+ transactional lock to some other duration.
+*/
+
+void MDL_context::set_lock_duration(MDL_ticket *mdl_ticket,
+ enum_mdl_duration duration)
+{
+ DBUG_ASSERT(mdl_ticket->m_duration == MDL_TRANSACTION &&
+ duration != MDL_TRANSACTION);
+
+ m_tickets[MDL_TRANSACTION].remove(mdl_ticket);
+ m_tickets[duration].push_front(mdl_ticket);
+#ifndef DBUG_OFF
+ mdl_ticket->m_duration= duration;
+#endif
}
/**
- Rearrange the ticket to reside in the part of the list that's
- beyond m_trans_sentinel. This effectively changes the ticket
- life cycle, from automatic to manual: i.e. the ticket is no
- longer released by MDL_context::release_transactional_locks() or
- MDL_context::rollback_to_savepoint(), it must be released manually.
+ Set explicit duration for all locks in the context.
*/
-void MDL_context::move_ticket_after_trans_sentinel(MDL_ticket *mdl_ticket)
+void MDL_context::set_explicit_duration_for_all_locks()
{
- m_tickets.remove(mdl_ticket);
- if (m_trans_sentinel == NULL)
+ int i;
+ MDL_ticket *ticket;
+
+ /*
+ In the most common case when this function is called list
+ of transactional locks is bigger than list of locks with
+ explicit duration. So we start by swapping these two lists
+ and then move elements from new list of transactional
+ locks and list of statement locks to list of locks with
+ explicit duration.
+ */
+
+ m_tickets[MDL_EXPLICIT].swap(m_tickets[MDL_TRANSACTION]);
+
+ for (i= 0; i < MDL_EXPLICIT; i++)
{
- m_trans_sentinel= mdl_ticket;
- m_tickets.push_back(mdl_ticket);
+ Ticket_iterator it_ticket(m_tickets[i]);
+
+ while ((ticket= it_ticket++))
+ {
+ m_tickets[i].remove(ticket);
+ m_tickets[MDL_EXPLICIT].push_front(ticket);
+ }
}
- else
- m_tickets.insert_after(m_trans_sentinel, mdl_ticket);
+
+#ifndef DBUG_OFF
+ Ticket_iterator exp_it(m_tickets[MDL_EXPLICIT]);
+
+ while ((ticket= exp_it++))
+ ticket->m_duration= MDL_EXPLICIT;
+#endif
+}
+
+
+/**
+ Set transactional duration for all locks in the context.
+*/
+
+void MDL_context::set_transaction_duration_for_all_locks()
+{
+ MDL_ticket *ticket;
+
+ /*
+ In the most common case when this function is called list
+ of explicit locks is bigger than two other lists (in fact,
+ list of statement locks is always empty). So we start by
+ swapping list of explicit and transactional locks and then
+ move contents of new list of explicit locks to list of
+ locks with transactional duration.
+ */
+
+ DBUG_ASSERT(m_tickets[MDL_STATEMENT].is_empty());
+
+ m_tickets[MDL_TRANSACTION].swap(m_tickets[MDL_EXPLICIT]);
+
+ Ticket_iterator it_ticket(m_tickets[MDL_EXPLICIT]);
+
+ while ((ticket= it_ticket++))
+ {
+ m_tickets[MDL_EXPLICIT].remove(ticket);
+ m_tickets[MDL_TRANSACTION].push_front(ticket);
+ }
+
+#ifndef DBUG_OFF
+ Ticket_iterator trans_it(m_tickets[MDL_TRANSACTION]);
+
+ while ((ticket= trans_it++))
+ ticket->m_duration= MDL_TRANSACTION;
+#endif
}
diff --git a/sql/mdl.h b/sql/mdl.h
index 7938d833eac..ff30746e739 100644
--- a/sql/mdl.h
+++ b/sql/mdl.h
@@ -150,6 +150,15 @@ enum enum_mdl_type {
MDL_TYPE_END};
+/** Duration of metadata lock. */
+
+enum enum_mdl_duration { MDL_STATEMENT= 0,
+ MDL_TRANSACTION,
+ MDL_EXPLICIT,
+ /* This should be the last ! */
+ MDL_DURATION_END };
+
+
/** Maximal length of key for metadata locking subsystem. */
#define MAX_MDLKEY_LENGTH (1 + NAME_LEN + 1 + NAME_LEN + 1)
@@ -167,13 +176,16 @@ class MDL_key
{
public:
/**
- Object namespaces
+ Object namespaces.
+ Sic: when adding a new member to this enum make sure to
+ update m_namespace_to_wait_state_name array in mdl.cc!
Different types of objects exist in different namespaces
- TABLE is for tables and views.
- FUNCTION is for stored functions.
- PROCEDURE is for stored procedures.
- TRIGGER is for triggers.
+ - EVENT is for event scheduler events
Note that although there isn't metadata locking on triggers,
it's necessary to have a separate namespace for them since
MDL_key is also used outside of the MDL subsystem.
@@ -184,6 +196,8 @@ public:
FUNCTION,
PROCEDURE,
TRIGGER,
+ EVENT,
+ COMMIT,
/* This should be the last ! */
NAMESPACE_END };
@@ -305,6 +319,8 @@ class MDL_request
public:
/** Type of metadata lock. */
enum enum_mdl_type type;
+ /** Duration for requested lock. */
+ enum enum_mdl_duration duration;
/**
Pointers for participating in the list of lock requests for this context.
@@ -327,17 +343,16 @@ public:
void init(MDL_key::enum_mdl_namespace namespace_arg,
const char *db_arg, const char *name_arg,
- enum_mdl_type mdl_type_arg);
- void init(const MDL_key *key_arg, enum_mdl_type mdl_type_arg);
+ enum_mdl_type mdl_type_arg,
+ enum_mdl_duration mdl_duration_arg);
+ void init(const MDL_key *key_arg, enum_mdl_type mdl_type_arg,
+ enum_mdl_duration mdl_duration_arg);
/** Set type of lock request. Can be only applied to pending locks. */
inline void set_type(enum_mdl_type type_arg)
{
DBUG_ASSERT(ticket == NULL);
type= type_arg;
}
- static MDL_request *create(MDL_key::enum_mdl_namespace mdl_namespace,
- const char *db, const char *name,
- enum_mdl_type mdl_type, MEM_ROOT *root);
/*
This is to work around the ugliness of TABLE_LIST
@@ -363,6 +378,7 @@ public:
MDL_request(const MDL_request *rhs)
:type(rhs->type),
+ duration(rhs->duration),
ticket(NULL),
key(&rhs->key)
{}
@@ -484,17 +500,35 @@ public:
private:
friend class MDL_context;
- MDL_ticket(MDL_context *ctx_arg, enum_mdl_type type_arg)
+ MDL_ticket(MDL_context *ctx_arg, enum_mdl_type type_arg
+#ifndef DBUG_OFF
+ , enum_mdl_duration duration_arg
+#endif
+ )
: m_type(type_arg),
+#ifndef DBUG_OFF
+ m_duration(duration_arg),
+#endif
m_ctx(ctx_arg),
m_lock(NULL)
{}
- static MDL_ticket *create(MDL_context *ctx_arg, enum_mdl_type type_arg);
+ static MDL_ticket *create(MDL_context *ctx_arg, enum_mdl_type type_arg
+#ifndef DBUG_OFF
+ , enum_mdl_duration duration_arg
+#endif
+ );
static void destroy(MDL_ticket *ticket);
private:
/** Type of metadata lock. Externally accessible. */
enum enum_mdl_type m_type;
+#ifndef DBUG_OFF
+ /**
+ Duration of lock represented by this ticket.
+ Context private. Debug-only.
+ */
+ enum_mdl_duration m_duration;
+#endif
/**
Context of the owner of the metadata lock ticket. Externally accessible.
*/
@@ -512,6 +546,39 @@ private:
/**
+ Savepoint for MDL context.
+
+ Doesn't include metadata locks with explicit duration as
+ they are not released during rollback to savepoint.
+*/
+
+class MDL_savepoint
+{
+public:
+ MDL_savepoint() {};
+
+private:
+ MDL_savepoint(MDL_ticket *stmt_ticket, MDL_ticket *trans_ticket)
+ : m_stmt_ticket(stmt_ticket), m_trans_ticket(trans_ticket)
+ {}
+
+ friend class MDL_context;
+
+private:
+ /**
+ Pointer to last lock with statement duration which was taken
+ before creation of savepoint.
+ */
+ MDL_ticket *m_stmt_ticket;
+ /**
+ Pointer to last lock with transaction duration which was taken
+ before creation of savepoint.
+ */
+ MDL_ticket *m_trans_ticket;
+};
+
+
+/**
A reliable way to wait on an MDL lock.
*/
@@ -559,9 +626,7 @@ public:
typedef I_P_List<MDL_ticket,
I_P_List_adapter<MDL_ticket,
&MDL_ticket::next_in_context,
- &MDL_ticket::prev_in_context>,
- I_P_List_null_counter,
- I_P_List_fast_push_back<MDL_ticket> >
+ &MDL_ticket::prev_in_context> >
Ticket_list;
typedef Ticket_list::Iterator Ticket_iterator;
@@ -584,37 +649,28 @@ public:
const char *db, const char *name,
enum_mdl_type mdl_type);
- bool has_lock(MDL_ticket *mdl_savepoint, MDL_ticket *mdl_ticket);
+ bool has_lock(const MDL_savepoint &mdl_savepoint, MDL_ticket *mdl_ticket);
inline bool has_locks() const
{
- return !m_tickets.is_empty();
- }
-
- MDL_ticket *mdl_savepoint()
- {
- /*
- NULL savepoint represents the start of the transaction.
- Checking for m_trans_sentinel also makes sure we never
- return a pointer to HANDLER ticket as a savepoint.
- */
- return m_tickets.front() == m_trans_sentinel ? NULL : m_tickets.front();
+ return !(m_tickets[MDL_STATEMENT].is_empty() &&
+ m_tickets[MDL_TRANSACTION].is_empty() &&
+ m_tickets[MDL_EXPLICIT].is_empty());
}
- void set_trans_sentinel()
+ MDL_savepoint mdl_savepoint()
{
- m_trans_sentinel= m_tickets.front();
+ return MDL_savepoint(m_tickets[MDL_STATEMENT].front(),
+ m_tickets[MDL_TRANSACTION].front());
}
- MDL_ticket *trans_sentinel() const { return m_trans_sentinel; }
- void reset_trans_sentinel(MDL_ticket *sentinel_arg)
- {
- m_trans_sentinel= sentinel_arg;
- }
- void move_ticket_after_trans_sentinel(MDL_ticket *mdl_ticket);
+ void set_explicit_duration_for_all_locks();
+ void set_transaction_duration_for_all_locks();
+ void set_lock_duration(MDL_ticket *mdl_ticket, enum_mdl_duration duration);
+ void release_statement_locks();
void release_transactional_locks();
- void rollback_to_savepoint(MDL_ticket *mdl_savepoint);
+ void rollback_to_savepoint(const MDL_savepoint &mdl_savepoint);
inline THD *get_thd() const { return m_thd; }
@@ -655,46 +711,43 @@ public:
MDL_wait m_wait;
private:
/**
- All MDL tickets acquired by this connection.
-
- The order of tickets in m_tickets list.
- ---------------------------------------
- The entire set of locks acquired by a connection
- can be separated in two subsets: transactional and
- non-transactional locks.
-
- Transactional locks are locks with automatic scope. They
- are accumulated in the course of a transaction, and
- released only on COMMIT, ROLLBACK or ROLLBACK TO SAVEPOINT.
- They must not be (and never are) released manually,
+ Lists of all MDL tickets acquired by this connection.
+
+ Lists of MDL tickets:
+ ---------------------
+ The entire set of locks acquired by a connection can be separated
+ in three subsets according to their: locks released at the end of
+ statement, at the end of transaction and locks are released
+ explicitly.
+
+ Statement and transactional locks are locks with automatic scope.
+ They are accumulated in the course of a transaction, and released
+ either at the end of uppermost statement (for statement locks) or
+ on COMMIT, ROLLBACK or ROLLBACK TO SAVEPOINT (for transactional
+ locks). They must not be (and never are) released manually,
i.e. with release_lock() call.
- Non-transactional locks are taken for locks that span
+ Locks with explicit duration are taken for locks that span
multiple transactions or savepoints.
These are: HANDLER SQL locks (HANDLER SQL is
transaction-agnostic), LOCK TABLES locks (you can COMMIT/etc
under LOCK TABLES, and the locked tables stay locked), and
- SET GLOBAL READ_ONLY=1 global shared lock.
+ locks implementing "global read lock".
- Transactional locks are always prepended to the beginning
- of the list. In other words, they are stored in reverse
- temporal order. Thus, when we rollback to a savepoint,
- we start popping and releasing tickets from the front
- until we reach the last ticket acquired after the
- savepoint.
+ Statement/transactional locks are always prepended to the
+ beginning of the appropriate list. In other words, they are
+ stored in reverse temporal order. Thus, when we rollback to
+ a savepoint, we start popping and releasing tickets from the
+ front until we reach the last ticket acquired after the savepoint.
- Non-transactional locks are always stored after
- transactional ones, and among each other can be
- split into three sets:
+ Locks with explicit duration stored are not stored in any
+ particular order, and among each other can be split into
+ three sets:
[LOCK TABLES locks] [HANDLER locks] [GLOBAL READ LOCK locks]
The following is known about these sets:
- * we can never have both HANDLER and LOCK TABLES locks
- together -- HANDLER statements are prohibited under LOCK
- TABLES, entering LOCK TABLES implicitly closes all open
- HANDLERs.
* GLOBAL READ LOCK locks are always stored after LOCK TABLES
locks and after HANDLER locks. This is because one can't say
SET GLOBAL read_only=1 or FLUSH TABLES WITH READ LOCK
@@ -709,14 +762,9 @@ private:
However, one can open a few HANDLERs after entering the
read only mode.
* LOCK TABLES locks include intention exclusive locks on
- involved schemas.
- */
- Ticket_list m_tickets;
- /**
- Separates transactional and non-transactional locks
- in m_tickets list, @sa m_tickets.
+ involved schemas and global intention exclusive lock.
*/
- MDL_ticket *m_trans_sentinel;
+ Ticket_list m_tickets[MDL_DURATION_END];
THD *m_thd;
/**
TRUE - if for this context we will break protocol and try to
@@ -747,8 +795,9 @@ private:
MDL_wait_for_subgraph *m_waiting_for;
private:
MDL_ticket *find_ticket(MDL_request *mdl_req,
- bool *is_transactional);
- void release_locks_stored_before(MDL_ticket *sentinel);
+ enum_mdl_duration *duration);
+ void release_locks_stored_before(enum_mdl_duration duration, MDL_ticket *sentinel);
+ void release_lock(enum_mdl_duration duration, MDL_ticket *ticket);
bool try_acquire_lock_impl(MDL_request *mdl_request,
MDL_ticket **out_ticket);
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index 21754b23940..9e4026f3a1c 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -604,8 +604,7 @@ pthread_key(MEM_ROOT**,THR_MALLOC);
pthread_key(THD*, THR_THD);
mysql_mutex_t LOCK_thread_count;
mysql_mutex_t
- LOCK_status, LOCK_global_read_lock,
- LOCK_error_log, LOCK_uuid_generator,
+ LOCK_status, LOCK_error_log, LOCK_uuid_generator,
LOCK_delayed_insert, LOCK_delayed_status, LOCK_delayed_create,
LOCK_crypt,
LOCK_global_system_variables,
@@ -625,7 +624,6 @@ mysql_mutex_t LOCK_des_key_file;
mysql_rwlock_t LOCK_grant, LOCK_sys_init_connect, LOCK_sys_init_slave;
mysql_rwlock_t LOCK_system_variables_hash;
mysql_cond_t COND_thread_count;
-mysql_cond_t COND_global_read_lock;
pthread_t signal_thread;
pthread_attr_t connection_attrib;
mysql_mutex_t LOCK_server_started;
@@ -1542,7 +1540,6 @@ static void clean_up_mutexes()
mysql_mutex_destroy(&LOCK_crypt);
mysql_mutex_destroy(&LOCK_user_conn);
mysql_mutex_destroy(&LOCK_connection_count);
- Events::destroy_mutexes();
#ifdef HAVE_OPENSSL
mysql_mutex_destroy(&LOCK_des_key_file);
#ifndef HAVE_YASSL
@@ -1560,12 +1557,10 @@ static void clean_up_mutexes()
mysql_rwlock_destroy(&LOCK_sys_init_slave);
mysql_mutex_destroy(&LOCK_global_system_variables);
mysql_rwlock_destroy(&LOCK_system_variables_hash);
- mysql_mutex_destroy(&LOCK_global_read_lock);
mysql_mutex_destroy(&LOCK_uuid_generator);
mysql_mutex_destroy(&LOCK_prepared_stmt_count);
mysql_mutex_destroy(&LOCK_error_messages);
mysql_cond_destroy(&COND_thread_count);
- mysql_cond_destroy(&COND_global_read_lock);
mysql_cond_destroy(&COND_thread_cache);
mysql_cond_destroy(&COND_flush_thread_cache);
mysql_cond_destroy(&COND_manager);
@@ -3524,8 +3519,6 @@ static int init_thread_environment()
&LOCK_global_system_variables, MY_MUTEX_INIT_FAST);
mysql_rwlock_init(key_rwlock_LOCK_system_variables_hash,
&LOCK_system_variables_hash);
- mysql_mutex_init(key_LOCK_global_read_lock,
- &LOCK_global_read_lock, MY_MUTEX_INIT_FAST);
mysql_mutex_init(key_LOCK_prepared_stmt_count,
&LOCK_prepared_stmt_count, MY_MUTEX_INIT_FAST);
mysql_mutex_init(key_LOCK_error_messages,
@@ -3553,7 +3546,6 @@ static int init_thread_environment()
mysql_rwlock_init(key_rwlock_LOCK_sys_init_slave, &LOCK_sys_init_slave);
mysql_rwlock_init(key_rwlock_LOCK_grant, &LOCK_grant);
mysql_cond_init(key_COND_thread_count, &COND_thread_count, NULL);
- mysql_cond_init(key_COND_global_read_lock, &COND_global_read_lock, NULL);
mysql_cond_init(key_COND_thread_cache, &COND_thread_cache, NULL);
mysql_cond_init(key_COND_flush_thread_cache, &COND_flush_thread_cache, NULL);
mysql_cond_init(key_COND_manager, &COND_manager, NULL);
@@ -7669,7 +7661,7 @@ PSI_mutex_key key_BINLOG_LOCK_index, key_BINLOG_LOCK_prep_xids,
key_delayed_insert_mutex, key_hash_filo_lock, key_LOCK_active_mi,
key_LOCK_connection_count, key_LOCK_crypt, key_LOCK_delayed_create,
key_LOCK_delayed_insert, key_LOCK_delayed_status, key_LOCK_error_log,
- key_LOCK_gdl, key_LOCK_global_read_lock, key_LOCK_global_system_variables,
+ key_LOCK_gdl, key_LOCK_global_system_variables,
key_LOCK_manager,
key_LOCK_prepared_stmt_count,
key_LOCK_rpl_status, key_LOCK_server_started, key_LOCK_status,
@@ -7707,7 +7699,6 @@ static PSI_mutex_info all_server_mutexes[]=
{ &key_LOCK_delayed_status, "LOCK_delayed_status", PSI_FLAG_GLOBAL},
{ &key_LOCK_error_log, "LOCK_error_log", PSI_FLAG_GLOBAL},
{ &key_LOCK_gdl, "LOCK_gdl", PSI_FLAG_GLOBAL},
- { &key_LOCK_global_read_lock, "LOCK_global_read_lock", PSI_FLAG_GLOBAL},
{ &key_LOCK_global_system_variables, "LOCK_global_system_variables", PSI_FLAG_GLOBAL},
{ &key_LOCK_manager, "LOCK_manager", PSI_FLAG_GLOBAL},
{ &key_LOCK_prepared_stmt_count, "LOCK_prepared_stmt_count", PSI_FLAG_GLOBAL},
@@ -7756,7 +7747,7 @@ PSI_cond_key key_PAGE_cond, key_COND_active, key_COND_pool;
#endif /* HAVE_MMAP */
PSI_cond_key key_BINLOG_COND_prep_xids, key_BINLOG_update_cond,
- key_COND_cache_status_changed, key_COND_global_read_lock, key_COND_manager,
+ key_COND_cache_status_changed, key_COND_manager,
key_COND_rpl_status, key_COND_server_started,
key_delayed_insert_cond, key_delayed_insert_cond_client,
key_item_func_sleep_cond, key_master_info_data_cond,
@@ -7779,7 +7770,6 @@ static PSI_cond_info all_server_conds[]=
{ &key_BINLOG_COND_prep_xids, "MYSQL_BIN_LOG::COND_prep_xids", 0},
{ &key_BINLOG_update_cond, "MYSQL_BIN_LOG::update_cond", 0},
{ &key_COND_cache_status_changed, "Query_cache::COND_cache_status_changed", 0},
- { &key_COND_global_read_lock, "COND_global_read_lock", PSI_FLAG_GLOBAL},
{ &key_COND_manager, "COND_manager", PSI_FLAG_GLOBAL},
{ &key_COND_rpl_status, "COND_rpl_status", PSI_FLAG_GLOBAL},
{ &key_COND_server_started, "COND_server_started", PSI_FLAG_GLOBAL},
diff --git a/sql/mysqld.h b/sql/mysqld.h
index dc9f94c0d03..5e65d0d2a8d 100644
--- a/sql/mysqld.h
+++ b/sql/mysqld.h
@@ -100,7 +100,7 @@ extern bool opt_ignore_builtin_innodb;
extern my_bool opt_character_set_client_handshake;
extern bool volatile abort_loop;
extern bool in_bootstrap;
-extern uint volatile thread_count, global_read_lock;
+extern uint volatile thread_count;
extern uint connection_count;
extern my_bool opt_safe_user_create;
extern my_bool opt_safe_show_db, opt_local_infile, opt_myisam_use_mmap;
@@ -227,7 +227,7 @@ extern PSI_mutex_key key_BINLOG_LOCK_index, key_BINLOG_LOCK_prep_xids,
key_delayed_insert_mutex, key_hash_filo_lock, key_LOCK_active_mi,
key_LOCK_connection_count, key_LOCK_crypt, key_LOCK_delayed_create,
key_LOCK_delayed_insert, key_LOCK_delayed_status, key_LOCK_error_log,
- key_LOCK_gdl, key_LOCK_global_read_lock, key_LOCK_global_system_variables,
+ key_LOCK_gdl, key_LOCK_global_system_variables,
key_LOCK_logger, key_LOCK_manager,
key_LOCK_prepared_stmt_count,
key_LOCK_rpl_status, key_LOCK_server_started, key_LOCK_status,
@@ -248,7 +248,7 @@ extern PSI_cond_key key_PAGE_cond, key_COND_active, key_COND_pool;
#endif /* HAVE_MMAP */
extern PSI_cond_key key_BINLOG_COND_prep_xids, key_BINLOG_update_cond,
- key_COND_cache_status_changed, key_COND_global_read_lock, key_COND_manager,
+ key_COND_cache_status_changed, key_COND_manager,
key_COND_rpl_status, key_COND_server_started,
key_delayed_insert_cond, key_delayed_insert_cond_client,
key_item_func_sleep_cond, key_master_info_data_cond,
@@ -320,7 +320,7 @@ extern mysql_mutex_t
LOCK_user_locks, LOCK_status,
LOCK_error_log, LOCK_delayed_insert, LOCK_uuid_generator,
LOCK_delayed_status, LOCK_delayed_create, LOCK_crypt, LOCK_timezone,
- LOCK_slave_list, LOCK_active_mi, LOCK_manager, LOCK_global_read_lock,
+ LOCK_slave_list, LOCK_active_mi, LOCK_manager,
LOCK_global_system_variables, LOCK_user_conn,
LOCK_prepared_stmt_count, LOCK_error_messages, LOCK_connection_count;
extern MYSQL_PLUGIN_IMPORT mysql_mutex_t LOCK_thread_count;
@@ -333,7 +333,6 @@ extern mysql_rwlock_t LOCK_grant, LOCK_sys_init_connect, LOCK_sys_init_slave;
extern mysql_rwlock_t LOCK_system_variables_hash;
extern mysql_cond_t COND_thread_count;
extern mysql_cond_t COND_manager;
-extern mysql_cond_t COND_global_read_lock;
extern int32 thread_running;
extern my_atomic_rwlock_t thread_running_lock;
diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc
index af9b452acd8..a35e7bb1612 100644
--- a/sql/rpl_rli.cc
+++ b/sql/rpl_rli.cc
@@ -1274,6 +1274,9 @@ void Relay_log_info::slave_close_thread_tables(THD *thd)
*/
if (! thd->in_multi_stmt_transaction_mode())
thd->mdl_context.release_transactional_locks();
+ else
+ thd->mdl_context.release_statement_locks();
+
clear_tables_to_lock();
}
#endif
diff --git a/sql/sp.cc b/sql/sp.cc
index 7385a6ffcae..4dea6843ef1 100644
--- a/sql/sp.cc
+++ b/sql/sp.cc
@@ -27,7 +27,7 @@
#include "sql_acl.h" // SUPER_ACL
#include "sp_head.h"
#include "sp_cache.h"
-#include "lock.h" // lock_routine_name
+#include "lock.h" // lock_object_name
#include <my_user.h>
@@ -440,7 +440,7 @@ static TABLE *open_proc_table_for_update(THD *thd)
{
TABLE_LIST table_list;
TABLE *table;
- MDL_ticket *mdl_savepoint= thd->mdl_context.mdl_savepoint();
+ MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
DBUG_ENTER("open_proc_table_for_update");
table_list.init_one_table("mysql", 5, "proc", 4, "proc", TL_WRITE);
@@ -925,6 +925,8 @@ sp_create_routine(THD *thd, int type, sp_head *sp)
TABLE *table;
char definer[USER_HOST_BUFF_SIZE];
ulong saved_mode= thd->variables.sql_mode;
+ MDL_key::enum_mdl_namespace mdl_type= type == TYPE_ENUM_FUNCTION ?
+ MDL_key::FUNCTION : MDL_key::PROCEDURE;
CHARSET_INFO *db_cs= get_default_db_collation(thd, sp->m_db.str);
@@ -944,8 +946,7 @@ sp_create_routine(THD *thd, int type, sp_head *sp)
type == TYPE_ENUM_FUNCTION);
/* Grab an exclusive MDL lock. */
- if (lock_routine_name(thd, type == TYPE_ENUM_FUNCTION,
- sp->m_db.str, sp->m_name.str))
+ if (lock_object_name(thd, mdl_type, sp->m_db.str, sp->m_name.str))
DBUG_RETURN(SP_OPEN_TABLE_FAILED);
/* Reset sql_mode during data dictionary operations. */
@@ -1193,6 +1194,8 @@ sp_drop_routine(THD *thd, int type, sp_name *name)
TABLE *table;
int ret;
bool save_binlog_row_based;
+ MDL_key::enum_mdl_namespace mdl_type= type == TYPE_ENUM_FUNCTION ?
+ MDL_key::FUNCTION : MDL_key::PROCEDURE;
DBUG_ENTER("sp_drop_routine");
DBUG_PRINT("enter", ("type: %d name: %.*s",
type, (int) name->m_name.length, name->m_name.str));
@@ -1201,8 +1204,7 @@ sp_drop_routine(THD *thd, int type, sp_name *name)
type == TYPE_ENUM_FUNCTION);
/* Grab an exclusive MDL lock. */
- if (lock_routine_name(thd, type == TYPE_ENUM_FUNCTION,
- name->m_db.str, name->m_name.str))
+ if (lock_object_name(thd, mdl_type, name->m_db.str, name->m_name.str))
DBUG_RETURN(SP_DELETE_ROW_FAILED);
if (!(table= open_proc_table_for_update(thd)))
@@ -1273,6 +1275,8 @@ sp_update_routine(THD *thd, int type, sp_name *name, st_sp_chistics *chistics)
TABLE *table;
int ret;
bool save_binlog_row_based;
+ MDL_key::enum_mdl_namespace mdl_type= type == TYPE_ENUM_FUNCTION ?
+ MDL_key::FUNCTION : MDL_key::PROCEDURE;
DBUG_ENTER("sp_update_routine");
DBUG_PRINT("enter", ("type: %d name: %.*s",
type, (int) name->m_name.length, name->m_name.str));
@@ -1281,8 +1285,7 @@ sp_update_routine(THD *thd, int type, sp_name *name, st_sp_chistics *chistics)
type == TYPE_ENUM_FUNCTION);
/* Grab an exclusive MDL lock. */
- if (lock_routine_name(thd, type == TYPE_ENUM_FUNCTION,
- name->m_db.str, name->m_name.str))
+ if (lock_object_name(thd, mdl_type, name->m_db.str, name->m_name.str))
DBUG_RETURN(SP_OPEN_TABLE_FAILED);
if (!(table= open_proc_table_for_update(thd)))
@@ -1370,7 +1373,7 @@ sp_drop_db_routines(THD *thd, char *db)
TABLE *table;
int ret;
uint key_len;
- MDL_ticket *mdl_savepoint= thd->mdl_context.mdl_savepoint();
+ MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
DBUG_ENTER("sp_drop_db_routines");
DBUG_PRINT("enter", ("db: %s", db));
@@ -1697,7 +1700,7 @@ bool sp_add_used_routine(Query_tables_list *prelocking_ctx, Query_arena *arena,
(Sroutine_hash_entry *)arena->alloc(sizeof(Sroutine_hash_entry));
if (!rn) // OOM. Error will be reported using fatal_error().
return FALSE;
- rn->mdl_request.init(key, MDL_SHARED);
+ rn->mdl_request.init(key, MDL_SHARED, MDL_TRANSACTION);
if (my_hash_insert(&prelocking_ctx->sroutines, (uchar *)rn))
return FALSE;
prelocking_ctx->sroutines_list.link_in_list(rn, &rn->next);
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index 0d33bd5c599..5089e7e0340 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -2140,6 +2140,8 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
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();
thd->rollback_item_tree_changes();
@@ -2978,6 +2980,8 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp,
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 (m_lex->query_tables_own_last)
@@ -4176,7 +4180,8 @@ sp_head::add_used_tables_to_table_list(THD *thd,
*/
table->mdl_request.init(MDL_key::TABLE, table->db, table->table_name,
table->lock_type >= TL_WRITE_ALLOW_WRITE ?
- MDL_SHARED_WRITE : MDL_SHARED_READ);
+ MDL_SHARED_WRITE : MDL_SHARED_READ,
+ MDL_TRANSACTION);
/* Everyting else should be zeroed */
@@ -4220,7 +4225,7 @@ sp_add_to_query_tables(THD *thd, LEX *lex,
table->select_lex= lex->current_select;
table->cacheable_table= 1;
table->mdl_request.init(MDL_key::TABLE, table->db, table->table_name,
- mdl_type);
+ mdl_type, MDL_TRANSACTION);
lex->add_to_query_tables(table);
return table;
diff --git a/sql/sql_admin.cc b/sql/sql_admin.cc
index 21a05a5baca..f648d219fac 100644
--- a/sql/sql_admin.cc
+++ b/sql/sql_admin.cc
@@ -85,7 +85,7 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list,
key_length= create_table_def_key(thd, key, table_list, 0);
table_list->mdl_request.init(MDL_key::TABLE,
table_list->db, table_list->table_name,
- MDL_EXCLUSIVE);
+ MDL_EXCLUSIVE, MDL_TRANSACTION);
if (lock_table_names(thd, table_list, table_list->next_global,
thd->variables.lock_wait_timeout,
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 8caae3ee406..60c32a1a376 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -21,7 +21,7 @@
#include "sql_priv.h"
#include "unireg.h"
#include "debug_sync.h"
-#include "lock.h" // broadcast_refresh, mysql_lock_remove,
+#include "lock.h" // mysql_lock_remove,
// mysql_unlock_tables,
// mysql_lock_have_duplicate
#include "sql_show.h" // append_identifier
@@ -1285,20 +1285,12 @@ static void mark_used_tables_as_free_for_reuse(THD *thd, TABLE *table)
static void close_open_tables(THD *thd)
{
- bool found_old_table= 0;
-
mysql_mutex_assert_not_owner(&LOCK_open);
DBUG_PRINT("info", ("thd->open_tables: 0x%lx", (long) thd->open_tables));
while (thd->open_tables)
- found_old_table|= close_thread_table(thd, &thd->open_tables);
-
- if (found_old_table)
- {
- /* Tell threads waiting for refresh that something has happened */
- broadcast_refresh();
- }
+ (void) close_thread_table(thd, &thd->open_tables);
}
@@ -1364,11 +1356,6 @@ close_all_tables_for_name(THD *thd, TABLE_SHARE *share,
/* Remove the table share from the cache. */
tdc_remove_table(thd, TDC_RT_REMOVE_ALL, db, table_name,
FALSE);
- /*
- There could be a FLUSH thread waiting
- on the table to go away. Wake it up.
- */
- broadcast_refresh();
}
@@ -2463,7 +2450,8 @@ open_table_get_mdl_lock(THD *thd, Open_table_context *ot_ctx,
mdl_request_shared.init(&mdl_request->key,
(flags & MYSQL_OPEN_FORCE_SHARED_MDL) ?
- MDL_SHARED : MDL_SHARED_HIGH_PRIO);
+ MDL_SHARED : MDL_SHARED_HIGH_PRIO,
+ MDL_TRANSACTION);
mdl_request= &mdl_request_shared;
}
@@ -2628,32 +2616,6 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
TMP_TABLE_KEY_EXTRA);
/*
- We need this to work for all tables, including temporary
- tables, for backwards compatibility. But not under LOCK
- TABLES, since under LOCK TABLES one can't use a non-prelocked
- table. This code only works for updates done inside DO/SELECT
- f1() statements, normal DML is handled by means of
- sql_command_flags.
- */
- if (global_read_lock && table_list->lock_type >= TL_WRITE_ALLOW_WRITE &&
- ! (flags & MYSQL_OPEN_IGNORE_GLOBAL_READ_LOCK) &&
- ! thd->locked_tables_mode)
- {
- /*
- Someone has issued FLUSH TABLES WITH READ LOCK and we want
- a write lock. Wait until the lock is gone.
- */
- if (thd->global_read_lock.wait_if_global_read_lock(thd, 1, 1))
- DBUG_RETURN(TRUE);
-
- if (thd->open_tables && thd->open_tables->s->version != refresh_version)
- {
- (void)ot_ctx->request_backoff_action(Open_table_context::OT_REOPEN_TABLES,
- NULL);
- DBUG_RETURN(TRUE);
- }
- }
- /*
Unless requested otherwise, try to resolve this table in the list
of temporary tables of this thread. In MySQL temporary tables
are always thread-local and "shadow" possible base tables with the
@@ -2824,6 +2786,59 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
if (! (flags & MYSQL_OPEN_HAS_MDL_LOCK))
{
+ /*
+ We are not under LOCK TABLES and going to acquire write-lock/
+ modify the base table. We need to acquire protection against
+ global read lock until end of this statement in order to have
+ this statement blocked by active FLUSH TABLES WITH READ LOCK.
+
+ We don't block acquire this protection under LOCK TABLES as
+ such protection already acquired at LOCK TABLES time and
+ not released until UNLOCK TABLES.
+
+ We don't block statements which modify only temporary tables
+ as these tables are not preserved by backup by any form of
+ backup which uses FLUSH TABLES WITH READ LOCK.
+
+ TODO: The fact that we sometimes acquire protection against
+ GRL only when we encounter table to be write-locked
+ slightly increases probability of deadlock.
+ This problem will be solved once Alik pushes his
+ temporary table refactoring patch and we can start
+ pre-acquiring metadata locks at the beggining of
+ open_tables() call.
+ */
+ if (table_list->mdl_request.type >= MDL_SHARED_WRITE &&
+ ! (flags & (MYSQL_OPEN_IGNORE_GLOBAL_READ_LOCK |
+ MYSQL_OPEN_FORCE_SHARED_MDL |
+ MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL |
+ MYSQL_OPEN_SKIP_SCOPED_MDL_LOCK)) &&
+ ! ot_ctx->has_protection_against_grl())
+ {
+ MDL_request protection_request;
+ MDL_deadlock_handler mdl_deadlock_handler(ot_ctx);
+
+ if (thd->global_read_lock.can_acquire_protection())
+ DBUG_RETURN(TRUE);
+
+ protection_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE,
+ MDL_STATEMENT);
+
+ /*
+ Install error handler which if possible will convert deadlock error
+ into request to back-off and restart process of opening tables.
+ */
+ thd->push_internal_handler(&mdl_deadlock_handler);
+ bool result= thd->mdl_context.acquire_lock(&protection_request,
+ ot_ctx->get_timeout());
+ thd->pop_internal_handler();
+
+ if (result)
+ DBUG_RETURN(TRUE);
+
+ ot_ctx->set_has_protection_against_grl();
+ }
+
if (open_table_get_mdl_lock(thd, ot_ctx, &table_list->mdl_request,
flags, &mdl_ticket) ||
mdl_ticket == NULL)
@@ -3379,7 +3394,6 @@ unlink_all_closed_tables(THD *thd, MYSQL_LOCK *lock, size_t reopen_count)
close_thread_table(thd, &thd->open_tables);
}
- broadcast_refresh();
}
/* Exclude all closed tables from the LOCK TABLES list. */
for (TABLE_LIST *table_list= m_locked_tables; table_list; table_list=
@@ -3856,7 +3870,8 @@ Open_table_context::Open_table_context(THD *thd, uint flags)
LONG_TIMEOUT : thd->variables.lock_wait_timeout),
m_flags(flags),
m_action(OT_NO_ACTION),
- m_has_locks(thd->mdl_context.has_locks())
+ m_has_locks(thd->mdl_context.has_locks()),
+ m_has_protection_against_grl(FALSE)
{}
@@ -4013,6 +4028,12 @@ recover_from_failed_open(THD *thd)
for safety.
*/
m_failed_table= NULL;
+ /*
+ Reset flag indicating that we have already acquired protection
+ against GRL. It is no longer valid as the corresponding lock was
+ released by close_tables_for_reopen().
+ */
+ m_has_protection_against_grl= FALSE;
/* Prepare for possible another back-off. */
m_action= OT_NO_ACTION;
return result;
@@ -4551,11 +4572,20 @@ lock_table_names(THD *thd,
if (schema_request == NULL)
return TRUE;
schema_request->init(MDL_key::SCHEMA, table->db, "",
- MDL_INTENTION_EXCLUSIVE);
+ MDL_INTENTION_EXCLUSIVE,
+ MDL_TRANSACTION);
mdl_requests.push_front(schema_request);
}
- /* Take the global intention exclusive lock. */
- global_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE);
+
+ /*
+ Protect this statement against concurrent global read lock
+ by acquiring global intention exclusive lock with statement
+ duration.
+ */
+ if (thd->global_read_lock.can_acquire_protection())
+ return TRUE;
+ global_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE,
+ MDL_STATEMENT);
mdl_requests.push_front(&global_request);
}
@@ -5362,7 +5392,7 @@ bool open_and_lock_tables(THD *thd, TABLE_LIST *tables,
Prelocking_strategy *prelocking_strategy)
{
uint counter;
- MDL_ticket *mdl_savepoint= thd->mdl_context.mdl_savepoint();
+ MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
DBUG_ENTER("open_and_lock_tables");
DBUG_PRINT("enter", ("derived handling: %d", derived));
@@ -5419,7 +5449,7 @@ bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables, uint flags)
{
DML_prelocking_strategy prelocking_strategy;
uint counter;
- MDL_ticket *mdl_savepoint= thd->mdl_context.mdl_savepoint();
+ MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
DBUG_ENTER("open_normal_and_derived_tables");
DBUG_ASSERT(!thd->fill_derived_tables());
if (open_tables(thd, &tables, &counter, flags, &prelocking_strategy) ||
@@ -5676,7 +5706,7 @@ bool lock_tables(THD *thd, TABLE_LIST *tables, uint count,
*/
void close_tables_for_reopen(THD *thd, TABLE_LIST **tables,
- MDL_ticket *start_of_statement_svp)
+ const MDL_savepoint &start_of_statement_svp)
{
TABLE_LIST *first_not_own_table= thd->lex->first_not_own_table();
TABLE_LIST *tmp;
diff --git a/sql/sql_base.h b/sql/sql_base.h
index 00f335ab209..35fa04b3674 100644
--- a/sql/sql_base.h
+++ b/sql/sql_base.h
@@ -159,7 +159,7 @@ thr_lock_type read_lock_type_for_table(THD *thd,
my_bool mysql_rm_tmp_tables(void);
bool rm_temporary_table(handlerton *base, char *path);
void close_tables_for_reopen(THD *thd, TABLE_LIST **tables,
- MDL_ticket *start_of_statement_svp);
+ const MDL_savepoint &start_of_statement_svp);
TABLE_LIST *find_table_in_list(TABLE_LIST *table,
TABLE_LIST *TABLE_LIST::*link,
const char *db_name,
@@ -507,7 +507,7 @@ public:
the statement, so that we can rollback to it before waiting on
locks.
*/
- MDL_ticket *start_of_statement_svp() const
+ const MDL_savepoint &start_of_statement_svp() const
{
return m_start_of_statement_svp;
}
@@ -518,6 +518,21 @@ public:
}
uint get_flags() const { return m_flags; }
+
+ /**
+ Set flag indicating that we have already acquired metadata lock
+ protecting this statement against GRL while opening tables.
+ */
+ void set_has_protection_against_grl()
+ {
+ m_has_protection_against_grl= TRUE;
+ }
+
+ bool has_protection_against_grl() const
+ {
+ return m_has_protection_against_grl;
+ }
+
private:
/**
For OT_DISCOVER and OT_REPAIR actions, the table list element for
@@ -525,7 +540,7 @@ private:
should be repaired.
*/
TABLE_LIST *m_failed_table;
- MDL_ticket *m_start_of_statement_svp;
+ MDL_savepoint m_start_of_statement_svp;
/**
Lock timeout in seconds. Initialized to LONG_TIMEOUT when opening system
tables or to the "lock_wait_timeout" system variable for regular tables.
@@ -541,6 +556,11 @@ private:
and we can't safely do back-off (and release them).
*/
bool m_has_locks;
+ /**
+ Indicates that in the process of opening tables we have acquired
+ protection against global read lock.
+ */
+ bool m_has_protection_against_grl;
};
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index c848d686299..1823a0416ff 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -3497,11 +3497,15 @@ void THD::set_mysys_var(struct st_my_thread_var *new_mysys_var)
void THD::leave_locked_tables_mode()
{
locked_tables_mode= LTM_NONE;
- /* Make sure we don't release the global read lock when leaving LTM. */
- mdl_context.reset_trans_sentinel(global_read_lock.global_shared_lock());
+ mdl_context.set_transaction_duration_for_all_locks();
+ /*
+ Make sure we don't release the global read lock and commit blocker
+ when leaving LTM.
+ */
+ global_read_lock.set_explicit_lock_duration(this);
/* Also ensure that we don't release metadata locks for open HANDLERs. */
if (handler_tables_hash.records)
- mysql_ha_move_tickets_after_trans_sentinel(this);
+ mysql_ha_set_explicit_lock_duration(this);
}
void THD::get_definer(LEX_USER *definer)
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 8c444f34364..8bf72f1cdb2 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -822,8 +822,8 @@ struct st_savepoint {
char *name;
uint length;
Ha_trx_info *ha_list;
- /** Last acquired lock before this savepoint was set. */
- MDL_ticket *mdl_savepoint;
+ /** State of metadata locks before this savepoint was set. */
+ MDL_savepoint mdl_savepoint;
};
enum xa_states {XA_NOTR=0, XA_ACTIVE, XA_IDLE, XA_PREPARED, XA_ROLLBACK_ONLY};
@@ -1058,12 +1058,12 @@ class Open_tables_backup: public Open_tables_state
public:
/**
When we backup the open tables state to open a system
- table or tables, points at the last metadata lock
- acquired before the backup. Is used to release
- metadata locks on system tables after they are
+ table or tables, we want to save state of metadata
+ locks which were acquired before the backup. It is used
+ to release metadata locks on system tables after they are
no longer used.
*/
- MDL_ticket *mdl_system_tables_svp;
+ MDL_savepoint mdl_system_tables_svp;
};
/**
@@ -1336,26 +1336,43 @@ public:
};
Global_read_lock()
- :m_protection_count(0), m_state(GRL_NONE), m_mdl_global_shared_lock(NULL)
+ : m_state(GRL_NONE),
+ m_mdl_global_shared_lock(NULL),
+ m_mdl_blocks_commits_lock(NULL)
{}
bool lock_global_read_lock(THD *thd);
void unlock_global_read_lock(THD *thd);
- bool wait_if_global_read_lock(THD *thd, bool abort_on_refresh,
- bool is_not_commit);
- void start_waiting_global_read_lock(THD *thd);
+ /**
+ Check if this connection can acquire protection against GRL and
+ emit error if otherwise.
+ */
+ bool can_acquire_protection() const
+ {
+ if (m_state)
+ {
+ my_error(ER_CANT_UPDATE_WITH_READLOCK, MYF(0));
+ return TRUE;
+ }
+ return FALSE;
+ }
bool make_global_read_lock_block_commit(THD *thd);
bool is_acquired() const { return m_state != GRL_NONE; }
- bool has_protection() const { return m_protection_count > 0; }
- MDL_ticket *global_shared_lock() const { return m_mdl_global_shared_lock; }
+ void set_explicit_lock_duration(THD *thd);
private:
- uint m_protection_count; // GRL protection count
+ enum_grl_state m_state;
/**
In order to acquire the global read lock, the connection must
- acquire a global shared metadata lock, to prohibit all DDL.
+ acquire shared metadata lock in GLOBAL namespace, to prohibit
+ all DDL.
*/
- enum_grl_state m_state;
MDL_ticket *m_mdl_global_shared_lock;
+ /**
+ Also in order to acquire the global read lock, the connection
+ must acquire a shared metadata lock in COMMIT namespace, to
+ prohibit commits.
+ */
+ MDL_ticket *m_mdl_blocks_commits_lock;
};
@@ -2697,7 +2714,7 @@ public:
{
DBUG_ASSERT(locked_tables_mode == LTM_NONE);
- mdl_context.set_trans_sentinel();
+ mdl_context.set_explicit_duration_for_all_locks();
locked_tables_mode= mode_arg;
}
void leave_locked_tables_mode();
@@ -3503,20 +3520,10 @@ public:
#define CF_DIAGNOSTIC_STMT (1U << 8)
/**
- SQL statements that must be protected against impending global read lock
- to prevent deadlock. This deadlock could otherwise happen if the statement
- starts waiting for the GRL to go away inside mysql_lock_tables while at the
- same time having "old" opened tables. The thread holding the GRL can be
- waiting for these "old" opened tables to be closed, causing a deadlock
- (FLUSH TABLES WITH READ LOCK).
- */
-#define CF_PROTECT_AGAINST_GRL (1U << 10)
-
-/**
Identifies statements that may generate row events
and that may end up in the binary log.
*/
-#define CF_CAN_GENERATE_ROW_EVENTS (1U << 11)
+#define CF_CAN_GENERATE_ROW_EVENTS (1U << 9)
/* Bits in server_command_flags */
diff --git a/sql/sql_db.cc b/sql/sql_db.cc
index 517cb9139e9..4df8748429c 100644
--- a/sql/sql_db.cc
+++ b/sql/sql_db.cc
@@ -1054,7 +1054,8 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db,
table_list->alias= table_list->table_name; // If lower_case_table_names=2
table_list->internal_tmp_table= is_prefix(file->name, tmp_file_prefix);
table_list->mdl_request.init(MDL_key::TABLE, table_list->db,
- table_list->table_name, MDL_EXCLUSIVE);
+ table_list->table_name, MDL_EXCLUSIVE,
+ MDL_TRANSACTION);
/* Link into list */
(*tot_list_next_local)= table_list;
(*tot_list_next_global)= table_list;
diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc
index a5c126a8521..b5cd3ac9e9a 100644
--- a/sql/sql_handler.cc
+++ b/sql/sql_handler.cc
@@ -55,7 +55,7 @@
#include "sql_handler.h"
#include "unireg.h" // REQUIRED: for other includes
#include "sql_base.h" // close_thread_tables
-#include "lock.h" // broadcast_refresh, mysql_unlock_tables
+#include "lock.h" // mysql_unlock_tables
#include "key.h" // key_copy
#include "sql_base.h" // insert_fields
#include "sql_select.h"
@@ -131,11 +131,7 @@ static void mysql_ha_close_table(THD *thd, TABLE_LIST *tables)
/* Non temporary table. */
tables->table->file->ha_index_or_rnd_end();
tables->table->open_by_handler= 0;
- if (close_thread_table(thd, &tables->table))
- {
- /* Tell threads waiting for refresh that something has happened */
- broadcast_refresh();
- }
+ (void) close_thread_table(thd, &tables->table);
thd->mdl_context.release_lock(tables->mdl_request.ticket);
}
else if (tables->table)
@@ -183,7 +179,7 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen)
uint dblen, namelen, aliaslen, counter;
bool error;
TABLE *backup_open_tables;
- MDL_ticket *mdl_savepoint;
+ MDL_savepoint mdl_savepoint;
DBUG_ENTER("mysql_ha_open");
DBUG_PRINT("enter",("'%s'.'%s' as '%s' reopen: %d",
tables->db, tables->table_name, tables->alias,
@@ -252,7 +248,13 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen)
memcpy(hash_tables->db, tables->db, dblen);
memcpy(hash_tables->table_name, tables->table_name, namelen);
memcpy(hash_tables->alias, tables->alias, aliaslen);
- hash_tables->mdl_request.init(MDL_key::TABLE, db, name, MDL_SHARED);
+ /*
+ We can't request lock with explicit duration for this table
+ right from the start as open_tables() can't handle properly
+ back-off for such locks.
+ */
+ hash_tables->mdl_request.init(MDL_key::TABLE, db, name, MDL_SHARED,
+ MDL_TRANSACTION);
/* for now HANDLER can be used only for real TABLES */
hash_tables->required_type= FRMTYPE_TABLE;
@@ -332,8 +334,8 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen)
thd->set_open_tables(backup_open_tables);
if (hash_tables->mdl_request.ticket)
{
- thd->mdl_context.
- move_ticket_after_trans_sentinel(hash_tables->mdl_request.ticket);
+ thd->mdl_context.set_lock_duration(hash_tables->mdl_request.ticket,
+ MDL_EXPLICIT);
thd->mdl_context.set_needs_thr_lock_abort(TRUE);
}
@@ -969,24 +971,23 @@ void mysql_ha_cleanup(THD *thd)
/**
- Move tickets for metadata locks corresponding to open HANDLERs
- after transaction sentinel in order to protect them from being
- released at the end of transaction.
+ Set explicit duration for metadata locks corresponding to open HANDLERs
+ to protect them from being released at the end of transaction.
@param thd Thread identifier.
*/
-void mysql_ha_move_tickets_after_trans_sentinel(THD *thd)
+void mysql_ha_set_explicit_lock_duration(THD *thd)
{
TABLE_LIST *hash_tables;
- DBUG_ENTER("mysql_ha_move_tickets_after_trans_sentinel");
+ DBUG_ENTER("mysql_ha_set_explicit_lock_duration");
for (uint i= 0; i < thd->handler_tables_hash.records; i++)
{
hash_tables= (TABLE_LIST*) my_hash_element(&thd->handler_tables_hash, i);
if (hash_tables->table && hash_tables->table->mdl_ticket)
- thd->mdl_context.
- move_ticket_after_trans_sentinel(hash_tables->table->mdl_ticket);
+ thd->mdl_context.set_lock_duration(hash_tables->table->mdl_ticket,
+ MDL_EXPLICIT);
}
DBUG_VOID_RETURN;
}
diff --git a/sql/sql_handler.h b/sql/sql_handler.h
index c5da3c4d468..2eea552d7c9 100644
--- a/sql/sql_handler.h
+++ b/sql/sql_handler.h
@@ -31,6 +31,6 @@ void mysql_ha_flush(THD *thd);
void mysql_ha_flush_tables(THD *thd, TABLE_LIST *all_tables);
void mysql_ha_rm_tables(THD *thd, TABLE_LIST *tables);
void mysql_ha_cleanup(THD *thd);
-void mysql_ha_move_tickets_after_trans_sentinel(THD *thd);
+void mysql_ha_set_explicit_lock_duration(THD *thd);
#endif /* SQL_HANDLER_INCLUDED */
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index 1f8da3fab5c..860a5b8941a 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -77,7 +77,8 @@
#include "sql_audit.h"
#ifndef EMBEDDED_LIBRARY
-static bool delayed_get_table(THD *thd, TABLE_LIST *table_list);
+static bool delayed_get_table(THD *thd, MDL_request *grl_protection_request,
+ TABLE_LIST *table_list);
static int write_delayed(THD *thd, TABLE *table, enum_duplicates duplic,
LEX_STRING query, bool ignore, bool log_on);
static void end_delayed_insert(THD *thd);
@@ -529,32 +530,28 @@ void upgrade_lock_type(THD *thd, thr_lock_type *lock_type,
static
bool open_and_lock_for_insert_delayed(THD *thd, TABLE_LIST *table_list)
{
+ MDL_request protection_request;
DBUG_ENTER("open_and_lock_for_insert_delayed");
#ifndef EMBEDDED_LIBRARY
- if (thd->locked_tables_mode && thd->global_read_lock.is_acquired())
- {
- /*
- If this connection has the global read lock, the handler thread
- will not be able to lock the table. It will wait for the global
- read lock to go away, but this will never happen since the
- connection thread will be stuck waiting for the handler thread
- to open and lock the table.
- If we are not in locked tables mode, INSERT will seek protection
- against the global read lock (and fail), thus we will only get
- to this point in locked tables mode.
- */
- my_error(ER_CANT_UPDATE_WITH_READLOCK, MYF(0));
- DBUG_RETURN(TRUE);
- }
-
/*
In order for the deadlock detector to be able to find any deadlocks
- caused by the handler thread locking this table, we take the metadata
- lock inside the connection thread. If this goes ok, the ticket is cloned
- and added to the list of granted locks held by the handler thread.
+ caused by the handler thread waiting for GRL or this table, we acquire
+ protection against GRL (global IX metadata lock) and metadata lock on
+ table to being inserted into inside the connection thread.
+ If this goes ok, the tickets are cloned and added to the list of granted
+ locks held by the handler thread.
*/
- MDL_ticket *mdl_savepoint= thd->mdl_context.mdl_savepoint();
+ if (thd->global_read_lock.can_acquire_protection())
+ DBUG_RETURN(TRUE);
+
+ protection_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE,
+ MDL_STATEMENT);
+
+ if (thd->mdl_context.acquire_lock(&protection_request,
+ thd->variables.lock_wait_timeout))
+ DBUG_RETURN(TRUE);
+
if (thd->mdl_context.acquire_lock(&table_list->mdl_request,
thd->variables.lock_wait_timeout))
/*
@@ -564,7 +561,7 @@ bool open_and_lock_for_insert_delayed(THD *thd, TABLE_LIST *table_list)
DBUG_RETURN(TRUE);
bool error= FALSE;
- if (delayed_get_table(thd, table_list))
+ if (delayed_get_table(thd, &protection_request, table_list))
error= TRUE;
else if (table_list->table)
{
@@ -589,13 +586,13 @@ bool open_and_lock_for_insert_delayed(THD *thd, TABLE_LIST *table_list)
}
/*
- If a lock was acquired above, we should release it after
- handle_delayed_insert() has cloned the ticket. Note that acquire_lock() can
- succeed because the connection already has the lock. In this case the ticket
- will be before the mdl_savepoint and we should not release it here.
+ We can't release protection against GRL and metadata lock on the table
+ being inserted into here. These locks might be required, for example,
+ because this INSERT DELAYED calls functions which may try to update
+ this or another tables (updating the same table is of course illegal,
+ but such an attempt can be discovered only later during statement
+ execution).
*/
- if (!thd->mdl_context.has_lock(mdl_savepoint, table_list->mdl_request.ticket))
- thd->mdl_context.release_lock(table_list->mdl_request.ticket);
/*
Reset the ticket in case we end up having to use normal insert and
@@ -1873,10 +1870,11 @@ public:
mysql_cond_t cond, cond_client;
volatile uint tables_in_use,stacked_inserts;
volatile bool status;
- /*
+ /**
When the handler thread starts, it clones a metadata lock ticket
- for the table to be inserted. This is done to allow the deadlock
- detector to detect deadlocks resulting from this lock.
+ which protects against GRL and ticket for the table to be inserted.
+ This is done to allow the deadlock detector to detect deadlocks
+ resulting from these locks.
Before this is done, the connection thread cannot safely exit
without causing problems for clone_ticket().
Once handler_thread_initialized has been set, it is safe for the
@@ -1888,6 +1886,11 @@ public:
I_List<delayed_row> rows;
ulong group_count;
TABLE_LIST table_list; // Argument
+ /**
+ Request for IX metadata lock protecting against GRL which is
+ passed from connection thread to the handler thread.
+ */
+ MDL_request grl_protection;
Delayed_insert()
:locks_in_memory(0), table(0),tables_in_use(0),stacked_inserts(0),
@@ -2066,7 +2069,8 @@ Delayed_insert *find_handler(THD *thd, TABLE_LIST *table_list)
*/
static
-bool delayed_get_table(THD *thd, TABLE_LIST *table_list)
+bool delayed_get_table(THD *thd, MDL_request *grl_protection_request,
+ TABLE_LIST *table_list)
{
int error;
Delayed_insert *di;
@@ -2110,7 +2114,10 @@ bool delayed_get_table(THD *thd, TABLE_LIST *table_list)
/* Replace volatile strings with local copies */
di->table_list.alias= di->table_list.table_name= di->thd.query();
di->table_list.db= di->thd.db;
- /* We need the ticket so that it can be cloned in handle_delayed_insert */
+ /* We need the tickets so that they can be cloned in handle_delayed_insert */
+ di->grl_protection.init(MDL_key::GLOBAL, "", "",
+ MDL_INTENTION_EXCLUSIVE, MDL_STATEMENT);
+ di->grl_protection.ticket= grl_protection_request->ticket;
init_mdl_requests(&di->table_list);
di->table_list.mdl_request.ticket= table_list->mdl_request.ticket;
@@ -2650,13 +2657,15 @@ pthread_handler_t handle_delayed_insert(void *arg)
thd->set_current_stmt_binlog_format_row_if_mixed();
/*
- Clone the ticket representing the lock on the target table for
- the insert and add it to the list of granted metadata locks held by
- the handler thread. This is safe since the handler thread is
- not holding nor waiting on any metadata locks.
+ Clone tickets representing protection against GRL and the lock on
+ the target table for the insert and add them to the list of granted
+ metadata locks held by the handler thread. This is safe since the
+ handler thread is not holding nor waiting on any metadata locks.
*/
- if (thd->mdl_context.clone_ticket(&di->table_list.mdl_request))
+ if (thd->mdl_context.clone_ticket(&di->grl_protection) ||
+ thd->mdl_context.clone_ticket(&di->table_list.mdl_request))
{
+ thd->mdl_context.release_transactional_locks();
di->handler_thread_initialized= TRUE;
goto err;
}
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index d91489b4a7a..7a8a86d3ca4 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -417,7 +417,6 @@ void lex_start(THD *thd)
lex->nest_level=0 ;
lex->allow_sum_func= 0;
lex->in_sum_func= NULL;
- lex->protect_against_global_read_lock= FALSE;
/*
ok, there must be a better solution for this, long-term
I tried "bzero" in the sql_yacc.yy code, but that for
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index c20c2f0bca2..191562bd3e8 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -2394,22 +2394,6 @@ struct LEX: public Query_tables_list
bool escape_used;
bool is_lex_started; /* If lex_start() did run. For debugging. */
- /*
- Special case for SELECT .. FOR UPDATE and LOCK TABLES .. WRITE.
-
- Protect from a impending GRL as otherwise the thread might deadlock
- if it starts waiting for the GRL in mysql_lock_tables.
-
- The protection is needed because there is a race between setting
- the global read lock and waiting for all open tables to be closed.
- The problem is a circular wait where a thread holding "old" open
- tables will wait for the global read lock to be released while the
- thread holding the global read lock will wait for all "old" open
- tables to be closed -- the flush part of flush tables with read
- lock.
- */
- bool protect_against_global_read_lock;
-
LEX();
virtual ~LEX()
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index c09a4abb2c8..e95745634e8 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -18,12 +18,9 @@
#include "sql_priv.h"
#include "unireg.h" // REQUIRED: for other includes
#include "sql_parse.h" // sql_kill, *_precheck, *_prepare
-#include "lock.h" // wait_if_global_read_lock,
- // unlock_global_read_lock,
- // try_transactional_lock,
+#include "lock.h" // try_transactional_lock,
// check_transactional_lock,
// set_handler_table_locks,
- // start_waiting_global_read_lock,
// lock_global_read_lock,
// make_global_read_lock_block_commit
#include "sql_base.h" // find_temporary_tablesx
@@ -260,21 +257,20 @@ void init_update_queries(void)
the code, in particular in the Query_log_event's constructor.
*/
sql_command_flags[SQLCOM_CREATE_TABLE]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
- CF_AUTO_COMMIT_TRANS | CF_PROTECT_AGAINST_GRL |
+ CF_AUTO_COMMIT_TRANS |
CF_CAN_GENERATE_ROW_EVENTS;
sql_command_flags[SQLCOM_CREATE_INDEX]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_ALTER_TABLE]= CF_CHANGES_DATA | CF_WRITE_LOGS_COMMAND |
- CF_AUTO_COMMIT_TRANS | CF_PROTECT_AGAINST_GRL;
+ CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_TRUNCATE]= CF_CHANGES_DATA | CF_WRITE_LOGS_COMMAND |
- CF_AUTO_COMMIT_TRANS | CF_PROTECT_AGAINST_GRL;
+ CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_DROP_TABLE]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_LOAD]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
- CF_PROTECT_AGAINST_GRL |
CF_CAN_GENERATE_ROW_EVENTS;
- sql_command_flags[SQLCOM_CREATE_DB]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS | CF_PROTECT_AGAINST_GRL;
- sql_command_flags[SQLCOM_DROP_DB]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS | CF_PROTECT_AGAINST_GRL;
- sql_command_flags[SQLCOM_ALTER_DB_UPGRADE]= CF_AUTO_COMMIT_TRANS | CF_PROTECT_AGAINST_GRL;
- sql_command_flags[SQLCOM_ALTER_DB]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS | CF_PROTECT_AGAINST_GRL;
+ sql_command_flags[SQLCOM_CREATE_DB]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_DROP_DB]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_ALTER_DB_UPGRADE]= CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_ALTER_DB]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_RENAME_TABLE]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_DROP_INDEX]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_CREATE_VIEW]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
@@ -285,26 +281,18 @@ void init_update_queries(void)
sql_command_flags[SQLCOM_CREATE_EVENT]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_ALTER_EVENT]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_DROP_EVENT]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
- sql_command_flags[SQLCOM_CREATE_TRIGGER]= CF_AUTO_COMMIT_TRANS;
- sql_command_flags[SQLCOM_DROP_TRIGGER]= CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_UPDATE]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
- CF_PROTECT_AGAINST_GRL |
CF_CAN_GENERATE_ROW_EVENTS;
sql_command_flags[SQLCOM_UPDATE_MULTI]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
- CF_PROTECT_AGAINST_GRL |
CF_CAN_GENERATE_ROW_EVENTS;
sql_command_flags[SQLCOM_INSERT]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
- CF_PROTECT_AGAINST_GRL |
CF_CAN_GENERATE_ROW_EVENTS;
sql_command_flags[SQLCOM_INSERT_SELECT]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
- CF_PROTECT_AGAINST_GRL |
CF_CAN_GENERATE_ROW_EVENTS;
sql_command_flags[SQLCOM_DELETE]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
- CF_PROTECT_AGAINST_GRL |
CF_CAN_GENERATE_ROW_EVENTS;
sql_command_flags[SQLCOM_DELETE_MULTI]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
- CF_PROTECT_AGAINST_GRL |
CF_CAN_GENERATE_ROW_EVENTS;
sql_command_flags[SQLCOM_REPLACE]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
CF_CAN_GENERATE_ROW_EVENTS;
@@ -366,20 +354,19 @@ void init_update_queries(void)
CF_REEXECUTION_FRAGILE);
- sql_command_flags[SQLCOM_CREATE_USER]= CF_CHANGES_DATA | CF_PROTECT_AGAINST_GRL;
- sql_command_flags[SQLCOM_RENAME_USER]= CF_CHANGES_DATA | CF_PROTECT_AGAINST_GRL;
- sql_command_flags[SQLCOM_DROP_USER]= CF_CHANGES_DATA | CF_PROTECT_AGAINST_GRL;
+ sql_command_flags[SQLCOM_CREATE_USER]= CF_CHANGES_DATA;
+ sql_command_flags[SQLCOM_RENAME_USER]= CF_CHANGES_DATA;
+ sql_command_flags[SQLCOM_DROP_USER]= CF_CHANGES_DATA;
sql_command_flags[SQLCOM_GRANT]= CF_CHANGES_DATA;
sql_command_flags[SQLCOM_REVOKE]= CF_CHANGES_DATA;
- sql_command_flags[SQLCOM_REVOKE_ALL]= CF_PROTECT_AGAINST_GRL;
sql_command_flags[SQLCOM_OPTIMIZE]= CF_CHANGES_DATA;
sql_command_flags[SQLCOM_CREATE_FUNCTION]= CF_CHANGES_DATA;
- sql_command_flags[SQLCOM_CREATE_PROCEDURE]= CF_CHANGES_DATA | CF_PROTECT_AGAINST_GRL | CF_AUTO_COMMIT_TRANS;
- sql_command_flags[SQLCOM_CREATE_SPFUNCTION]= CF_CHANGES_DATA | CF_PROTECT_AGAINST_GRL | CF_AUTO_COMMIT_TRANS;
- sql_command_flags[SQLCOM_DROP_PROCEDURE]= CF_CHANGES_DATA | CF_PROTECT_AGAINST_GRL | CF_AUTO_COMMIT_TRANS;
- sql_command_flags[SQLCOM_DROP_FUNCTION]= CF_CHANGES_DATA | CF_PROTECT_AGAINST_GRL | CF_AUTO_COMMIT_TRANS;
- sql_command_flags[SQLCOM_ALTER_PROCEDURE]= CF_CHANGES_DATA | CF_PROTECT_AGAINST_GRL | CF_AUTO_COMMIT_TRANS;
- sql_command_flags[SQLCOM_ALTER_FUNCTION]= CF_CHANGES_DATA | CF_PROTECT_AGAINST_GRL | CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_CREATE_PROCEDURE]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_CREATE_SPFUNCTION]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_DROP_PROCEDURE]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_DROP_FUNCTION]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_ALTER_PROCEDURE]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_ALTER_FUNCTION]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_INSTALL_PLUGIN]= CF_CHANGES_DATA;
sql_command_flags[SQLCOM_UNINSTALL_PLUGIN]= CF_CHANGES_DATA;
@@ -1111,7 +1098,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
SHOW statements should not add the used tables to the list of tables
used in a transaction.
*/
- MDL_ticket *mdl_savepoint= thd->mdl_context.mdl_savepoint();
+ MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
status_var_increment(thd->status_var.com_stat[SQLCOM_SHOW_FIELDS]);
if (thd->copy_db_to(&db.str, &db.length))
@@ -1754,16 +1741,6 @@ bool sp_process_definer(THD *thd)
/**
Execute command saved in thd and lex->sql_command.
- Before every operation that can request a write lock for a table
- wait if a global read lock exists. However do not wait if this
- thread has locked tables already. No new locks can be requested
- until the other locks are released. The thread that requests the
- global read lock waits for write locked tables to become unlocked.
-
- Note that wait_if_global_read_lock() sets a protection against a new
- global read lock when it succeeds. This needs to be released by
- start_waiting_global_read_lock() after the operation.
-
@param thd Thread handle
@todo
@@ -1797,7 +1774,6 @@ mysql_execute_command(THD *thd)
/* have table map for update for multi-update statement (BUG#37051) */
bool have_table_map_for_update= FALSE;
#endif
- /* Saved variable value */
DBUG_ENTER("mysql_execute_command");
#ifdef WITH_PARTITION_STORAGE_ENGINE
thd->work_part_info= 0;
@@ -1989,17 +1965,6 @@ mysql_execute_command(THD *thd)
thd->mdl_context.release_transactional_locks();
}
- /*
- Check if this command needs protection against the global read lock
- to avoid deadlock. See CF_PROTECT_AGAINST_GRL.
- start_waiting_global_read_lock() is called at the end of
- mysql_execute_command().
- */
- if (((sql_command_flags[lex->sql_command] & CF_PROTECT_AGAINST_GRL) != 0) &&
- !thd->locked_tables_mode)
- if (thd->global_read_lock.wait_if_global_read_lock(thd, FALSE, TRUE))
- goto error;
-
#ifndef DBUG_OFF
if (lex->sql_command != SQLCOM_SET_OPTION)
DEBUG_SYNC(thd,"before_execute_sql_command");
@@ -2074,10 +2039,6 @@ mysql_execute_command(THD *thd)
if (res)
break;
- if (!thd->locked_tables_mode && lex->protect_against_global_read_lock &&
- thd->global_read_lock.wait_if_global_read_lock(thd, FALSE, TRUE))
- break;
-
res= execute_sqlcom_select(thd, all_tables);
break;
}
@@ -2336,20 +2297,6 @@ case SQLCOM_PREPARE:
create_info.default_table_charset= create_info.table_charset;
create_info.table_charset= 0;
}
- /*
- The create-select command will open and read-lock the select table
- and then create, open and write-lock the new table. If a global
- read lock steps in, we get a deadlock. The write lock waits for
- the global read lock, while the global read lock waits for the
- select table to be closed. So we wait until the global readlock is
- gone before starting both steps. Note that
- wait_if_global_read_lock() sets a protection against a new global
- read lock when it succeeds. This needs to be released by
- start_waiting_global_read_lock(). We protect the normal CREATE
- TABLE in the same way. That way we avoid that a new table is
- created during a global read lock.
- Protection against grl is covered by the CF_PROTECT_AGAINST_GRL flag.
- */
#ifdef WITH_PARTITION_STORAGE_ENGINE
{
@@ -3143,9 +3090,6 @@ end_with_restore_list:
if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, all_tables,
FALSE, UINT_MAX, FALSE))
goto error;
- if (lex->protect_against_global_read_lock &&
- thd->global_read_lock.wait_if_global_read_lock(thd, FALSE, TRUE))
- goto error;
thd->variables.option_bits|= OPTION_TABLE_LOCK;
thd->in_lock_tables=1;
@@ -3801,13 +3745,22 @@ end_with_restore_list:
Security_context *backup= NULL;
LEX_USER *definer= thd->lex->definer;
/*
- We're going to issue an implicit GRANT statement.
- It takes metadata locks and updates system tables.
- Make sure that sp_create_routine() did not leave any
- locks in the MDL context, so there is no risk to
- deadlock.
+ We're going to issue an implicit GRANT statement so we close all
+ open tables. We have to keep metadata locks as this ensures that
+ this statement is atomic against concurent FLUSH TABLES WITH READ
+ LOCK. Deadlocks which can arise due to fact that this implicit
+ statement takes metadata locks should be detected by a deadlock
+ detector in MDL subsystem and reported as errors.
+
+ No need to commit/rollback statement transaction, it's not started.
+
+ TODO: Long-term we should either ensure that implicit GRANT statement
+ is written into binary log as a separate statement or make both
+ creation of routine and implicit GRANT parts of one fully atomic
+ statement.
*/
- close_mysql_tables(thd);
+ DBUG_ASSERT(thd->transaction.stmt.is_empty());
+ close_thread_tables(thd);
/*
Check if the definer exists on slave,
then use definer privilege to insert routine privileges to mysql.procs_priv.
@@ -4067,13 +4020,22 @@ create_sp_error:
#ifndef NO_EMBEDDED_ACCESS_CHECKS
/*
- We're going to issue an implicit REVOKE statement.
- It takes metadata locks and updates system tables.
- Make sure that sp_create_routine() did not leave any
- locks in the MDL context, so there is no risk to
- deadlock.
+ We're going to issue an implicit REVOKE statement so we close all
+ open tables. We have to keep metadata locks as this ensures that
+ this statement is atomic against concurent FLUSH TABLES WITH READ
+ LOCK. Deadlocks which can arise due to fact that this implicit
+ statement takes metadata locks should be detected by a deadlock
+ detector in MDL subsystem and reported as errors.
+
+ No need to commit/rollback statement transaction, it's not started.
+
+ TODO: Long-term we should either ensure that implicit REVOKE statement
+ is written into binary log as a separate statement or make both
+ dropping of routine and implicit REVOKE parts of one fully atomic
+ statement.
*/
- close_mysql_tables(thd);
+ DBUG_ASSERT(thd->transaction.stmt.is_empty());
+ close_thread_tables(thd);
if (sp_result != SP_KEY_NOT_FOUND &&
sp_automatic_privileges && !opt_noacl &&
@@ -4362,14 +4324,6 @@ error:
res= TRUE;
finish:
- if (thd->global_read_lock.has_protection())
- {
- /*
- Release the protection against the global read lock and wake
- everyone, who might want to set a global read lock.
- */
- thd->global_read_lock.start_waiting_global_read_lock(thd);
- }
DBUG_ASSERT(!thd->in_active_multi_stmt_transaction() ||
thd->in_multi_stmt_transaction_mode());
@@ -4405,6 +4359,11 @@ finish:
close_thread_tables(thd);
thd_proc_info(thd, 0);
+#ifndef DBUG_OFF
+ if (lex->sql_command != SQLCOM_SET_OPTION && ! thd->in_sub_stmt)
+ DEBUG_SYNC(thd, "execute_command_after_close_tables");
+#endif
+
if (stmt_causes_implicit_commit(thd, CF_IMPLICIT_COMMIT_END))
{
/* No transaction control allowed in sub-statements. */
@@ -4430,6 +4389,10 @@ finish:
*/
thd->mdl_context.release_transactional_locks();
}
+ else if (! thd->in_sub_stmt)
+ {
+ thd->mdl_context.release_statement_locks();
+ }
DBUG_RETURN(res || thd->is_error());
}
@@ -5899,7 +5862,8 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
ptr->next_name_resolution_table= NULL;
/* Link table in global list (all used tables) */
lex->add_to_query_tables(ptr);
- ptr->mdl_request.init(MDL_key::TABLE, ptr->db, ptr->table_name, mdl_type);
+ ptr->mdl_request.init(MDL_key::TABLE, ptr->db, ptr->table_name, mdl_type,
+ MDL_TRANSACTION);
DBUG_RETURN(ptr);
}
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index fcbf2c48896..2b51305d099 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -3172,7 +3172,6 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len)
bool error;
Statement stmt_backup;
Query_arena *old_stmt_arena;
- MDL_ticket *mdl_savepoint= NULL;
DBUG_ENTER("Prepared_statement::prepare");
/*
If this is an SQLCOM_PREPARE, we also increase Com_prepare_sql.
@@ -3244,7 +3243,7 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len)
Marker used to release metadata locks acquired while the prepared
statement is being checked.
*/
- mdl_savepoint= thd->mdl_context.mdl_savepoint();
+ MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
/*
The only case where we should have items in the thd->free_list is
diff --git a/sql/sql_rename.cc b/sql/sql_rename.cc
index 8f990eae001..6a7b0b0b3ad 100644
--- a/sql/sql_rename.cc
+++ b/sql/sql_rename.cc
@@ -24,8 +24,7 @@
#include "sql_table.h" // build_table_filename
#include "sql_view.h" // mysql_frm_type, mysql_rename_view
#include "sql_trigger.h"
-#include "lock.h" // wait_if_global_read_lock
- // start_waiting_global_read_lock
+#include "lock.h" // MYSQL_OPEN_SKIP_TEMPORARY
#include "sql_base.h" // tdc_remove_table, lock_table_names,
#include "sql_handler.h" // mysql_ha_rm_tables
#include "datadict.h"
@@ -63,9 +62,6 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent)
mysql_ha_rm_tables(thd, table_list);
- if (thd->global_read_lock.wait_if_global_read_lock(thd, FALSE, TRUE))
- DBUG_RETURN(1);
-
if (logger.is_log_table_enabled(QUERY_LOG_GENERAL) ||
logger.is_log_table_enabled(QUERY_LOG_SLOW))
{
@@ -189,7 +185,6 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent)
query_cache_invalidate3(thd, table_list, 0);
err:
- thd->global_read_lock.start_waiting_global_read_lock(thd);
DBUG_RETURN(error || binlog_error);
}
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index 19a73c85a9b..b8c66562b1a 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -671,7 +671,7 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list)
Metadata locks taken during SHOW CREATE should be released when
the statmement completes as it is an information statement.
*/
- MDL_ticket *mdl_savepoint= thd->mdl_context.mdl_savepoint();
+ MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
/* We want to preserve the tree for views. */
thd->lex->view_prepare_mode= TRUE;
@@ -3186,7 +3186,7 @@ try_acquire_high_prio_shared_mdl_lock(THD *thd, TABLE_LIST *table,
{
bool error;
table->mdl_request.init(MDL_key::TABLE, table->db, table->table_name,
- MDL_SHARED_HIGH_PRIO);
+ MDL_SHARED_HIGH_PRIO, MDL_TRANSACTION);
if (can_deadlock)
{
@@ -7745,7 +7745,7 @@ bool show_create_trigger(THD *thd, const sp_name *trg_name)
Metadata locks taken during SHOW CREATE TRIGGER should be released when
the statement completes as it is an information statement.
*/
- MDL_ticket *mdl_savepoint= thd->mdl_context.mdl_savepoint();
+ MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
/*
Open the table by name in order to load Table_triggers_list object.
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index 1c6ca4a48d9..ed0acc66e49 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -23,9 +23,7 @@
#include "sql_parse.h" // test_if_data_home_dir
#include "sql_cache.h" // query_cache_*
#include "sql_base.h" // open_table_uncached, lock_table_names
-#include "lock.h" // wait_if_global_read_lock
- // start_waiting_global_read_lock,
- // mysql_unlock_tables
+#include "lock.h" // mysql_unlock_tables
#include "strfunc.h" // find_type2, find_set
#include "sql_view.h" // view_checksum
#include "sql_truncate.h" // regenerate_locked_table
@@ -1855,21 +1853,10 @@ bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists,
DBUG_ENTER("mysql_rm_table");
/* mark for close and remove all cached entries */
-
- if (!drop_temporary)
- {
- if (!thd->locked_tables_mode &&
- thd->global_read_lock.wait_if_global_read_lock(thd, FALSE, TRUE))
- DBUG_RETURN(TRUE);
- }
-
thd->push_internal_handler(&err_handler);
error= mysql_rm_table_part2(thd, tables, if_exists, drop_temporary, 0, 0);
thd->pop_internal_handler();
- if (thd->global_read_lock.has_protection())
- thd->global_read_lock.start_waiting_global_read_lock(thd);
-
if (error)
DBUG_RETURN(TRUE);
my_ok(thd);
@@ -5592,7 +5579,6 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
TABLE *table, *new_table= 0;
MDL_ticket *mdl_ticket;
MDL_request target_mdl_request;
- bool has_target_mdl_lock= FALSE;
int error= 0;
char tmp_name[80],old_name[32],new_name_buff[FN_REFLEN + 1];
char new_alias_buff[FN_REFLEN], *table_name, *db, *new_alias, *alias;
@@ -5754,7 +5740,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
else
{
target_mdl_request.init(MDL_key::TABLE, new_db, new_name,
- MDL_EXCLUSIVE);
+ MDL_EXCLUSIVE, MDL_TRANSACTION);
/*
Global intention exclusive lock must have been already acquired when
table to be altered was open, so there is no need to do it here.
@@ -5772,7 +5758,6 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
DBUG_RETURN(TRUE);
}
DEBUG_SYNC(thd, "locked_table_name");
- has_target_mdl_lock= TRUE;
/*
Table maybe does not exist, but we got an exclusive lock
on the name, now we can safely try to find out for sure.
@@ -5959,10 +5944,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
along with the implicit commit.
*/
if (new_name != table_name || new_db != db)
- {
- thd->mdl_context.release_lock(target_mdl_request.ticket);
thd->mdl_context.release_all_locks_for_name(mdl_ticket);
- }
else
mdl_ticket->downgrade_exclusive_lock(MDL_SHARED_NO_READ_WRITE);
}
@@ -6667,10 +6649,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
thd->locked_tables_mode == LTM_PRELOCKED_UNDER_LOCK_TABLES)
{
if ((new_name != table_name || new_db != db))
- {
- thd->mdl_context.release_lock(target_mdl_request.ticket);
thd->mdl_context.release_all_locks_for_name(mdl_ticket);
- }
else
mdl_ticket->downgrade_exclusive_lock(MDL_SHARED_NO_READ_WRITE);
}
@@ -6731,8 +6710,6 @@ err:
alter_info->datetime_field->field_name);
thd->abort_on_warning= save_abort_on_warning;
}
- if (has_target_mdl_lock)
- thd->mdl_context.release_lock(target_mdl_request.ticket);
DBUG_RETURN(TRUE);
@@ -6744,9 +6721,6 @@ err_with_mdl:
tables and release the exclusive metadata lock.
*/
thd->locked_tables_list.unlink_all_closed_tables(thd, NULL, 0);
- if (has_target_mdl_lock)
- thd->mdl_context.release_lock(target_mdl_request.ticket);
-
thd->mdl_context.release_all_locks_for_name(mdl_ticket);
DBUG_RETURN(TRUE);
}
diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc
index a9b52eee9fc..c2e3cd9944a 100644
--- a/sql/sql_trigger.cc
+++ b/sql/sql_trigger.cc
@@ -24,8 +24,6 @@
#include "parse_file.h"
#include "sp.h"
#include "sql_base.h" // find_temporary_table
-#include "lock.h" // wait_if_global_read_lock,
- // start_waiting_global_read_lock
#include "sql_show.h" // append_definer, append_identifier
#include "sql_table.h" // build_table_filename,
// check_n_cut_mysql50_prefix
@@ -391,15 +389,6 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
DBUG_RETURN(TRUE);
}
- /*
- We don't want perform our operations while global read lock is held
- so we have to wait until its end and then prevent it from occurring
- again until we are done, unless we are under lock tables.
- */
- if (!thd->locked_tables_mode &&
- thd->global_read_lock.wait_if_global_read_lock(thd, FALSE, TRUE))
- DBUG_RETURN(TRUE);
-
if (!create)
{
bool if_exists= thd->lex->drop_if_exists;
@@ -547,9 +536,6 @@ end:
if (!create)
thd->lex->restore_backup_query_tables_list(&backup);
- if (thd->global_read_lock.has_protection())
- thd->global_read_lock.start_waiting_global_read_lock(thd);
-
if (!result)
my_ok(thd);
diff --git a/sql/sql_update.cc b/sql/sql_update.cc
index 96b1ac67b49..8a9feb1b3f8 100644
--- a/sql/sql_update.cc
+++ b/sql/sql_update.cc
@@ -1026,9 +1026,17 @@ int mysql_multi_update_prepare(THD *thd)
/* following need for prepared statements, to run next time multi-update */
thd->lex->sql_command= SQLCOM_UPDATE_MULTI;
- /* open tables and create derived ones, but do not lock and fill them */
+ /*
+ Open tables and create derived ones, but do not lock and fill them yet.
+
+ During prepare phase acquire only S metadata locks instead of SW locks to
+ keep prepare of multi-UPDATE compatible with concurrent LOCK TABLES WRITE
+ and global read lock.
+ */
if ((original_multiupdate &&
- open_tables(thd, &table_list, &table_count, 0)) ||
+ open_tables(thd, &table_list, &table_count,
+ (thd->stmt_arena->is_stmt_prepare() ?
+ MYSQL_OPEN_FORCE_SHARED_MDL : 0))) ||
mysql_handle_derived(lex, &mysql_derived_prepare))
DBUG_RETURN(TRUE);
/*
diff --git a/sql/sql_view.cc b/sql/sql_view.cc
index 5fdf7b8d850..54b5eb43ab1 100644
--- a/sql/sql_view.cc
+++ b/sql/sql_view.cc
@@ -22,7 +22,7 @@
#include "sql_base.h" // find_table_in_global_list, lock_table_names
#include "sql_parse.h" // sql_parse
#include "sql_cache.h" // query_cache_*
-#include "lock.h" // wait_if_global_read_lock
+#include "lock.h" // MYSQL_OPEN_SKIP_TEMPORARY
#include "sql_show.h" // append_identifier
#include "sql_table.h" // build_table_filename
#include "sql_db.h" // mysql_opt_change_db, mysql_change_db
@@ -649,13 +649,6 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views,
}
#endif
-
- if (thd->global_read_lock.wait_if_global_read_lock(thd, FALSE, TRUE))
- {
- res= TRUE;
- goto err;
- }
-
res= mysql_register_view(thd, view, mode);
if (mysql_bin_log.is_open())
@@ -704,7 +697,6 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views,
if (mode != VIEW_CREATE_NEW)
query_cache_invalidate3(thd, view, 0);
- thd->global_read_lock.start_waiting_global_read_lock(thd);
if (res)
goto err;
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 66d74a398fb..9aa938437b1 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -7385,7 +7385,6 @@ select_lock_type:
LEX *lex=Lex;
lex->current_select->set_lock_for_tables(TL_WRITE);
lex->safe_to_cache_query=0;
- lex->protect_against_global_read_lock= TRUE;
}
| LOCK_SYM IN_SYM SHARE_SYM MODE_SYM
{
@@ -13182,9 +13181,6 @@ table_lock:
MDL_SHARED_NO_READ_WRITE :
MDL_SHARED_READ)))
MYSQL_YYABORT;
- /* If table is to be write locked, protect from a impending GRL. */
- if (lock_for_write)
- Lex->protect_against_global_read_lock= TRUE;
}
;
diff --git a/sql/table.cc b/sql/table.cc
index f55095d6e82..8cd2e9e9bab 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -5219,7 +5219,8 @@ void init_mdl_requests(TABLE_LIST *table_list)
table_list->mdl_request.init(MDL_key::TABLE,
table_list->db, table_list->table_name,
table_list->lock_type >= TL_WRITE_ALLOW_WRITE ?
- MDL_SHARED_WRITE : MDL_SHARED_READ);
+ MDL_SHARED_WRITE : MDL_SHARED_READ,
+ MDL_TRANSACTION);
}
diff --git a/sql/table.h b/sql/table.h
index c8e1ad8e658..6aba4f21db2 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -1384,7 +1384,8 @@ struct TABLE_LIST
lock_type= lock_type_arg;
mdl_request.init(MDL_key::TABLE, db, table_name,
(lock_type >= TL_WRITE_ALLOW_WRITE) ?
- MDL_SHARED_WRITE : MDL_SHARED_READ);
+ MDL_SHARED_WRITE : MDL_SHARED_READ,
+ MDL_TRANSACTION);
}
/*
diff --git a/sql/transaction.cc b/sql/transaction.cc
index d3e3ba142b9..b331fea89fe 100644
--- a/sql/transaction.cc
+++ b/sql/transaction.cc
@@ -21,6 +21,7 @@
#include "sql_priv.h"
#include "transaction.h"
#include "rpl_handler.h"
+#include "debug_sync.h" // DEBUG_SYNC
/* Conditions under which the transaction state must not change. */
static bool trans_check(THD *thd)
@@ -391,15 +392,15 @@ bool trans_savepoint(THD *thd, LEX_STRING name)
thd->transaction.savepoints= newsv;
/*
- Remember the last acquired lock before the savepoint was set.
- This is used as a marker to only release locks acquired after
+ Remember locks acquired before the savepoint was set.
+ They are used as a marker to only release locks acquired after
the setting of this savepoint.
Note: this works just fine if we're under LOCK TABLES,
since mdl_savepoint() is guaranteed to be beyond
the last locked table. This allows to release some
locks acquired during LOCK TABLES.
*/
- newsv->mdl_savepoint = thd->mdl_context.mdl_savepoint();
+ newsv->mdl_savepoint= thd->mdl_context.mdl_savepoint();
DBUG_RETURN(FALSE);
}
@@ -645,17 +646,31 @@ bool trans_xa_commit(THD *thd)
}
else if (xa_state == XA_PREPARED && thd->lex->xa_opt == XA_NONE)
{
- if (thd->global_read_lock.wait_if_global_read_lock(thd, FALSE, FALSE))
+ MDL_request mdl_request;
+
+ /*
+ Acquire metadata lock which will ensure that COMMIT is blocked
+ by active FLUSH TABLES WITH READ LOCK (and vice versa COMMIT in
+ progress blocks FTWRL).
+
+ We allow FLUSHer to COMMIT; we assume FLUSHer knows what it does.
+ */
+ mdl_request.init(MDL_key::COMMIT, "", "", MDL_INTENTION_EXCLUSIVE,
+ MDL_TRANSACTION);
+
+ if (thd->mdl_context.acquire_lock(&mdl_request,
+ thd->variables.lock_wait_timeout))
{
ha_rollback_trans(thd, TRUE);
my_error(ER_XAER_RMERR, MYF(0));
}
else
{
+ DEBUG_SYNC(thd, "trans_xa_commit_after_acquire_commit_lock");
+
res= test(ha_commit_one_phase(thd, 1));
if (res)
my_error(ER_XAER_RMERR, MYF(0));
- thd->global_read_lock.start_waiting_global_read_lock(thd);
}
}
else