summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.bzrignore6
-rw-r--r--libmysqld/CMakeLists.txt3
-rw-r--r--libmysqld/Makefile.am2
-rw-r--r--mysql-test/extra/binlog_tests/binlog_truncate.test41
-rw-r--r--mysql-test/include/mix1.inc41
-rw-r--r--mysql-test/r/innodb_bug38231.result9
-rw-r--r--mysql-test/r/innodb_mysql.result34
-rw-r--r--mysql-test/r/mdl_sync.result39
-rw-r--r--mysql-test/r/partition_innodb_semi_consistent.result2
-rw-r--r--mysql-test/r/truncate.result8
-rw-r--r--mysql-test/suite/binlog/r/binlog_truncate_innodb.result340
-rw-r--r--mysql-test/suite/binlog/r/binlog_truncate_myisam.result90
-rw-r--r--mysql-test/suite/binlog/t/binlog_truncate_innodb.test33
-rw-r--r--mysql-test/suite/binlog/t/binlog_truncate_myisam.test16
-rw-r--r--mysql-test/suite/binlog/t/disabled.def1
-rw-r--r--mysql-test/t/innodb_bug38231.test18
-rw-r--r--mysql-test/t/mdl_sync.test78
-rw-r--r--mysql-test/t/partition_innodb_semi_consistent.test2
-rw-r--r--mysql-test/t/truncate.test8
-rwxr-xr-xsql/CMakeLists.txt2
-rw-r--r--sql/Makefile.am11
-rw-r--r--sql/datadict.cc161
-rw-r--r--sql/datadict.h40
-rw-r--r--sql/sql_base.cc5
-rw-r--r--sql/sql_bitmap.h1
-rw-r--r--sql/sql_delete.cc314
-rw-r--r--sql/sql_delete.h4
-rw-r--r--sql/sql_parse.cc11
-rw-r--r--sql/sql_parse.h5
-rw-r--r--sql/sql_rename.cc3
-rw-r--r--sql/sql_show.cc8
-rw-r--r--sql/sql_table.cc19
-rw-r--r--sql/sql_truncate.cc464
-rw-r--r--sql/sql_truncate.h23
-rw-r--r--sql/sql_view.cc51
-rw-r--r--sql/sql_view.h2
-rw-r--r--sql/sql_yacc.yy6
-rw-r--r--sql/table.h9
38 files changed, 1470 insertions, 440 deletions
diff --git a/.bzrignore b/.bzrignore
index d51ffc7265b..b90eca9caf1 100644
--- a/.bzrignore
+++ b/.bzrignore
@@ -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
diff --git a/libmysqld/CMakeLists.txt b/libmysqld/CMakeLists.txt
index fa5088f288b..1cdf8e41a74 100644
--- a/libmysqld/CMakeLists.txt
+++ b/libmysqld/CMakeLists.txt
@@ -63,7 +63,8 @@ SET(SQL_EMBEDDED_SOURCES emb_qcache.cc libmysqld.c lib_sql.cc
../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
diff --git a/libmysqld/Makefile.am b/libmysqld/Makefile.am
index 68c31fbf79c..d4f000864ea 100644
--- a/libmysqld/Makefile.am
+++ b/libmysqld/Makefile.am
@@ -63,7 +63,7 @@ sqlsources = derror.cc field.cc field_conv.cc strfunc.cc filesort.cc \
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 \
diff --git a/mysql-test/extra/binlog_tests/binlog_truncate.test b/mysql-test/extra/binlog_tests/binlog_truncate.test
index dce33b3cef0..24cf363f780 100644
--- a/mysql-test/extra/binlog_tests/binlog_truncate.test
+++ b/mysql-test/extra/binlog_tests/binlog_truncate.test
@@ -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;
diff --git a/mysql-test/include/mix1.inc b/mysql-test/include/mix1.inc
index 66648aaf1bf..fe6abe13892 100644
--- a/mysql-test/include/mix1.inc
+++ b/mysql-test/include/mix1.inc
@@ -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;
diff --git a/mysql-test/r/innodb_bug38231.result b/mysql-test/r/innodb_bug38231.result
index 2f909779755..a2a872e573f 100644
--- a/mysql-test/r/innodb_bug38231.result
+++ b/mysql-test/r/innodb_bug38231.result
@@ -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;
diff --git a/mysql-test/r/innodb_mysql.result b/mysql-test/r/innodb_mysql.result
index 0a3df1f99b5..4dba86752f0 100644
--- a/mysql-test/r/innodb_mysql.result
+++ b/mysql-test/r/innodb_mysql.result
@@ -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 key key_len ref rows Extra
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);
diff --git a/mysql-test/r/mdl_sync.result b/mysql-test/r/mdl_sync.result
index 984f0df3d0e..8e004ba23f7 100644
--- a/mysql-test/r/mdl_sync.result
+++ b/mysql-test/r/mdl_sync.result
@@ -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;
diff --git a/mysql-test/r/partition_innodb_semi_consistent.result b/mysql-test/r/partition_innodb_semi_consistent.result
index 48a1bb3d258..ef9337fa65c 100644
--- a/mysql-test/r/partition_innodb_semi_consistent.result
+++ b/mysql-test/r/partition_innodb_semi_consistent.result
@@ -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;
diff --git a/mysql-test/r/truncate.result b/mysql-test/r/truncate.result
index 8f237c81e75..773075f9dae 100644
--- a/mysql-test/r/truncate.result
+++ b/mysql-test/r/truncate.result
@@ -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
diff --git a/mysql-test/suite/binlog/r/binlog_truncate_innodb.result b/mysql-test/suite/binlog/r/binlog_truncate_innodb.result
index ab237898a74..8beeeb1a428 100644
--- a/mysql-test/suite/binlog/r/binlog_truncate_innodb.result
+++ b/mysql-test/suite/binlog/r/binlog_truncate_innodb.result
@@ -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_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);
+# 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_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 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_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 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_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 # 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_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;
+SET BINLOG_FORMAT=@old_binlog_format;
diff --git a/mysql-test/suite/binlog/r/binlog_truncate_myisam.result b/mysql-test/suite/binlog/r/binlog_truncate_myisam.result
index 9f01c015178..1f5b206fd6f 100644
--- a/mysql-test/suite/binlog/r/binlog_truncate_myisam.result
+++ b/mysql-test/suite/binlog/r/binlog_truncate_myisam.result
@@ -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_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 # 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;
diff --git a/mysql-test/suite/binlog/t/binlog_truncate_innodb.test b/mysql-test/suite/binlog/t/binlog_truncate_innodb.test
index be0918a43f0..56dd5bda505 100644
--- a/mysql-test/suite/binlog/t/binlog_truncate_innodb.test
+++ b/mysql-test/suite/binlog/t/binlog_truncate_innodb.test
@@ -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_truncate.test;
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;
diff --git a/mysql-test/suite/binlog/t/binlog_truncate_myisam.test b/mysql-test/suite/binlog/t/binlog_truncate_myisam.test
index e0e4673e876..fc28bd7c7e2 100644
--- a/mysql-test/suite/binlog/t/binlog_truncate_myisam.test
+++ b/mysql-test/suite/binlog/t/binlog_truncate_myisam.test
@@ -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;
diff --git a/mysql-test/suite/binlog/t/disabled.def b/mysql-test/suite/binlog/t/disabled.def
index a86136fec69..b12a0a1d96d 100644
--- a/mysql-test/suite/binlog/t/disabled.def
+++ b/mysql-test/suite/binlog/t/disabled.def
@@ -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
diff --git a/mysql-test/t/innodb_bug38231.test b/mysql-test/t/innodb_bug38231.test
index b3fcd89f371..f70a4c86dd1 100644
--- a/mysql-test/t/innodb_bug38231.test
+++ b/mysql-test/t/innodb_bug38231.test
@@ -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;
diff --git a/mysql-test/t/mdl_sync.test b/mysql-test/t/mdl_sync.test
index ef434e33cfa..a5b631058c5 100644
--- a/mysql-test/t/mdl_sync.test
+++ b/mysql-test/t/mdl_sync.test
@@ -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.
diff --git a/mysql-test/t/partition_innodb_semi_consistent.test b/mysql-test/t/partition_innodb_semi_consistent.test
index 294521a45d5..00ea34c390a 100644
--- a/mysql-test/t/partition_innodb_semi_consistent.test
+++ b/mysql-test/t/partition_innodb_semi_consistent.test
@@ -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;
diff --git a/mysql-test/t/truncate.test b/mysql-test/t/truncate.test
index cdfa448f78a..c7a066cc203 100644
--- a/mysql-test/t/truncate.test
+++ b/mysql-test/t/truncate.test
@@ -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;
diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt
index 357575c5b47..87461be88f3 100755
--- a/sql/CMakeLists.txt
+++ b/sql/CMakeLists.txt
@@ -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})
diff --git a/sql/Makefile.am b/sql/Makefile.am
index 93595a964eb..0fba8d12406 100644
--- a/sql/Makefile.am
+++ b/sql/Makefile.am
@@ -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_sum.h item_cmpfunc.h \
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.cc sql_partition.cc \
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 \
diff --git a/sql/datadict.cc b/sql/datadict.cc
new file mode 100644
index 00000000000..5067c13a255
--- /dev/null
+++ b/sql/datadict.cc
@@ -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);
+}
+
diff --git a/sql/datadict.h b/sql/datadict.h
new file mode 100644
index 00000000000..05b5a9bba4b
--- /dev/null
+++ b/sql/datadict.h
@@ -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
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 85017886d24..fa484abf0be 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -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
@@ -2678,7 +2679,7 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
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))
diff --git a/sql/sql_bitmap.h b/sql/sql_bitmap.h
index 80a4712dd69..8d00c984d14 100644
--- a/sql/sql_bitmap.h
+++ b/sql/sql_bitmap.h
@@ -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
diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc
index 10bdb8a22a6..730cb711a05 100644
--- a/sql/sql_delete.cc
+++ b/sql/sql_delete.cc
@@ -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 *table_list, COND *conds,
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 *table_list, COND *conds,
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 *table_list, COND *conds,
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 *table_list, COND *conds,
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 *table_list, COND *conds,
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_list, COND *conds,
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 *table_list, COND *conds,
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 *table_list, COND *conds,
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 *table_list, COND *conds,
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)))
+ if (mysql_bin_log.is_open())
{
- bool const is_trans=
- thd->lex->sql_command == SQLCOM_TRUNCATE ?
- FALSE :
- transactional_table;
-
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);
-}
diff --git a/sql/sql_delete.h b/sql/sql_delete.h
index d1c1b363abd..c718323ce1e 100644
--- a/sql/sql_delete.h
+++ b/sql/sql_delete.h
@@ -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 */
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 4c3f44ea75c..adab0b8e3d0 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -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;
@@ -3280,9 +3281,8 @@ end_with_restore_list:
ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
goto error;
}
- 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:
{
@@ -3295,8 +3295,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;
}
diff --git a/sql/sql_parse.h b/sql/sql_parse.h
index e1543a09549..ca804d5fab0 100644
--- a/sql/sql_parse.h
+++ b/sql/sql_parse.h
@@ -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;
diff --git a/sql/sql_rename.cc b/sql/sql_rename.cc
index ea95b59b0c2..130a99a374f 100644
--- a/sql/sql_rename.cc
+++ b/sql/sql_rename.cc
@@ -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_table, char *new_db, char *new_table_name,
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:
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index a33bc5943da..7fe84ce0dd4 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -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, TABLE_LIST *tables,
(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 *thd, TABLE *table,
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);
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index b2a950ca4b0..bad88476c09 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -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_LIST *tables, bool if_exists,
((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_LIST *tables, bool if_exists,
*/
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, TABLE_LIST *table_list,
"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",
@@ -6544,7 +6551,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
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))
diff --git a/sql/sql_truncate.cc b/sql/sql_truncate.cc
new file mode 100644
index 00000000000..47a98769d57
--- /dev/null
+++ b/sql/sql_truncate.cc
@@ -0,0 +1,464 @@
+/* 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;
+ /* We don't need to load triggers. */
+ DBUG_ASSERT(table_ref->trg_event_map == 0);
+ /* Work around partition parser rules using alter table's. */
+ if (thd->lex->alter_info.flags & ALTER_ADMIN_PARTITION)
+ {
+ table_ref->lock_type= TL_WRITE;
+ table_ref->mdl_request.set_type(MDL_SHARED_WRITE);
+ }
+ /* Ensure proper lock types (e.g. from the parser). */
+ DBUG_ASSERT(table_ref->lock_type == TL_WRITE);
+ DBUG_ASSERT(table_ref->mdl_request.type == MDL_SHARED_WRITE);
+
+ /*
+ 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));
+}
+
diff --git a/sql/sql_truncate.h b/sql/sql_truncate.h
new file mode 100644
index 00000000000..a9eec384718
--- /dev/null
+++ b/sql/sql_truncate.h
@@ -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
diff --git a/sql/sql_view.cc b/sql/sql_view.cc
index 4eee9502177..3c8de0a253c 100644
--- a/sql/sql_view.cc
+++ b/sql/sql_view.cc
@@ -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_LIST *views, enum_drop_mode drop_mode)
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_LIST *views, enum_drop_mode drop_mode)
/*
- 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
diff --git a/sql/sql_view.h b/sql/sql_view.h
index 7d06abb9068..c15ecffccb8 100644
--- a/sql/sql_view.h
+++ b/sql/sql_view.h
@@ -43,8 +43,6 @@ bool check_key_in_view(THD *thd, TABLE_LIST * view);
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;
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index e5875663d4e..fdc8af942d1 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -10685,7 +10685,7 @@ opt_delete_option:
;
truncate:
- TRUNCATE_SYM opt_table_sym table_name
+ TRUNCATE_SYM opt_table_sym
{
LEX* lex= Lex;
lex->sql_command= SQLCOM_TRUNCATE;
@@ -10693,7 +10693,11 @@ truncate:
lex->select_lex.options= 0;
lex->select_lex.sql_cache= SELECT_LEX::SQL_CACHE_UNSPECIFIED;
lex->select_lex.init_order();
+ YYPS->m_lock_type= TL_WRITE;
+ YYPS->m_mdl_type= MDL_SHARED_WRITE;
}
+ table_name
+ {}
;
opt_table_sym:
diff --git a/sql/table.h b/sql/table.h
index ea585208b83..cb773052c8b 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -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