summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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