List:Commits« Previous MessageNext Message »
From:Davi Arnaut Date:February 11 2010 2:54pm
Subject:bzr commit into mysql-5.5-next-mr branch (davi:3090) Bug#42643
View as plain text  
# At a local mysql-5.5-next-mr repository of davi

 3090 Davi Arnaut	2010-02-11
      Bug#42643: InnoDB does not support replication of TRUNCATE TABLE
      
      The problem was that TRUNCATE TABLE didn't take a exclusive
      lock on a table if it resorted to truncating via delete of
      all rows in the table. Specifically for InnoDB tables, this
      could break proper isolation as InnoDB ends up aborting some
      granted locks when truncating a table.
      
      The solution is to take a exclusive metadata lock before
      TRUNCATE TABLE can proceed. This guarantees that no other
      transaction is using the table.
     @ mysql-test/extra/binlog_tests/binlog_truncate.test
        Add test cae for Bug#42643
     @ mysql-test/include/mix1.inc
        Update test case as TRUNCATE TABLE now grabs a exclusive lock.
     @ mysql-test/r/innodb_mysql.result
        Update test case result.
     @ mysql-test/r/truncate.result
        Update test case result.
     @ mysql-test/suite/binlog/t/binlog_truncate_innodb.test
        As with other data modifying statements, TRUNCATE is still not
        possible in a transaction with isolation level READ COMMITTED
        or READ UNCOMMITED. It would be possible to implement so, but
        it is not worth the effort.
     @ mysql-test/suite/binlog/t/binlog_truncate_myisam.test
        Test under different binlog formats.
     @ mysql-test/suite/binlog/t/disabled.def
        Re-enable test case.
     @ mysql-test/t/truncate.test
        A metadata lock is now taken before the object is verified.
     @ sql/mysql_priv.h
        Rename functions.
     @ sql/sql_delete.cc
        Move truncation of a pre-locked table to its own function -- this
        is only used by repair. Also, move temporary table truncation to
        its own function.
        
        Acquire a exclusive metadata lock before accessing table metadata.
     @ sql/sql_parse.cc
        Rename function.
     @ sql/sql_table.cc
        Rename function.

    modified:
      mysql-test/extra/binlog_tests/binlog_truncate.test
      mysql-test/include/mix1.inc
      mysql-test/r/innodb_mysql.result
      mysql-test/r/partition_innodb_semi_consistent.result
      mysql-test/r/truncate.result
      mysql-test/suite/binlog/r/binlog_truncate_innodb.result
      mysql-test/suite/binlog/r/binlog_truncate_myisam.result
      mysql-test/suite/binlog/t/binlog_truncate_innodb.test
      mysql-test/suite/binlog/t/binlog_truncate_myisam.test
      mysql-test/suite/binlog/t/disabled.def
      mysql-test/t/partition_innodb_semi_consistent.test
      mysql-test/t/truncate.test
      sql/mysql_priv.h
      sql/sql_delete.cc
      sql/sql_parse.cc
      sql/sql_table.cc
=== modified file 'mysql-test/extra/binlog_tests/binlog_truncate.test'
--- a/mysql-test/extra/binlog_tests/binlog_truncate.test	2009-02-06 16:06:41 +0000
+++ b/mysql-test/extra/binlog_tests/binlog_truncate.test	2010-02-11 14:54:53 +0000
@@ -25,3 +25,43 @@ TRUNCATE TABLE t2;
 source include/show_binlog_events.inc;
 
 DROP TABLE t1,t2;
+
+--echo #
+--echo # Bug#42643: InnoDB does not support replication of TRUNCATE TABLE
+--echo #
+
+eval CREATE TABLE t1 (a INT) ENGINE=$engine;
+eval CREATE TABLE t2 (a INT) ENGINE=$engine;
+INSERT INTO t1 VALUES (1),(2);
+
+let $binlog_start = query_get_value("SHOW MASTER STATUS", Position, 1);
+if (`select length('$before_truncate') > 0`) {
+  eval $before_truncate;
+}
+
+--echo # Connection: default
+BEGIN;
+INSERT INTO t2 SELECT * FROM t1;
+
+connect (truncate,localhost,root,,);
+--echo # Connection: truncate
+--send TRUNCATE TABLE t1
+
+connection default;
+--echo # Connection: default
+INSERT INTO t2 SELECT * FROM t1;
+SELECT COUNT(*) FROM t2;
+COMMIT;
+
+connection truncate;
+--echo # Connection: truncate
+--reap
+SELECT COUNT(*) FROM t1;
+SELECT COUNT(*) FROM t2;
+
+connection default;
+--echo # Connection: default
+
+source include/show_binlog_events.inc;
+disconnect truncate;
+DROP TABLE t1,t2;

=== modified file 'mysql-test/include/mix1.inc'
--- a/mysql-test/include/mix1.inc	2009-12-08 07:39:49 +0000
+++ b/mysql-test/include/mix1.inc	2010-02-11 14:54:53 +0000
@@ -1351,6 +1351,13 @@ connection con1;
 SELECT * FROM t1;
 ROLLBACK;
 
+--echo # Switch to connection con2
+connection con2;
+ROLLBACK;
+
+--echo # Switch to connection con1
+connection con1;
+
 --echo # 2. test for serialized update:
 
 CREATE TABLE t2 (a INT);

=== modified file 'mysql-test/r/innodb_mysql.result'
--- a/mysql-test/r/innodb_mysql.result	2010-02-01 23:22:16 +0000
+++ b/mysql-test/r/innodb_mysql.result	2010-02-11 14:54:53 +0000
@@ -1590,6 +1590,9 @@ SELECT * FROM t1;
 a	b
 1	12
 ROLLBACK;
+# Switch to connection con2
+ROLLBACK;
+# Switch to connection con1
 # 2. test for serialized update:
 CREATE TABLE t2 (a INT);
 TRUNCATE t1;

=== modified file 'mysql-test/r/partition_innodb_semi_consistent.result'
--- a/mysql-test/r/partition_innodb_semi_consistent.result	2009-12-04 23:02:48 +0000
+++ b/mysql-test/r/partition_innodb_semi_consistent.result	2010-02-11 14:54:53 +0000
@@ -64,6 +64,7 @@ a	b
 # Switch to connection con2
 UPDATE t1 SET b = 21 WHERE a = 1;
 ERROR HY000: Lock wait timeout exceeded; try restarting transaction
+ROLLBACK;
 # Switch to connection con1
 SELECT * FROM t1;
 a	b
@@ -99,6 +100,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;

=== modified file 'mysql-test/r/truncate.result'
--- a/mysql-test/r/truncate.result	2009-12-11 12:24:23 +0000
+++ b/mysql-test/r/truncate.result	2010-02-11 14:54:53 +0000
@@ -99,7 +99,7 @@ LOCK TABLE t1 WRITE;
 SELECT * FROM v1;
 ERROR HY000: Table 'v1' was not locked with LOCK TABLES
 TRUNCATE v1;
-ERROR 42S02: Table 'test.v1' doesn't exist
+ERROR HY000: Table 'v1' was not locked with LOCK TABLES
 SELECT * FROM v1;
 ERROR HY000: Table 'v1' was not locked with LOCK TABLES
 UNLOCK TABLES;
@@ -107,7 +107,7 @@ LOCK TABLE t1 WRITE, t2 WRITE;
 SELECT * FROM v1;
 ERROR HY000: Table 'v1' was not locked with LOCK TABLES
 TRUNCATE v1;
-ERROR 42S02: Table 'test.v1' doesn't exist
+ERROR HY000: Table 'v1' was not locked with LOCK TABLES
 SELECT * FROM v1;
 ERROR HY000: Table 'v1' was not locked with LOCK TABLES
 UNLOCK TABLES;
@@ -117,7 +117,7 @@ c1
 1
 3
 TRUNCATE v1;
-ERROR 42S02: Table 'test.v1' doesn't exist
+ERROR HY000: Table 'v1' was not locked with LOCK TABLES
 SELECT * FROM v1;
 c1
 1
@@ -129,7 +129,7 @@ c1
 1
 3
 TRUNCATE v1;
-ERROR 42S02: Table 'test.v1' doesn't exist
+ERROR HY000: Table 'v1' was not locked with LOCK TABLES
 SELECT * FROM v1;
 c1
 1

=== modified file 'mysql-test/suite/binlog/r/binlog_truncate_innodb.result'
--- a/mysql-test/suite/binlog/r/binlog_truncate_innodb.result	2009-02-06 16:06:41 +0000
+++ b/mysql-test/suite/binlog/r/binlog_truncate_innodb.result	2010-02-11 14:54:53 +0000
@@ -1,3 +1,5 @@
+SET BINLOG_FORMAT=ROW;
+RESET MASTER;
 CREATE TABLE t1 (a INT) ENGINE=InnoDB;
 CREATE TABLE t2 (a INT) ENGINE=InnoDB;
 INSERT INTO t2 VALUES (1),(2),(3);
@@ -9,6 +11,41 @@ Log_name	Pos	Event_type	Server_id	End_lo
 master-bin.000001	#	Query	#	#	use `test`; TRUNCATE TABLE t1
 master-bin.000001	#	Query	#	#	use `test`; TRUNCATE TABLE t2
 DROP TABLE t1,t2;
+#
+# Bug#42643: InnoDB does not support replication of TRUNCATE TABLE
+#
+CREATE TABLE t1 (a INT) ENGINE=InnoDB;
+CREATE TABLE t2 (a INT) ENGINE=InnoDB;
+INSERT INTO t1 VALUES (1),(2);
+# Connection: default
+BEGIN;
+INSERT INTO t2 SELECT * FROM t1;
+# Connection: truncate
+TRUNCATE TABLE t1;
+# Connection: default
+INSERT INTO t2 SELECT * FROM t1;
+SELECT COUNT(*) FROM t2;
+COUNT(*)
+4
+COMMIT;
+# Connection: truncate
+SELECT COUNT(*) FROM t1;
+COUNT(*)
+0
+SELECT COUNT(*) FROM t2;
+COUNT(*)
+4
+# Connection: default
+show binlog events from <binlog_start>;
+Log_name	Pos	Event_type	Server_id	End_log_pos	Info
+master-bin.000001	#	Query	#	#	BEGIN
+master-bin.000001	#	Table_map	#	#	table_id: # (test.t2)
+master-bin.000001	#	Write_rows	#	#	table_id: # flags: STMT_END_F
+master-bin.000001	#	Table_map	#	#	table_id: # (test.t2)
+master-bin.000001	#	Write_rows	#	#	table_id: # flags: STMT_END_F
+master-bin.000001	#	Xid	#	#	COMMIT /* XID */
+master-bin.000001	#	Query	#	#	use `test`; TRUNCATE TABLE t1
+DROP TABLE t1,t2;
 CREATE TABLE t1 (a INT) ENGINE=InnoDB;
 CREATE TABLE t2 (a INT) ENGINE=InnoDB;
 INSERT INTO t2 VALUES (1),(2),(3);
@@ -22,6 +59,42 @@ Log_name	Pos	Event_type	Server_id	End_lo
 master-bin.000001	#	Query	#	#	use `test`; TRUNCATE TABLE t1
 master-bin.000001	#	Query	#	#	use `test`; TRUNCATE TABLE t2
 DROP TABLE t1,t2;
+#
+# Bug#42643: InnoDB does not support replication of TRUNCATE TABLE
+#
+CREATE TABLE t1 (a INT) ENGINE=InnoDB;
+CREATE TABLE t2 (a INT) ENGINE=InnoDB;
+INSERT INTO t1 VALUES (1),(2);
+SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
+# Connection: default
+BEGIN;
+INSERT INTO t2 SELECT * FROM t1;
+# Connection: truncate
+TRUNCATE TABLE t1;
+# Connection: default
+INSERT INTO t2 SELECT * FROM t1;
+SELECT COUNT(*) FROM t2;
+COUNT(*)
+4
+COMMIT;
+# Connection: truncate
+SELECT COUNT(*) FROM t1;
+COUNT(*)
+0
+SELECT COUNT(*) FROM t2;
+COUNT(*)
+4
+# Connection: default
+show binlog events from <binlog_start>;
+Log_name	Pos	Event_type	Server_id	End_log_pos	Info
+master-bin.000001	#	Query	#	#	BEGIN
+master-bin.000001	#	Table_map	#	#	table_id: # (test.t2)
+master-bin.000001	#	Write_rows	#	#	table_id: # flags: STMT_END_F
+master-bin.000001	#	Table_map	#	#	table_id: # (test.t2)
+master-bin.000001	#	Write_rows	#	#	table_id: # flags: STMT_END_F
+master-bin.000001	#	Xid	#	#	COMMIT /* XID */
+master-bin.000001	#	Query	#	#	use `test`; TRUNCATE TABLE t1
+DROP TABLE t1,t2;
 CREATE TABLE t1 (a INT) ENGINE=InnoDB;
 CREATE TABLE t2 (a INT) ENGINE=InnoDB;
 INSERT INTO t2 VALUES (1),(2),(3);
@@ -35,6 +108,189 @@ Log_name	Pos	Event_type	Server_id	End_lo
 master-bin.000001	#	Query	#	#	use `test`; TRUNCATE TABLE t1
 master-bin.000001	#	Query	#	#	use `test`; TRUNCATE TABLE t2
 DROP TABLE t1,t2;
+#
+# Bug#42643: InnoDB does not support replication of TRUNCATE TABLE
+#
+CREATE TABLE t1 (a INT) ENGINE=InnoDB;
+CREATE TABLE t2 (a INT) ENGINE=InnoDB;
+INSERT INTO t1 VALUES (1),(2);
+SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
+# Connection: default
+BEGIN;
+INSERT INTO t2 SELECT * FROM t1;
+# Connection: truncate
+TRUNCATE TABLE t1;
+# Connection: default
+INSERT INTO t2 SELECT * FROM t1;
+SELECT COUNT(*) FROM t2;
+COUNT(*)
+4
+COMMIT;
+# Connection: truncate
+SELECT COUNT(*) FROM t1;
+COUNT(*)
+0
+SELECT COUNT(*) FROM t2;
+COUNT(*)
+4
+# Connection: default
+show binlog events from <binlog_start>;
+Log_name	Pos	Event_type	Server_id	End_log_pos	Info
+master-bin.000001	#	Query	#	#	BEGIN
+master-bin.000001	#	Table_map	#	#	table_id: # (test.t2)
+master-bin.000001	#	Write_rows	#	#	table_id: # flags: STMT_END_F
+master-bin.000001	#	Table_map	#	#	table_id: # (test.t2)
+master-bin.000001	#	Write_rows	#	#	table_id: # flags: STMT_END_F
+master-bin.000001	#	Xid	#	#	COMMIT /* XID */
+master-bin.000001	#	Query	#	#	use `test`; TRUNCATE TABLE t1
+DROP TABLE t1,t2;
+CREATE TABLE t1 (a INT) ENGINE=InnoDB;
+CREATE TABLE t2 (a INT) ENGINE=InnoDB;
+INSERT INTO t2 VALUES (1),(2),(3);
+SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
+**** Truncate of empty table shall be logged
+TRUNCATE TABLE t1;
+SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
+TRUNCATE TABLE t2;
+show binlog events from <binlog_start>;
+Log_name	Pos	Event_type	Server_id	End_log_pos	Info
+master-bin.000001	#	Query	#	#	use `test`; TRUNCATE TABLE t1
+master-bin.000001	#	Query	#	#	use `test`; TRUNCATE TABLE t2
+DROP TABLE t1,t2;
+#
+# Bug#42643: InnoDB does not support replication of TRUNCATE TABLE
+#
+CREATE TABLE t1 (a INT) ENGINE=InnoDB;
+CREATE TABLE t2 (a INT) ENGINE=InnoDB;
+INSERT INTO t1 VALUES (1),(2);
+SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
+# Connection: default
+BEGIN;
+INSERT INTO t2 SELECT * FROM t1;
+# Connection: truncate
+TRUNCATE TABLE t1;
+# Connection: default
+INSERT INTO t2 SELECT * FROM t1;
+SELECT COUNT(*) FROM t2;
+COUNT(*)
+4
+COMMIT;
+# Connection: truncate
+SELECT COUNT(*) FROM t1;
+COUNT(*)
+0
+SELECT COUNT(*) FROM t2;
+COUNT(*)
+4
+# Connection: default
+show binlog events from <binlog_start>;
+Log_name	Pos	Event_type	Server_id	End_log_pos	Info
+master-bin.000001	#	Query	#	#	BEGIN
+master-bin.000001	#	Table_map	#	#	table_id: # (test.t2)
+master-bin.000001	#	Write_rows	#	#	table_id: # flags: STMT_END_F
+master-bin.000001	#	Table_map	#	#	table_id: # (test.t2)
+master-bin.000001	#	Write_rows	#	#	table_id: # flags: STMT_END_F
+master-bin.000001	#	Xid	#	#	COMMIT /* XID */
+master-bin.000001	#	Query	#	#	use `test`; TRUNCATE TABLE t1
+DROP TABLE t1,t2;
+CREATE TABLE t1 (a INT) ENGINE=InnoDB;
+CREATE TABLE t2 (a INT) ENGINE=InnoDB;
+INSERT INTO t2 VALUES (1),(2),(3);
+SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
+**** Truncate of empty table shall be logged
+TRUNCATE TABLE t1;
+SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
+TRUNCATE TABLE t2;
+show binlog events from <binlog_start>;
+Log_name	Pos	Event_type	Server_id	End_log_pos	Info
+master-bin.000001	#	Query	#	#	use `test`; TRUNCATE TABLE t1
+master-bin.000001	#	Query	#	#	use `test`; TRUNCATE TABLE t2
+DROP TABLE t1,t2;
+#
+# Bug#42643: InnoDB does not support replication of TRUNCATE TABLE
+#
+CREATE TABLE t1 (a INT) ENGINE=InnoDB;
+CREATE TABLE t2 (a INT) ENGINE=InnoDB;
+INSERT INTO t1 VALUES (1),(2);
+SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
+# Connection: default
+BEGIN;
+INSERT INTO t2 SELECT * FROM t1;
+# Connection: truncate
+TRUNCATE TABLE t1;
+# Connection: default
+INSERT INTO t2 SELECT * FROM t1;
+SELECT COUNT(*) FROM t2;
+COUNT(*)
+4
+COMMIT;
+# Connection: truncate
+SELECT COUNT(*) FROM t1;
+COUNT(*)
+0
+SELECT COUNT(*) FROM t2;
+COUNT(*)
+4
+# Connection: default
+show binlog events from <binlog_start>;
+Log_name	Pos	Event_type	Server_id	End_log_pos	Info
+master-bin.000001	#	Query	#	#	BEGIN
+master-bin.000001	#	Table_map	#	#	table_id: # (test.t2)
+master-bin.000001	#	Write_rows	#	#	table_id: # flags: STMT_END_F
+master-bin.000001	#	Table_map	#	#	table_id: # (test.t2)
+master-bin.000001	#	Write_rows	#	#	table_id: # flags: STMT_END_F
+master-bin.000001	#	Xid	#	#	COMMIT /* XID */
+master-bin.000001	#	Query	#	#	use `test`; TRUNCATE TABLE t1
+DROP TABLE t1,t2;
+SET BINLOG_FORMAT=STATEMENT;
+RESET MASTER;
+CREATE TABLE t1 (a INT) ENGINE=InnoDB;
+CREATE TABLE t2 (a INT) ENGINE=InnoDB;
+INSERT INTO t2 VALUES (1),(2),(3);
+SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
+**** Truncate of empty table shall be logged
+TRUNCATE TABLE t1;
+SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
+TRUNCATE TABLE t2;
+show binlog events from <binlog_start>;
+Log_name	Pos	Event_type	Server_id	End_log_pos	Info
+master-bin.000001	#	Query	#	#	use `test`; TRUNCATE TABLE t1
+master-bin.000001	#	Query	#	#	use `test`; TRUNCATE TABLE t2
+DROP TABLE t1,t2;
+#
+# Bug#42643: InnoDB does not support replication of TRUNCATE TABLE
+#
+CREATE TABLE t1 (a INT) ENGINE=InnoDB;
+CREATE TABLE t2 (a INT) ENGINE=InnoDB;
+INSERT INTO t1 VALUES (1),(2);
+SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
+# Connection: default
+BEGIN;
+INSERT INTO t2 SELECT * FROM t1;
+# Connection: truncate
+TRUNCATE TABLE t1;
+# Connection: default
+INSERT INTO t2 SELECT * FROM t1;
+SELECT COUNT(*) FROM t2;
+COUNT(*)
+4
+COMMIT;
+# Connection: truncate
+SELECT COUNT(*) FROM t1;
+COUNT(*)
+0
+SELECT COUNT(*) FROM t2;
+COUNT(*)
+4
+# Connection: default
+show binlog events from <binlog_start>;
+Log_name	Pos	Event_type	Server_id	End_log_pos	Info
+master-bin.000001	#	Query	#	#	BEGIN
+master-bin.000001	#	Query	#	#	use `test`; INSERT INTO t2 SELECT * FROM t1
+master-bin.000001	#	Query	#	#	use `test`; INSERT INTO t2 SELECT * FROM t1
+master-bin.000001	#	Xid	#	#	COMMIT /* XID */
+master-bin.000001	#	Query	#	#	use `test`; TRUNCATE TABLE t1
+DROP TABLE t1,t2;
 CREATE TABLE t1 (a INT) ENGINE=InnoDB;
 CREATE TABLE t2 (a INT) ENGINE=InnoDB;
 INSERT INTO t2 VALUES (1),(2),(3);
@@ -48,6 +304,40 @@ Log_name	Pos	Event_type	Server_id	End_lo
 master-bin.000001	#	Query	#	#	use `test`; TRUNCATE TABLE t1
 master-bin.000001	#	Query	#	#	use `test`; TRUNCATE TABLE t2
 DROP TABLE t1,t2;
+#
+# Bug#42643: InnoDB does not support replication of TRUNCATE TABLE
+#
+CREATE TABLE t1 (a INT) ENGINE=InnoDB;
+CREATE TABLE t2 (a INT) ENGINE=InnoDB;
+INSERT INTO t1 VALUES (1),(2);
+SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
+# Connection: default
+BEGIN;
+INSERT INTO t2 SELECT * FROM t1;
+# Connection: truncate
+TRUNCATE TABLE t1;
+# Connection: default
+INSERT INTO t2 SELECT * FROM t1;
+SELECT COUNT(*) FROM t2;
+COUNT(*)
+4
+COMMIT;
+# Connection: truncate
+SELECT COUNT(*) FROM t1;
+COUNT(*)
+0
+SELECT COUNT(*) FROM t2;
+COUNT(*)
+4
+# Connection: default
+show binlog events from <binlog_start>;
+Log_name	Pos	Event_type	Server_id	End_log_pos	Info
+master-bin.000001	#	Query	#	#	BEGIN
+master-bin.000001	#	Query	#	#	use `test`; INSERT INTO t2 SELECT * FROM t1
+master-bin.000001	#	Query	#	#	use `test`; INSERT INTO t2 SELECT * FROM t1
+master-bin.000001	#	Xid	#	#	COMMIT /* XID */
+master-bin.000001	#	Query	#	#	use `test`; TRUNCATE TABLE t1
+DROP TABLE t1,t2;
 CREATE TABLE t1 (a INT) ENGINE=InnoDB;
 CREATE TABLE t2 (a INT) ENGINE=InnoDB;
 INSERT INTO t2 VALUES (1),(2),(3);
@@ -61,3 +351,38 @@ Log_name	Pos	Event_type	Server_id	End_lo
 master-bin.000001	#	Query	#	#	use `test`; TRUNCATE TABLE t1
 master-bin.000001	#	Query	#	#	use `test`; TRUNCATE TABLE t2
 DROP TABLE t1,t2;
+#
+# Bug#42643: InnoDB does not support replication of TRUNCATE TABLE
+#
+CREATE TABLE t1 (a INT) ENGINE=InnoDB;
+CREATE TABLE t2 (a INT) ENGINE=InnoDB;
+INSERT INTO t1 VALUES (1),(2);
+SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
+# Connection: default
+BEGIN;
+INSERT INTO t2 SELECT * FROM t1;
+# Connection: truncate
+TRUNCATE TABLE t1;
+# Connection: default
+INSERT INTO t2 SELECT * FROM t1;
+SELECT COUNT(*) FROM t2;
+COUNT(*)
+4
+COMMIT;
+# Connection: truncate
+SELECT COUNT(*) FROM t1;
+COUNT(*)
+0
+SELECT COUNT(*) FROM t2;
+COUNT(*)
+4
+# Connection: default
+show binlog events from <binlog_start>;
+Log_name	Pos	Event_type	Server_id	End_log_pos	Info
+master-bin.000001	#	Query	#	#	BEGIN
+master-bin.000001	#	Query	#	#	use `test`; INSERT INTO t2 SELECT * FROM t1
+master-bin.000001	#	Query	#	#	use `test`; INSERT INTO t2 SELECT * FROM t1
+master-bin.000001	#	Xid	#	#	COMMIT /* XID */
+master-bin.000001	#	Query	#	#	use `test`; TRUNCATE TABLE t1
+DROP TABLE t1,t2;
+SET BINLOG_FORMAT=DEFAULT;

=== modified file 'mysql-test/suite/binlog/r/binlog_truncate_myisam.result'
--- a/mysql-test/suite/binlog/r/binlog_truncate_myisam.result	2009-02-10 21:26:37 +0000
+++ b/mysql-test/suite/binlog/r/binlog_truncate_myisam.result	2010-02-11 14:54:53 +0000
@@ -1,3 +1,4 @@
+SET BINLOG_FORMAT=ROW;
 RESET MASTER;
 CREATE TABLE t1 (a INT) ENGINE=MyISAM;
 CREATE TABLE t2 (a INT) ENGINE=MyISAM;
@@ -10,3 +11,89 @@ Log_name	Pos	Event_type	Server_id	End_lo
 master-bin.000001	#	Query	#	#	use `test`; TRUNCATE TABLE t1
 master-bin.000001	#	Query	#	#	use `test`; TRUNCATE TABLE t2
 DROP TABLE t1,t2;
+#
+# Bug#42643: InnoDB does not support replication of TRUNCATE TABLE
+#
+CREATE TABLE t1 (a INT) ENGINE=MyISAM;
+CREATE TABLE t2 (a INT) ENGINE=MyISAM;
+INSERT INTO t1 VALUES (1),(2);
+# Connection: default
+BEGIN;
+INSERT INTO t2 SELECT * FROM t1;
+# Connection: truncate
+TRUNCATE TABLE t1;
+# Connection: default
+INSERT INTO t2 SELECT * FROM t1;
+SELECT COUNT(*) FROM t2;
+COUNT(*)
+4
+COMMIT;
+# Connection: truncate
+SELECT COUNT(*) FROM t1;
+COUNT(*)
+0
+SELECT COUNT(*) FROM t2;
+COUNT(*)
+4
+# Connection: default
+show binlog events from <binlog_start>;
+Log_name	Pos	Event_type	Server_id	End_log_pos	Info
+master-bin.000001	#	Query	#	#	BEGIN
+master-bin.000001	#	Table_map	#	#	table_id: # (test.t2)
+master-bin.000001	#	Write_rows	#	#	table_id: # flags: STMT_END_F
+master-bin.000001	#	Query	#	#	COMMIT
+master-bin.000001	#	Query	#	#	BEGIN
+master-bin.000001	#	Table_map	#	#	table_id: # (test.t2)
+master-bin.000001	#	Write_rows	#	#	table_id: # flags: STMT_END_F
+master-bin.000001	#	Query	#	#	COMMIT
+master-bin.000001	#	Query	#	#	use `test`; TRUNCATE TABLE t1
+DROP TABLE t1,t2;
+SET BINLOG_FORMAT=STATEMENT;
+RESET MASTER;
+CREATE TABLE t1 (a INT) ENGINE=MyISAM;
+CREATE TABLE t2 (a INT) ENGINE=MyISAM;
+INSERT INTO t2 VALUES (1),(2),(3);
+**** Truncate of empty table shall be logged
+TRUNCATE TABLE t1;
+TRUNCATE TABLE t2;
+show binlog events from <binlog_start>;
+Log_name	Pos	Event_type	Server_id	End_log_pos	Info
+master-bin.000001	#	Query	#	#	use `test`; TRUNCATE TABLE t1
+master-bin.000001	#	Query	#	#	use `test`; TRUNCATE TABLE t2
+DROP TABLE t1,t2;
+#
+# Bug#42643: InnoDB does not support replication of TRUNCATE TABLE
+#
+CREATE TABLE t1 (a INT) ENGINE=MyISAM;
+CREATE TABLE t2 (a INT) ENGINE=MyISAM;
+INSERT INTO t1 VALUES (1),(2);
+# Connection: default
+BEGIN;
+INSERT INTO t2 SELECT * FROM t1;
+# Connection: truncate
+TRUNCATE TABLE t1;
+# Connection: default
+INSERT INTO t2 SELECT * FROM t1;
+SELECT COUNT(*) FROM t2;
+COUNT(*)
+4
+COMMIT;
+# Connection: truncate
+SELECT COUNT(*) FROM t1;
+COUNT(*)
+0
+SELECT COUNT(*) FROM t2;
+COUNT(*)
+4
+# Connection: default
+show binlog events from <binlog_start>;
+Log_name	Pos	Event_type	Server_id	End_log_pos	Info
+master-bin.000001	#	Query	#	#	BEGIN
+master-bin.000001	#	Query	#	#	use `test`; INSERT INTO t2 SELECT * FROM t1
+master-bin.000001	#	Query	#	#	COMMIT
+master-bin.000001	#	Query	#	#	BEGIN
+master-bin.000001	#	Query	#	#	use `test`; INSERT INTO t2 SELECT * FROM t1
+master-bin.000001	#	Query	#	#	COMMIT
+master-bin.000001	#	Query	#	#	use `test`; TRUNCATE TABLE t1
+DROP TABLE t1,t2;
+SET BINLOG_FORMAT=DEFAULT;

=== modified file 'mysql-test/suite/binlog/t/binlog_truncate_innodb.test'
--- a/mysql-test/suite/binlog/t/binlog_truncate_innodb.test	2009-02-10 21:26:37 +0000
+++ b/mysql-test/suite/binlog/t/binlog_truncate_innodb.test	2010-02-11 14:54:53 +0000
@@ -1,21 +1,13 @@
 source include/have_log_bin.inc;
 source include/have_innodb.inc;
 
-# It is necessary to reset the master since otherwise the binlog test
-# might show the wrong binary log. The default for SHOW BINLOG EVENTS
-# is to show the first binary log, not the current one (which is
-# actually a better idea).
+let $engine = InnoDB;
 
+SET BINLOG_FORMAT=ROW;
 RESET MASTER;
 
-let $engine = InnoDB;
 source extra/binlog_tests/binlog_truncate.test;
 
-# Under transaction isolation level READ UNCOMMITTED and READ
-# COMMITTED, InnoDB does not permit statement-based replication of
-# row-deleting statement. In these cases, TRUNCATE TABLE should still
-# be replicated as a statement.
-
 let $before_truncate = SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
 source extra/binlog_tests/binlog_truncate.test;
 
@@ -27,3 +19,16 @@ source extra/binlog_tests/binlog_truncat
 
 let $before_truncate = SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
 source extra/binlog_tests/binlog_truncate.test;
+
+SET BINLOG_FORMAT=STATEMENT;
+RESET MASTER;
+
+source extra/binlog_tests/binlog_truncate.test;
+
+let $before_truncate = SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
+source extra/binlog_tests/binlog_truncate.test;
+
+let $before_truncate = SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
+source extra/binlog_tests/binlog_truncate.test;
+
+SET BINLOG_FORMAT=DEFAULT;

=== modified file 'mysql-test/suite/binlog/t/binlog_truncate_myisam.test'
--- a/mysql-test/suite/binlog/t/binlog_truncate_myisam.test	2009-02-10 21:26:37 +0000
+++ b/mysql-test/suite/binlog/t/binlog_truncate_myisam.test	2010-02-11 14:54:53 +0000
@@ -1,11 +1,15 @@
 source include/have_log_bin.inc;
 
-# It is necessary to reset the master since otherwise the binlog test
-# might show the wrong binary log. The default for SHOW BINLOG EVENTS
-# is to show the first binary log, not the current one (which is
-# actually a better idea).
+let $engine = MyISAM;
 
+SET BINLOG_FORMAT=ROW;
 RESET MASTER;
 
-let $engine = MyISAM;
 source extra/binlog_tests/binlog_truncate.test;
+
+SET BINLOG_FORMAT=STATEMENT;
+RESET MASTER;
+
+source extra/binlog_tests/binlog_truncate.test;
+
+SET BINLOG_FORMAT=DEFAULT;

=== modified file 'mysql-test/suite/binlog/t/disabled.def'
--- a/mysql-test/suite/binlog/t/disabled.def	2010-01-13 23:27:22 +0000
+++ b/mysql-test/suite/binlog/t/disabled.def	2010-02-11 14:54:53 +0000
@@ -9,6 +9,5 @@
 #  Do not use any TAB characters for whitespace.
 #
 ##############################################################################
-binlog_truncate_innodb	: BUG#42643 2009-02-06 mats Changes to InnoDB requires to complete fix for BUG#36763
 binlog_unsafe           : BUG#50312 2010-01-13 lsoares Warnings for unsafe sub-statement not returned to client
 

=== modified file 'mysql-test/t/partition_innodb_semi_consistent.test'
--- a/mysql-test/t/partition_innodb_semi_consistent.test	2009-12-04 23:02:48 +0000
+++ b/mysql-test/t/partition_innodb_semi_consistent.test	2010-02-11 14:54:53 +0000
@@ -101,6 +101,7 @@ connection con2;
 --error ER_LOCK_WAIT_TIMEOUT
 UPDATE t1 SET b = 21 WHERE a = 1;
 --disable_info
+ROLLBACK;
 
 --echo # Switch to connection con1
 connection con1;
@@ -150,6 +151,7 @@ SELECT * FROM t1;
 connection con2;
 --reap
 SELECT * FROM t1;
+COMMIT;
 
 --echo # Switch to connection con1
 connection con1;

=== modified file 'mysql-test/t/truncate.test'
--- a/mysql-test/t/truncate.test	2009-12-11 12:24:23 +0000
+++ b/mysql-test/t/truncate.test	2010-02-11 14:54:53 +0000
@@ -102,7 +102,7 @@ SELECT * FROM v1;
 LOCK TABLE t1 WRITE;
 --error ER_TABLE_NOT_LOCKED
 SELECT * FROM v1;
---error ER_NO_SUCH_TABLE
+--error ER_TABLE_NOT_LOCKED
 TRUNCATE v1;
 --error ER_TABLE_NOT_LOCKED
 SELECT * FROM v1;
@@ -111,7 +111,7 @@ UNLOCK TABLES;
 LOCK TABLE t1 WRITE, t2 WRITE;
 --error ER_TABLE_NOT_LOCKED
 SELECT * FROM v1;
---error ER_NO_SUCH_TABLE
+--error ER_TABLE_NOT_LOCKED
 TRUNCATE v1;
 --error ER_TABLE_NOT_LOCKED
 SELECT * FROM v1;
@@ -119,14 +119,14 @@ UNLOCK TABLES;
 #
 LOCK TABLE v1 WRITE;
 SELECT * FROM v1;
---error ER_NO_SUCH_TABLE
+--error ER_TABLE_NOT_LOCKED
 TRUNCATE v1;
 SELECT * FROM v1;
 UNLOCK TABLES;
 #
 LOCK TABLE t1 WRITE, t2 WRITE, v1 WRITE;
 SELECT * FROM v1;
---error ER_NO_SUCH_TABLE
+--error ER_TABLE_NOT_LOCKED
 TRUNCATE v1;
 SELECT * FROM v1;
 UNLOCK TABLES;

=== modified file 'sql/mysql_priv.h'
--- a/sql/mysql_priv.h	2010-02-08 20:19:55 +0000
+++ b/sql/mysql_priv.h	2010-02-11 14:54:53 +0000
@@ -1278,7 +1278,8 @@ int mysql_prepare_delete(THD *thd, TABLE
 bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
                   SQL_LIST *order, ha_rows rows, ulonglong options,
                   bool reset_auto_increment);
-bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok);
+bool mysql_truncate_table(THD *thd, TABLE_LIST *table_ref);
+bool mysql_truncate_locked_table(THD *thd, const char *db, const char *table);
 bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create);
 uint create_table_def_key(THD *thd, char *key, TABLE_LIST *table_list,
                           bool tmp_table);

=== modified file 'sql/sql_delete.cc'
--- a/sql/sql_delete.cc	2010-02-08 20:19:55 +0000
+++ b/sql/sql_delete.cc	2010-02-11 14:54:53 +0000
@@ -1074,19 +1074,69 @@ static bool mysql_truncate_by_delete(THD
 
 
 /*
-  Optimize delete of all rows by doing a full generate of the table
-  This will work even if the .ISM and .ISD tables are destroyed
+  Close and regenerate a temporary table.
 
-  dont_send_ok should be set if:
-  - We should always wants to generate the table (even if the table type
-    normally can't safely do this.
-  - We don't want an ok to be sent to the end user.
-  - We don't want to log the truncate command
-  - If we want to keep exclusive metadata lock on the table (obtained by
-    caller) on exit without errors.
+  @param  thd     Thread context.
+  @param  table   The temporary table.
+
+  @retval  FALSE  Success.
+  @retval  TRUE   Error.
 */
 
-bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok)
+static bool truncate_temporary_table(THD *thd, TABLE *table)
+{
+  bool error= TRUE;
+  TABLE_SHARE *share= table->s;
+  HA_CREATE_INFO create_info;
+  handlerton *table_type= table->s->db_type();
+  DBUG_ENTER("truncate_temporary_table");
+
+  memset(&create_info, 0, sizeof(create_info));
+
+  table->file->info(HA_STATUS_AUTO | HA_STATUS_NO_LOCK);
+
+  /* Don't free share. */
+  close_temporary_table(thd, table, FALSE, FALSE);
+
+  ha_create_table(thd, share->normalized_path.str, share->db.str,
+                  share->table_name.str, &create_info, 1);
+
+  if (open_temporary_table(thd, share->path.str, share->db.str,
+                           share->table_name.str, 1))
+  {
+    error= FALSE;
+    thd->thread_specific_used= TRUE;
+  }
+  else
+    rm_temporary_table(table_type, share->path.str);
+
+  free_table_share(share);
+  my_free(table, MYF(0));
+
+  /* In RBR, the statement is not binlogged if the table is temporary. */
+  if (!error && !thd->is_current_stmt_binlog_format_row())
+    error= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
+
+  if (!error)
+    my_ok(thd);
+
+  DBUG_RETURN(error);
+}
+
+
+/*
+  Optimized delete of all rows by doing a full generate of the table.
+
+  @remark Will work even if the .ISM and .ISD tables are destroyed.
+
+  @param  thd         Thread context.
+  @param  table_ref   Reference to the table to be truncated.
+
+  @retval  FALSE  Success.
+  @retval  TRUE   Error.
+*/
+
+bool mysql_truncate_table(THD *thd, TABLE_LIST *table_ref)
 {
   HA_CREATE_INFO create_info;
   char path[FN_REFLEN + 1];
@@ -1094,133 +1144,121 @@ bool mysql_truncate(THD *thd, TABLE_LIST
   bool error= TRUE;
   uint path_length;
   /*
-    Is set if we're under LOCK TABLES, and used
-    to downgrade the exclusive lock after the
-    table was truncated.
+    Is set if we're under LOCK TABLES: used to downgrade the
+    exclusive lock after the table was truncated.
   */
   MDL_ticket *mdl_ticket= NULL;
   bool has_mdl_lock= FALSE;
-  bool is_temporary_table= false;
-  DBUG_ENTER("mysql_truncate");
-
-  bzero((char*) &create_info,sizeof(create_info));
+  enum legacy_db_type db_type;
+  handlerton *table_type;
+  DBUG_ENTER("mysql_truncate_table");
 
   /* Remove tables from the HANDLER's hash. */
-  mysql_ha_rm_tables(thd, table_list);
+  mysql_ha_rm_tables(thd, table_ref);
 
   /* If it is a temporary table, close and regenerate it */
-  if (!dont_send_ok && (table= find_temporary_table(thd, table_list)))
+  if ((table= find_temporary_table(thd, table_ref)))
   {
-    is_temporary_table= true;
-    handlerton *table_type= table->s->db_type();
-    TABLE_SHARE *share= table->s;
     /* Note that a temporary table cannot be partitioned */
-    if (!ha_check_storage_engine_flag(table_type, HTON_CAN_RECREATE))
-      goto trunc_by_del;
+    if (ha_check_storage_engine_flag(table->s->db_type(), HTON_CAN_RECREATE))
+      error= truncate_temporary_table(thd, table);
+    else
+      error= mysql_truncate_by_delete(thd, table_ref);
 
-    table->file->info(HA_STATUS_AUTO | HA_STATUS_NO_LOCK);
+    DBUG_RETURN(error);
+  }
 
-    close_temporary_table(thd, table, 0, 0);    // Don't free share
-    ha_create_table(thd, share->normalized_path.str,
-                    share->db.str, share->table_name.str, &create_info, 1);
-    // We don't need to call invalidate() because this table is not in cache
-    if ((error= (int) !(open_temporary_table(thd, share->path.str,
-                                             share->db.str,
-					     share->table_name.str, 1))))
-      (void) rm_temporary_table(table_type, path);
-    else
-      thd->thread_specific_used= TRUE;
-    
-    free_table_share(share);
-    my_free((char*) table,MYF(0));
+  if (thd->locked_tables_mode)
+  {
+    if (!(table= find_table_for_mdl_upgrade(thd->open_tables, table_ref->db,
+                                            table_ref->table_name, FALSE)))
+      DBUG_RETURN(TRUE);
+  }
+  else
+  {
     /*
-      If we return here we will not have logged the truncation to the bin log
-      and we will not my_ok() to the client.
+      Even though we could use the previous execution branch here just as
+      well, we must not try to open the table: MySQL manual documents that
+      TRUNCATE can be used to repair a damaged table, i.e. a table that can
+      not be fully "opened". In particular MySQL manual says:
+
+      As long as the table format file tbl_name.frm is valid, the table can be
+      re-created as an empty table with TRUNCATE TABLE, even if the data or
+      index files have become corrupted.
     */
-    goto end;
+    MDL_request mdl_global_request, mdl_request;
+    MDL_request_list mdl_requests;
+
+    mdl_global_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE);
+    mdl_request.init(MDL_key::TABLE, table_ref->db, table_ref->table_name,
+                     MDL_EXCLUSIVE);
+    mdl_requests.push_front(&mdl_request);
+    mdl_requests.push_front(&mdl_global_request);
+
+    if (thd->mdl_context.acquire_locks(&mdl_requests))
+      DBUG_RETURN(TRUE);
   }
 
-  path_length= build_table_filename(path, sizeof(path) - 1, table_list->db,
-                                    table_list->table_name, reg_ext, 0);
+  path_length= build_table_filename(path, sizeof(path) - 1, table_ref->db,
+                                    table_ref->table_name, reg_ext, 0);
+
+  mysql_frm_type(thd, path, &db_type);
 
-  if (!dont_send_ok)
+  /* Type is unknown if the object is not found or is not a table. */
+  if (db_type == DB_TYPE_UNKNOWN)
   {
-    enum legacy_db_type table_type;
-    /*
-      FIXME: Code of TRUNCATE breaks the meta-data
-      locking protocol since it tries to find out the table storage
-      engine and therefore accesses table in some way without holding
-      any kind of meta-data lock.
-    */
-    mysql_frm_type(thd, path, &table_type);
-    if (table_type == DB_TYPE_UNKNOWN)
-    {
-      my_error(ER_NO_SUCH_TABLE, MYF(0),
-               table_list->db, table_list->table_name);
-      DBUG_RETURN(TRUE);
-    }
+    my_error(ER_NO_SUCH_TABLE, MYF(0), table_ref->db, table_ref->table_name);
+    DBUG_RETURN(TRUE);
+  }
+
 #ifdef WITH_PARTITION_STORAGE_ENGINE
+  /*
+    TODO: Add support for TRUNCATE PARTITION for NDB and other engines
+    supporting native partitioning
+  */
+  if (db_type != DB_TYPE_PARTITION_DB &&
+      thd->lex->alter_info.flags & ALTER_ADMIN_PARTITION)
+  {
+    my_error(ER_PARTITION_MGMT_ON_NONPARTITIONED, MYF(0));
+    DBUG_RETURN(TRUE);
+  }
+#endif
+
+  table_type= ha_resolve_by_legacy_type(thd, db_type);
+
+  if (!ha_check_storage_engine_flag(table_type, HTON_CAN_RECREATE) ||
+      thd->lex->alter_info.flags & ALTER_ADMIN_PARTITION)
+  {
     /*
-      TODO: Add support for TRUNCATE PARTITION for NDB and other engines
-      supporting native partitioning
+      If under LOCK TABLES mode, there is no reason to upgrade the
+      shared metadata lock as this session holds a write lock on the
+      data. This guarantees that no other transaction has access to
+      the table data. If not under LOCK TABLES mode, the exclusive
+      metadata lock is preserved. This behavior is important to
+      ensure serializability as InnoDB delete_all_rows implementation
+      aborts some granted locks.
     */
-    if (table_type != DB_TYPE_PARTITION_DB &&
-        thd->lex->alter_info.flags & ALTER_ADMIN_PARTITION)
-    {
-      my_error(ER_PARTITION_MGMT_ON_NONPARTITIONED, MYF(0));
-      DBUG_RETURN(TRUE);
-    }
-#endif
-    if (!ha_check_storage_engine_flag(ha_resolve_by_legacy_type(thd,
-                                                                table_type),
-                                      HTON_CAN_RECREATE) ||
-        thd->lex->alter_info.flags & ALTER_ADMIN_PARTITION)
-      goto trunc_by_del;
-
-
-    if (thd->locked_tables_mode)
-    {
-      if (!(table= find_table_for_mdl_upgrade(thd->open_tables, table_list->db,
-                                              table_list->table_name, FALSE)))
-        DBUG_RETURN(TRUE);
-      mdl_ticket= table->mdl_ticket;
-      if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN))
-        goto end;
-      close_all_tables_for_name(thd, table->s, FALSE);
-    }
-    else
-    {
-      MDL_request mdl_global_request, mdl_request;
-      MDL_request_list mdl_requests;
-      /*
-        Even though we could use the previous execution branch
-        here just as well, we must not try to open the table: 
-        MySQL manual documents that TRUNCATE can be used to 
-        repair a damaged table, i.e. a table that can not be
-        fully "opened". In particular MySQL manual says:
-
-        As long as the table format file tbl_name.frm  is valid,
-        the table can be re-created as an empty table with TRUNCATE
-        TABLE, even if the data or index files have become corrupted.
-      */
-
-      mdl_global_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE);
-      mdl_request.init(MDL_key::TABLE, table_list->db, table_list->table_name,
-                       MDL_EXCLUSIVE);
-      mdl_requests.push_front(&mdl_request);
-      mdl_requests.push_front(&mdl_global_request);
-
-      if (thd->mdl_context.acquire_locks(&mdl_requests))
-        DBUG_RETURN(TRUE);
-
-      has_mdl_lock= TRUE;
-      mysql_mutex_lock(&LOCK_open);
-      tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table_list->db,
-                       table_list->table_name);
-      mysql_mutex_unlock(&LOCK_open);
-    }
+    error= mysql_truncate_by_delete(thd, table_ref);
+    DBUG_RETURN(error);
   }
 
+  if (thd->locked_tables_mode)
+  {
+    mdl_ticket= table->mdl_ticket;
+    if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN))
+      goto end;
+    close_all_tables_for_name(thd, table->s, FALSE);
+  }
+  else
+  {
+    has_mdl_lock= TRUE;
+    mysql_mutex_lock(&LOCK_open);
+    tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table_ref->db, table_ref->table_name);
+    mysql_mutex_unlock(&LOCK_open);
+  }
+
+  memset(&create_info, 0, sizeof(create_info));
+
   /*
     Remove the .frm extension AIX 5.2 64-bit compiler bug (BUG#16155): this
     crashes, replacement works.  *(path + path_length - reg_ext_length)=
@@ -1228,38 +1266,78 @@ bool mysql_truncate(THD *thd, TABLE_LIST
   */
   path[path_length - reg_ext_length] = 0;
   mysql_mutex_lock(&LOCK_open);
-  error= ha_create_table(thd, path, table_list->db, table_list->table_name,
+  error= ha_create_table(thd, path, table_ref->db, table_ref->table_name,
                          &create_info, 1);
   mysql_mutex_unlock(&LOCK_open);
-  query_cache_invalidate3(thd, table_list, 0);
+  query_cache_invalidate3(thd, table_ref, 0);
 
 end:
-  if (!dont_send_ok)
-  {
-    if (thd->locked_tables_mode && thd->locked_tables_list.reopen_tables(thd))
+  if (thd->locked_tables_mode && thd->locked_tables_list.reopen_tables(thd))
       thd->locked_tables_list.unlink_all_closed_tables(thd, NULL, 0);
-    /*
-      Even if we failed to reopen some tables,
-      the operation itself succeeded, write the binlog.
-    */
+
+  /*
+    Even if we failed to reopen some tables, the operation itself
+    succeeded, write the binlog.
+  */
+  if (!error)
+  {
+    /* In RBR, the statement is not binlogged if the table is temporary. */
+    error= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
     if (!error)
-    {
-      /* In RBR, the statement is not binlogged if the table is temporary. */
-      if (!is_temporary_table || !thd->is_current_stmt_binlog_format_row())
-        error= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
-      if (!error)
-        my_ok(thd);             // This should return record count
-    }
-    if (has_mdl_lock)
-      thd->mdl_context.release_transactional_locks();
-    if (mdl_ticket)
-      mdl_ticket->downgrade_exclusive_lock(MDL_SHARED_NO_READ_WRITE);
+      my_ok(thd);
   }
 
+  if (has_mdl_lock)
+    thd->mdl_context.release_transactional_locks();
+
+  if (mdl_ticket)
+    mdl_ticket->downgrade_exclusive_lock(MDL_SHARED_NO_READ_WRITE);
+
   DBUG_PRINT("exit", ("error: %d", error));
   DBUG_RETURN(error);
+}
+
+
+/*
+  Regenerate a metadata locked table.
+
+  @param  thd   Thread context.
+  @param  db    Name of the database to which the table belongs to.
+  @param  name  Table name.
+
+  @retval  FALSE  Success.
+  @retval  TRUE   Error.
+*/
+
+bool mysql_truncate_locked_table(THD *thd, const char *db, const char *table_name)
+{
+  bool error= TRUE;
+  HA_CREATE_INFO create_info;
+  char path[FN_REFLEN + 1];
+  DBUG_ENTER("mysql_truncate_locked_table");
+
+  /* There should be a exclusive metadata lock on the table. */
+  DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, db, table_name,
+                                             MDL_EXCLUSIVE));
+
+  memset(&create_info, 0, sizeof(create_info));
+
+  /* Create a path to the table, but without a extension. */
+  build_table_filename(path, sizeof(path) - 1, db, table_name, "", 0);
+
+  /* Attempt to reconstruct the table. */
+  mysql_mutex_lock(&LOCK_open);
+  error= ha_create_table(thd, path, db, table_name, &create_info, TRUE);
+  mysql_mutex_unlock(&LOCK_open);
+
+#ifdef HAVE_QUERY_CACHE
+  uint key_length;
+  char key[MAX_DBKEY_LENGTH];
+
+  key_length= (uint) (strmov(strmov(key, db) + 1, table_name) - key) + 1;
+  query_cache.invalidate(thd, key, key_length, FALSE);
+#endif
 
-trunc_by_del:
-  error= mysql_truncate_by_delete(thd, table_list);
   DBUG_RETURN(error);
 }
+

=== modified file 'sql/sql_parse.cc'
--- a/sql/sql_parse.cc	2010-02-08 20:19:55 +0000
+++ b/sql/sql_parse.cc	2010-02-11 14:54:53 +0000
@@ -3074,7 +3074,7 @@ end_with_restore_list:
     }
     if (thd->global_read_lock.wait_if_global_read_lock(thd, FALSE, TRUE))
       goto error;
-    res= mysql_truncate(thd, first_table, 0);
+    res= mysql_truncate_table(thd, first_table);
     break;
   case SQLCOM_DELETE:
   {

=== modified file 'sql/sql_table.cc'
--- a/sql/sql_table.cc	2010-02-06 10:28:06 +0000
+++ b/sql/sql_table.cc	2010-02-11 14:54:53 +0000
@@ -4497,7 +4497,7 @@ static int prepare_for_repair(THD *thd, 
 			     "Failed renaming data file");
     goto end;
   }
-  if (mysql_truncate(thd, table_list, 1))
+  if (mysql_truncate_locked_table(thd, table_list->db, table_list->table_name))
   {
     error= send_check_errmsg(thd, table_list, "repair",
 			     "Failed generating table from .frm file");


Attachment: [text/bzr-bundle] bzr/davi.arnaut@sun.com-20100211145453-p4yjkxr4j99vjasx.bundle
Thread
bzr commit into mysql-5.5-next-mr branch (davi:3090) Bug#42643Davi Arnaut11 Feb
  • Re: bzr commit into mysql-5.5-next-mr branch (davi:3090) Bug#42643Konstantin Osipov9 Mar
    • Re: bzr commit into mysql-5.5-next-mr branch (davi:3090) Bug#42643Davi Arnaut11 Mar