diff options
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; } |