List:Commits« Previous MessageNext Message »
From:Davi Arnaut Date:May 24 2010 7:25pm
Subject:bzr commit into mysql-trunk-runtime branch (davi:3022) Bug#42643
View as plain text  
# At a local mysql-trunk-runtime repository of davi

 3022 Davi Arnaut	2010-05-24
      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.
      
      Incompatible change: Truncate via delete no longer fails
      if sql_safe_updates is activated (this was a undocumented
      side effect).
     @ libmysqld/CMakeLists.txt
        Add new files to the build list.
     @ libmysqld/Makefile.am
        Add new files to the build list.
     @ mysql-test/extra/binlog_tests/binlog_truncate.test
        Add test case for Bug#42643
     @ mysql-test/include/mix1.inc
        Update test case as TRUNCATE TABLE now grabs a exclusive lock.
        Ensure that TRUNCATE waits for granted locks on the table.
     @ 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/innodb_bug38231.test
        Truncate no longer works with row-level locks.
     @ mysql-test/t/mdl_sync.test
        Ensure that a acquired lock is not given up due to a conflict.
     @ mysql-test/t/partition_innodb_semi_consistent.test
        End transaction as to release metadata locks.
     @ mysql-test/t/truncate.test
        A metadata lock is now taken before the object is verified.
     @ sql/CMakeLists.txt
        Add new files to the build list.
     @ sql/Makefile.am
        Add new files to the build list.
     @ sql/datadict.cc
        Introduce a new file specific for data dictionary operations.
     @ sql/datadict.h
        Add header file.
     @ sql/sql_base.cc
        Rename data dictionary function.
     @ sql/sql_bitmap.h
        Include dependency.
     @ sql/sql_delete.cc
        Move away from relying on mysql_delete() to delete all rows of
        a table. Thus, move any bits related to truncate to sql_truncate.cc
     @ sql/sql_delete.h
        Remove parameter.
     @ sql/sql_parse.cc
        Add protection against the global read lock -- a intention
        exclusive lock can be acquired in the truncate path.
     @ sql/sql_show.cc
        Add sync point for testing scenarios where a pending flush
        is ignored.
     @ sql/sql_truncate.cc
        Acquire a shared metadata lock before accessing table metadata.
        Upgrade the lock to a exclusive one if the table can be re-created.
        Rework binlog rules to better reflect the requirements.
     @ sql/table.h
        Move to data dictionary header.

    added:
      sql/datadict.cc
      sql/datadict.h
      sql/sql_truncate.cc
      sql/sql_truncate.h
    modified:
      .bzrignore
      libmysqld/CMakeLists.txt
      libmysqld/Makefile.am
      mysql-test/extra/binlog_tests/binlog_truncate.test
      mysql-test/include/mix1.inc
      mysql-test/r/innodb_bug38231.result
      mysql-test/r/innodb_mysql.result
      mysql-test/r/mdl_sync.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/innodb_bug38231.test
      mysql-test/t/mdl_sync.test
      mysql-test/t/partition_innodb_semi_consistent.test
      mysql-test/t/truncate.test
      sql/CMakeLists.txt
      sql/Makefile.am
      sql/sql_base.cc
      sql/sql_bitmap.h
      sql/sql_delete.cc
      sql/sql_delete.h
      sql/sql_parse.cc
      sql/sql_parse.h
      sql/sql_rename.cc
      sql/sql_show.cc
      sql/sql_table.cc
      sql/sql_view.cc
      sql/sql_view.h
      sql/table.h
=== modified file '.bzrignore'
--- a/.bzrignore	2010-04-21 19:59:19 +0000
+++ b/.bzrignore	2010-05-24 19:25:36 +0000
@@ -996,6 +996,8 @@ libmysqld/.deps/sql_crypt.Po
 libmysqld/.deps/sql_cursor.Po
 libmysqld/.deps/sql_db.Po
 libmysqld/.deps/sql_delete.Po
+libmysqld/.deps/sql_truncate.Po
+libmysqld/.deps/datadict.Po
 libmysqld/.deps/sql_derived.Po
 libmysqld/.deps/sql_do.Po
 libmysqld/.deps/sql_error.Po
@@ -1172,6 +1174,8 @@ libmysqld/sql_cursor.cc
 libmysqld/sql_cursor.h
 libmysqld/sql_db.cc
 libmysqld/sql_delete.cc
+libmysqld/sql_truncate.cc
+libmysqld/datadict.cc
 libmysqld/sql_derived.cc
 libmysqld/sql_do.cc
 libmysqld/sql_error.cc
@@ -2062,6 +2066,8 @@ sql/.deps/sql_crypt.Po
 sql/.deps/sql_cursor.Po
 sql/.deps/sql_db.Po
 sql/.deps/sql_delete.Po
+sql/.deps/sql_truncate.Po
+sql/.deps/datadict.Po
 sql/.deps/sql_derived.Po
 sql/.deps/sql_do.Po
 sql/.deps/sql_error.Po

=== modified file 'libmysqld/CMakeLists.txt'
--- a/libmysqld/CMakeLists.txt	2010-05-12 11:51:23 +0000
+++ b/libmysqld/CMakeLists.txt	2010-05-24 19:25:36 +0000
@@ -63,7 +63,8 @@ SET(SQL_EMBEDDED_SOURCES emb_qcache.cc l
            ../sql/sql_class.cc ../sql/sql_crypt.cc ../sql/sql_cursor.cc 
            ../sql/sql_db.cc ../sql/sql_delete.cc ../sql/sql_derived.cc 
            ../sql/sql_do.cc ../sql/sql_error.cc ../sql/sql_handler.cc 
-           ../sql/sql_help.cc ../sql/sql_insert.cc
+           ../sql/sql_help.cc ../sql/sql_insert.cc ../sql/datadict.cc
+           ../sql/sql_truncate.cc
            ../sql/sql_lex.cc ../sql/keycaches.cc
            ../sql/sql_list.cc ../sql/sql_load.cc ../sql/sql_locale.cc 
            ../sql/sql_binlog.cc ../sql/sql_manager.cc ../sql/sql_map.cc 

=== modified file 'libmysqld/Makefile.am'
--- a/libmysqld/Makefile.am	2010-04-13 15:04:45 +0000
+++ b/libmysqld/Makefile.am	2010-05-24 19:25:36 +0000
@@ -63,7 +63,7 @@ sqlsources = derror.cc field.cc field_co
 	protocol.cc net_serv.cc opt_range.cc \
 	opt_sum.cc procedure.cc records.cc sql_acl.cc \
 	sql_load.cc discover.cc sql_locale.cc \
-	sql_profile.cc \
+	sql_profile.cc sql_truncate.cc datadict.cc \
 	sql_analyse.cc sql_base.cc sql_cache.cc sql_class.cc \
 	sql_crypt.cc sql_db.cc sql_delete.cc sql_error.cc sql_insert.cc \
 	sql_lex.cc sql_list.cc sql_manager.cc sql_map.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-05-24 19:25:36 +0000
@@ -25,3 +25,44 @@ 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
+--echo # Reaping TRUNCATE TABLE
+--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-05-24 19:25:36 +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);
@@ -1435,6 +1442,7 @@ connection con2;
 --reap
 SELECT * FROM t1;
 
+--enable_abort_on_error
 connection default;
 disconnect con1;
 disconnect con2;
@@ -1556,3 +1564,36 @@ SELECT 1 FROM (SELECT COUNT(DISTINCT c1)
 DROP TABLE t1;
 
 --echo End of 5.1 tests
+
+--echo #
+--echo # Bug#42643: InnoDB does not support replication of TRUNCATE TABLE
+--echo #
+--echo # Check that a TRUNCATE TABLE statement, needing an exclusive meta
+--echo # data lock, waits for a shared metadata lock owned by a concurrent
+--echo # transaction.
+--echo #
+
+eval CREATE TABLE t1 (a INT) ENGINE=$engine_type;
+INSERT INTO t1 VALUES (1),(2),(3);
+BEGIN;
+SELECT * FROM t1 ORDER BY a;
+--echo # Connection con1
+connect (con1, localhost, root,,);
+--send TRUNCATE TABLE t1;
+--echo # Connection default
+connection default;
+let $wait_condition= SELECT COUNT(*)=1 FROM information_schema.processlist
+  WHERE state='Waiting for table' AND info='TRUNCATE TABLE t1';
+--source include/wait_condition.inc
+SELECT * FROM t1 ORDER BY a;
+ROLLBACK;
+--echo # Connection con1
+connection con1;
+--echo # Reaping TRUNCATE TABLE
+--reap
+SELECT * FROM t1;
+--echo # Disconnect con1
+disconnect con1;
+--echo # Connection default
+connection default;
+DROP TABLE t1;

=== modified file 'mysql-test/r/innodb_bug38231.result'
--- a/mysql-test/r/innodb_bug38231.result	2008-12-14 21:26:31 +0000
+++ b/mysql-test/r/innodb_bug38231.result	2010-05-24 19:25:36 +0000
@@ -1,11 +1,2 @@
 SET storage_engine=InnoDB;
-INSERT INTO bug38231 VALUES (1), (10), (300);
-SET autocommit=0;
-SELECT * FROM bug38231 FOR UPDATE;
-a
-1
-10
-300
-TRUNCATE TABLE bug38231;
-COMMIT;
 DROP TABLE bug38231;

=== modified file 'mysql-test/r/innodb_mysql.result'
--- a/mysql-test/r/innodb_mysql.result	2010-03-24 15:03:44 +0000
+++ b/mysql-test/r/innodb_mysql.result	2010-05-24 19:25:36 +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;
@@ -1764,6 +1767,37 @@ id	select_type	table	type	possible_keys	
 2	DERIVED	t1	index	c3,c2	c2	14	NULL	5	
 DROP TABLE t1;
 End of 5.1 tests
+#
+# Bug#42643: InnoDB does not support replication of TRUNCATE TABLE
+#
+# Check that a TRUNCATE TABLE statement, needing an exclusive meta
+# data lock, waits for a shared metadata lock owned by a concurrent
+# transaction.
+#
+CREATE TABLE t1 (a INT) ENGINE=InnoDB;
+INSERT INTO t1 VALUES (1),(2),(3);
+BEGIN;
+SELECT * FROM t1 ORDER BY a;
+a
+1
+2
+3
+# Connection con1
+TRUNCATE TABLE t1;;
+# Connection default
+SELECT * FROM t1 ORDER BY a;
+a
+1
+2
+3
+ROLLBACK;
+# Connection con1
+# Reaping TRUNCATE TABLE
+SELECT * FROM t1;
+a
+# Disconnect con1
+# Connection default
+DROP TABLE t1;
 drop table if exists t1, t2, t3;
 create table t1(a int);
 insert into t1 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);

=== modified file 'mysql-test/r/mdl_sync.result'
--- a/mysql-test/r/mdl_sync.result	2010-03-13 10:58:27 +0000
+++ b/mysql-test/r/mdl_sync.result	2010-05-24 19:25:36 +0000
@@ -2381,3 +2381,42 @@ commit;
 # Reap ALTER TABLE.
 set debug_sync= 'RESET';
 drop table t1;
+#
+# Bug#42643: InnoDB does not support replication of TRUNCATE TABLE
+#
+# Ensure that a acquired lock is not given up due to a conflict.
+#
+DROP TABLE IF EXISTS t1;
+CREATE TABLE t1 (a INT) ENGINE=InnoDB;
+INSERT INTO t1 VALUES (1),(2),(3);
+# Connection: con1
+SET debug_sync='lock_table_for_truncate SIGNAL parked_truncate WAIT_FOR go_truncate';
+TRUNCATE TABLE t1;
+# Connection: default
+SET debug_sync='now WAIT_FOR parked_truncate';
+# Connection: con2
+SET debug_sync='after_open_table_ignore_flush SIGNAL parked_show WAIT_FOR go_show';
+SHOW FIELDS FROM t1;
+# Connection: default
+SET debug_sync='now WAIT_FOR parked_show';
+# Connection: con3
+SET debug_sync='after_flush_unlock SIGNAL parked_flush WAIT_FOR go_flush';
+FLUSH TABLES t1;
+# Connection: default
+SET debug_sync='now WAIT_FOR parked_flush';
+SET debug_sync='now SIGNAL go_truncate';
+# Connection: con1
+# Reaping...
+# Connection: default
+SET debug_sync= 'now SIGNAL go_show';
+# Connection: con2 (SHOW FIELDS FROM t1)
+# Reaping...
+Field	Type	Null	Key	Default	Extra
+a	int(11)	YES		NULL	
+# Connection: default
+SET debug_sync= 'now SIGNAL go_flush';
+# Connection: con3 (FLUSH TABLES t1)
+# Reaping...
+# Connection: default
+SET debug_sync= 'RESET';
+DROP TABLE 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-05-24 19:25:36 +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-05-24 19:25:36 +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-05-24 19:25:36 +0000
@@ -1,3 +1,6 @@
+SET @old_binlog_format=@@binlog_format;
+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 +12,45 @@ 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
+# Reaping TRUNCATE TABLE
+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;
+# Even though the isolation level might be permissive, truncate
+# table follows a stricter isolation as its locking is based on
+# (exclusive) metadata locks.
 CREATE TABLE t1 (a INT) ENGINE=InnoDB;
 CREATE TABLE t2 (a INT) ENGINE=InnoDB;
 INSERT INTO t2 VALUES (1),(2),(3);
@@ -22,6 +64,43 @@ 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
+# Reaping TRUNCATE TABLE
+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 +114,196 @@ 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
+# Reaping TRUNCATE TABLE
+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
+# Reaping TRUNCATE TABLE
+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
+# Reaping TRUNCATE TABLE
+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
+# Reaping TRUNCATE TABLE
+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;
+# Truncate is not supported for SBR if the isolation level is
+# READ UNCOMMITTED or READ COMMITTED. These specific isolation
+# levels are tested elsewhere.
 CREATE TABLE t1 (a INT) ENGINE=InnoDB;
 CREATE TABLE t2 (a INT) ENGINE=InnoDB;
 INSERT INTO t2 VALUES (1),(2),(3);
@@ -48,6 +317,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);
+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
+# Reaping TRUNCATE TABLE
+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 +365,39 @@ 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
+# Reaping TRUNCATE TABLE
+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=@old_binlog_format;

=== 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-05-24 19:25:36 +0000
@@ -1,3 +1,5 @@
+SET @old_binlog_format=@@binlog_format;
+SET BINLOG_FORMAT=ROW;
 RESET MASTER;
 CREATE TABLE t1 (a INT) ENGINE=MyISAM;
 CREATE TABLE t2 (a INT) ENGINE=MyISAM;
@@ -10,3 +12,91 @@ 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
+# Reaping TRUNCATE TABLE
+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
+# Reaping TRUNCATE TABLE
+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=@old_binlog_format;

=== 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-05-24 19:25:36 +0000
@@ -1,20 +1,18 @@
 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 @old_binlog_format=@@binlog_format;
 
+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.
+--echo # Even though the isolation level might be permissive, truncate
+--echo # table follows a stricter isolation as its locking is based on
+--echo # (exclusive) metadata locks.
 
 let $before_truncate = SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
 source extra/binlog_tests/binlog_truncate.test;
@@ -27,3 +25,20 @@ 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;
+
+--echo # Truncate is not supported for SBR if the isolation level is
+--echo # READ UNCOMMITTED or READ COMMITTED. These specific isolation
+--echo # levels are tested elsewhere.
+
+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=@old_binlog_format;

=== 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-05-24 19:25:36 +0000
@@ -1,11 +1,17 @@
 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).
+SET @old_binlog_format=@@binlog_format;
 
+let $engine = MyISAM;
+
+SET BINLOG_FORMAT=ROW;
+RESET MASTER;
+
+source extra/binlog_tests/binlog_truncate.test;
+
+SET BINLOG_FORMAT=STATEMENT;
 RESET MASTER;
 
-let $engine = MyISAM;
 source extra/binlog_tests/binlog_truncate.test;
+
+SET BINLOG_FORMAT=@old_binlog_format;

=== 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-05-24 19:25:36 +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/innodb_bug38231.test'
--- a/mysql-test/t/innodb_bug38231.test	2009-01-08 12:31:34 +0000
+++ b/mysql-test/t/innodb_bug38231.test	2010-05-24 19:25:36 +0000
@@ -49,27 +49,9 @@ UNLOCK TABLES;
 -- disconnect con1
 -- disconnect con2
 
-# test that TRUNCATE works with with row-level locks
-
 -- enable_query_log
 -- enable_result_log
 
-INSERT INTO bug38231 VALUES (1), (10), (300);
-
--- connect (con4,localhost,root,,)
-
--- connection con4
-SET autocommit=0;
-SELECT * FROM bug38231 FOR UPDATE;
-
 -- connection default
-TRUNCATE TABLE bug38231;
-
--- connection con4
-COMMIT;
-
--- connection default
-
--- disconnect con4
 
 DROP TABLE bug38231;

=== modified file 'mysql-test/t/mdl_sync.test'
--- a/mysql-test/t/mdl_sync.test	2010-03-13 10:58:27 +0000
+++ b/mysql-test/t/mdl_sync.test	2010-05-24 19:25:36 +0000
@@ -3468,6 +3468,84 @@ connection default;
 set debug_sync= 'RESET';
 drop table t1;
 
+--echo #
+--echo # Bug#42643: InnoDB does not support replication of TRUNCATE TABLE
+--echo #
+--echo # Ensure that a acquired lock is not given up due to a conflict.
+--echo #
+
+connect (con1,localhost,root,,test,,);
+connect (con2,localhost,root,,test,,);
+connect (con3,localhost,root,,test,,);
+
+connection default;
+
+--disable_warnings
+DROP TABLE IF EXISTS t1;
+--enable_warnings
+
+CREATE TABLE t1 (a INT) ENGINE=InnoDB;
+INSERT INTO t1 VALUES (1),(2),(3);
+
+--echo # Connection: con1
+connection con1;
+SET debug_sync='lock_table_for_truncate SIGNAL parked_truncate WAIT_FOR go_truncate';
+send TRUNCATE TABLE t1;
+
+connection default;
+--echo # Connection: default
+SET debug_sync='now WAIT_FOR parked_truncate';
+
+connection con2;
+--echo # Connection: con2
+SET debug_sync='after_open_table_ignore_flush SIGNAL parked_show WAIT_FOR go_show';
+send SHOW FIELDS FROM t1;
+
+connection default;
+--echo # Connection: default
+SET debug_sync='now WAIT_FOR parked_show';
+
+connection con3;
+--echo # Connection: con3
+SET debug_sync='after_flush_unlock SIGNAL parked_flush WAIT_FOR go_flush';
+send FLUSH TABLES t1;
+
+connection default;
+--echo # Connection: default
+SET debug_sync='now WAIT_FOR parked_flush';
+SET debug_sync='now SIGNAL go_truncate';
+
+connection con1;
+--echo # Connection: con1
+--echo # Reaping...
+reap;
+
+connection default;
+--echo # Connection: default
+SET debug_sync= 'now SIGNAL go_show';
+
+connection con2;
+--echo # Connection: con2 (SHOW FIELDS FROM t1)
+--echo # Reaping...
+reap;
+
+connection default;
+--echo # Connection: default
+SET debug_sync= 'now SIGNAL go_flush';
+
+connection con3;
+--echo # Connection: con3 (FLUSH TABLES t1)
+--echo # Reaping...
+reap;
+
+disconnect con1;
+disconnect con2;
+disconnect con3;
+
+connection default;
+--echo # Connection: default
+SET debug_sync= 'RESET';
+DROP TABLE t1;
 
 # Check that all connections opened by test cases in this file are really
 # gone so execution of other tests won't be affected by their presence.

=== modified file 'mysql-test/t/partition_innodb_semi_consistent.test'
--- a/mysql-test/t/partition_innodb_semi_consistent.test	2010-02-16 17:23:21 +0000
+++ b/mysql-test/t/partition_innodb_semi_consistent.test	2010-05-24 19:25:36 +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-05-24 19:25:36 +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/CMakeLists.txt'
--- a/sql/CMakeLists.txt	2010-05-12 11:51:23 +0000
+++ b/sql/CMakeLists.txt	2010-05-24 19:25:36 +0000
@@ -75,7 +75,7 @@ SET (SQL_SOURCE
                sql_connect.cc scheduler.cc 
                sql_profile.cc event_parse_data.cc
                sql_signal.cc rpl_handler.cc mdl.cc
-               transaction.cc sys_vars.cc
+               transaction.cc sys_vars.cc sql_truncate.cc datadict.cc
                ${GEN_SOURCES}
                ${MYSYS_LIBWRAP_SOURCE})
 

=== modified file 'sql/Makefile.am'
--- a/sql/Makefile.am	2010-04-13 15:04:45 +0000
+++ b/sql/Makefile.am	2010-05-24 19:25:36 +0000
@@ -39,7 +39,9 @@ DTRACEFILES =           filesort.o \
                         sql_connect.o \
                         sql_cursor.o \
                         sql_delete.o \
+                        sql_truncate.o \
                         sql_insert.o \
+                        datadict.o \
                         sql_parse.o \
                         sql_prepare.o \
                         sql_select.o \
@@ -56,7 +58,9 @@ DTRACEFILES_DEPEND =    filesort.o \
                         sql_connect.o \
                         sql_cursor.o \
                         sql_delete.o \
+                        sql_truncate.o \
                         sql_insert.o \
+                        datadict.o \
                         sql_parse.o \
                         sql_prepare.o \
                         sql_select.o \
@@ -121,7 +125,8 @@ noinst_HEADERS =	item.h item_func.h item
 			sql_audit.h \
 			contributors.h sql_servers.h sql_signal.h records.h \
 			sql_prepare.h rpl_handler.h replication.h mdl.h \
-			sql_plist.h transaction.h sys_vars.h
+			sql_plist.h transaction.h sys_vars.h sql_truncate.h \
+			datadict.h
 
 mysqld_SOURCES =	sql_lex.cc sql_handler.cc sql_partition.cc \
 			item.cc item_sum.cc item_buff.cc item_func.cc \
@@ -136,10 +141,10 @@ mysqld_SOURCES =	sql_lex.cc sql_handler.
 			sql_connect.cc scheduler.cc sql_parse.cc \
 			keycaches.cc set_var.cc sql_yacc.yy sys_vars.cc \
 			sql_base.cc table.cc sql_select.cc sql_insert.cc \
-			sql_profile.cc \
+			datadict.cc sql_profile.cc \
 			sql_prepare.cc sql_error.cc sql_locale.cc \
 			sql_update.cc sql_delete.cc uniques.cc sql_do.cc \
-			procedure.cc sql_test.cc \
+			procedure.cc sql_test.cc sql_truncate.cc \
 			log.cc init.cc derror.cc sql_acl.cc \
 			unireg.cc des_key_file.cc \
 			log_event.cc rpl_record.cc \

=== added file 'sql/datadict.cc'
--- a/sql/datadict.cc	1970-01-01 00:00:00 +0000
+++ b/sql/datadict.cc	2010-05-24 19:25:36 +0000
@@ -0,0 +1,161 @@
+/* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; version 2 of the License.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA */
+
+#include "datadict.h"
+#include "sql_priv.h"
+#include "sql_class.h"
+#include "sql_table.h"
+
+
+/**
+  Check type of .frm if we are not going to parse it.
+
+  @param  path  path to FRM file
+
+  @retval  FRMTYPE_ERROR        error
+  @retval  FRMTYPE_TABLE        table
+  @retval  FRMTYPE_VIEW         view
+*/
+
+frm_type_enum dd_frm_type(THD *thd, char *path, enum legacy_db_type *dbt)
+{
+  File file;
+  uchar header[10];     //"TYPE=VIEW\n" it is 10 characters
+  size_t error;
+  DBUG_ENTER("dd_frm_type");
+
+  *dbt= DB_TYPE_UNKNOWN;
+
+  if ((file= mysql_file_open(key_file_frm, path, O_RDONLY | O_SHARE, MYF(0))) < 0)
+    DBUG_RETURN(FRMTYPE_ERROR);
+  error= mysql_file_read(file, (uchar*) header, sizeof(header), MYF(MY_NABP));
+  mysql_file_close(file, MYF(MY_WME));
+
+  if (error)
+    DBUG_RETURN(FRMTYPE_ERROR);
+  if (!strncmp((char*) header, "TYPE=VIEW\n", sizeof(header)))
+    DBUG_RETURN(FRMTYPE_VIEW);
+
+  /*
+    This is just a check for DB_TYPE. We'll return default unknown type
+    if the following test is true (arg #3). This should not have effect
+    on return value from this function (default FRMTYPE_TABLE)
+  */
+  if (header[0] != (uchar) 254 || header[1] != 1 ||
+      (header[2] != FRM_VER && header[2] != FRM_VER+1 &&
+       (header[2] < FRM_VER+3 || header[2] > FRM_VER+4)))
+    DBUG_RETURN(FRMTYPE_TABLE);
+
+  *dbt= (enum legacy_db_type) (uint) *(header + 3);
+
+  /* Probably a table. */
+  DBUG_RETURN(FRMTYPE_TABLE);
+}
+
+
+/**
+  Given a table name, check if the storage engine for the
+  table referred by this name supports an option 'flag'.
+  Return an error if the table does not exist or is not a
+  base table.
+
+  @pre Any metadata lock on the table.
+
+  @param[in]    thd         The current session.
+  @param[in]    db          Table schema.
+  @param[in]    table_name  Table database.
+  @param[in]    flag        The option to check.
+  @param[out]   yes_no      The result. Undefined if error.
+*/
+
+bool dd_check_storage_engine_flag(THD *thd,
+                                  const char *db, const char *table_name,
+                                  uint32 flag, bool *yes_no)
+{
+  char path[FN_REFLEN + 1];
+  enum legacy_db_type db_type;
+  handlerton *table_type;
+  LEX_STRING db_name = {(char *) db, strlen(db)};
+
+  if (check_db_name(&db_name))
+  {
+    my_error(ER_WRONG_DB_NAME, MYF(0), db_name.str);
+    return TRUE;
+  }
+
+  if (check_table_name(table_name, strlen(table_name)))
+  {
+    my_error(ER_WRONG_TABLE_NAME, MYF(0), table_name);
+    return TRUE;
+  }
+
+  /* There should be at least some lock on the table.  */
+  DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, db,
+                                             table_name, MDL_SHARED));
+
+  (void) build_table_filename(path, sizeof(path) - 1, db,
+                              table_name, reg_ext, 0);
+
+  dd_frm_type(thd, path, &db_type);
+
+  /* Type is unknown if the object is not found or is not a table. */
+  if (db_type == DB_TYPE_UNKNOWN)
+  {
+    my_error(ER_NO_SUCH_TABLE, MYF(0), db, table_name);
+    return TRUE;
+  }
+
+  table_type= ha_resolve_by_legacy_type(thd, db_type);
+  *yes_no= ha_check_storage_engine_flag(table_type, flag);
+
+  return FALSE;
+}
+
+
+/*
+  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 dd_recreate_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("dd_recreate_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);
+
+  DBUG_RETURN(error);
+}
+

=== added file 'sql/datadict.h'
--- a/sql/datadict.h	1970-01-01 00:00:00 +0000
+++ b/sql/datadict.h	2010-05-24 19:25:36 +0000
@@ -0,0 +1,40 @@
+#ifndef DATADICT_INCLUDED
+#define DATADICT_INCLUDED
+/* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; version 2 of the License.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA */
+
+#include "handler.h"
+
+/*
+  Data dictionary API.
+*/
+
+enum frm_type_enum
+{
+  FRMTYPE_ERROR= 0,
+  FRMTYPE_TABLE,
+  FRMTYPE_VIEW
+};
+
+
+frm_type_enum dd_frm_type(THD *thd, char *path, enum legacy_db_type *dbt);
+
+bool dd_check_storage_engine_flag(THD *thd,
+                                  const char *db, const char *table_name,
+                                  uint32 flag,
+                                  bool *yes_no);
+bool dd_recreate_table(THD *thd, const char *db, const char *table_name);
+
+#endif // DATADICT_INCLUDED

=== modified file 'sql/sql_base.cc'
--- a/sql/sql_base.cc	2010-05-21 12:41:24 +0000
+++ b/sql/sql_base.cc	2010-05-24 19:25:36 +0000
@@ -27,7 +27,7 @@
 #include "sql_show.h"    // append_identifier
 #include "strfunc.h"     // find_type
 #include "parse_file.h"  // sql_parse_prepare, File_parser
-#include "sql_view.h"    // mysql_frm_type, mysql_make_view, VIEW_ANY_ACL
+#include "sql_view.h"    // mysql_make_view, VIEW_ANY_ACL
 #include "sql_parse.h"   // check_table_access
 #include "sql_insert.h"  // kill_delayed_threads
 #include "sql_acl.h"     // *_ACL, check_grant_all_columns,
@@ -52,6 +52,7 @@
 #include <hash.h>
 #include "rpl_filter.h"
 #include "sql_table.h"                          // build_table_filename
+#include "datadict.h"   // dd_frm_type()
 #ifdef  __WIN__
 #include <io.h>
 #endif
@@ -2672,7 +2673,7 @@ bool open_table(THD *thd, TABLE_LIST *ta
         during prelocking process (in this case in theory we still
         should hold shared metadata lock on it).
       */
-      if (mysql_frm_type(thd, path, &not_used) == FRMTYPE_VIEW)
+      if (dd_frm_type(thd, path, &not_used) == FRMTYPE_VIEW)
       {
         if (!tdc_open_view(thd, table_list, alias, key, key_length,
                            mem_root, 0))

=== modified file 'sql/sql_bitmap.h'
--- a/sql/sql_bitmap.h	2010-03-31 14:05:33 +0000
+++ b/sql/sql_bitmap.h	2010-05-24 19:25:36 +0000
@@ -22,6 +22,7 @@
 #ifndef SQL_BITMAP_INCLUDED
 #define SQL_BITMAP_INCLUDED
 
+#include <my_sys.h>
 #include <my_bitmap.h>
 
 template <uint default_width> class Bitmap

=== modified file 'sql/sql_delete.cc'
--- a/sql/sql_delete.cc	2010-05-14 05:28:51 +0000
+++ b/sql/sql_delete.cc	2010-05-24 19:25:36 +0000
@@ -14,7 +14,7 @@
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
 
 /*
-  Delete of records and truncate of tables.
+  Delete of records tables.
 
   Multi-table deletes were introduced by Monty and Sinisa
 */
@@ -48,8 +48,7 @@
 */
 
 bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
-                  SQL_LIST *order, ha_rows limit, ulonglong options,
-                  bool reset_auto_increment)
+                  SQL_LIST *order, ha_rows limit, ulonglong options)
 {
   bool          will_batch;
   int		error, loc_error;
@@ -60,17 +59,11 @@ bool mysql_delete(THD *thd, TABLE_LIST *
   bool		transactional_table, safe_update, const_cond;
   bool          const_cond_result;
   ha_rows	deleted= 0;
-  bool          triggers_applicable;
   uint usable_index= MAX_KEY;
   SELECT_LEX   *select_lex= &thd->lex->select_lex;
   THD::killed_state killed_status= THD::NOT_KILLED;
+  THD::enum_binlog_query_type query_type= THD::ROW_QUERY_TYPE;
   DBUG_ENTER("mysql_delete");
-  bool save_binlog_row_based;
-
-  THD::enum_binlog_query_type query_type=
-    thd->lex->sql_command == SQLCOM_TRUNCATE ?
-    THD::STMT_QUERY_TYPE :
-    THD::ROW_QUERY_TYPE;
 
   if (open_and_lock_tables(thd, table_list, TRUE, 0))
     DBUG_RETURN(TRUE);
@@ -130,25 +123,20 @@ bool mysql_delete(THD *thd, TABLE_LIST *
     any side-effects (because of triggers), so we can use optimized
     handler::delete_all_rows() method.
 
-    We implement fast TRUNCATE for InnoDB even if triggers are
-    present.  TRUNCATE ignores triggers.
-
     We can use delete_all_rows() if and only if:
     - We allow new functions (not using option --skip-new), and are
       not in safe mode (not using option --safe-mode)
     - There is no limit clause
     - The condition is constant
     - If there is a condition, then it it produces a non-zero value
-    - If the current command is DELETE FROM with no where clause
-      (i.e., not TRUNCATE) then:
-      - We should not be binlogging this statement row-based, and
+    - If the current command is DELETE FROM with no where clause, then:
+      - We should not be binlogging this statement in row-based, and
       - there should be no delete triggers associated with the table.
   */
   if (!using_limit && const_cond_result &&
       !(specialflag & (SPECIAL_NO_NEW_FUNC | SPECIAL_SAFE_MODE)) &&
-      (thd->lex->sql_command == SQLCOM_TRUNCATE ||
        (!thd->is_current_stmt_binlog_format_row() &&
-        !(table->triggers && table->triggers->has_delete_triggers()))))
+        !(table->triggers && table->triggers->has_delete_triggers())))
   {
     /* Update the table->file->stats.records number */
     table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
@@ -161,16 +149,14 @@ bool mysql_delete(THD *thd, TABLE_LIST *
         query in row format, so we have to log it in statement format.
       */
       query_type= THD::STMT_QUERY_TYPE;
-      error= -1;				// ok
+      error= -1;
       deleted= maybe_deleted;
-      save_binlog_row_based= thd->is_current_stmt_binlog_format_row();
       goto cleanup;
     }
     if (error != HA_ERR_WRONG_COMMAND)
     {
       table->file->print_error(error,MYF(0));
       error=0;
-      save_binlog_row_based= thd->is_current_stmt_binlog_format_row();
       goto cleanup;
     }
     /* Handler didn't support fast delete; Delete rows one by one */
@@ -213,11 +199,6 @@ bool mysql_delete(THD *thd, TABLE_LIST *
     if (thd->is_error())
       DBUG_RETURN(TRUE);
     my_ok(thd, 0);
-    /*
-      We don't need to call reset_auto_increment in this case, because
-      mysql_truncate always gives a NULL conds argument, hence we never
-      get here.
-    */
     DBUG_RETURN(0);				// Nothing to delete
   }
 
@@ -287,12 +268,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *
   init_ftfuncs(thd, select_lex, 1);
   thd_proc_info(thd, "updating");
 
-  /* NOTE: TRUNCATE must not invoke triggers. */
-
-  triggers_applicable= table->triggers &&
-                       thd->lex->sql_command != SQLCOM_TRUNCATE;
-
-  if (triggers_applicable &&
+  if (table->triggers &&
       table->triggers->has_triggers(TRG_EVENT_DELETE,
                                     TRG_ACTION_AFTER))
   {
@@ -310,11 +286,6 @@ bool mysql_delete(THD *thd, TABLE_LIST *
 
   table->mark_columns_needed_for_delete();
 
-  save_binlog_row_based= thd->is_current_stmt_binlog_format_row();
-  if (thd->lex->sql_command == SQLCOM_TRUNCATE &&
-      thd->is_current_stmt_binlog_format_row())
-    thd->clear_current_stmt_binlog_format_row();
-
   while (!(error=info.read_record(&info)) && !thd->killed &&
 	 ! thd->is_error())
   {
@@ -322,7 +293,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *
     if (!(select && select->skip_record())&& ! thd->is_error() )
     {
 
-      if (triggers_applicable &&
+      if (table->triggers &&
           table->triggers->process_triggers(thd, TRG_EVENT_DELETE,
                                             TRG_ACTION_BEFORE, FALSE))
       {
@@ -333,7 +304,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *
       if (!(error= table->file->ha_delete_row(table->record[0])))
       {
 	deleted++;
-        if (triggers_applicable &&
+        if (table->triggers &&
             table->triggers->process_triggers(thd, TRG_EVENT_DELETE,
                                               TRG_ACTION_AFTER, FALSE))
         {
@@ -378,21 +349,6 @@ bool mysql_delete(THD *thd, TABLE_LIST *
   if (options & OPTION_QUICK)
     (void) table->file->extra(HA_EXTRA_NORMAL);
 
-  if (reset_auto_increment && (error < 0))
-  {
-    /*
-      We're really doing a truncate and need to reset the table's
-      auto-increment counter.
-    */
-    int error2= table->file->ha_reset_auto_increment(0);
-
-    if (error2 && (error2 != HA_ERR_WRONG_COMMAND))
-    {
-      table->file->print_error(error2, MYF(0));
-      error= 1;
-    }
-  }
-
 cleanup:
   /*
     Invalidate the table in the query cache if something changed. This must
@@ -413,34 +369,24 @@ cleanup:
   /* See similar binlogging code in sql_update.cc, for comments */
   if ((error < 0) || thd->transaction.stmt.modified_non_trans_table)
   {
-    if (mysql_bin_log.is_open() &&
-        !(thd->lex->sql_command == SQLCOM_TRUNCATE &&
-          thd->is_current_stmt_binlog_format_row() &&
-          find_temporary_table(thd, table_list)))
-    {
-      bool const is_trans=
-        thd->lex->sql_command == SQLCOM_TRUNCATE ?
-        FALSE :
-        transactional_table;
-
+    if (mysql_bin_log.is_open())
+    {
       int errcode= 0;
       if (error < 0)
         thd->clear_error();
       else
         errcode= query_error_code(thd, killed_status == THD::NOT_KILLED);
-      
+
       /*
         [binlog]: If 'handler::delete_all_rows()' was called and the
         storage engine does not inject the rows itself, we replicate
         statement-based; otherwise, 'ha_delete_row()' was used to
         delete specific rows which we might log row-based.
-
-        Note that TRUNCATE TABLE is not transactional and should
-        therefore be treated as a DDL.
       */
       int log_result= thd->binlog_query(query_type,
                                         thd->query(), thd->query_length(),
-                                        is_trans, FALSE, FALSE, errcode);
+                                        transactional_table, FALSE, FALSE,
+                                        errcode);
 
       if (log_result)
       {
@@ -448,18 +394,12 @@ cleanup:
       }
     }
   }
-  if (save_binlog_row_based)
-    thd->set_current_stmt_binlog_format_row();
   DBUG_ASSERT(transactional_table || !deleted || thd->transaction.stmt.modified_non_trans_table);
   free_underlaid_joins(thd, select_lex);
   if (error < 0 || 
       (thd->lex->ignore && !thd->is_error() && !thd->is_fatal_error))
   {
-    /*
-      If a TRUNCATE TABLE was issued, the number of rows should be reported as
-      zero since the exact number is unknown.
-    */
-    my_ok(thd, reset_auto_increment ? 0 : deleted);
+    my_ok(thd, deleted);
     DBUG_PRINT("info",("%ld records deleted",(long) deleted));
   }
   DBUG_RETURN(error >= 0 || thd->is_error());
@@ -1061,227 +1001,3 @@ bool multi_delete::send_eof()
   return 0;
 }
 
-
-/***************************************************************************
-  TRUNCATE TABLE
-****************************************************************************/
-
-/*
-  Row-by-row truncation if the engine does not support table recreation.
-  Probably a InnoDB table.
-*/
-
-static bool mysql_truncate_by_delete(THD *thd, TABLE_LIST *table_list)
-{
-  bool error;
-  DBUG_ENTER("mysql_truncate_by_delete");
-  table_list->lock_type= TL_WRITE;
-  table_list->mdl_request.set_type(MDL_SHARED_WRITE);
-  mysql_init_select(thd->lex);
-  /* Delete all rows from table */
-  error= mysql_delete(thd, table_list, NULL, NULL, HA_POS_ERROR, LL(0), TRUE);
-  /*
-    All effects of a TRUNCATE TABLE operation are rolled back if a row by row
-    deletion fails. Otherwise, operation is automatically committed at the end.
-  */
-  if (error)
-  {
-    DBUG_ASSERT(thd->stmt_da->is_error());
-    trans_rollback_stmt(thd);
-    trans_rollback(thd);
-  }
-  DBUG_RETURN(error);
-}
-
-
-/*
-  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
-
-  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.
-*/
-
-bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok)
-{
-  HA_CREATE_INFO create_info;
-  char path[FN_REFLEN + 1];
-  TABLE *table;
-  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.
-  */
-  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));
-
-  /* Remove tables from the HANDLER's hash. */
-  mysql_ha_rm_tables(thd, table_list);
-
-  /* If it is a temporary table, close and regenerate it */
-  if (!dont_send_ok && (table= find_temporary_table(thd, table_list)))
-  {
-    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;
-
-    table->file->info(HA_STATUS_AUTO | HA_STATUS_NO_LOCK);
-
-    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 we return here we will not have logged the truncation to the bin log
-      and we will not my_ok() to the client.
-    */
-    goto end;
-  }
-
-  path_length= build_table_filename(path, sizeof(path) - 1, table_list->db,
-                                    table_list->table_name, reg_ext, 0);
-
-  if (!dont_send_ok)
-  {
-    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);
-    }
-#ifdef WITH_PARTITION_STORAGE_ENGINE
-    /*
-      TODO: Add support for TRUNCATE PARTITION for NDB and other engines
-      supporting native partitioning
-    */
-    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,
-                                         thd->variables.lock_wait_timeout))
-        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);
-    }
-  }
-
-  /*
-    Remove the .frm extension AIX 5.2 64-bit compiler bug (BUG#16155): this
-    crashes, replacement works.  *(path + path_length - reg_ext_length)=
-     '\0';
-  */
-  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,
-                         &create_info, 1);
-  mysql_mutex_unlock(&LOCK_open);
-  query_cache_invalidate3(thd, table_list, 0);
-
-end:
-  if (!dont_send_ok)
-  {
-    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.
-    */
-    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);
-  }
-
-  DBUG_PRINT("exit", ("error: %d", error));
-  DBUG_RETURN(error);
-
-trunc_by_del:
-  error= mysql_truncate_by_delete(thd, table_list);
-  DBUG_RETURN(error);
-}

=== modified file 'sql/sql_delete.h'
--- a/sql/sql_delete.h	2010-04-12 13:17:37 +0000
+++ b/sql/sql_delete.h	2010-05-24 19:25:36 +0000
@@ -27,8 +27,6 @@ typedef struct st_sql_list SQL_LIST;
 
 int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds);
 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);
+                  SQL_LIST *order, ha_rows rows, ulonglong options);
 
 #endif /* SQL_DELETE_INCLUDED */

=== modified file 'sql/sql_parse.cc'
--- a/sql/sql_parse.cc	2010-05-18 12:52:51 +0000
+++ b/sql/sql_parse.cc	2010-05-24 19:25:36 +0000
@@ -49,6 +49,7 @@
                               // mysql_recreate_table,
                               // mysql_backup_table,
                               // mysql_restore_table
+#include "sql_truncate.h"     // mysql_truncate_table
 #include "sql_connect.h"      // check_user,
                               // decrease_user_connections,
                               // thd_init_client_charset, check_mqh,
@@ -253,7 +254,7 @@ void init_update_queries(void)
   sql_command_flags[SQLCOM_ALTER_TABLE]=    CF_CHANGES_DATA | CF_WRITE_LOGS_COMMAND |
                                             CF_AUTO_COMMIT_TRANS | CF_PROTECT_AGAINST_GRL;
   sql_command_flags[SQLCOM_TRUNCATE]=       CF_CHANGES_DATA | CF_WRITE_LOGS_COMMAND |
-                                            CF_AUTO_COMMIT_TRANS;
+                                            CF_AUTO_COMMIT_TRANS | CF_PROTECT_AGAINST_GRL;
   sql_command_flags[SQLCOM_DROP_TABLE]=     CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
   sql_command_flags[SQLCOM_LOAD]=           CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
                                             CF_PROTECT_AGAINST_GRL;
@@ -3281,7 +3282,8 @@ 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);
+    if (! (res= mysql_truncate_table(thd, first_table)))
+      my_ok(thd);
     break;
   case SQLCOM_DELETE:
   {
@@ -3294,8 +3296,7 @@ end_with_restore_list:
     MYSQL_DELETE_START(thd->query());
     res = mysql_delete(thd, all_tables, select_lex->where,
                        &select_lex->order_list,
-                       unit->select_limit_cnt, select_lex->options,
-                       FALSE);
+                       unit->select_limit_cnt, select_lex->options);
     MYSQL_DELETE_DONE(res, (ulong) thd->get_row_count_func());
     break;
   }

=== modified file 'sql/sql_parse.h'
--- a/sql/sql_parse.h	2010-05-14 18:11:25 +0000
+++ b/sql/sql_parse.h	2010-05-24 19:25:36 +0000
@@ -129,11 +129,6 @@ bool check_simple_select();
 Item *negate_expression(THD *thd, Item *expr);
 bool check_stack_overrun(THD *thd, long margin, uchar *dummy);
 
-bool begin_trans(THD *thd);
-bool end_active_trans(THD *thd);
-int end_trans(THD *thd, enum enum_mysql_completiontype completion);
-
-
 /* Variables */
 
 extern const char* any_db;

=== modified file 'sql/sql_rename.cc'
--- a/sql/sql_rename.cc	2010-05-05 22:02:08 +0000
+++ b/sql/sql_rename.cc	2010-05-24 19:25:36 +0000
@@ -29,6 +29,7 @@
                         // start_waiting_global_read_lock
 #include "sql_base.h"   // tdc_remove_table
 #include "sql_handler.h"                        // mysql_ha_rm_tables
+#include "datadict.h"
 
 static TABLE_LIST *rename_tables(THD *thd, TABLE_LIST *table_list,
 				 bool skip_error);
@@ -283,7 +284,7 @@ do_rename(THD *thd, TABLE_LIST *ren_tabl
   build_table_filename(name, sizeof(name) - 1,
                        ren_table->db, old_alias, reg_ext, 0);
 
-  frm_type= mysql_frm_type(thd, name, &table_type);
+  frm_type= dd_frm_type(thd, name, &table_type);
   switch (frm_type)
   {
     case FRMTYPE_TABLE:

=== modified file 'sql/sql_show.cc'
--- a/sql/sql_show.cc	2010-04-28 10:04:11 +0000
+++ b/sql/sql_show.cc	2010-05-24 19:25:36 +0000
@@ -27,7 +27,6 @@
                                               // primary_key_name,
                                               // build_table_filename
 #include "repl_failsafe.h"
-#include "sql_view.h"                           // mysql_frm_type
 #include "sql_parse.h"             // check_access, check_table_access
 #include "sql_partition.h"         // partition_element
 #include "sql_db.h"     // check_db_dir_existence, load_db_opt_by_name
@@ -50,6 +49,8 @@
 #endif
 #include <my_dir.h>
 #include "lock.h"                           // MYSQL_LOCK_IGNORE_FLUSH
+#include "debug_sync.h"
+#include "datadict.h"   // dd_frm_type()
 
 #define STR_OR_NIL(S) ((S) ? (S) : "<nil>")
 
@@ -2959,6 +2960,9 @@ fill_schema_show_cols_or_idxs(THD *thd, 
                                        (can_deadlock ?
                                         MYSQL_OPEN_FAIL_ON_MDL_CONFLICT : 0)));
   lex->sql_command= save_sql_command;
+
+  DEBUG_SYNC(thd, "after_open_table_ignore_flush");
+
   /*
     get_all_tables() returns 1 on failure and 0 on success thus
     return only these and not the result code of ::process_table()
@@ -3018,7 +3022,7 @@ static int fill_schema_table_names(THD *
     char path[FN_REFLEN + 1];
     (void) build_table_filename(path, sizeof(path) - 1, db_name->str, 
                                 table_name->str, reg_ext, 0);
-    switch (mysql_frm_type(thd, path, &not_used)) {
+    switch (dd_frm_type(thd, path, &not_used)) {
     case FRMTYPE_ERROR:
       table->field[3]->store(STRING_WITH_LEN("ERROR"),
                              system_charset_info);

=== modified file 'sql/sql_table.cc'
--- a/sql/sql_table.cc	2010-05-13 09:24:59 +0000
+++ b/sql/sql_table.cc	2010-05-24 19:25:36 +0000
@@ -27,8 +27,8 @@
                         // start_waiting_global_read_lock,
                         // unlock_table_names, mysql_unlock_tables
 #include "strfunc.h"    // find_type2, find_set
-#include "sql_view.h" // mysql_frm_type, view_checksum, mysql_frm_type
-#include "sql_delete.h"                         // mysql_truncate
+#include "sql_view.h" // view_checksum 
+#include "sql_truncate.h"                       // regenerate_locked_table 
 #include "sql_partition.h"                      // mem_alloc_error,
                                                 // generate_partition_syntax,
                                                 // partition_info
@@ -52,6 +52,7 @@
 #include "sql_show.h"
 #include "transaction.h"
 #include "keycaches.h"
+#include "datadict.h"  // dd_frm_type()
 
 #ifdef __WIN__
 #include <io.h>
@@ -2096,7 +2097,7 @@ int mysql_rm_table_part2(THD *thd, TABLE
         ((access(path, F_OK) &&
           ha_create_table_from_engine(thd, db, alias)) ||
          (!drop_view &&
-          mysql_frm_type(thd, path, &frm_db_type) != FRMTYPE_TABLE)))
+          dd_frm_type(thd, path, &frm_db_type) != FRMTYPE_TABLE)))
     {
       // Table was not found on disk and table can't be created from engine
       if (if_exists)
@@ -2116,7 +2117,7 @@ int mysql_rm_table_part2(THD *thd, TABLE
       */
       if (frm_db_type == DB_TYPE_UNKNOWN)
       {
-        mysql_frm_type(thd, path, &frm_db_type);
+        dd_frm_type(thd, path, &frm_db_type);
         DBUG_PRINT("info", ("frm_db_type %d from %s", frm_db_type, path));
       }
       table_type= ha_resolve_by_legacy_type(thd, frm_db_type);
@@ -4557,12 +4558,18 @@ static int prepare_for_repair(THD *thd, 
 			     "Failed renaming data file");
     goto end;
   }
-  if (mysql_truncate(thd, table_list, 1))
+  if (dd_recreate_table(thd, table_list->db, table_list->table_name))
   {
     error= send_check_errmsg(thd, table_list, "repair",
 			     "Failed generating table from .frm file");
     goto end;
   }
+  /*
+    'FALSE' for 'using_transactions' means don't postpone
+    invalidation till the end of a transaction, but do it
+    immediately.
+  */
+  query_cache_invalidate3(thd, table_list, FALSE);
   if (mysql_file_rename(key_file_misc, tmp, from, MYF(MY_WME)))
   {
     error= send_check_errmsg(thd, table_list, "repair",
@@ -6535,7 +6542,7 @@ bool mysql_alter_table(THD *thd,char *ne
     into the main table list, like open_tables does).
     This code is wrong and will be removed, please do not copy.
   */
-  frm_type= mysql_frm_type(thd, new_name_buff, &table_type);
+  frm_type= dd_frm_type(thd, new_name_buff, &table_type);
   /* Rename a view */
   /* Sic: there is a race here */
   if (frm_type == FRMTYPE_VIEW && !(alter_info->flags & ~ALTER_RENAME))

=== added file 'sql/sql_truncate.cc'
--- a/sql/sql_truncate.cc	1970-01-01 00:00:00 +0000
+++ b/sql/sql_truncate.cc	2010-05-24 19:25:36 +0000
@@ -0,0 +1,456 @@
+/* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; version 2 of the License.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA */
+
+#include "sql_truncate.h"
+#include "sql_priv.h"
+#include "transaction.h"
+#include "debug_sync.h"
+#include "records.h"     // READ_RECORD
+#include "table.h"       // TABLE
+#include "sql_class.h"   // THD
+#include "sql_base.h"    // open_and_lock_tables
+#include "sql_table.h"   // write_bin_log
+#include "sql_handler.h" // mysql_ha_rm_tables
+#include "datadict.h"    // dd_recreate_table()
+#include "lock.h"        // MYSQL_OPEN_TEMPORARY_ONLY
+
+
+/*
+  Delete all rows of a locked table.
+
+  @param  thd           Thread context.
+  @param  table_list    Table list element for the table.
+  @param  rows_deleted  Whether rows might have been deleted.
+
+  @retval  FALSE  Success.
+  @retval  TRUE   Error.
+*/
+
+static bool
+delete_all_rows(THD *thd, TABLE *table)
+{
+  int error;
+  READ_RECORD info;
+  bool is_bulk_delete;
+  bool some_rows_deleted= FALSE;
+  bool save_binlog_row_based= thd->is_current_stmt_binlog_format_row();
+  DBUG_ENTER("delete_all_rows");
+
+  /* Replication of truncate table must be statement based. */
+  thd->clear_current_stmt_binlog_format_row();
+
+  /*
+    Attempt to delete all rows in the table.
+    If it is unsupported, switch to row by row deletion.
+  */
+  if (! (error= table->file->ha_delete_all_rows()))
+    goto end;
+
+  if (error != HA_ERR_WRONG_COMMAND)
+  {
+    /*
+      If a transactional engine fails in the middle of deletion,
+      we expect it to be able to roll it back.  Some reasons
+      for the engine to fail would be media failure or corrupted
+      data dictionary (i.e. in case of a partitioned table). We
+      have sufficiently strong metadata locks to rule out any
+      potential deadlocks.
+
+      If a non-transactional engine fails here (that would
+      not be MyISAM, since MyISAM does TRUNCATE by recreate),
+      and binlog is on, replication breaks, since nothing gets
+      written to the binary log.  (XXX: is this a bug?)
+    */
+    table->file->print_error(error, MYF(0));
+    goto end;
+  }
+
+  /*
+    A workaround for Bug#53696  "Performance schema engine violates the
+    PSEA API by calling my_error()".
+  */
+  if (thd->is_error())
+    goto end;
+
+  /* Handler didn't support fast delete. Delete rows one by one. */
+
+  init_read_record(&info, thd, table, NULL, TRUE, TRUE, FALSE);
+
+  /*
+    Start bulk delete. If the engine does not support it, go on,
+    it's not an error.
+  */
+  is_bulk_delete= ! table->file->start_bulk_delete();
+
+  table->mark_columns_needed_for_delete();
+
+  while (!(error= info.read_record(&info)) && !thd->killed)
+  {
+    if ((error= table->file->ha_delete_row(table->record[0])))
+    {
+      table->file->print_error(error, MYF(0));
+      break;
+    }
+
+    some_rows_deleted= TRUE;
+  }
+
+  /* HA_ERR_END_OF_FILE */
+  if (error == -1)
+    error= 0;
+
+  /* Close down the bulk delete. */
+  if (is_bulk_delete)
+  {
+    int bulk_delete_error= table->file->end_bulk_delete();
+    if (bulk_delete_error && !error)
+    {
+      table->file->print_error(bulk_delete_error, MYF(0));
+      error= bulk_delete_error;
+    }
+  }
+
+  end_read_record(&info);
+
+  /*
+    Regardless of the error status, the query must be written to the
+    binary log if rows of the table is non-transactional.
+  */
+  if (some_rows_deleted && !table->file->has_transactions())
+  {
+    thd->transaction.stmt.modified_non_trans_table= TRUE;
+    thd->transaction.all.modified_non_trans_table= TRUE;
+  }
+
+  if (error || thd->killed)
+    goto end;
+
+  /* Truncate resets the auto-increment counter. */
+  error= table->file->ha_reset_auto_increment(0);
+  if (error)
+  {
+    if (error != HA_ERR_WRONG_COMMAND)
+      table->file->print_error(error, MYF(0));
+    else
+      error= 0;
+  }
+
+end:
+  if (save_binlog_row_based)
+    thd->set_current_stmt_binlog_format_row();
+
+  DBUG_RETURN(error);
+}
+
+
+/*
+  Close and recreate a temporary table. In case of success,
+  write truncate statement into the binary log if in statement
+  mode.
+
+  @param  thd     Thread context.
+  @param  table   The temporary table.
+
+  @retval  FALSE  Success.
+  @retval  TRUE   Error.
+*/
+
+static bool recreate_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("recreate_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);
+
+  /*
+    We must use share->normalized_path.str since for temporary tables it
+    differs from what dd_recreate_table() would generate based
+    on table and schema names.
+  */
+  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));
+
+  DBUG_RETURN(error);
+}
+
+
+/*
+  Handle opening and locking if a base table for truncate.
+
+  @param[in]  thd               Thread context.
+  @param[in]  table_ref         Table list element for the table to
+                                be truncated.
+  @param[out] hton_can_recreate Set to TRUE if table can be dropped
+                                and recreated.
+  @param[out] ticket_downgrade  Set if a lock must be downgraded after
+                                truncate is done.
+
+  @retval  FALSE  Success.
+  @retval  TRUE   Error.
+*/
+
+static bool open_and_lock_table_for_truncate(THD *thd, TABLE_LIST *table_ref,
+                                             bool *hton_can_recreate,
+                                             MDL_ticket **ticket_downgrade)
+{
+  TABLE *table= NULL;
+  MDL_ticket *mdl_ticket= NULL;
+  DBUG_ENTER("open_and_lock_table_for_truncate");
+
+  /*
+    Before doing anything else, acquire a metadata lock on the table,
+    or ensure we have one.  We don't use open_and_lock_tables()
+    right away because we want to be able to truncate (and recreate)
+    corrupted tables, those that we can't fully open.
+
+    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.
+  */
+  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);
+
+    *hton_can_recreate= ha_check_storage_engine_flag(table->s->db_type(),
+                                                     HTON_CAN_RECREATE);
+  }
+  else
+  {
+    /*
+      Even though we could use the previous execution branch here just as
+      well, we must not try to open the table:
+    */
+    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_SHARED_NO_READ_WRITE);
+    mdl_requests.push_front(&mdl_request);
+    mdl_requests.push_front(&mdl_global_request);
+
+    if (thd->mdl_context.acquire_locks(&mdl_requests,
+                                        thd->variables.lock_wait_timeout))
+      DBUG_RETURN(TRUE);
+
+    mdl_ticket= mdl_request.ticket;
+
+    if (dd_check_storage_engine_flag(thd, table_ref->db, table_ref->table_name,
+                                     HTON_CAN_RECREATE, hton_can_recreate))
+      DBUG_RETURN(TRUE);
+  }
+
+  DEBUG_SYNC(thd, "lock_table_for_truncate");
+
+  if (*hton_can_recreate)
+  {
+    /*
+      Acquire an exclusive lock. The storage engine can recreate the
+      table only if there are no references to it from anywhere, i.e.
+      no cached TABLE in the table cache. To remove the table from the
+      cache we need an exclusive lock.
+    */
+    if (thd->locked_tables_mode)
+    {
+      if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN))
+        DBUG_RETURN(TRUE);
+      *ticket_downgrade= table->mdl_ticket;
+      close_all_tables_for_name(thd, table->s, FALSE);
+    }
+    else
+    {
+      ulong timeout= thd->variables.lock_wait_timeout;
+      if (thd->mdl_context.upgrade_shared_lock_to_exclusive(mdl_ticket, timeout))
+        DBUG_RETURN(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);
+    }
+  }
+  else
+  {
+    /*
+      Can't recreate, we must mechanically delete all rows in
+      the table. Our metadata lock guarantees that no transaction
+      is reading or writing into the table. Yet, to open a write
+      cursor we need a thr_lock lock. Use open_and_lock_tables()
+      to do the necessary job.
+    */
+
+    /* Allow to open base tables only. */
+    table_ref->required_type= FRMTYPE_TABLE;
+    table_ref->lock_type= TL_WRITE;
+    /* We don't need to load triggers. */
+    DBUG_ASSERT(table_ref->trg_event_map == 0);
+
+    /*
+      Open the table as it will handle some required preparations.
+      Ignore pending FLUSH TABLES since we don't want to release
+      the MDL lock taken above and otherwise there is no way to
+      wait for FLUSH TABLES in deadlock-free fashion.
+    */
+    if (open_and_lock_tables(thd, table_ref, TL_WRITE,
+                              MYSQL_OPEN_IGNORE_FLUSH |
+                              MYSQL_OPEN_SKIP_TEMPORARY))
+      DBUG_RETURN(TRUE);
+  }
+
+  DBUG_RETURN(FALSE);
+}
+
+
+/*
+  Optimized delete of all rows by doing a full generate of the table.
+
+  @remark Will work even if the .MYI and .MYD files are destroyed.
+          In other words, it works as long as the .FRM is intact and
+          the engine supports re-create.
+
+  @param  thd         Thread context.
+  @param  table_ref   Table list element for the table to be truncated.
+
+  @retval  FALSE  Success.
+  @retval  TRUE   Error.
+*/
+
+bool mysql_truncate_table(THD *thd, TABLE_LIST *table_ref)
+{
+  TABLE *table;
+  bool error= TRUE, binlog_stmt;
+  MDL_ticket *mdl_ticket= NULL;
+  DBUG_ENTER("mysql_truncate_table");
+
+  /* Remove tables from the HANDLER's hash. */
+  mysql_ha_rm_tables(thd, table_ref);
+
+  /* If it is a temporary table, no need to take locks. */
+  if ((table= find_temporary_table(thd, table_ref)))
+  {
+    /* In RBR, the statement is not binlogged if the table is temporary. */
+    binlog_stmt= !thd->is_current_stmt_binlog_format_row();
+
+    /* Note that a temporary table cannot be partitioned. */
+    if (ha_check_storage_engine_flag(table->s->db_type(), HTON_CAN_RECREATE))
+    {
+      if ((error= recreate_temporary_table(thd, table)))
+        binlog_stmt= FALSE; /* No need to binlog failed truncate-by-recreate. */
+
+      DBUG_ASSERT(! thd->transaction.stmt.modified_non_trans_table);
+    }
+    else
+    {
+      /*
+        The engine does not support truncate-by-recreate. Open the
+        table and delete all rows. In such a manner this can in fact
+        open several tables if it's a temporary MyISAMMRG table.
+      */
+      if (open_and_lock_tables(thd, table_ref, TL_WRITE,
+                               MYSQL_OPEN_TEMPORARY_ONLY))
+        DBUG_RETURN(TRUE);
+
+      error= delete_all_rows(thd, table_ref->table);
+    }
+
+    /*
+      No need to invalidate the query cache, queries with temporary
+      tables are not in the cache. No need to write to the binary
+      log a failed row-by-row delete even if under RBR as the table
+      might not exist on the slave.
+    */
+  }
+  else /* It's not a temporary table. */
+  {
+    bool hton_can_recreate;
+
+    if (open_and_lock_table_for_truncate(thd, table_ref,
+                                         &hton_can_recreate, &mdl_ticket))
+      DBUG_RETURN(TRUE);
+
+    if (hton_can_recreate)
+    {
+     /*
+        The storage engine can truncate the table by creating an
+        empty table with the same structure.
+      */
+      error= dd_recreate_table(thd, table_ref->db, table_ref->table_name);
+
+      if (thd->locked_tables_mode && thd->locked_tables_list.reopen_tables(thd))
+          thd->locked_tables_list.unlink_all_closed_tables(thd, NULL, 0);
+
+      /* No need to binlog a failed truncate-by-recreate. */
+      binlog_stmt= !error;
+    }
+    else
+    {
+      error= delete_all_rows(thd, table_ref->table);
+
+      /*
+        Regardless of the error status, the query must be written to the
+        binary log if rows of a non-transactional table were deleted.
+      */
+      binlog_stmt= !error || thd->transaction.stmt.modified_non_trans_table;
+    }
+
+    query_cache_invalidate3(thd, table_ref, FALSE);
+  }
+
+  /* DDL is logged in statement format, regardless of binlog format. */
+  if (binlog_stmt)
+    error|= write_bin_log(thd, !error, thd->query(), thd->query_length());
+
+  /*
+    All effects of a TRUNCATE TABLE operation are rolled back if a row
+    by row deletion fails. Otherwise, it is automatically committed at
+    the end.
+  */
+  if (error)
+  {
+    trans_rollback_stmt(thd);
+    trans_rollback(thd);
+  }
+
+  if (mdl_ticket)
+    mdl_ticket->downgrade_exclusive_lock(MDL_SHARED_NO_READ_WRITE);
+
+  DBUG_PRINT("exit", ("error: %d", error));
+  DBUG_RETURN(test(error));
+}
+

=== added file 'sql/sql_truncate.h'
--- a/sql/sql_truncate.h	1970-01-01 00:00:00 +0000
+++ b/sql/sql_truncate.h	2010-05-24 19:25:36 +0000
@@ -0,0 +1,23 @@
+#ifndef SQL_TRUNCATE_INCLUDED
+#define SQL_TRUNCATE_INCLUDED
+/* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; version 2 of the License.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA */
+
+class THD;
+class TABLE_LIST;
+
+bool mysql_truncate_table(THD *thd, TABLE_LIST *table_ref);
+
+#endif

=== modified file 'sql/sql_view.cc'
--- a/sql/sql_view.cc	2010-04-07 11:58:40 +0000
+++ b/sql/sql_view.cc	2010-05-24 19:25:36 +0000
@@ -32,6 +32,7 @@
 #include "sp.h"
 #include "sp_head.h"
 #include "sp_cache.h"
+#include "datadict.h"   // dd_frm_type()
 
 #define MD5_BUFF_LENGTH 33
 
@@ -1663,7 +1664,7 @@ bool mysql_drop_view(THD *thd, TABLE_LIS
                          view->db, view->table_name, reg_ext, 0);
 
     if (access(path, F_OK) || 
-        FRMTYPE_VIEW != (type= mysql_frm_type(thd, path, &not_used)))
+        FRMTYPE_VIEW != (type= dd_frm_type(thd, path, &not_used)))
     {
       char name[FN_REFLEN];
       my_snprintf(name, sizeof(name), "%s.%s", view->db, view->table_name);
@@ -1742,54 +1743,6 @@ bool mysql_drop_view(THD *thd, TABLE_LIS
 
 
 /*
-  Check type of .frm if we are not going to parse it
-
-  SYNOPSIS
-    mysql_frm_type()
-    path	path to file
-
-  RETURN
-    FRMTYPE_ERROR	error
-    FRMTYPE_TABLE	table
-    FRMTYPE_VIEW	view
-*/
-
-frm_type_enum mysql_frm_type(THD *thd, char *path, enum legacy_db_type *dbt)
-{
-  File file;
-  uchar header[10];	//"TYPE=VIEW\n" it is 10 characters
-  size_t error;
-  DBUG_ENTER("mysql_frm_type");
-
-  *dbt= DB_TYPE_UNKNOWN;
-
-  if ((file= mysql_file_open(key_file_frm,
-                             path, O_RDONLY | O_SHARE, MYF(0))) < 0)
-    DBUG_RETURN(FRMTYPE_ERROR);
-  error= mysql_file_read(file, (uchar*) header, sizeof(header), MYF(MY_NABP));
-  mysql_file_close(file, MYF(MY_WME));
-
-  if (error)
-    DBUG_RETURN(FRMTYPE_ERROR);
-  if (!strncmp((char*) header, "TYPE=VIEW\n", sizeof(header)))
-    DBUG_RETURN(FRMTYPE_VIEW);
-
-  /*
-    This is just a check for DB_TYPE. We'll return default unknown type
-    if the following test is true (arg #3). This should not have effect
-    on return value from this function (default FRMTYPE_TABLE)
-  */
-  if (header[0] != (uchar) 254 || header[1] != 1 ||
-      (header[2] != FRM_VER && header[2] != FRM_VER+1 &&
-       (header[2] < FRM_VER+3 || header[2] > FRM_VER+4)))
-    DBUG_RETURN(FRMTYPE_TABLE);
-
-  *dbt= (enum legacy_db_type) (uint) *(header + 3);
-  DBUG_RETURN(FRMTYPE_TABLE);                   // Is probably a .frm table
-}
-
-
-/*
   check of key (primary or unique) presence in updatable view
 
   SYNOPSIS

=== modified file 'sql/sql_view.h'
--- a/sql/sql_view.h	2010-03-31 14:05:33 +0000
+++ b/sql/sql_view.h	2010-05-24 19:25:36 +0000
@@ -43,8 +43,6 @@ bool check_key_in_view(THD *thd, TABLE_L
 
 bool insert_view_fields(THD *thd, List<Item> *list, TABLE_LIST *view);
 
-frm_type_enum mysql_frm_type(THD *thd, char *path, enum legacy_db_type *dbt);
-
 int view_checksum(THD *thd, TABLE_LIST *view);
 
 extern TYPELIB updatable_views_with_limit_typelib;

=== modified file 'sql/table.h'
--- a/sql/table.h	2010-04-12 13:17:37 +0000
+++ b/sql/table.h	2010-05-24 19:25:36 +0000
@@ -20,6 +20,7 @@
 #include "sql_plist.h"
 #include "sql_list.h"                           /* Sql_alloc */
 #include "mdl.h"
+#include "datadict.h"
 
 #ifndef MYSQL_CLIENT
 
@@ -305,14 +306,6 @@ enum tmp_table_type
   NO_TMP_TABLE, NON_TRANSACTIONAL_TMP_TABLE, TRANSACTIONAL_TMP_TABLE,
   INTERNAL_TMP_TABLE, SYSTEM_TMP_TABLE
 };
-
-enum frm_type_enum
-{
-  FRMTYPE_ERROR= 0,
-  FRMTYPE_TABLE,
-  FRMTYPE_VIEW
-};
-
 enum release_type { RELEASE_NORMAL, RELEASE_WAIT_FOR_DROP };
 
 typedef struct st_filesort_info


Attachment: [text/bzr-bundle] bzr/davi.arnaut@sun.com-20100524192536-39qw5g038cumek24.bundle
Thread
bzr commit into mysql-trunk-runtime branch (davi:3022) Bug#42643Davi Arnaut24 May
Re: bzr commit into mysql-trunk-runtime branch (davi:3022) Bug#42643Konstantin Osipov24 May