summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mysql-test/extra/binlog_tests/drop_table.test34
-rw-r--r--mysql-test/extra/binlog_tests/mix_innodb_myisam_binlog.test4
-rw-r--r--mysql-test/include/mix1.inc1
-rw-r--r--mysql-test/include/mix2.inc2
-rw-r--r--mysql-test/r/flush_block_commit.result18
-rw-r--r--mysql-test/r/flush_block_commit_notembedded.result15
-rw-r--r--mysql-test/r/innodb.result3
-rw-r--r--mysql-test/r/innodb_mysql.result1
-rw-r--r--mysql-test/r/lock.result22
-rw-r--r--mysql-test/r/mix2_myisam.result2
-rw-r--r--mysql-test/r/not_embedded_server.result16
-rw-r--r--mysql-test/r/partition_innodb_semi_consistent.result2
-rw-r--r--mysql-test/r/partition_sync.result27
-rw-r--r--mysql-test/r/ps.result28
-rw-r--r--mysql-test/r/read_only_innodb.result6
-rw-r--r--mysql-test/suite/binlog/r/binlog_row_drop_tbl.result16
-rw-r--r--mysql-test/suite/binlog/r/binlog_row_mix_innodb_myisam.result1
-rw-r--r--mysql-test/suite/binlog/r/binlog_stm_drop_tbl.result13
-rw-r--r--mysql-test/suite/binlog/r/binlog_stm_mix_innodb_myisam.result1
-rw-r--r--mysql-test/suite/binlog/r/binlog_unsafe.result20
-rw-r--r--mysql-test/suite/binlog/t/binlog_row_drop_tbl.test5
-rw-r--r--mysql-test/suite/binlog/t/binlog_stm_drop_tbl.test5
-rw-r--r--mysql-test/suite/binlog/t/binlog_stm_row.test2
-rw-r--r--mysql-test/suite/ndb/r/ndb_index_ordered.result15
-rw-r--r--mysql-test/suite/ndb/t/disabled.def1
-rw-r--r--mysql-test/suite/ndb/t/ndb_index_ordered.test36
-rw-r--r--mysql-test/suite/rpl/t/disabled.def2
-rw-r--r--mysql-test/suite/sys_vars/r/autocommit_func.result2
-rw-r--r--mysql-test/suite/sys_vars/t/autocommit_func.test4
-rw-r--r--mysql-test/t/flush_block_commit.test36
-rw-r--r--mysql-test/t/flush_block_commit_notembedded.test27
-rw-r--r--mysql-test/t/innodb.test10
-rw-r--r--mysql-test/t/lock.test37
-rw-r--r--mysql-test/t/not_embedded_server.test23
-rw-r--r--mysql-test/t/partition_innodb_semi_consistent.test2
-rw-r--r--mysql-test/t/partition_sync.test67
-rw-r--r--mysql-test/t/ps.test41
-rw-r--r--mysql-test/t/read_only_innodb.test14
-rw-r--r--mysql-test/t/xa.test3
-rw-r--r--sql/ha_ndbcluster_binlog.cc37
-rw-r--r--sql/log_event.cc9
-rw-r--r--sql/mdl.cc6
-rw-r--r--sql/mysql_priv.h2
-rw-r--r--sql/rpl_injector.cc8
-rw-r--r--sql/rpl_rli.cc15
-rw-r--r--sql/set_var.cc12
-rw-r--r--sql/slave.cc3
-rw-r--r--sql/sql_acl.cc10
-rw-r--r--sql/sql_base.cc55
-rw-r--r--sql/sql_class.cc10
-rw-r--r--sql/sql_parse.cc85
-rw-r--r--sql/sql_plugin.cc4
-rw-r--r--sql/sql_prepare.cc25
-rw-r--r--sql/sql_servers.cc7
-rw-r--r--sql/sql_table.cc19
-rw-r--r--sql/sql_view.cc3
-rw-r--r--sql/table.h38
-rw-r--r--sql/transaction.cc17
-rw-r--r--tests/mysql_client_test.c6
59 files changed, 672 insertions, 263 deletions
diff --git a/mysql-test/extra/binlog_tests/drop_table.test b/mysql-test/extra/binlog_tests/drop_table.test
new file mode 100644
index 00000000000..c55cbb67560
--- /dev/null
+++ b/mysql-test/extra/binlog_tests/drop_table.test
@@ -0,0 +1,34 @@
+#
+# Bug#989: If DROP TABLE while there's an active transaction, wrong binlog order
+#
+
+--disable_warnings
+DROP TABLE IF EXISTS t1;
+--enable_warnings
+
+connect (con1,localhost,root,,);
+connect (con2,localhost,root,,);
+
+connection con1;
+RESET MASTER;
+CREATE TABLE t1 (a INT);
+SET AUTOCOMMIT=OFF;
+BEGIN;
+INSERT INTO t1 VALUES(1);
+
+connection con2;
+--send DROP TABLE t1;
+
+connection con1;
+COMMIT;
+
+connection con2;
+--reap
+
+connection default;
+
+--disconnect con1
+--disconnect con2
+
+let $VERSION=`select version()`;
+source include/show_binlog_events.inc;
diff --git a/mysql-test/extra/binlog_tests/mix_innodb_myisam_binlog.test b/mysql-test/extra/binlog_tests/mix_innodb_myisam_binlog.test
index da0b77fbc23..7c928b565dd 100644
--- a/mysql-test/extra/binlog_tests/mix_innodb_myisam_binlog.test
+++ b/mysql-test/extra/binlog_tests/mix_innodb_myisam_binlog.test
@@ -204,6 +204,10 @@ select (@after:=unix_timestamp())*0; # always give repeatable output
# the bug, the reap would return immediately after the insert into t2.
select (@after-@before) >= 2;
+connection con3;
+commit;
+
+connection con2;
drop table t1,t2;
commit;
diff --git a/mysql-test/include/mix1.inc b/mysql-test/include/mix1.inc
index 194d9e41108..3eaaf37cd83 100644
--- a/mysql-test/include/mix1.inc
+++ b/mysql-test/include/mix1.inc
@@ -1392,6 +1392,7 @@ SELECT * FROM t1;
connection con2;
--reap
SELECT * FROM t1;
+COMMIT;
--echo # Switch to connection con1
connection con1;
diff --git a/mysql-test/include/mix2.inc b/mysql-test/include/mix2.inc
index b4c4a9b8836..001d4cf44d4 100644
--- a/mysql-test/include/mix2.inc
+++ b/mysql-test/include/mix2.inc
@@ -1994,6 +1994,7 @@ commit;
connection b;
set autocommit = 0;
update t1 set b = 5 where a = 2;
+commit;
connection a;
delimiter |;
create trigger t1t before insert on t1 for each row begin set NEW.b = NEW.a * 10 + 5, NEW.c = NEW.a / 10; end |
@@ -2056,6 +2057,7 @@ update t2 set b = b + 5 where a = 1;
update t3 set b = b + 5 where a = 1;
update t4 set b = b + 5 where a = 1;
insert into t5(a) values(20);
+commit;
connection b;
set autocommit = 0;
insert into t1(a) values(7);
diff --git a/mysql-test/r/flush_block_commit.result b/mysql-test/r/flush_block_commit.result
index d2197beaaab..da09d07b813 100644
--- a/mysql-test/r/flush_block_commit.result
+++ b/mysql-test/r/flush_block_commit.result
@@ -1,3 +1,4 @@
+# Save the initial number of concurrent sessions
# Establish connection con1 (user=root)
# Establish connection con2 (user=root)
# Establish connection con3 (user=root)
@@ -8,13 +9,15 @@ BEGIN;
INSERT INTO t1 VALUES(1);
# Switch to connection con2
FLUSH TABLES WITH READ LOCK;
-SELECT * FROM t1;
-a
# Switch to connection con1
+# Sending:
COMMIT;
# Switch to connection con2
+# Wait until COMMIT gets blocked.
+# Verify that 'con1' was blocked and data did not move.
SELECT * FROM t1;
a
+1
UNLOCK TABLES;
# Switch to connection con1
# Switch to connection con1
@@ -32,6 +35,7 @@ COMMIT;
# Switch to connection con2
a
1
+COMMIT;
# Switch to connection con3
UNLOCK TABLES;
# Switch to connection con2
@@ -40,8 +44,6 @@ COMMIT;
BEGIN;
INSERT INTO t1 VALUES(10);
FLUSH TABLES WITH READ LOCK;
-COMMIT;
-UNLOCK TABLES;
# Switch to connection con2
FLUSH TABLES WITH READ LOCK;
UNLOCK TABLES;
@@ -53,5 +55,11 @@ a
SHOW CREATE DATABASE test;
Database Create Database
test CREATE DATABASE `test` /*!40100 DEFAULT CHARACTER SET latin1 */
-DROP TABLE t1;
+COMMIT;
+# Cleanup
# Switch to connection default and close connections con1, con2, con3
+# We commit open transactions when we disconnect: only then we can
+# drop the table.
+DROP TABLE t1;
+# End of 4.1 tests
+# Wait till all disconnects are completed
diff --git a/mysql-test/r/flush_block_commit_notembedded.result b/mysql-test/r/flush_block_commit_notembedded.result
index 4348dbd67e5..6d8af3f5864 100644
--- a/mysql-test/r/flush_block_commit_notembedded.result
+++ b/mysql-test/r/flush_block_commit_notembedded.result
@@ -1,17 +1,20 @@
+# Save the initial number of concurrent sessions
# Establish connection con1 (user=root)
# Establish connection con2 (user=root)
# Switch to connection con1
CREATE TABLE t1 (a INT) ENGINE=innodb;
RESET MASTER;
SET AUTOCOMMIT=0;
-INSERT t1 VALUES (1);
+SELECT 1;
+1
+1
# Switch to connection con2
FLUSH TABLES WITH READ LOCK;
SHOW MASTER STATUS;
File Position Binlog_Do_DB Binlog_Ignore_DB
master-bin.000001 107
# Switch to connection con1
-COMMIT;
+INSERT INTO t1 VALUES (1);
# Switch to connection con2
SHOW MASTER STATUS;
File Position Binlog_Do_DB Binlog_Ignore_DB
@@ -20,4 +23,12 @@ UNLOCK TABLES;
# Switch to connection con1
DROP TABLE t1;
SET AUTOCOMMIT=1;
+create table t1 (a int) engine=innodb;
+flush tables with read lock;
+begin;
+insert into t1 values (1);;
+unlock tables;
+commit;
+drop table t1;
# Switch to connection default and close connections con1 and con2
+# Wait till all disconnects are completed
diff --git a/mysql-test/r/innodb.result b/mysql-test/r/innodb.result
index 17b679d99e4..268bc839483 100644
--- a/mysql-test/r/innodb.result
+++ b/mysql-test/r/innodb.result
@@ -2836,10 +2836,10 @@ t2 CREATE TABLE `t2` (
DROP TABLE t2,t1;
create table t1(a int not null, b int, c int, d int, primary key(a)) engine=innodb;
insert into t1(a) values (1),(2),(3);
+create trigger t1t before insert on t1 for each row begin set NEW.b = NEW.a * 10 + 5, NEW.c = NEW.a / 10; end |
commit;
set autocommit = 0;
update t1 set b = 5 where a = 2;
-create trigger t1t before insert on t1 for each row begin set NEW.b = NEW.a * 10 + 5, NEW.c = NEW.a / 10; end |
set autocommit = 0;
insert into t1(a) values (10),(20),(30),(40),(50),(60),(70),(80),(90),(100),
(11),(21),(31),(41),(51),(61),(71),(81),(91),(101),
@@ -2887,6 +2887,7 @@ insert into t2(a) values(8);
delete from t2 where a = 3;
update t4 set b = b + 1 where a = 3;
commit;
+commit;
drop trigger t1t;
drop trigger t2t;
drop trigger t3t;
diff --git a/mysql-test/r/innodb_mysql.result b/mysql-test/r/innodb_mysql.result
index d6aa6a02468..0fe704c13f6 100644
--- a/mysql-test/r/innodb_mysql.result
+++ b/mysql-test/r/innodb_mysql.result
@@ -1618,6 +1618,7 @@ a b
SELECT * FROM t1;
a b
1 init+con1+con2
+COMMIT;
# Switch to connection con1
# 3. test for updated key column:
TRUNCATE t1;
diff --git a/mysql-test/r/lock.result b/mysql-test/r/lock.result
index 092c376b34a..c60ec4bc1bd 100644
--- a/mysql-test/r/lock.result
+++ b/mysql-test/r/lock.result
@@ -1,4 +1,4 @@
-drop table if exists t1,t2;
+drop table if exists t1,t2,t3;
CREATE TABLE t1 ( `id` int(11) NOT NULL default '0', `id2` int(11) NOT NULL default '0', `id3` int(11) NOT NULL default '0', `dummy1` char(30) default NULL, PRIMARY KEY (`id`,`id2`), KEY `index_id3` (`id3`)) ENGINE=MyISAM;
insert into t1 (id,id2) values (1,1),(1,2),(1,3);
LOCK TABLE t1 WRITE;
@@ -262,5 +262,25 @@ unlock tables;
drop table t1;
drop view v1;
#
+# WL#4284: Transactional DDL locking
+#
+drop table if exists t1;
+create table t1 (a int);
+set autocommit= 0;
+insert into t1 values (1);
+lock table t1 write;
+# Disconnect
+# Ensure that metadata locks will be released if there is an open
+# transaction (autocommit=off) in conjunction with lock tables.
+drop table t1;
+# Same problem but now for BEGIN
+drop table if exists t1;
+create table t1 (a int);
+begin;
+insert into t1 values (1);
+# Disconnect
+# Ensure that metadata locks held by the transaction are released.
+drop table t1;
+#
# End of 6.0 tests.
#
diff --git a/mysql-test/r/mix2_myisam.result b/mysql-test/r/mix2_myisam.result
index cabc4de8d21..99596c7774d 100644
--- a/mysql-test/r/mix2_myisam.result
+++ b/mysql-test/r/mix2_myisam.result
@@ -2063,6 +2063,7 @@ insert into t1(a) values (1),(2),(3);
commit;
set autocommit = 0;
update t1 set b = 5 where a = 2;
+commit;
create trigger t1t before insert on t1 for each row begin set NEW.b = NEW.a * 10 + 5, NEW.c = NEW.a / 10; end |
set autocommit = 0;
insert into t1(a) values (10),(20),(30),(40),(50),(60),(70),(80),(90),(100),
@@ -2105,6 +2106,7 @@ update t2 set b = b + 5 where a = 1;
update t3 set b = b + 5 where a = 1;
update t4 set b = b + 5 where a = 1;
insert into t5(a) values(20);
+commit;
set autocommit = 0;
insert into t1(a) values(7);
insert into t2(a) values(8);
diff --git a/mysql-test/r/not_embedded_server.result b/mysql-test/r/not_embedded_server.result
index 60c92bd0196..fac38624695 100644
--- a/mysql-test/r/not_embedded_server.result
+++ b/mysql-test/r/not_embedded_server.result
@@ -1,6 +1,16 @@
-select 1;
-1
-1
+call mtr.add_suppression("Can't open and lock privilege tables: Table 'host' was not locked with LOCK TABLES");
SHOW VARIABLES like 'slave_skip_errors';
Variable_name Value
slave_skip_errors OFF
+#
+# WL#4284: Transactional DDL locking
+#
+# FLUSH PRIVILEGES should not implicitly unlock locked tables.
+#
+drop table if exists t1;
+create table t1 (c1 int);
+lock tables t1 read;
+flush privileges;
+ERROR HY000: Table 'host' was not locked with LOCK TABLES
+unlock tables;
+drop table t1;
diff --git a/mysql-test/r/partition_innodb_semi_consistent.result b/mysql-test/r/partition_innodb_semi_consistent.result
index 471da4c1c2e..48a1bb3d258 100644
--- a/mysql-test/r/partition_innodb_semi_consistent.result
+++ b/mysql-test/r/partition_innodb_semi_consistent.result
@@ -102,7 +102,7 @@ a b
# Switch to connection con1
# 3. test for updated key column:
TRUNCATE t1;
-TRUNCATE t2;
+DELETE FROM t2;
INSERT INTO t1 VALUES (1,'init');
BEGIN;
UPDATE t1 SET a = 2, b = CONCAT(b, '+con1') WHERE a = 1;
diff --git a/mysql-test/r/partition_sync.result b/mysql-test/r/partition_sync.result
index 31cf0569464..41ca19426fe 100644
--- a/mysql-test/r/partition_sync.result
+++ b/mysql-test/r/partition_sync.result
@@ -1,25 +1,2 @@
-#
-# Bug #43867 ALTER TABLE on a partitioned table
-# causes unnecessary deadlocks
-#
-CREATE TABLE t1 (a int) PARTITION BY RANGE (a)
-(PARTITION p0 VALUES LESS THAN (1),
-PARTITION p1 VALUES LESS THAN (2));
-INSERT INTO t1 VALUES (0),(1);
-# Connection 2
-BEGIN;
-SELECT * FROM t1;
-a
-0
-1
-# Connection 1
-ALTER TABLE t1 DROP PARTITION p3;
-ERROR HY000: Error in list of partitions to DROP
-# Connection 2
-# This failed with deadlock and should not do so.
-SELECT * FROM t1;
-a
-0
-1
-# Connection 1
-DROP TABLE t1;
+# Disabled until Bug#46654 False deadlock on concurrent DML/DDL
+# with partitions, inconsistent behavior is backported
diff --git a/mysql-test/r/ps.result b/mysql-test/r/ps.result
index 6c7e83134d7..2526049c539 100644
--- a/mysql-test/r/ps.result
+++ b/mysql-test/r/ps.result
@@ -3086,5 +3086,29 @@ DROP PROCEDURE p1;
DROP PROCEDURE p2;
# End of WL#4435.
-
-End of 6.0 tests.
+#
+# WL#4284: Transactional DDL locking
+#
+DROP TABLE IF EXISTS t1;
+CREATE TABLE t1 (a INT);
+BEGIN;
+SELECT * FROM t1;
+a
+# Test that preparing a CREATE TABLE does not take a exclusive metdata lock.
+PREPARE stmt1 FROM "CREATE TABLE t1 AS SELECT 1";
+EXECUTE stmt1;
+ERROR 42S01: Table 't1' already exists
+DEALLOCATE PREPARE stmt1;
+DROP TABLE t1;
+#
+# WL#4284: Transactional DDL locking
+#
+# Test that metadata locks taken during prepare are released.
+#
+DROP TABLE IF EXISTS t1;
+CREATE TABLE t1 (a INT);
+BEGIN;
+PREPARE stmt1 FROM "SELECT * FROM t1";
+DROP TABLE t1;
+#
+# End of 6.0 tests.
diff --git a/mysql-test/r/read_only_innodb.result b/mysql-test/r/read_only_innodb.result
index 690de085bf9..4cba98900a1 100644
--- a/mysql-test/r/read_only_innodb.result
+++ b/mysql-test/r/read_only_innodb.result
@@ -7,12 +7,10 @@ insert into table_11733 values(11733);
set global read_only=1;
select @@global.read_only;
@@global.read_only
-1
+0
select * from table_11733 ;
-a
-11733
+ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
COMMIT;
-ERROR HY000: The MySQL server is running with the --read-only option so it cannot execute this statement
set global read_only=0;
drop table table_11733 ;
drop user test@localhost;
diff --git a/mysql-test/suite/binlog/r/binlog_row_drop_tbl.result b/mysql-test/suite/binlog/r/binlog_row_drop_tbl.result
new file mode 100644
index 00000000000..8b32e9e5a45
--- /dev/null
+++ b/mysql-test/suite/binlog/r/binlog_row_drop_tbl.result
@@ -0,0 +1,16 @@
+DROP TABLE IF EXISTS t1;
+RESET MASTER;
+CREATE TABLE t1 (a INT);
+SET AUTOCOMMIT=OFF;
+BEGIN;
+INSERT INTO t1 VALUES(1);
+DROP TABLE t1;;
+COMMIT;
+show binlog events from <binlog_start>;
+Log_name Pos Event_type Server_id End_log_pos Info
+master-bin.000001 # Query # # use `test`; CREATE TABLE t1 (a INT)
+master-bin.000001 # Query # # BEGIN
+master-bin.000001 # Table_map # # table_id: # (test.t1)
+master-bin.000001 # Write_rows # # table_id: # flags: STMT_END_F
+master-bin.000001 # Query # # COMMIT
+master-bin.000001 # Query # # use `test`; DROP TABLE t1
diff --git a/mysql-test/suite/binlog/r/binlog_row_mix_innodb_myisam.result b/mysql-test/suite/binlog/r/binlog_row_mix_innodb_myisam.result
index 4ccc3b5e797..c1254643a18 100644
--- a/mysql-test/suite/binlog/r/binlog_row_mix_innodb_myisam.result
+++ b/mysql-test/suite/binlog/r/binlog_row_mix_innodb_myisam.result
@@ -238,6 +238,7 @@ select (@after:=unix_timestamp())*0;
select (@after-@before) >= 2;
(@after-@before) >= 2
1
+commit;
drop table t1,t2;
commit;
begin;
diff --git a/mysql-test/suite/binlog/r/binlog_stm_drop_tbl.result b/mysql-test/suite/binlog/r/binlog_stm_drop_tbl.result
new file mode 100644
index 00000000000..f4596b808cf
--- /dev/null
+++ b/mysql-test/suite/binlog/r/binlog_stm_drop_tbl.result
@@ -0,0 +1,13 @@
+DROP TABLE IF EXISTS t1;
+RESET MASTER;
+CREATE TABLE t1 (a INT);
+SET AUTOCOMMIT=OFF;
+BEGIN;
+INSERT INTO t1 VALUES(1);
+DROP TABLE t1;;
+COMMIT;
+show binlog events from <binlog_start>;
+Log_name Pos Event_type Server_id End_log_pos Info
+master-bin.000001 # Query # # use `test`; CREATE TABLE t1 (a INT)
+master-bin.000001 # Query # # use `test`; INSERT INTO t1 VALUES(1)
+master-bin.000001 # Query # # use `test`; DROP TABLE t1
diff --git a/mysql-test/suite/binlog/r/binlog_stm_mix_innodb_myisam.result b/mysql-test/suite/binlog/r/binlog_stm_mix_innodb_myisam.result
index ea081183cd1..f05e184976c 100644
--- a/mysql-test/suite/binlog/r/binlog_stm_mix_innodb_myisam.result
+++ b/mysql-test/suite/binlog/r/binlog_stm_mix_innodb_myisam.result
@@ -207,6 +207,7 @@ select (@after:=unix_timestamp())*0;
select (@after-@before) >= 2;
(@after-@before) >= 2
1
+commit;
drop table t1,t2;
commit;
begin;
diff --git a/mysql-test/suite/binlog/r/binlog_unsafe.result b/mysql-test/suite/binlog/r/binlog_unsafe.result
index 58738a0d97c..3885acd079b 100644
--- a/mysql-test/suite/binlog/r/binlog_unsafe.result
+++ b/mysql-test/suite/binlog/r/binlog_unsafe.result
@@ -320,10 +320,10 @@ INSERT INTO t2 SET a = func_modify_t1();
SHOW BINLOG EVENTS FROM 12283;
Log_name Pos Event_type Server_id End_log_pos Info
master-bin.000001 12283 Query 1 12351 BEGIN
-master-bin.000001 12351 Table_map 1 12393 table_id: 44 (test.t2)
-master-bin.000001 12393 Table_map 1 12435 table_id: 45 (test.t1)
-master-bin.000001 12435 Write_rows 1 12473 table_id: 45
-master-bin.000001 12473 Write_rows 1 12511 table_id: 44 flags: STMT_END_F
+master-bin.000001 12351 Table_map 1 12393 table_id: 41 (test.t2)
+master-bin.000001 12393 Table_map 1 12435 table_id: 42 (test.t1)
+master-bin.000001 12435 Write_rows 1 12473 table_id: 42
+master-bin.000001 12473 Write_rows 1 12511 table_id: 41 flags: STMT_END_F
master-bin.000001 12511 Query 1 12580 COMMIT
DROP TABLE t1,t2;
DROP FUNCTION func_modify_t1;
@@ -347,12 +347,12 @@ INSERT INTO t1 SET a = 2;
SHOW BINLOG EVENTS FROM 13426;
Log_name Pos Event_type Server_id End_log_pos Info
master-bin.000001 13426 Query 1 13494 BEGIN
-master-bin.000001 13494 Table_map 1 13535 table_id: 47 (test.t1)
-master-bin.000001 13535 Table_map 1 13577 table_id: 48 (test.t3)
-master-bin.000001 13577 Table_map 1 13619 table_id: 49 (test.t2)
-master-bin.000001 13619 Write_rows 1 13657 table_id: 49
-master-bin.000001 13657 Write_rows 1 13695 table_id: 48
-master-bin.000001 13695 Write_rows 1 13729 table_id: 47 flags: STMT_END_F
+master-bin.000001 13494 Table_map 1 13535 table_id: 44 (test.t1)
+master-bin.000001 13535 Table_map 1 13577 table_id: 45 (test.t3)
+master-bin.000001 13577 Table_map 1 13619 table_id: 46 (test.t2)
+master-bin.000001 13619 Write_rows 1 13657 table_id: 46
+master-bin.000001 13657 Write_rows 1 13695 table_id: 45
+master-bin.000001 13695 Write_rows 1 13729 table_id: 44 flags: STMT_END_F
master-bin.000001 13729 Query 1 13798 COMMIT
DROP TABLE t1,t2,t3;
"End of tests"
diff --git a/mysql-test/suite/binlog/t/binlog_row_drop_tbl.test b/mysql-test/suite/binlog/t/binlog_row_drop_tbl.test
new file mode 100644
index 00000000000..06854900612
--- /dev/null
+++ b/mysql-test/suite/binlog/t/binlog_row_drop_tbl.test
@@ -0,0 +1,5 @@
+# This is a wrapper for drop_table.test so that the same test case can be used
+# For both statement and row based bin logs
+
+-- source include/have_binlog_format_row.inc
+-- source extra/binlog_tests/drop_table.test
diff --git a/mysql-test/suite/binlog/t/binlog_stm_drop_tbl.test b/mysql-test/suite/binlog/t/binlog_stm_drop_tbl.test
new file mode 100644
index 00000000000..f2b07bb69f6
--- /dev/null
+++ b/mysql-test/suite/binlog/t/binlog_stm_drop_tbl.test
@@ -0,0 +1,5 @@
+# This is a wrapper for drop_table.test so that the same test case can be used
+# For both statement and row based bin logs
+
+-- source include/have_binlog_format_mixed_or_statement.inc
+-- source extra/binlog_tests/drop_table.test
diff --git a/mysql-test/suite/binlog/t/binlog_stm_row.test b/mysql-test/suite/binlog/t/binlog_stm_row.test
index e923faae940..c501df324e8 100644
--- a/mysql-test/suite/binlog/t/binlog_stm_row.test
+++ b/mysql-test/suite/binlog/t/binlog_stm_row.test
@@ -57,7 +57,7 @@ let $wait_condition=
--echo # con1
let $wait_condition=
SELECT COUNT(*) = 1 FROM information_schema.processlist WHERE
- state = "Locked" and info = "INSERT INTO t2 VALUES (3)";
+ state = "Table lock" and info = "INSERT INTO t2 VALUES (3)";
--source include/wait_condition.inc
SELECT RELEASE_LOCK('Bug#34306');
--connection con2
diff --git a/mysql-test/suite/ndb/r/ndb_index_ordered.result b/mysql-test/suite/ndb/r/ndb_index_ordered.result
index a29b5343d7c..c99db354314 100644
--- a/mysql-test/suite/ndb/r/ndb_index_ordered.result
+++ b/mysql-test/suite/ndb/r/ndb_index_ordered.result
@@ -637,21 +637,6 @@ select count(*)- 4 from t1 use index (v) where v > 0000965.00042;
count(*)- 4
0
drop table t1;
-create table t1(a int primary key, b int not null, index(b));
-insert into t1 values (1,1), (2,2);
-set autocommit=0;
-begin;
-select count(*) from t1;
-count(*)
-2
-ALTER TABLE t1 ADD COLUMN c int;
-select a from t1 where b = 2;
-a
-2
-show tables;
-Tables_in_test
-t1
-drop table t1;
create table t1 (a int, c varchar(10),
primary key using hash (a), index(c)) engine=ndb;
insert into t1 (a, c) values (1,'aaa'),(3,'bbb');
diff --git a/mysql-test/suite/ndb/t/disabled.def b/mysql-test/suite/ndb/t/disabled.def
index 0fc9a5d3ad6..b2aa3e515be 100644
--- a/mysql-test/suite/ndb/t/disabled.def
+++ b/mysql-test/suite/ndb/t/disabled.def
@@ -13,3 +13,4 @@
ndb_partition_error2 : Bug#40989 ndb_partition_error2 needs maintenance
# the below testcase have been reworked to avoid the bug, test contains comment, keep bug open
+ndb_alter_table3 : Bug#45621 2009-06-10 alik A few test files are disabled due to WL#4284
diff --git a/mysql-test/suite/ndb/t/ndb_index_ordered.test b/mysql-test/suite/ndb/t/ndb_index_ordered.test
index 782f17ca5b2..c8dfc1de59f 100644
--- a/mysql-test/suite/ndb/t/ndb_index_ordered.test
+++ b/mysql-test/suite/ndb/t/ndb_index_ordered.test
@@ -333,21 +333,29 @@ select count(*)- 4 from t1 use index (v) where v > 0000965.00042;
drop table t1;
+#
+# Disabled due to WL#4284
+#
+# Needs to be reworked. It's not possible anymore to do a non-fast alter table
+# on a table that is being used by a pending transaction (transaction holds a
+# metadata lock on the table).
+#
# bug#7798
-create table t1(a int primary key, b int not null, index(b));
-insert into t1 values (1,1), (2,2);
-connect (con1,localhost,root,,test);
-connect (con2,localhost,root,,test);
-connection con1;
-set autocommit=0;
-begin;
-select count(*) from t1;
-connection con2;
-ALTER TABLE t1 ADD COLUMN c int;
-connection con1;
-select a from t1 where b = 2;
-show tables;
-drop table t1;
+# create table t1(a int primary key, b int not null, c int, index(b));
+# insert into t1 values (1,1,1), (2,2,2);
+# connect (con1,localhost,root,,test);
+# connect (con2,localhost,root,,test);
+# connection con1;
+# set autocommit=0;
+# begin;
+# select count(*) from t1;
+# connection con2;
+# ALTER TABLE t1 ADD COLUMN c int
+# connection con1;
+# select a from t1 where b = 2;
+# show tables;
+# drop table t1;
+#
# mysqld 5.0.13 crash, no bug#
create table t1 (a int, c varchar(10),
diff --git a/mysql-test/suite/rpl/t/disabled.def b/mysql-test/suite/rpl/t/disabled.def
index 485ba229257..6bd4e7e833a 100644
--- a/mysql-test/suite/rpl/t/disabled.def
+++ b/mysql-test/suite/rpl/t/disabled.def
@@ -12,3 +12,5 @@
rpl_cross_version : BUG#43913 2009-10-22 luis rpl_cross_version fails with symptom in described in bug report
rpl_spec_variables : BUG#47661 2009-10-27 jasonh rpl_spec_variables fails on PB2 hpux
+rpl_failed_optimize : WL#4284: Can't optimize table used by a pending transaction (there is metadata lock on the table).
+rpl_read_only : WL#4284: Setting Read only won't succeed until all metadata locks are released.
diff --git a/mysql-test/suite/sys_vars/r/autocommit_func.result b/mysql-test/suite/sys_vars/r/autocommit_func.result
index 47c2c921022..cb59c9a7b32 100644
--- a/mysql-test/suite/sys_vars/r/autocommit_func.result
+++ b/mysql-test/suite/sys_vars/r/autocommit_func.result
@@ -104,6 +104,8 @@ id name
2 Record_2
4 Record_4
5 Record_5
+## Commit changes
+COMMIT;
## Dropping table t1 ##
DROP table t1;
## Disconnecting both connections ##
diff --git a/mysql-test/suite/sys_vars/t/autocommit_func.test b/mysql-test/suite/sys_vars/t/autocommit_func.test
index 07e15ce40da..716189bb7be 100644
--- a/mysql-test/suite/sys_vars/t/autocommit_func.test
+++ b/mysql-test/suite/sys_vars/t/autocommit_func.test
@@ -153,6 +153,10 @@ SELECT * from t1;
CONNECTION test_con2;
SELECT * from t1;
+--echo ## Commit changes
+CONNECTION test_con1;
+COMMIT;
+
--echo ## Dropping table t1 ##
DROP table t1;
diff --git a/mysql-test/t/flush_block_commit.test b/mysql-test/t/flush_block_commit.test
index 74892def63f..98bca8cdad7 100644
--- a/mysql-test/t/flush_block_commit.test
+++ b/mysql-test/t/flush_block_commit.test
@@ -6,7 +6,7 @@
# And it requires InnoDB
--source include/have_innodb.inc
-# Save the initial number of concurrent sessions
+--echo # Save the initial number of concurrent sessions
--source include/count_sessions.inc
--echo # Establish connection con1 (user=root)
@@ -29,19 +29,26 @@ BEGIN;
INSERT INTO t1 VALUES(1);
--echo # Switch to connection con2
connection con2;
-FLUSH TABLES WITH READ LOCK;
-SELECT * FROM t1;
+--send FLUSH TABLES WITH READ LOCK
--echo # Switch to connection con1
connection con1;
-send COMMIT; # blocked by con2
-sleep 1;
+--echo # Sending:
+COMMIT;
--echo # Switch to connection con2
connection con2;
-SELECT * FROM t1; # verify con1 was blocked and data did not move
+--reap
+--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";
+#--source include/wait_condition.inc
+--echo # Verify that 'con1' was blocked and data did not move.
+SELECT * FROM t1;
UNLOCK TABLES;
--echo # Switch to connection con1
connection con1;
-reap;
+#--echo # Reaping COMMIT
+#--reap
# No deadlock ?
@@ -63,6 +70,7 @@ COMMIT; # should not be blocked by con3
--echo # Switch to connection con2
connection con2;
reap;
+COMMIT;
--echo # Switch to connection con3
connection con3;
reap;
@@ -79,8 +87,6 @@ connection con1;
BEGIN;
INSERT INTO t1 VALUES(10);
FLUSH TABLES WITH READ LOCK;
-COMMIT;
-UNLOCK TABLES;
--echo # Switch to connection con2
connection con2;
FLUSH TABLES WITH READ LOCK; # bug caused hang here
@@ -91,19 +97,21 @@ UNLOCK TABLES;
BEGIN;
SELECT * FROM t1;
SHOW CREATE DATABASE test;
-
-DROP TABLE t1;
+COMMIT;
-# Cleanup
+--echo # Cleanup
--echo # Switch to connection default and close connections con1, con2, con3
connection default;
disconnect con1;
disconnect con2;
disconnect con3;
-# End of 4.1 tests
+--echo # We commit open transactions when we disconnect: only then we can
+--echo # drop the table.
+DROP TABLE t1;
+--echo # End of 4.1 tests
-# Wait till all disconnects are completed
+--echo # Wait till all disconnects are completed
--source include/wait_until_count_sessions.inc
diff --git a/mysql-test/t/flush_block_commit_notembedded.test b/mysql-test/t/flush_block_commit_notembedded.test
index aea38250218..d7ffbd475b4 100644
--- a/mysql-test/t/flush_block_commit_notembedded.test
+++ b/mysql-test/t/flush_block_commit_notembedded.test
@@ -9,7 +9,7 @@
--source include/have_log_bin.inc
--source include/have_innodb.inc
-# Save the initial number of concurrent sessions
+--echo # Save the initial number of concurrent sessions
--source include/count_sessions.inc
@@ -24,14 +24,14 @@ connection con1;
CREATE TABLE t1 (a INT) ENGINE=innodb;
RESET MASTER;
SET AUTOCOMMIT=0;
-INSERT t1 VALUES (1);
+SELECT 1;
--echo # Switch to connection con2
connection con2;
FLUSH TABLES WITH READ LOCK;
SHOW MASTER STATUS;
--echo # Switch to connection con1
connection con1;
-send COMMIT;
+send INSERT INTO t1 VALUES (1);
--echo # Switch to connection con2
connection con2;
sleep 1;
@@ -43,11 +43,30 @@ reap;
DROP TABLE t1;
SET AUTOCOMMIT=1;
+# GLR blocks new transactions
+create table t1 (a int) engine=innodb;
+connection con1;
+flush tables with read lock;
+connection con2;
+begin;
+--send insert into t1 values (1);
+connection con1;
+let $wait_condition=
+ select count(*) = 1 from information_schema.processlist
+ where state = "Waiting for release of readlock" and
+ info = "insert into t1 values (1)";
+--source include/wait_condition.inc
+unlock tables;
+connection con2;
+--reap
+commit;
+drop table t1;
+
--echo # Switch to connection default and close connections con1 and con2
connection default;
disconnect con1;
disconnect con2;
-# Wait till all disconnects are completed
+--echo # Wait till all disconnects are completed
--source include/wait_until_count_sessions.inc
diff --git a/mysql-test/t/innodb.test b/mysql-test/t/innodb.test
index 2879a4c0b9f..6e7d9b5e780 100644
--- a/mysql-test/t/innodb.test
+++ b/mysql-test/t/innodb.test
@@ -1849,16 +1849,15 @@ connect (b,localhost,root,,);
connection a;
create table t1(a int not null, b int, c int, d int, primary key(a)) engine=innodb;
insert into t1(a) values (1),(2),(3);
+delimiter |;
+create trigger t1t before insert on t1 for each row begin set NEW.b = NEW.a * 10 + 5, NEW.c = NEW.a / 10; end |
+delimiter ;|
commit;
connection b;
set autocommit = 0;
update t1 set b = 5 where a = 2;
connection a;
-delimiter |;
-create trigger t1t before insert on t1 for each row begin set NEW.b = NEW.a * 10 + 5, NEW.c = NEW.a / 10; end |
-delimiter ;|
set autocommit = 0;
-connection a;
insert into t1(a) values (10),(20),(30),(40),(50),(60),(70),(80),(90),(100),
(11),(21),(31),(41),(51),(61),(71),(81),(91),(101),
(12),(22),(32),(42),(52),(62),(72),(82),(92),(102),
@@ -1922,6 +1921,9 @@ insert into t2(a) values(8);
delete from t2 where a = 3;
update t4 set b = b + 1 where a = 3;
commit;
+connection a;
+commit;
+connection b;
drop trigger t1t;
drop trigger t2t;
drop trigger t3t;
diff --git a/mysql-test/t/lock.test b/mysql-test/t/lock.test
index 51900be4df8..c2aeac05cd1 100644
--- a/mysql-test/t/lock.test
+++ b/mysql-test/t/lock.test
@@ -3,7 +3,7 @@
#
--disable_warnings
-drop table if exists t1,t2;
+drop table if exists t1,t2,t3;
--enable_warnings
CREATE TABLE t1 ( `id` int(11) NOT NULL default '0', `id2` int(11) NOT NULL default '0', `id3` int(11) NOT NULL default '0', `dummy1` char(30) default NULL, PRIMARY KEY (`id`,`id2`), KEY `index_id3` (`id3`)) ENGINE=MyISAM;
insert into t1 (id,id2) values (1,1),(1,2),(1,3);
@@ -311,5 +311,40 @@ drop table t1;
drop view v1;
--echo #
+--echo # WL#4284: Transactional DDL locking
+--echo #
+
+--disable_warnings
+drop table if exists t1;
+--enable_warnings
+create table t1 (a int);
+connect(con1,localhost,root,,);
+set autocommit= 0;
+insert into t1 values (1);
+lock table t1 write;
+--echo # Disconnect
+--echo # Ensure that metadata locks will be released if there is an open
+--echo # transaction (autocommit=off) in conjunction with lock tables.
+disconnect con1;
+connection default;
+drop table t1;
+
+--echo # Same problem but now for BEGIN
+
+--disable_warnings
+drop table if exists t1;
+--enable_warnings
+create table t1 (a int);
+connect(con1,localhost,root,,);
+begin;
+insert into t1 values (1);
+--echo # Disconnect
+--echo # Ensure that metadata locks held by the transaction are released.
+disconnect con1;
+connection default;
+drop table t1;
+
+
+--echo #
--echo # End of 6.0 tests.
--echo #
diff --git a/mysql-test/t/not_embedded_server.test b/mysql-test/t/not_embedded_server.test
index fa2b659ec57..917d5871682 100644
--- a/mysql-test/t/not_embedded_server.test
+++ b/mysql-test/t/not_embedded_server.test
@@ -4,12 +4,6 @@
-- source include/not_embedded.inc
-#
-# Produce output
-#
-
-select 1;
-
# The following fails sporadically because 'check-testcase' runs
# queries before this test and there is no way to guarantee that any
# previous process finishes. The purpose of the test is not clearly
@@ -36,6 +30,8 @@ select 1;
#execute stmt1;
#deallocate prepare stmt1;
+call mtr.add_suppression("Can't open and lock privilege tables: Table 'host' was not locked with LOCK TABLES");
+
#
# Bug#43835: SHOW VARIABLES does not include 0 for slave_skip_errors
#
@@ -43,3 +39,18 @@ select 1;
SHOW VARIABLES like 'slave_skip_errors';
# End of 5.1 tests
+
+--echo #
+--echo # WL#4284: Transactional DDL locking
+--echo #
+--echo # FLUSH PRIVILEGES should not implicitly unlock locked tables.
+--echo #
+--disable_warnings
+drop table if exists t1;
+--enable_warnings
+create table t1 (c1 int);
+lock tables t1 read;
+--error ER_TABLE_NOT_LOCKED
+flush privileges;
+unlock tables;
+drop table t1;
diff --git a/mysql-test/t/partition_innodb_semi_consistent.test b/mysql-test/t/partition_innodb_semi_consistent.test
index 6a6a7cf958e..2bf879603a4 100644
--- a/mysql-test/t/partition_innodb_semi_consistent.test
+++ b/mysql-test/t/partition_innodb_semi_consistent.test
@@ -157,7 +157,7 @@ connection con1;
--echo # 3. test for updated key column:
TRUNCATE t1;
-TRUNCATE t2;
+DELETE FROM t2;
INSERT INTO t1 VALUES (1,'init');
diff --git a/mysql-test/t/partition_sync.test b/mysql-test/t/partition_sync.test
index a732b35b8b9..5d2b25e87f3 100644
--- a/mysql-test/t/partition_sync.test
+++ b/mysql-test/t/partition_sync.test
@@ -2,38 +2,41 @@
# Save the initial number of concurrent sessions.
--source include/count_sessions.inc
---echo #
---echo # Bug #43867 ALTER TABLE on a partitioned table
---echo # causes unnecessary deadlocks
---echo #
-
-CREATE TABLE t1 (a int) PARTITION BY RANGE (a)
-(PARTITION p0 VALUES LESS THAN (1),
- PARTITION p1 VALUES LESS THAN (2));
-
-INSERT INTO t1 VALUES (0),(1);
-
-connect(con1,localhost,root);
-
---echo # Connection 2
-connection con1;
-BEGIN;
-SELECT * FROM t1;
-
---echo # Connection 1
-connection default;
---error ER_DROP_PARTITION_NON_EXISTENT
-ALTER TABLE t1 DROP PARTITION p3;
-
---echo # Connection 2
-connection con1;
---echo # This failed with deadlock and should not do so.
-SELECT * FROM t1;
-
---echo # Connection 1
-connection default;
-disconnect con1;
-DROP TABLE t1;
+--echo # Disabled until Bug#46654 False deadlock on concurrent DML/DDL
+--echo # with partitions, inconsistent behavior is backported
+
+#--echo #
+#--echo # Bug #43867 ALTER TABLE on a partitioned table
+#--echo # causes unnecessary deadlocks
+#--echo #
+#
+#CREATE TABLE t1 (a int) PARTITION BY RANGE (a)
+#(PARTITION p0 VALUES LESS THAN (1),
+# PARTITION p1 VALUES LESS THAN (2));
+#
+#INSERT INTO t1 VALUES (0),(1);
+#
+#connect(con1,localhost,root);
+#
+#--echo # Connection 2
+#connection con1;
+#BEGIN;
+#SELECT * FROM t1;
+#
+#--echo # Connection 1
+#connection default;
+#--error ER_DROP_PARTITION_NON_EXISTENT
+#ALTER TABLE t1 DROP PARTITION p3;
+#
+#--echo # Connection 2
+#connection con1;
+#--echo # This failed with deadlock and should not do so.
+#SELECT * FROM t1;
+#
+#--echo # Connection 1
+#connection default;
+#disconnect con1;
+#DROP TABLE t1;
# Check that all connections opened by test cases in this file are really
diff --git a/mysql-test/t/ps.test b/mysql-test/t/ps.test
index 844be582290..6cebbe6c7a1 100644
--- a/mysql-test/t/ps.test
+++ b/mysql-test/t/ps.test
@@ -3211,7 +3211,44 @@ DROP PROCEDURE p2;
###########################################################################
---echo
---echo End of 6.0 tests.
+
+--echo #
+--echo # WL#4284: Transactional DDL locking
+--echo #
+
+--disable_warnings
+DROP TABLE IF EXISTS t1;
+--enable_warnings
+CREATE TABLE t1 (a INT);
+BEGIN;
+SELECT * FROM t1;
+--echo # Test that preparing a CREATE TABLE does not take a exclusive metdata lock.
+PREPARE stmt1 FROM "CREATE TABLE t1 AS SELECT 1";
+--error ER_TABLE_EXISTS_ERROR
+EXECUTE stmt1;
+DEALLOCATE PREPARE stmt1;
+DROP TABLE t1;
+
+--echo #
+--echo # WL#4284: Transactional DDL locking
+--echo #
+--echo # Test that metadata locks taken during prepare are released.
+--echo #
+
+connect(con1,localhost,root,,);
+connection default;
+--disable_warnings
+DROP TABLE IF EXISTS t1;
+--enable_warnings
+CREATE TABLE t1 (a INT);
+connection con1;
+BEGIN;
+PREPARE stmt1 FROM "SELECT * FROM t1";
+connection default;
+DROP TABLE t1;
+disconnect con1;
+
+--echo #
+--echo # End of 6.0 tests.
###########################################################################
diff --git a/mysql-test/t/read_only_innodb.test b/mysql-test/t/read_only_innodb.test
index f8c25fdee1d..98e704e25c7 100644
--- a/mysql-test/t/read_only_innodb.test
+++ b/mysql-test/t/read_only_innodb.test
@@ -16,6 +16,8 @@ DROP TABLE IF EXISTS table_11733 ;
grant CREATE, SELECT, DROP on *.* to test@localhost;
connect (con1,localhost,test,,test);
+connect (con2,localhost,root,,);
+
connection default;
set global read_only=0;
@@ -28,15 +30,22 @@ BEGIN;
insert into table_11733 values(11733);
connection default;
-set global read_only=1;
+send set global read_only=1;
+
+connection con2;
+let $wait_condition=
+ select count(*) = 1 from information_schema.processlist
+ where state = "Flushing tables" and info = "set global read_only=1";
+--source include/wait_condition.inc
connection con1;
select @@global.read_only;
+--error ER_LOCK_DEADLOCK
select * from table_11733 ;
--- error ER_OPTION_PREVENTS_STATEMENT
COMMIT;
connection default;
+reap;
set global read_only=0;
drop table table_11733 ;
drop user test@localhost;
@@ -81,5 +90,6 @@ DROP TABLE t1;
DROP USER test@localhost;
disconnect con1;
+disconnect con2;
--echo echo End of 5.1 tests
diff --git a/mysql-test/t/xa.test b/mysql-test/t/xa.test
index f84d822170f..0f705ae20c6 100644
--- a/mysql-test/t/xa.test
+++ b/mysql-test/t/xa.test
@@ -76,9 +76,10 @@ xa rollback 'testa','testb';
xa start 'zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz';
select * from t1;
-drop table t1;
disconnect con1;
+connection default;
+drop table t1;
#
# Bug#28323: Server crashed in xid cache operations
diff --git a/sql/ha_ndbcluster_binlog.cc b/sql/ha_ndbcluster_binlog.cc
index 08abb88e768..7ce318394d4 100644
--- a/sql/ha_ndbcluster_binlog.cc
+++ b/sql/ha_ndbcluster_binlog.cc
@@ -2347,7 +2347,7 @@ static int open_ndb_binlog_index(THD *thd, TABLE **ndb_binlog_index)
tables->required_type= FRMTYPE_TABLE;
uint counter;
thd->clear_error();
- if (open_tables(thd, &tables, &counter, MYSQL_LOCK_IGNORE_FLUSH))
+ if (simple_open_n_lock_tables(thd, tables))
{
if (thd->killed)
sql_print_error("NDB Binlog: Opening ndb_binlog_index: killed");
@@ -2381,28 +2381,11 @@ int ndb_add_ndb_binlog_index(THD *thd, void *_row)
ulong saved_options= thd->options;
thd->options&= ~(OPTION_BIN_LOG);
- for ( ; ; ) /* loop for need_reopen */
+ if (!ndb_binlog_index && open_ndb_binlog_index(thd, &ndb_binlog_index))
{
- if (!ndb_binlog_index && open_ndb_binlog_index(thd, &ndb_binlog_index))
- {
- error= -1;
- goto add_ndb_binlog_index_err;
- }
-
- if (lock_tables(thd, &binlog_tables, 1, 0, &need_reopen))
- {
- if (need_reopen)
- {
- TABLE_LIST *p_binlog_tables= &binlog_tables;
- close_tables_for_reopen(thd, &p_binlog_tables, FALSE);
- ndb_binlog_index= 0;
- continue;
- }
- sql_print_error("NDB Binlog: Unable to lock table ndb_binlog_index");
- error= -1;
- goto add_ndb_binlog_index_err;
- }
- break;
+ sql_print_error("NDB Binlog: Unable to lock table ndb_binlog_index");
+ error= -1;
+ goto add_ndb_binlog_index_err;
}
/*
@@ -2428,13 +2411,6 @@ int ndb_add_ndb_binlog_index(THD *thd, void *_row)
}
- if (! thd->locked_tables_mode) /* Is always TRUE */
- {
- mysql_unlock_tables(thd, thd->lock);
- thd->lock= 0;
- }
- thd->options= saved_options;
- return 0;
add_ndb_binlog_index_err:
close_thread_tables(thd);
ndb_binlog_index= 0;
@@ -3905,9 +3881,6 @@ restart:
{
static char db[]= "";
thd->db= db;
- if (ndb_binlog_running)
- open_ndb_binlog_index(thd, &ndb_binlog_index);
- thd->db= db;
}
do_ndbcluster_binlog_close_connection= BCCC_running;
for ( ; !((ndbcluster_binlog_terminating ||
diff --git a/sql/log_event.cc b/sql/log_event.cc
index e41bb690e8a..aa8dd6e9bff 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -5314,10 +5314,17 @@ void Xid_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
int Xid_log_event::do_apply_event(Relay_log_info const *rli)
{
+ bool res;
/* For a slave Xid_log_event is COMMIT */
general_log_print(thd, COM_QUERY,
"COMMIT /* implicit, from Xid_log_event */");
- return trans_commit(thd);
+ if (!(res= trans_commit(thd)))
+ {
+ close_thread_tables(thd);
+ if (!thd->locked_tables_mode)
+ thd->mdl_context.release_all_locks();
+ }
+ return res;
}
Log_event::enum_skip_reason
diff --git a/sql/mdl.cc b/sql/mdl.cc
index 1d591bb2244..eb8fcdb323e 100644
--- a/sql/mdl.cc
+++ b/sql/mdl.cc
@@ -491,7 +491,7 @@ void MDL_ticket::destroy(MDL_ticket *ticket)
@sa THD::enter_cond()/exit_cond()/killed.
@note We can't use THD::enter_cond()/exit_cond()/killed directly here
- since this will make metadata subsystem dependant on THD class
+ since this will make metadata subsystem dependent on THD class
and thus prevent us from writing unit tests for it. And usage of
wrapper functions to access THD::killed/enter_cond()/exit_cond()
will probably introduce too much overhead.
@@ -881,6 +881,7 @@ static bool notify_shared_lock(THD *thd, MDL_ticket *conflicting_ticket)
if (conflicting_ticket->is_shared())
{
THD *conflicting_thd= conflicting_ticket->get_ctx()->get_thd();
+ DBUG_ASSERT(thd != conflicting_thd); /* Self-deadlock */
woke= mysql_notify_thread_having_shared_lock(thd, conflicting_thd);
}
return woke;
@@ -1089,7 +1090,6 @@ MDL_ticket::upgrade_shared_lock_to_exclusive()
old_msg= MDL_ENTER_COND(thd, mysys_var);
-
/*
Since we should have already acquired an intention exclusive
global lock this call is only enforcing asserts.
@@ -1164,7 +1164,7 @@ MDL_ticket::upgrade_shared_lock_to_exclusive()
@param conflict [out] Indicates that conflicting lock exists
@retval TRUE Failure either conflicting lock exists or some error
- occured (probably OOM).
+ occurred (probably OOM).
@retval FALSE Success, lock was acquired.
FIXME: Compared to lock_table_name_if_not_cached()
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index 3f6ed2b1cb0..159613c58a0 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -827,7 +827,7 @@ extern my_decimal decimal_zero;
void free_items(Item *item);
void cleanup_items(Item *item);
class THD;
-void close_thread_tables(THD *thd, bool skip_mdl= 0);
+void close_thread_tables(THD *thd, bool is_back_off= 0);
#ifndef NO_EMBEDDED_ACCESS_CHECKS
bool check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *tables);
diff --git a/sql/rpl_injector.cc b/sql/rpl_injector.cc
index 738341cc034..9d82307d2e7 100644
--- a/sql/rpl_injector.cc
+++ b/sql/rpl_injector.cc
@@ -83,10 +83,16 @@ int injector::transaction::commit()
explicitly.
*/
trans_commit_stmt(m_thd);
- trans_commit(m_thd);
+ if (!trans_commit(m_thd))
+ {
+ close_thread_tables(m_thd);
+ if (!m_thd->locked_tables_mode)
+ m_thd->mdl_context.release_all_locks();
+ }
DBUG_RETURN(0);
}
+
int injector::transaction::use_table(server_id_type sid, table tbl)
{
DBUG_ENTER("injector::transaction::use_table");
diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc
index 695b160fc01..b4554bb4b6c 100644
--- a/sql/rpl_rli.cc
+++ b/sql/rpl_rli.cc
@@ -1189,6 +1189,8 @@ void Relay_log_info::cleanup_context(THD *thd, bool error)
}
m_table_map.clear_tables();
slave_close_thread_tables(thd);
+ if (error && !thd->locked_tables_mode)
+ thd->mdl_context.release_all_locks();
clear_flag(IN_STMT);
/*
Cleanup for the flags that have been set at do_apply_event.
@@ -1200,13 +1202,6 @@ void Relay_log_info::cleanup_context(THD *thd, bool error)
void Relay_log_info::clear_tables_to_lock()
{
- /*
- Deallocating elements of table list below will also free memory where
- meta-data locks are stored. So we want to be sure that we don't have
- any references to this memory left.
- */
- DBUG_ASSERT(!current_thd->mdl_context.has_locks());
-
while (tables_to_lock)
{
uchar* to_free= reinterpret_cast<uchar*>(tables_to_lock);
@@ -1225,12 +1220,6 @@ void Relay_log_info::clear_tables_to_lock()
void Relay_log_info::slave_close_thread_tables(THD *thd)
{
- /*
- Since we use same memory chunks for allocation of metadata lock
- objects for tables as we use for allocating corresponding elements
- of 'tables_to_lock' list, we have to release metadata locks by
- closing tables before calling clear_tables_to_lock().
- */
close_thread_tables(thd);
clear_tables_to_lock();
}
diff --git a/sql/set_var.cc b/sql/set_var.cc
index 266cdd9ad6d..dd009541274 100644
--- a/sql/set_var.cc
+++ b/sql/set_var.cc
@@ -3190,9 +3190,15 @@ static bool set_option_autocommit(THD *thd, set_var *var)
need to commit any outstanding transactions.
*/
if (var->save_result.ulong_value != 0 &&
- (thd->options & OPTION_NOT_AUTOCOMMIT) &&
- trans_commit(thd))
- return 1;
+ (thd->options & OPTION_NOT_AUTOCOMMIT))
+ {
+ if (trans_commit(thd))
+ return TRUE;
+
+ close_thread_tables(thd);
+ if (!thd->locked_tables_mode)
+ thd->mdl_context.release_all_locks();
+ }
if (var->save_result.ulong_value != 0)
thd->options&= ~((sys_var_thd_bit*) var->var)->bit_flag;
diff --git a/sql/slave.cc b/sql/slave.cc
index ed722305b29..0316ae2591d 100644
--- a/sql/slave.cc
+++ b/sql/slave.cc
@@ -2432,6 +2432,9 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli)
{
exec_res= 0;
trans_rollback(thd);
+ close_thread_tables(thd);
+ if (!thd->locked_tables_mode)
+ thd->mdl_context.release_all_locks();
/* chance for concurrent connection to get more locks */
safe_sleep(thd, min(rli->trans_retries, MAX_SLAVE_RETRY_PAUSE),
(CHECK_KILLED_FUNC)sql_slave_killed, (void*)rli);
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index aa96483cf09..f4a182b321f 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -30,6 +30,7 @@
#include <stdarg.h>
#include "sp_head.h"
#include "sp.h"
+#include "transaction.h"
time_t mysql_db_table_last_check= 0L;
@@ -676,9 +677,6 @@ my_bool acl_reload(THD *thd)
my_bool return_val= 1;
DBUG_ENTER("acl_reload");
- /* Can't have locked tables here. */
- thd->locked_tables_list.unlock_locked_tables(thd);
-
/*
To avoid deadlocks we should obtain table locks before
obtaining acl_cache->lock mutex.
@@ -732,7 +730,10 @@ my_bool acl_reload(THD *thd)
if (old_initialized)
pthread_mutex_unlock(&acl_cache->lock);
end:
+ trans_commit_implicit(thd);
close_thread_tables(thd);
+ if (!thd->locked_tables_mode)
+ thd->mdl_context.release_all_locks();
DBUG_RETURN(return_val);
}
@@ -3900,7 +3901,10 @@ my_bool grant_reload(THD *thd)
free_root(&old_mem,MYF(0));
}
rw_unlock(&LOCK_grant);
+ trans_commit_implicit(thd);
close_thread_tables(thd);
+ if (!thd->locked_tables_mode)
+ thd->mdl_context.release_all_locks();
/*
It is OK failing to load procs_priv table because we may be
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 0441458510f..42fc3ba0566 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -1478,11 +1478,22 @@ void close_thread_tables(THD *thd,
if (thd->open_tables)
close_open_tables(thd);
- thd->mdl_context.release_all_locks();
if (!is_back_off)
{
thd->mdl_context.remove_all_requests();
}
+
+ /*
+ Defer the release of metadata locks until the current transaction
+ is either committed or rolled back. This prevents other statements
+ from modifying the table for the entire duration of this transaction.
+ This provides commitment ordering for guaranteeing serializability
+ across multiple transactions.
+ */
+ if (!thd->in_multi_stmt_transaction() ||
+ (thd->state_flags & Open_tables_state::BACKUPS_AVAIL))
+ thd->mdl_context.release_all_locks();
+
DBUG_VOID_RETURN;
}
@@ -2284,7 +2295,7 @@ open_table_get_mdl_lock(THD *thd, TABLE_LIST *table_list,
{
thd->mdl_context.add_request(mdl_request);
- if (table_list->open_type)
+ if (table_list->lock_strategy)
{
/*
In case of CREATE TABLE .. If NOT EXISTS .. SELECT, the table
@@ -2358,10 +2369,16 @@ open_table_get_mdl_lock(THD *thd, TABLE_LIST *table_list,
IMPLEMENTATION
Uses a cache of open tables to find a table not in use.
- If table list element for the table to be opened has "open_type" set
- to OPEN_OR_CREATE and table does not exist, this function will take
- exclusive metadata lock on the table, also it will do this if
- "open_type" is TAKE_EXCLUSIVE_MDL.
+ If TABLE_LIST::open_strategy is set to OPEN_IF_EXISTS, the table is opened
+ only if it exists. If the open strategy is OPEN_STUB, the underlying table
+ is never opened. In both cases, metadata locks are always taken according
+ to the lock strategy.
+
+ This function will take a exclusive metadata lock on the table if
+ TABLE_LIST::lock_strategy is EXCLUSIVE_DOWNGRADABLE_MDL or EXCLUSIVE_MDL.
+ If the lock strategy is EXCLUSIVE_DOWNGRADABLE_MDL and opening the table
+ is successful, the exclusive metadata lock is downgraded to a shared
+ lock.
RETURN
TRUE Open failed. "action" parameter may contain type of action
@@ -2595,7 +2612,7 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
DBUG_RETURN(TRUE);
}
- if (table_list->open_type == TABLE_LIST::OPEN_OR_CREATE)
+ if (table_list->open_strategy == TABLE_LIST::OPEN_IF_EXISTS)
{
bool exists;
@@ -2609,7 +2626,7 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
}
/* Table exists. Let us try to open it. */
}
- else if (table_list->open_type == TABLE_LIST::TAKE_EXCLUSIVE_MDL)
+ else if (table_list->open_strategy == TABLE_LIST::OPEN_STUB)
{
pthread_mutex_unlock(&LOCK_open);
DBUG_RETURN(FALSE);
@@ -2794,7 +2811,7 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
table exists now we should downgrade our exclusive metadata
lock on this table to shared metadata lock.
*/
- if (table_list->open_type == TABLE_LIST::OPEN_OR_CREATE)
+ if (table_list->lock_strategy == TABLE_LIST::EXCLUSIVE_DOWNGRADABLE_MDL)
mdl_ticket->downgrade_exclusive_lock();
table->mdl_ticket= mdl_ticket;
@@ -3623,7 +3640,7 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags)
/* Also used for indicating that prelocking is need */
TABLE_LIST **query_tables_last_own;
bool safe_to_ignore_table;
-
+ bool has_locks= thd->mdl_context.has_locks();
DBUG_ENTER("open_tables");
/*
temporary mem_root for new .frm parsing.
@@ -3764,6 +3781,18 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags)
if (action)
{
/*
+ We have met a exclusive metadata lock or a old version of table and
+ we are inside a transaction that already hold locks. We can't follow
+ the locking protocol in this scenario as it might lead to deadlocks.
+ */
+ if (thd->in_multi_stmt_transaction() && has_locks)
+ {
+ my_error(ER_LOCK_DEADLOCK, MYF(0));
+ result= -1;
+ goto err;
+ }
+
+ /*
We have met exclusive metadata lock or old version of table. Now we
have to close all tables which are not up to date/release metadata
locks. We also have to throw away set of prelocked tables (and thus
@@ -3841,7 +3870,7 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags)
Special types of open can succeed but still don't set
TABLE_LIST::table to anything.
*/
- if (tables->open_type && !tables->table)
+ if (tables->open_strategy && !tables->table)
continue;
/*
@@ -4122,7 +4151,7 @@ retry:
if (!error)
{
/*
- We can't have a view or some special "open_type" in this function
+ We can't have a view or some special "open_strategy" in this function
so there should be a TABLE instance.
*/
DBUG_ASSERT(table_list->table);
@@ -4662,6 +4691,8 @@ void close_tables_for_reopen(THD *thd, TABLE_LIST **tables, bool is_back_off)
for (TABLE_LIST *tmp= *tables; tmp; tmp= tmp->next_global)
tmp->table= 0;
close_thread_tables(thd, is_back_off);
+ if (!thd->locked_tables_mode)
+ thd->mdl_context.release_all_locks();
}
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 654d7ad718d..7252f078f81 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -995,8 +995,18 @@ void THD::cleanup(void)
trans_rollback(this);
xid_cache_delete(&transaction.xid_state);
}
+
locked_tables_list.unlock_locked_tables(this);
+ /*
+ If the thread was in the middle of an ongoing transaction (rolled
+ back a few lines above) or under LOCK TABLES (unlocked the tables
+ and left the mode a few lines above), there will be outstanding
+ metadata locks. Release them.
+ */
+ DBUG_ASSERT(open_tables == NULL);
+ mdl_context.release_all_locks();
+
#if defined(ENABLED_DEBUG_SYNC)
/* End the Debug Sync Facility. See debug_sync.cc. */
debug_sync_end_thread(this);
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 579fbcea4ea..ddc163072a7 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -122,11 +122,11 @@ static bool some_non_temp_table_to_be_updated(THD *thd, TABLE_LIST *tables)
@param mask Bitmask used for the SQL command match.
*/
-static bool opt_implicit_commit(THD *thd, uint mask)
+static bool stmt_causes_implicit_commit(THD *thd, uint mask)
{
LEX *lex= thd->lex;
- bool res= FALSE, skip= FALSE;
- DBUG_ENTER("opt_implicit_commit");
+ bool skip= FALSE;
+ DBUG_ENTER("stmt_causes_implicit_commit");
if (!(sql_command_flags[lex->sql_command] & mask))
DBUG_RETURN(FALSE);
@@ -147,15 +147,7 @@ static bool opt_implicit_commit(THD *thd, uint mask)
break;
}
- if (!skip)
- {
- /* Commit or rollback the statement transaction. */
- thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd);
- /* Commit the normal transaction if one is active. */
- res= trans_commit_implicit(thd);
- }
-
- DBUG_RETURN(res);
+ DBUG_RETURN(!skip);
}
@@ -1168,6 +1160,9 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
ulong options= (ulong) (uchar) packet[0];
if (trans_commit_implicit(thd))
break;
+ close_thread_tables(thd);
+ if (!thd->locked_tables_mode)
+ thd->mdl_context.release_all_locks();
if (check_global_access(thd,RELOAD_ACL))
break;
general_log_print(thd, command, NullS);
@@ -1196,6 +1191,9 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
break;
if (trans_commit_implicit(thd))
break;
+ close_thread_tables(thd);
+ if (!thd->locked_tables_mode)
+ thd->mdl_context.release_all_locks();
my_ok(thd);
break;
}
@@ -1942,8 +1940,18 @@ mysql_execute_command(THD *thd)
not run in it's own transaction it may simply never appear on
the slave in case the outside transaction rolls back.
*/
- if (opt_implicit_commit(thd, CF_IMPLICT_COMMIT_BEGIN))
- goto error;
+ if (stmt_causes_implicit_commit(thd, CF_IMPLICT_COMMIT_BEGIN))
+ {
+ /* Commit or rollback the statement transaction. */
+ thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd);
+ /* Commit the normal transaction if one is active. */
+ if (trans_commit_implicit(thd))
+ goto error;
+ /* Close tables and release metadata locks. */
+ close_thread_tables(thd);
+ if (!thd->locked_tables_mode)
+ thd->mdl_context.release_all_locks();
+ }
switch (lex->sql_command) {
@@ -2363,7 +2371,9 @@ case SQLCOM_PREPARE:
if (!(create_info.options & HA_LEX_CREATE_TMP_TABLE))
{
lex->link_first_table_back(create_table, link_to_local);
- create_table->open_type= TABLE_LIST::OPEN_OR_CREATE;
+ /* Set strategies: reset default or 'prepared' values. */
+ create_table->open_strategy= TABLE_LIST::OPEN_IF_EXISTS;
+ create_table->lock_strategy= TABLE_LIST::EXCLUSIVE_DOWNGRADABLE_MDL;
}
if (!(res= open_and_lock_tables(thd, lex->query_tables)))
@@ -3306,6 +3316,7 @@ end_with_restore_list:
if (thd->options & OPTION_TABLE_LOCK)
{
trans_commit_implicit(thd);
+ thd->mdl_context.release_all_locks();
thd->options&= ~(OPTION_TABLE_LOCK);
}
if (thd->global_read_lock)
@@ -3317,6 +3328,8 @@ end_with_restore_list:
/* we must end the trasaction first, regardless of anything */
if (trans_commit_implicit(thd))
goto error;
+ /* release transactional metadata locks. */
+ thd->mdl_context.release_all_locks();
if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, all_tables,
FALSE, UINT_MAX, FALSE))
goto error;
@@ -3346,6 +3359,13 @@ end_with_restore_list:
*/
trans_rollback_stmt(thd);
trans_commit_implicit(thd);
+ /*
+ Close tables and release metadata locks otherwise a later call to
+ close_thread_tables might not release the locks if autocommit is off.
+ */
+ close_thread_tables(thd);
+ DBUG_ASSERT(!thd->locked_tables_mode);
+ thd->mdl_context.release_all_locks();
thd->options&= ~(OPTION_TABLE_LOCK);
}
else
@@ -3836,6 +3856,8 @@ end_with_restore_list:
thd->locked_tables_mode == LTM_LOCK_TABLES);
if (trans_commit(thd))
goto error;
+ if (!thd->locked_tables_mode)
+ thd->mdl_context.release_all_locks();
/* Begin transaction with the same isolation level. */
if (lex->tx_chain && trans_begin(thd))
goto error;
@@ -3849,6 +3871,8 @@ end_with_restore_list:
thd->locked_tables_mode == LTM_LOCK_TABLES);
if (trans_rollback(thd))
goto error;
+ if (!thd->locked_tables_mode)
+ thd->mdl_context.release_all_locks();
/* Begin transaction with the same isolation level. */
if (lex->tx_chain && trans_begin(thd))
goto error;
@@ -4196,6 +4220,12 @@ create_sp_error:
if (trans_commit_implicit(thd))
goto error;
+
+ close_thread_tables(thd);
+
+ if (!thd->locked_tables_mode)
+ thd->mdl_context.release_all_locks();
+
#ifndef NO_EMBEDDED_ACCESS_CHECKS
if (sp_automatic_privileges && !opt_noacl &&
sp_revoke_privileges(thd, db, name,
@@ -4375,11 +4405,15 @@ create_sp_error:
case SQLCOM_XA_COMMIT:
if (trans_xa_commit(thd))
goto error;
+ if (!thd->locked_tables_mode)
+ thd->mdl_context.release_all_locks();
my_ok(thd);
break;
case SQLCOM_XA_ROLLBACK:
if (trans_xa_rollback(thd))
goto error;
+ if (!thd->locked_tables_mode)
+ thd->mdl_context.release_all_locks();
my_ok(thd);
break;
case SQLCOM_XA_RECOVER:
@@ -4524,10 +4558,20 @@ finish:
start_waiting_global_read_lock(thd);
}
- /* If commit fails, we should be able to reset the OK status. */
- thd->stmt_da->can_overwrite_status= TRUE;
- opt_implicit_commit(thd, CF_IMPLICIT_COMMIT_END);
- thd->stmt_da->can_overwrite_status= FALSE;
+ if (stmt_causes_implicit_commit(thd, CF_IMPLICIT_COMMIT_END))
+ {
+ /* If commit fails, we should be able to reset the OK status. */
+ thd->stmt_da->can_overwrite_status= TRUE;
+ /* Commit or rollback the statement transaction. */
+ thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd);
+ /* Commit the normal transaction if one is active. */
+ trans_commit_implicit(thd);
+ /* Close tables and release metadata locks. */
+ close_thread_tables(thd);
+ if (!thd->locked_tables_mode)
+ thd->mdl_context.release_all_locks();
+ thd->stmt_da->can_overwrite_status= FALSE;
+ }
DBUG_RETURN(res || thd->is_error());
}
@@ -6508,6 +6552,9 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables,
query_cache.flush(); // RESET QUERY CACHE
}
#endif /*HAVE_QUERY_CACHE*/
+
+ DBUG_ASSERT(thd->locked_tables_mode || !thd->mdl_context.has_locks());
+
/*
Note that if REFRESH_READ_LOCK bit is set then REFRESH_TABLES is set too
(see sql_yacc.yy)
diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc
index 77b5552d977..b7efe13e26e 100644
--- a/sql/sql_plugin.cc
+++ b/sql/sql_plugin.cc
@@ -1349,6 +1349,7 @@ static void plugin_load(MEM_ROOT *tmp_root, int *argc, char **argv)
#ifdef EMBEDDED_LIBRARY
bool table_exists;
#endif /* EMBEDDED_LIBRARY */
+ MDL_request mdl_request;
DBUG_ENTER("plugin_load");
if (!(new_thd= new THD))
@@ -1366,7 +1367,8 @@ static void plugin_load(MEM_ROOT *tmp_root, int *argc, char **argv)
tables.alias= tables.table_name= (char*)"plugin";
tables.lock_type= TL_READ;
tables.db= new_thd->db;
- alloc_mdl_requests(&tables, tmp_root);
+ tables.mdl_request= &mdl_request;
+ mdl_request.init(0, tables.db, tables.table_name);
#ifdef EMBEDDED_LIBRARY
/*
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index 5efa0cea7a9..9c7949eaf1d 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -1673,7 +1673,13 @@ static bool mysql_test_create_table(Prepared_statement *stmt)
if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE))
{
lex->link_first_table_back(create_table, link_to_local);
- create_table->open_type= TABLE_LIST::OPEN_OR_CREATE;
+ /*
+ The open and lock strategies will be set again once the
+ statement is executed. These values are only meaningful
+ for the prepare phase.
+ */
+ create_table->open_strategy= TABLE_LIST::OPEN_IF_EXISTS;
+ create_table->lock_strategy= TABLE_LIST::SHARED_MDL;
}
if (open_normal_and_derived_tables(stmt->thd, lex->query_tables, 0))
@@ -3130,6 +3136,7 @@ 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.
@@ -3188,6 +3195,13 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len)
*/
DBUG_ASSERT(thd->change_list.is_empty());
+ /*
+ Marker used to release metadata locks acquired while the prepared
+ statement is being checked.
+ */
+ if (thd->in_multi_stmt_transaction())
+ mdl_savepoint= thd->mdl_context.mdl_savepoint();
+
/*
The only case where we should have items in the thd->free_list is
after stmt->set_params_from_vars(), which may in some cases create
@@ -3211,6 +3225,15 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len)
lex_end(lex);
cleanup_stmt();
+ /*
+ If not inside a multi-statement transaction, the metadata locks have
+ already been released and the rollback_to_savepoint is a nop.
+ Otherwise, release acquired locks -- a NULL mdl_savepoint means that
+ all locks are going to be released or that the transaction didn't
+ own any locks.
+ */
+ if (!thd->locked_tables_mode)
+ thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
thd->restore_backup_statement(this, &stmt_backup);
thd->stmt_arena= old_stmt_arena;
diff --git a/sql/sql_servers.cc b/sql/sql_servers.cc
index 40cdfeed946..c46a01cd534 100644
--- a/sql/sql_servers.cc
+++ b/sql/sql_servers.cc
@@ -39,6 +39,7 @@
#include <stdarg.h>
#include "sp_head.h"
#include "sp.h"
+#include "transaction.h"
/*
We only use 1 mutex to guard the data structures - THR_LOCK_servers.
@@ -224,9 +225,6 @@ bool servers_reload(THD *thd)
bool return_val= TRUE;
DBUG_ENTER("servers_reload");
- /* Can't have locked tables here */
- thd->locked_tables_list.unlock_locked_tables(thd);
-
DBUG_PRINT("info", ("locking servers_cache"));
rw_wrlock(&THR_LOCK_servers);
@@ -252,7 +250,10 @@ bool servers_reload(THD *thd)
}
end:
+ trans_commit_implicit(thd);
close_thread_tables(thd);
+ if (!thd->locked_tables_mode)
+ thd->mdl_context.release_all_locks();
DBUG_PRINT("info", ("unlocking servers_cache"));
rw_unlock(&THR_LOCK_servers);
DBUG_RETURN(return_val);
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index 42ae0a89458..8fdaa2cd93a 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -1974,7 +1974,8 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
}
/* Probably a non-temporary table. */
- non_temp_tables_count++;
+ if (!drop_temporary)
+ non_temp_tables_count++;
/*
If row-based replication is used and the table is not a
@@ -4738,6 +4739,8 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
trans_commit_stmt(thd);
trans_commit(thd);
close_thread_tables(thd);
+ if (!thd->locked_tables_mode)
+ thd->mdl_context.release_all_locks();
lex->reset_query_tables_list(FALSE);
table->table=0; // For query cache
if (protocol->write())
@@ -4786,7 +4789,10 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
{
DBUG_PRINT("admin", ("recreating table"));
trans_rollback_stmt(thd);
+ trans_rollback(thd);
close_thread_tables(thd);
+ if (!thd->locked_tables_mode)
+ thd->mdl_context.release_all_locks();
tmp_disable_binlog(thd); // binlogging is done by caller if wanted
result_code= mysql_recreate_table(thd, table);
reenable_binlog(thd);
@@ -4899,14 +4905,17 @@ send_result_message:
reopen the table and do ha_innobase::analyze() on it.
We have to end the row, so analyze could return more rows.
*/
+ trans_commit_stmt(thd);
+ trans_commit(thd);
+ close_thread_tables(thd);
+ if (!thd->locked_tables_mode)
+ thd->mdl_context.release_all_locks();
protocol->store(STRING_WITH_LEN("note"), system_charset_info);
protocol->store(STRING_WITH_LEN(
"Table does not support optimize, doing recreate + analyze instead"),
system_charset_info);
if (protocol->write())
goto err;
- trans_commit_stmt(thd);
- close_thread_tables(thd);
DBUG_PRINT("info", ("HA_ADMIN_TRY_ALTER, trying analyze..."));
TABLE_LIST *save_next_local= table->next_local,
*save_next_global= table->next_global;
@@ -4923,7 +4932,10 @@ send_result_message:
if (thd->stmt_da->is_ok())
thd->stmt_da->reset_diagnostics_area();
trans_commit_stmt(thd);
+ trans_commit(thd);
close_thread_tables(thd);
+ if (!thd->locked_tables_mode)
+ thd->mdl_context.release_all_locks();
if (!result_code) // recreation went ok
{
if ((table->table= open_ltable(thd, table, lock_type, 0)) &&
@@ -5017,6 +5029,7 @@ send_result_message:
query_cache_invalidate3(thd, table->table, 0);
}
}
+ /* Error path, a admin command failed. */
trans_commit_stmt(thd);
trans_commit_implicit(thd);
close_thread_tables(thd);
diff --git a/sql/sql_view.cc b/sql/sql_view.cc
index b370fc79b17..b36ff6b6743 100644
--- a/sql/sql_view.cc
+++ b/sql/sql_view.cc
@@ -395,7 +395,8 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views,
goto err;
lex->link_first_table_back(view, link_to_local);
- view->open_type= TABLE_LIST::TAKE_EXCLUSIVE_MDL;
+ view->open_strategy= TABLE_LIST::OPEN_STUB;
+ view->lock_strategy= TABLE_LIST::EXCLUSIVE_MDL;
if (open_and_lock_tables(thd, lex->query_tables))
{
diff --git a/sql/table.h b/sql/table.h
index b6ea372b41e..1762ae8785d 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -1350,24 +1350,36 @@ struct TABLE_LIST
bool prelocking_placeholder;
/**
Indicates that if TABLE_LIST object corresponds to the table/view
- which requires special handling/meta-data locking.
+ which requires special handling.
*/
enum
{
- /* Normal open, shared metadata lock should be taken. */
- NORMAL_OPEN= 0,
- /*
- It's target table of CREATE TABLE ... SELECT so we should
- either open table if it exists (and take shared metadata lock)
- or take exclusive metadata lock if it doesn't exist.
- */
- OPEN_OR_CREATE,
+ /* Normal open. */
+ OPEN_NORMAL= 0,
+ /* Associate a table share only if the the table exists. */
+ OPEN_IF_EXISTS,
+ /* Don't associate a table share. */
+ OPEN_STUB
+ } open_strategy;
+ /**
+ Indicates the locking strategy for the object being opened:
+ whether the associated metadata lock is shared or exclusive.
+ */
+ enum
+ {
+ /* Take a shared metadata lock before the object is opened. */
+ SHARED_MDL= 0,
/*
- It's target view of CREATE/ALTER VIEW. We should take exclusive
- metadata lock for this table list element.
+ Take a exclusive metadata lock before the object is opened.
+ If opening is successful, downgrade to a shared lock.
*/
- TAKE_EXCLUSIVE_MDL
- } open_type;
+ EXCLUSIVE_DOWNGRADABLE_MDL,
+ /* Take a exclusive metadata lock before the object is opened. */
+ EXCLUSIVE_MDL
+ } lock_strategy;
+ /* For transactional locking. */
+ int lock_timeout; /* NOWAIT or WAIT [X] */
+ bool lock_transactional; /* If transactional lock requested. */
bool internal_tmp_table;
/** TRUE if an alias for this table was specified in the SQL. */
bool is_alias;
diff --git a/sql/transaction.cc b/sql/transaction.cc
index 7bfaf4846cf..d1c7244ba83 100644
--- a/sql/transaction.cc
+++ b/sql/transaction.cc
@@ -99,6 +99,12 @@ bool trans_begin(THD *thd, uint flags)
if (trans_commit_implicit(thd))
DBUG_RETURN(TRUE);
+ /*
+ Release transactional metadata locks only after the
+ transaction has been committed.
+ */
+ thd->mdl_context.release_all_locks();
+
thd->options|= OPTION_BEGIN;
thd->server_status|= SERVER_STATUS_IN_TRANS;
@@ -331,6 +337,13 @@ bool trans_savepoint(THD *thd, LEX_STRING name)
newsv->prev= thd->transaction.savepoints;
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
+ the setting of this savepoint.
+ */
+ newsv->mdl_savepoint = thd->mdl_context.mdl_savepoint();
+
DBUG_RETURN(FALSE);
}
@@ -375,6 +388,10 @@ bool trans_rollback_to_savepoint(THD *thd, LEX_STRING name)
thd->transaction.savepoints= sv;
+ /* Release metadata locks that were acquired during this savepoint unit. */
+ if (!res && !thd->locked_tables_mode)
+ thd->mdl_context.rollback_to_savepoint(sv->mdl_savepoint);
+
DBUG_RETURN(test(res));
}
diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c
index c9edd210c32..56e1d609754 100644
--- a/tests/mysql_client_test.c
+++ b/tests/mysql_client_test.c
@@ -18479,8 +18479,8 @@ static void test_wl4284_1()
mysql_free_result(result);
- /* set AUTOCOMMIT to OFF */
- rc= mysql_autocommit(mysql, FALSE);
+ /* set AUTOCOMMIT to ON */
+ rc= mysql_autocommit(mysql, TRUE);
myquery(rc);
rc= mysql_query(mysql, "DROP TABLE trans");
@@ -18637,6 +18637,8 @@ static void test_bug40365(void)
DIE_UNLESS(tm[i].day == 0);
}
mysql_stmt_close(stmt);
+ rc= mysql_commit(mysql);
+ myquery(rc);
DBUG_VOID_RETURN;
}