diff options
author | Monty <monty@mariadb.org> | 2020-12-20 17:44:11 +0200 |
---|---|---|
committer | Sergei Golubchik <serg@mariadb.org> | 2021-05-19 22:54:13 +0200 |
commit | 7a588c30b19d9ee21be2755d70f7ed7fd3676c4b (patch) | |
tree | 4228b80c323bdca2afebc673e5e9e52d3c44820a | |
parent | 407e9b78cf64a3b8aca441ab585601e173f9ba32 (diff) | |
download | mariadb-git-7a588c30b19d9ee21be2755d70f7ed7fd3676c4b.tar.gz |
MDEV-24408 Crash-safe DROP DATABASE
Description of how DROP DATABASE works after this patch
- Collect list of tables
- DDL log tables as they are dropped
- DDL log drop database
- Delete db.opt
- Delete data directory
- Log either DROP TABLE or DROP DATABASE to binary log
- De active ddl log entry
This is in line of how things where before (minus ddl logging) except that
we delete db.opt file last to not loose it if DROP DATABASE fails.
On recovery we have to ensure that all dropped tables are logged in
binary log and that they are properly dropped (as with atomic drop
table).
No new tables be dropped as part of recovery.
Recovery of active drop database ddl log entry:
- If drop database was logged to ddl log but was not found in the binary
log:
- drop the db.opt file and database directory.
- Log DROP DATABASE to binary log
- If drop database was not logged to ddl log
- Update binary log with DROP TABLE of the dropped tables. If table list
is longer than max_allowed_packet, then the query will be split into
multiple DROP TABLE/VIEW queries.
Other things:
- Added DDL_LOG_STATE and 'current database' as arguments to
mysql_rm_table_no_locks(). This was needed to be able to combine
ddl logging of DROP DATABASE and DROP TABLE and make the generated
DROP TABLE statements shorter.
- To make the DROP TABLE statement created by ddl log shorter, I changed
the binlogged query to use current directory and omit the directory
part for all tables in the current directory.
- Merged some DROP TABLE and DROP VIEW code in ddl logger. This was done
to be able get separate DROP VIEW and DROP TABLE statements in the binary
log.
- Added a 'recovery_state' variable to remember the state of dropped
tables and views.
- Moved out code that drops database objects (stored procedures) from
mysql_rm_db_internal() to drop_database_objects() for better code reuse.
- Made mysql_rm_db_internal() global so that could be used by the ddl
recovery code.
-rw-r--r-- | mysql-test/suite/atomic/drop_db.result | 95 | ||||
-rw-r--r-- | mysql-test/suite/atomic/drop_db.test | 112 | ||||
-rw-r--r-- | mysql-test/suite/atomic/drop_db_long_names.opt | 1 | ||||
-rw-r--r-- | mysql-test/suite/atomic/drop_db_long_names.result | 11 | ||||
-rw-r--r-- | mysql-test/suite/atomic/drop_db_long_names.test | 108 | ||||
-rw-r--r-- | mysql-test/suite/atomic/drop_sequence.result | 75 | ||||
-rw-r--r-- | mysql-test/suite/atomic/drop_sequence.test | 18 | ||||
-rw-r--r-- | mysql-test/suite/atomic/drop_table.result | 110 | ||||
-rw-r--r-- | mysql-test/suite/atomic/drop_view.result | 10 | ||||
-rw-r--r-- | sql/ddl_log.cc | 353 | ||||
-rw-r--r-- | sql/ddl_log.h | 30 | ||||
-rw-r--r-- | sql/handler.cc | 5 | ||||
-rw-r--r-- | sql/handler.h | 2 | ||||
-rw-r--r-- | sql/sql_db.cc | 182 | ||||
-rw-r--r-- | sql/sql_db.h | 4 | ||||
-rw-r--r-- | sql/sql_table.cc | 68 | ||||
-rw-r--r-- | sql/sql_table.h | 6 | ||||
-rw-r--r-- | sql/sql_view.cc | 2 |
18 files changed, 910 insertions, 282 deletions
diff --git a/mysql-test/suite/atomic/drop_db.result b/mysql-test/suite/atomic/drop_db.result new file mode 100644 index 00000000000..46838402308 --- /dev/null +++ b/mysql-test/suite/atomic/drop_db.result @@ -0,0 +1,95 @@ +call mtr.add_suppression("InnoDB: .* does not exist in the InnoDB internal"); +"engine: aria crash point: ddl_log_drop_before_delete_table position: 1" +t1v.frm +t2.MAD +t2.MAI +t2.frm +Procedure sql_mode Create Procedure character_set_client collation_connection Database Collation +foo STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` PROCEDURE `foo`() +insert into test.t1 values (42) latin1 latin1_swedish_ci latin1_swedish_ci +master-bin.000002 # Query # # use `test2`; DROP TABLE IF EXISTS `t1` /* generated by ddl recovery */ +"engine: aria crash point: ddl_log_drop_before_delete_table position: 2" +t1v.frm +t2.MAD +t2.MAI +t2.frm +Procedure sql_mode Create Procedure character_set_client collation_connection Database Collation +foo STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` PROCEDURE `foo`() +insert into test.t1 values (42) latin1 latin1_swedish_ci latin1_swedish_ci +master-bin.000002 # Query # # use `test2`; DROP TABLE IF EXISTS `t1` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test2`; DROP VIEW IF EXISTS `t1v` /* generated by ddl recovery */ +"engine: aria crash point: ddl_log_drop_before_delete_table position: 3" +Procedure sql_mode Create Procedure character_set_client collation_connection Database Collation +foo STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` PROCEDURE `foo`() +insert into test.t1 values (42) latin1 latin1_swedish_ci latin1_swedish_ci +master-bin.000002 # Query # # use `test2`; DROP TABLE IF EXISTS `t1`,`t2` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test2`; DROP VIEW IF EXISTS `t1v` /* generated by ddl recovery */ +"engine: aria crash point: ddl_log_drop_after_drop_tables position: 1" +Procedure sql_mode Create Procedure character_set_client collation_connection Database Collation +foo STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` PROCEDURE `foo`() +insert into test.t1 values (42) latin1 latin1_swedish_ci latin1_swedish_ci +master-bin.000002 # Query # # use `test2`; DROP TABLE IF EXISTS `t1`,`t2` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test2`; DROP VIEW IF EXISTS `t1v` /* generated by ddl recovery */ +"engine: aria crash point: ddl_log_drop_before_ha_drop_database position: 1" +master-bin.000002 # Query # # DROP DATABASE IF EXISTS `test2` /* generated by ddl recovery */ +"engine: aria crash point: ddl_log_drop_before_drop_db_routines position: 1" +master-bin.000002 # Query # # DROP DATABASE IF EXISTS `test2` /* generated by ddl recovery */ +"engine: aria crash point: ddl_log_drop_after_drop_db_routines position: 1" +master-bin.000002 # Query # # DROP DATABASE IF EXISTS `test2` /* generated by ddl recovery */ +"engine: aria crash point: ddl_log_drop_before_drop_option_file position: 1" +master-bin.000002 # Query # # DROP DATABASE IF EXISTS `test2` /* generated by ddl recovery */ +"engine: aria crash point: ddl_log_drop_before_drop_dir position: 1" +master-bin.000002 # Query # # DROP DATABASE IF EXISTS `test2` /* generated by ddl recovery */ +"engine: aria crash point: ddl_log_drop_after_drop_dir position: 1" +master-bin.000002 # Query # # DROP DATABASE IF EXISTS `test2` /* generated by ddl recovery */ +"engine: aria crash point: ddl_log_drop_before_binlog position: 1" +master-bin.000002 # Query # # DROP DATABASE IF EXISTS `test2` /* generated by ddl recovery */ +"engine: aria crash point: ddl_log_drop_after_binlog position: 1" +master-bin.000001 # Query # # DROP DATABASE test2 +"engine: innodb crash point: ddl_log_drop_before_delete_table position: 1" +t1v.frm +t2.frm +t2.ibd +Procedure sql_mode Create Procedure character_set_client collation_connection Database Collation +foo STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` PROCEDURE `foo`() +insert into test.t1 values (42) latin1 latin1_swedish_ci latin1_swedish_ci +master-bin.000002 # Query # # use `test2`; DROP TABLE IF EXISTS `t1` /* generated by ddl recovery */ +"engine: innodb crash point: ddl_log_drop_before_delete_table position: 2" +t1v.frm +t2.frm +t2.ibd +Procedure sql_mode Create Procedure character_set_client collation_connection Database Collation +foo STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` PROCEDURE `foo`() +insert into test.t1 values (42) latin1 latin1_swedish_ci latin1_swedish_ci +master-bin.000002 # Query # # use `test2`; DROP TABLE IF EXISTS `t1` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test2`; DROP VIEW IF EXISTS `t1v` /* generated by ddl recovery */ +"engine: innodb crash point: ddl_log_drop_before_delete_table position: 3" +Procedure sql_mode Create Procedure character_set_client collation_connection Database Collation +foo STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` PROCEDURE `foo`() +insert into test.t1 values (42) latin1 latin1_swedish_ci latin1_swedish_ci +master-bin.000002 # Query # # use `test2`; DROP TABLE IF EXISTS `t1`,`t2` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test2`; DROP VIEW IF EXISTS `t1v` /* generated by ddl recovery */ +"engine: innodb crash point: ddl_log_drop_after_drop_tables position: 1" +Procedure sql_mode Create Procedure character_set_client collation_connection Database Collation +foo STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` PROCEDURE `foo`() +insert into test.t1 values (42) latin1 latin1_swedish_ci latin1_swedish_ci +master-bin.000002 # Query # # use `test2`; DROP TABLE IF EXISTS `t1`,`t2` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test2`; DROP VIEW IF EXISTS `t1v` /* generated by ddl recovery */ +"engine: innodb crash point: ddl_log_drop_before_ha_drop_database position: 1" +master-bin.000002 # Query # # DROP DATABASE IF EXISTS `test2` /* generated by ddl recovery */ +"engine: innodb crash point: ddl_log_drop_before_drop_db_routines position: 1" +master-bin.000002 # Query # # DROP DATABASE IF EXISTS `test2` /* generated by ddl recovery */ +"engine: innodb crash point: ddl_log_drop_after_drop_db_routines position: 1" +master-bin.000002 # Query # # DROP DATABASE IF EXISTS `test2` /* generated by ddl recovery */ +"engine: innodb crash point: ddl_log_drop_before_drop_option_file position: 1" +master-bin.000002 # Query # # DROP DATABASE IF EXISTS `test2` /* generated by ddl recovery */ +"engine: innodb crash point: ddl_log_drop_before_drop_dir position: 1" +master-bin.000002 # Query # # DROP DATABASE IF EXISTS `test2` /* generated by ddl recovery */ +"engine: innodb crash point: ddl_log_drop_after_drop_dir position: 1" +master-bin.000002 # Query # # DROP DATABASE IF EXISTS `test2` /* generated by ddl recovery */ +"engine: innodb crash point: ddl_log_drop_before_binlog position: 1" +master-bin.000002 # Query # # DROP DATABASE IF EXISTS `test2` /* generated by ddl recovery */ +"engine: innodb crash point: ddl_log_drop_after_binlog position: 1" +master-bin.000001 # Query # # DROP DATABASE test2 +Warnings: +Note 1008 Can't drop database 'test2'; database doesn't exist diff --git a/mysql-test/suite/atomic/drop_db.test b/mysql-test/suite/atomic/drop_db.test new file mode 100644 index 00000000000..9630815e18e --- /dev/null +++ b/mysql-test/suite/atomic/drop_db.test @@ -0,0 +1,112 @@ +--source include/have_debug.inc +--source include/have_innodb.inc +--source include/have_log_bin.inc +--source include/not_valgrind.inc + +# +# Testing of atomic DROP DATABASE with crashes in a lot of different places +# + +call mtr.add_suppression("InnoDB: .* does not exist in the InnoDB internal"); +let $MYSQLD_DATADIR= `SELECT @@datadir`; + +let $engine_count=2; +let $engines='aria','innodb'; + +let $crash_count=10; +let $crash_points='ddl_log_drop_before_delete_table','ddl_log_drop_after_drop_tables','ddl_log_drop_before_ha_drop_database','ddl_log_drop_before_drop_db_routines','ddl_log_drop_after_drop_db_routines','ddl_log_drop_before_drop_option_file','ddl_log_drop_before_drop_dir','ddl_log_drop_after_drop_dir','ddl_log_drop_before_binlog','ddl_log_drop_after_binlog'; + +let $old_debug=`select @@debug_dbug`; + +let $e=0; +let $keep_include_silent=1; +let $grep_script=DROP; +--disable_query_log + +while ($e < $engine_count) +{ + inc $e; + let $engine=`select ELT($e, $engines)`; + let $default_engine=$engine; + let $extra_option=; + + if ($engine == "aria") + { + let $extra_option=transactional=1; + } + if ($engine == "aria_notrans") + { + let $default_engine="aria"; + let $extra_option=transactional=0; + } + + # Number of tables that should be dropped (we try to crash after each drop) + let $drops=3; + + let $c=0; + while ($c < $crash_count) + { + inc $c; + let $crash=`select ELT($c, $crash_points)`; + let $r=0; + while ($r < $drops) + { + inc $r; + create database test2; + use test2; + --eval set @@default_storage_engine=$default_engine; + --eval create table t1 (a int not null) $extra_option; + create view t1v as select * from t1; + --eval create table t2 (b int not null) $extra_option; + create procedure foo() + insert into test.t1 values (42); + + flush tables; + use test; + + RESET MASTER; + + echo "engine: $engine crash point: $crash position: $r"; + --exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect + --disable_reconnect + --eval set @@debug_dbug="+d,$crash",@debug_crash_counter=$r + let $errno=0; + --error 0,2013 + DROP DATABASE test2; + let $error=$errno; + --enable_reconnect + --source include/wait_until_connected_again.inc + --disable_query_log + --eval set @@debug_dbug="$old_debug" + + if ($error == 0) + { + echo "No crash!"; + } + use test; + # Check which tables still exists + --error 0,1 + --list_files $MYSQLD_DATADIR/test2 t* + --error 0,ER_SP_DOES_NOT_EXIST + show create procedure test2.foo; + + --let $binlog_file=master-bin.000001 + --source include/show_binlog_events.inc + if ($error) + { + --let $binlog_file=master-bin.000002 + --source include/show_binlog_events.inc + } + # Really drop the tables. The warnings will show what was dropped + --disable_warnings + --error 0, ER_DB_DROP_EXISTS + DROP DATABASE test2; + --enable_warnings + } + # We only need to test drops for all tables for the first crash point + let $drops=1; + } +} +drop database if exists test2; + +--enable_query_log diff --git a/mysql-test/suite/atomic/drop_db_long_names.opt b/mysql-test/suite/atomic/drop_db_long_names.opt new file mode 100644 index 00000000000..968c8f1a56a --- /dev/null +++ b/mysql-test/suite/atomic/drop_db_long_names.opt @@ -0,0 +1 @@ +--max-allowed-packet=1024 --net-buffer-length=1024 diff --git a/mysql-test/suite/atomic/drop_db_long_names.result b/mysql-test/suite/atomic/drop_db_long_names.result new file mode 100644 index 00000000000..8a4ff7f762e --- /dev/null +++ b/mysql-test/suite/atomic/drop_db_long_names.result @@ -0,0 +1,11 @@ +"engine: aria crash point: ddl_log_drop_after_drop_tables position: 1" +master-bin.000002 # Query # # use `test2`; DROP TABLE IF EXISTS `tABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB`,`tACCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC`,`tADDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD`,`tAEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE`,`tAFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF`,`tAGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG`,`tAHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH`,`tAIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII`,`tAJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ`,`tAKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK`,`tALLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test2`; DROP VIEW IF EXISTS `tABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBv`,`tACCCCCCCCCCCCCCCCCCCCCCCCCCCCCCv`,`tADDDDDDDDDDDDDDDDDDDDDDDDDDDDDDv`,`tAEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEv`,`tAFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFv`,`tAGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGv`,`tAHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHv`,`tAIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIv`,`tAJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJv`,`tAKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKv` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test2`; DROP TABLE IF EXISTS `tAMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM`,`tANNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN`,`tAOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO`,`tAPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP`,`tAQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ`,`tARRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR`,`tASSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS`,`tATTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT`,`tAUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU`,`tAVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV`,`tAWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test2`; DROP VIEW IF EXISTS `tALLLLLLLLLLLLLLLLLLLLLLLLLLLLLLv`,`tAMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMv`,`tANNNNNNNNNNNNNNNNNNNNNNNNNNNNNNv`,`tAOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOv`,`tAPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPv`,`tAQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQv`,`tARRRRRRRRRRRRRRRRRRRRRRRRRRRRRRv`,`tASSSSSSSSSSSSSSSSSSSSSSSSSSSSSSv`,`tATTTTTTTTTTTTTTTTTTTTTTTTTTTTTTv`,`tAUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUv`,`tAVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVv` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test2`; DROP TABLE IF EXISTS `tAXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX`,`tAYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY`,`tAZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ`,`tBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA`,`tBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB`,`tBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC`,`tBDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD`,`tBEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test2`; DROP VIEW IF EXISTS `tAWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWv`,`tAXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXv`,`tAYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYv`,`tAZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZv`,`tBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv`,`tBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBv`,`tBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCv`,`tBDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDv`,`tBEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEv` /* generated by ddl recovery */ +"engine: aria crash point: ddl_log_drop_before_binlog position: 1" +master-bin.000002 # Query # # DROP DATABASE IF EXISTS `test2` /* generated by ddl recovery */ +Warnings: +Note 1008 Can't drop database 'test2'; database doesn't exist diff --git a/mysql-test/suite/atomic/drop_db_long_names.test b/mysql-test/suite/atomic/drop_db_long_names.test new file mode 100644 index 00000000000..c96e59fb4b9 --- /dev/null +++ b/mysql-test/suite/atomic/drop_db_long_names.test @@ -0,0 +1,108 @@ +--source include/have_debug.inc +--source include/have_log_bin.inc +--source include/not_valgrind.inc + +# +# Testing of atomic DROP DATABASE when the generated query could be too long +# + +let $engine_count=1; +let $engines='aria'; + +let $crash_count=2; +let $crash_points='ddl_log_drop_after_drop_tables','ddl_log_drop_before_binlog'; + +let $max_tables=30; + +let $old_debug=`select @@debug_dbug`; + +let $keep_include_silent=1; +let $grep_script=DROP; +--disable_query_log + +let $e=0; +while ($e < $engine_count) +{ + inc $e; + let $engine=`select ELT($e, $engines)`; + let $default_engine=$engine; + let $extra_option=; + + if ($engine == "aria") + { + let $extra_option=transactional=1; + } + if ($engine == "aria_notrans") + { + let $default_engine="aria"; + let $extra_option=transactional=0; + } + + let $c=0; + while ($c < $crash_count) + { + inc $c; + let $crash=`select ELT($c, $crash_points)`; + let $r=0; + while ($r < 1) + { + inc $r; + create database test2; + use test2; + --eval set @@default_storage_engine=$default_engine + let $t=0; + while ($t < $max_tables) + { + inc $t; + let $name=`select concat("t",char(floor(65+$t/26)),repeat(char(65+mod($t,26)),60))`; + let $view=`select concat("t",char(floor(65+$t/26)),repeat(char(65+mod($t,26)),30),'v')`; + --eval create table $name (a int not null) $extra_option + --eval create view $view as select * from $name + } + + flush tables; + use test; + + RESET MASTER; + + echo "engine: $engine crash point: $crash position: $r"; + --exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect + --disable_reconnect + --eval set @@debug_dbug="+d,$crash",@debug_crash_counter=$r + let $errno=0; + --error 0,2013 + DROP DATABASE test2; + let $error=$errno; + --enable_reconnect + --source include/wait_until_connected_again.inc + --disable_query_log + --eval set @@debug_dbug="$old_debug" + + if ($error == 0) + { + echo "No crash!"; + } + use test; + # Check which tables still exists + --error 0,1 + --list_files $MYSQLD_DATADIR/test2 t* + --error 0,ER_SP_DOES_NOT_EXIST + + --let $binlog_file=master-bin.000001 + --source include/show_binlog_events.inc + if ($error) + { + --let $binlog_file=master-bin.000002 + --source include/show_binlog_events.inc + } + # Really drop the tables. The warnings will show what was dropped + --disable_warnings + --error 0, ER_DB_DROP_EXISTS + DROP DATABASE test2; + --enable_warnings + } + } +} +drop database if exists test2; + +--enable_query_log diff --git a/mysql-test/suite/atomic/drop_sequence.result b/mysql-test/suite/atomic/drop_sequence.result index 6eb7d68961c..64ed2dd5698 100644 --- a/mysql-test/suite/atomic/drop_sequence.result +++ b/mysql-test/suite/atomic/drop_sequence.result @@ -1,104 +1,105 @@ call mtr.add_suppression("InnoDB: .* does not exist in the InnoDB internal"); +create database test2; "engine: aria crash point: ddl_log_drop_before_delete_table position: 1" +ts.MAD +ts.MAI +ts.frm t2.MAD t2.MAI t2.TRG t2.frm t2_trg.TRN -ts.MAD -ts.MAI -ts.frm -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1` /* generated by ddl recovery */ "engine: aria crash point: ddl_log_drop_before_delete_table position: 2" ts.MAD ts.MAI ts.frm -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1`,`test2`.`t2` /* generated by ddl recovery */ "engine: aria crash point: ddl_log_drop_before_delete_table position: 3" -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2`,`test`.`ts` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1`,`test2`.`t2`,`ts` /* generated by ddl recovery */ "engine: aria crash point: ddl_log_drop_after_delete_table position: 1" +ts.MAD +ts.MAI +ts.frm t2.MAD t2.MAI t2.TRG t2.frm t2_trg.TRN -ts.MAD -ts.MAI -ts.frm -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1` /* generated by ddl recovery */ "engine: aria crash point: ddl_log_drop_after_delete_table position: 2" ts.MAD ts.MAI ts.frm -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1`,`test2`.`t2` /* generated by ddl recovery */ "engine: aria crash point: ddl_log_drop_after_delete_table position: 3" -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2`,`test`.`ts` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1`,`test2`.`t2`,`ts` /* generated by ddl recovery */ "engine: aria crash point: ddl_log_drop_before_drop_trigger position: 1" +ts.MAD +ts.MAI +ts.frm t2.MAD t2.MAI t2.TRG t2.frm t2_trg.TRN -ts.MAD -ts.MAI -ts.frm -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1` /* generated by ddl recovery */ "engine: aria crash point: ddl_log_drop_before_drop_trigger position: 2" ts.MAD ts.MAI ts.frm -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1`,`test2`.`t2` /* generated by ddl recovery */ "engine: aria crash point: ddl_log_drop_before_drop_trigger position: 3" -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2`,`test`.`ts` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1`,`test2`.`t2`,`ts` /* generated by ddl recovery */ "engine: aria crash point: ddl_log_drop_before_drop_trigger2 position: 1" +ts.MAD +ts.MAI +ts.frm t2.MAD t2.MAI t2.TRG t2.frm t2_trg.TRN -ts.MAD -ts.MAI -ts.frm -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1` /* generated by ddl recovery */ "engine: aria crash point: ddl_log_drop_before_drop_trigger2 position: 2" ts.MAD ts.MAI ts.frm -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1`,`test2`.`t2` /* generated by ddl recovery */ "engine: aria crash point: ddl_log_drop_before_drop_trigger2 position: 3" -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2`,`test`.`ts` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1`,`test2`.`t2`,`ts` /* generated by ddl recovery */ "engine: aria crash point: ddl_log_drop_after_drop_trigger position: 1" +ts.MAD +ts.MAI +ts.frm t2.MAD t2.MAI t2.TRG t2.frm t2_trg.TRN -ts.MAD -ts.MAI -ts.frm -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1` /* generated by ddl recovery */ "engine: aria crash point: ddl_log_drop_after_drop_trigger position: 2" ts.MAD ts.MAI ts.frm -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1`,`test2`.`t2` /* generated by ddl recovery */ "engine: aria crash point: ddl_log_drop_after_drop_trigger position: 3" -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2`,`test`.`ts` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1`,`test2`.`t2`,`ts` /* generated by ddl recovery */ "engine: aria crash point: ddl_log_drop_before_binlog position: 1" -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2`,`test`.`ts` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1`,`test2`.`t2`,`ts` /* generated by ddl recovery */ "engine: aria crash point: ddl_log_drop_before_binlog position: 2" "No crash!" -master-bin.000001 # Query # # use `test`; DROP TABLE `t1`,`t2`,`ts` /* generated by server */ +master-bin.000001 # Query # # use `test`; DROP TABLE `t1`,`test2`.`t2`,`ts` /* generated by server */ "engine: aria crash point: ddl_log_drop_before_binlog position: 3" "No crash!" -master-bin.000001 # Query # # use `test`; DROP TABLE `t1`,`t2`,`ts` /* generated by server */ +master-bin.000001 # Query # # use `test`; DROP TABLE `t1`,`test2`.`t2`,`ts` /* generated by server */ "engine: aria crash point: ddl_log_drop_after_binlog position: 1" -master-bin.000001 # Query # # use `test`; DROP TABLE `t1`,`t2`,`ts` /* generated by server */ +master-bin.000001 # Query # # use `test`; DROP TABLE `t1`,`test2`.`t2`,`ts` /* generated by server */ "engine: aria crash point: ddl_log_drop_after_binlog position: 2" "No crash!" -master-bin.000001 # Query # # use `test`; DROP TABLE `t1`,`t2`,`ts` /* generated by server */ +master-bin.000001 # Query # # use `test`; DROP TABLE `t1`,`test2`.`t2`,`ts` /* generated by server */ "engine: aria crash point: ddl_log_drop_after_binlog position: 3" "No crash!" -master-bin.000001 # Query # # use `test`; DROP TABLE `t1`,`t2`,`ts` /* generated by server */ +master-bin.000001 # Query # # use `test`; DROP TABLE `t1`,`test2`.`t2`,`ts` /* generated by server */ Warnings: -Note 1051 Unknown table 'test.t1,test.t2,test.ts' +Note 1051 Unknown table 'test.t1,test2.t2,test.ts' diff --git a/mysql-test/suite/atomic/drop_sequence.test b/mysql-test/suite/atomic/drop_sequence.test index b2fd60e8649..a7a398a1200 100644 --- a/mysql-test/suite/atomic/drop_sequence.test +++ b/mysql-test/suite/atomic/drop_sequence.test @@ -4,11 +4,14 @@ # # Testing of atomic drop with crashes in a lot of different places +# We also test having the tables in different databases # call mtr.add_suppression("InnoDB: .* does not exist in the InnoDB internal"); let $MYSQLD_DATADIR= `SELECT @@datadir`; +create database test2; + let $engine_count=1; let $engines='aria'; @@ -53,10 +56,10 @@ while ($e < $engine_count) inc $r; --eval set @@default_storage_engine=$default_engine --eval create table t1 (a int not null) $extra_option; - --eval create table t2 (b int not null) $extra_option; + --eval create table test2.t2 (b int not null) $extra_option; create sequence ts; insert into t1 values(1); - insert into t2 values(2); + insert into test2.t2 values(2); flush tables; delimiter |; @@ -66,7 +69,7 @@ while ($e < $engine_count) set new.a:= 1000; end if; end| - create trigger t2_trg before insert on t2 for each row + create trigger test2.t2_trg before insert on test2.t2 for each row begin if isnull(new.b) then set new.b:= 2000; @@ -82,7 +85,7 @@ while ($e < $engine_count) --eval set @@debug_dbug="+d,$crash",@debug_crash_counter=$r let $errno=0; --error 0,2013 - drop table t1,t2,ts; + drop table t1,test2.t2,ts; let $error=$errno; --enable_reconnect --source include/wait_until_connected_again.inc @@ -95,6 +98,7 @@ while ($e < $engine_count) } # Check which tables still exists --list_files $MYSQLD_DATADIR/test t* + --list_files $MYSQLD_DATADIR/test2 t* --let $binlog_file=master-bin.000001 --source include/show_binlog_events.inc @@ -105,11 +109,11 @@ while ($e < $engine_count) } # Really drop the tables. The warnings will show what was dropped --disable_warnings - drop table if exists t1,t2,ts; + drop table if exists t1,test2.t2,ts; --enable_warnings } } } -drop table if exists t1,t2,ts; - +drop table if exists t1,test2.t2,ts; +drop database test2; --enable_query_log diff --git a/mysql-test/suite/atomic/drop_table.result b/mysql-test/suite/atomic/drop_table.result index 50c9708341f..fbff9464e2e 100644 --- a/mysql-test/suite/atomic/drop_table.result +++ b/mysql-test/suite/atomic/drop_table.result @@ -5,47 +5,47 @@ t2.MYI t2.TRG t2.frm t2_trg.TRN -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1` /* generated by ddl recovery */ "engine: myisam crash point: ddl_log_drop_before_delete_table position: 2" -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1`,`t2` /* generated by ddl recovery */ "engine: myisam crash point: ddl_log_drop_after_delete_table position: 1" t2.MYD t2.MYI t2.TRG t2.frm t2_trg.TRN -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1` /* generated by ddl recovery */ "engine: myisam crash point: ddl_log_drop_after_delete_table position: 2" -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1`,`t2` /* generated by ddl recovery */ "engine: myisam crash point: ddl_log_drop_before_drop_trigger position: 1" t2.MYD t2.MYI t2.TRG t2.frm t2_trg.TRN -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1` /* generated by ddl recovery */ "engine: myisam crash point: ddl_log_drop_before_drop_trigger position: 2" -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1`,`t2` /* generated by ddl recovery */ "engine: myisam crash point: ddl_log_drop_before_drop_trigger2 position: 1" t2.MYD t2.MYI t2.TRG t2.frm t2_trg.TRN -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1` /* generated by ddl recovery */ "engine: myisam crash point: ddl_log_drop_before_drop_trigger2 position: 2" -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1`,`t2` /* generated by ddl recovery */ "engine: myisam crash point: ddl_log_drop_after_drop_trigger position: 1" t2.MYD t2.MYI t2.TRG t2.frm t2_trg.TRN -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1` /* generated by ddl recovery */ "engine: myisam crash point: ddl_log_drop_after_drop_trigger position: 2" -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1`,`t2` /* generated by ddl recovery */ "engine: myisam crash point: ddl_log_drop_before_binlog position: 1" -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1`,`t2` /* generated by ddl recovery */ "engine: myisam crash point: ddl_log_drop_before_binlog position: 2" "No crash!" master-bin.000001 # Query # # use `test`; DROP TABLE `t1`,`t2` /* generated by server */ @@ -60,47 +60,47 @@ t2.MAI t2.TRG t2.frm t2_trg.TRN -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1` /* generated by ddl recovery */ "engine: aria crash point: ddl_log_drop_before_delete_table position: 2" -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1`,`t2` /* generated by ddl recovery */ "engine: aria crash point: ddl_log_drop_after_delete_table position: 1" t2.MAD t2.MAI t2.TRG t2.frm t2_trg.TRN -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1` /* generated by ddl recovery */ "engine: aria crash point: ddl_log_drop_after_delete_table position: 2" -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1`,`t2` /* generated by ddl recovery */ "engine: aria crash point: ddl_log_drop_before_drop_trigger position: 1" t2.MAD t2.MAI t2.TRG t2.frm t2_trg.TRN -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1` /* generated by ddl recovery */ "engine: aria crash point: ddl_log_drop_before_drop_trigger position: 2" -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1`,`t2` /* generated by ddl recovery */ "engine: aria crash point: ddl_log_drop_before_drop_trigger2 position: 1" t2.MAD t2.MAI t2.TRG t2.frm t2_trg.TRN -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1` /* generated by ddl recovery */ "engine: aria crash point: ddl_log_drop_before_drop_trigger2 position: 2" -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1`,`t2` /* generated by ddl recovery */ "engine: aria crash point: ddl_log_drop_after_drop_trigger position: 1" t2.MAD t2.MAI t2.TRG t2.frm t2_trg.TRN -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1` /* generated by ddl recovery */ "engine: aria crash point: ddl_log_drop_after_drop_trigger position: 2" -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1`,`t2` /* generated by ddl recovery */ "engine: aria crash point: ddl_log_drop_before_binlog position: 1" -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1`,`t2` /* generated by ddl recovery */ "engine: aria crash point: ddl_log_drop_before_binlog position: 2" "No crash!" master-bin.000001 # Query # # use `test`; DROP TABLE `t1`,`t2` /* generated by server */ @@ -115,47 +115,47 @@ t2.MAI t2.TRG t2.frm t2_trg.TRN -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1` /* generated by ddl recovery */ "engine: aria_notrans crash point: ddl_log_drop_before_delete_table position: 2" -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1`,`t2` /* generated by ddl recovery */ "engine: aria_notrans crash point: ddl_log_drop_after_delete_table position: 1" t2.MAD t2.MAI t2.TRG t2.frm t2_trg.TRN -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1` /* generated by ddl recovery */ "engine: aria_notrans crash point: ddl_log_drop_after_delete_table position: 2" -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1`,`t2` /* generated by ddl recovery */ "engine: aria_notrans crash point: ddl_log_drop_before_drop_trigger position: 1" t2.MAD t2.MAI t2.TRG t2.frm t2_trg.TRN -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1` /* generated by ddl recovery */ "engine: aria_notrans crash point: ddl_log_drop_before_drop_trigger position: 2" -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1`,`t2` /* generated by ddl recovery */ "engine: aria_notrans crash point: ddl_log_drop_before_drop_trigger2 position: 1" t2.MAD t2.MAI t2.TRG t2.frm t2_trg.TRN -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1` /* generated by ddl recovery */ "engine: aria_notrans crash point: ddl_log_drop_before_drop_trigger2 position: 2" -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1`,`t2` /* generated by ddl recovery */ "engine: aria_notrans crash point: ddl_log_drop_after_drop_trigger position: 1" t2.MAD t2.MAI t2.TRG t2.frm t2_trg.TRN -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1` /* generated by ddl recovery */ "engine: aria_notrans crash point: ddl_log_drop_after_drop_trigger position: 2" -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1`,`t2` /* generated by ddl recovery */ "engine: aria_notrans crash point: ddl_log_drop_before_binlog position: 1" -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1`,`t2` /* generated by ddl recovery */ "engine: aria_notrans crash point: ddl_log_drop_before_binlog position: 2" "No crash!" master-bin.000001 # Query # # use `test`; DROP TABLE `t1`,`t2` /* generated by server */ @@ -169,43 +169,43 @@ t2.TRG t2.frm t2.ibd t2_trg.TRN -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1` /* generated by ddl recovery */ "engine: innodb crash point: ddl_log_drop_before_delete_table position: 2" -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1`,`t2` /* generated by ddl recovery */ "engine: innodb crash point: ddl_log_drop_after_delete_table position: 1" t2.TRG t2.frm t2.ibd t2_trg.TRN -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1` /* generated by ddl recovery */ "engine: innodb crash point: ddl_log_drop_after_delete_table position: 2" -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1`,`t2` /* generated by ddl recovery */ "engine: innodb crash point: ddl_log_drop_before_drop_trigger position: 1" t2.TRG t2.frm t2.ibd t2_trg.TRN -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1` /* generated by ddl recovery */ "engine: innodb crash point: ddl_log_drop_before_drop_trigger position: 2" -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1`,`t2` /* generated by ddl recovery */ "engine: innodb crash point: ddl_log_drop_before_drop_trigger2 position: 1" t2.TRG t2.frm t2.ibd t2_trg.TRN -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1` /* generated by ddl recovery */ "engine: innodb crash point: ddl_log_drop_before_drop_trigger2 position: 2" -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1`,`t2` /* generated by ddl recovery */ "engine: innodb crash point: ddl_log_drop_after_drop_trigger position: 1" t2.TRG t2.frm t2.ibd t2_trg.TRN -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1` /* generated by ddl recovery */ "engine: innodb crash point: ddl_log_drop_after_drop_trigger position: 2" -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1`,`t2` /* generated by ddl recovery */ "engine: innodb crash point: ddl_log_drop_before_binlog position: 1" -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1`,`t2` /* generated by ddl recovery */ "engine: innodb crash point: ddl_log_drop_before_binlog position: 2" "No crash!" master-bin.000001 # Query # # use `test`; DROP TABLE `t1`,`t2` /* generated by server */ @@ -220,47 +220,47 @@ t2.CSV t2.TRG t2.frm t2_trg.TRN -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1` /* generated by ddl recovery */ "engine: csv crash point: ddl_log_drop_before_delete_table position: 2" -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1`,`t2` /* generated by ddl recovery */ "engine: csv crash point: ddl_log_drop_after_delete_table position: 1" t2.CSM t2.CSV t2.TRG t2.frm t2_trg.TRN -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1` /* generated by ddl recovery */ "engine: csv crash point: ddl_log_drop_after_delete_table position: 2" -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1`,`t2` /* generated by ddl recovery */ "engine: csv crash point: ddl_log_drop_before_drop_trigger position: 1" t2.CSM t2.CSV t2.TRG t2.frm t2_trg.TRN -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1` /* generated by ddl recovery */ "engine: csv crash point: ddl_log_drop_before_drop_trigger position: 2" -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1`,`t2` /* generated by ddl recovery */ "engine: csv crash point: ddl_log_drop_before_drop_trigger2 position: 1" t2.CSM t2.CSV t2.TRG t2.frm t2_trg.TRN -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1` /* generated by ddl recovery */ "engine: csv crash point: ddl_log_drop_before_drop_trigger2 position: 2" -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1`,`t2` /* generated by ddl recovery */ "engine: csv crash point: ddl_log_drop_after_drop_trigger position: 1" t2.CSM t2.CSV t2.TRG t2.frm t2_trg.TRN -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1` /* generated by ddl recovery */ "engine: csv crash point: ddl_log_drop_after_drop_trigger position: 2" -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1`,`t2` /* generated by ddl recovery */ "engine: csv crash point: ddl_log_drop_before_binlog position: 1" -master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP TABLE IF EXISTS `t1`,`t2` /* generated by ddl recovery */ "engine: csv crash point: ddl_log_drop_before_binlog position: 2" "No crash!" master-bin.000001 # Query # # use `test`; DROP TABLE `t1`,`t2` /* generated by server */ diff --git a/mysql-test/suite/atomic/drop_view.result b/mysql-test/suite/atomic/drop_view.result index b2009fa41b6..b71b088b1a5 100644 --- a/mysql-test/suite/atomic/drop_view.result +++ b/mysql-test/suite/atomic/drop_view.result @@ -1,15 +1,15 @@ "engine: aria crash point: ddl_log_drop_before_delete_view position: 1" v2.frm -master-bin.000002 # Query # # DROP VIEW IF EXISTS `test`.`v1` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP VIEW IF EXISTS `v1` /* generated by ddl recovery */ "engine: aria crash point: ddl_log_drop_before_delete_view position: 2" -master-bin.000002 # Query # # DROP VIEW IF EXISTS `test`.`v1`,`test`.`v2` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP VIEW IF EXISTS `v1`,`v2` /* generated by ddl recovery */ "engine: aria crash point: ddl_log_drop_after_delete_view position: 1" v2.frm -master-bin.000002 # Query # # DROP VIEW IF EXISTS `test`.`v1` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP VIEW IF EXISTS `v1` /* generated by ddl recovery */ "engine: aria crash point: ddl_log_drop_after_delete_view position: 2" -master-bin.000002 # Query # # DROP VIEW IF EXISTS `test`.`v1`,`test`.`v2` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP VIEW IF EXISTS `v1`,`v2` /* generated by ddl recovery */ "engine: aria crash point: ddl_log_drop_before_binlog position: 1" -master-bin.000002 # Query # # DROP VIEW IF EXISTS `test`.`v1`,`test`.`v2` /* generated by ddl recovery */ +master-bin.000002 # Query # # use `test`; DROP VIEW IF EXISTS `v1`,`v2` /* generated by ddl recovery */ "engine: aria crash point: ddl_log_drop_before_binlog position: 2" "No crash!" master-bin.000001 # Query # # use `test`; DROP VIEW v1,v2 diff --git a/sql/ddl_log.cc b/sql/ddl_log.cc index ebede9cd414..f6f4a3f43f2 100644 --- a/sql/ddl_log.cc +++ b/sql/ddl_log.cc @@ -27,6 +27,7 @@ #include "sql_view.h" // mysql_rename_view() #include "strfunc.h" // strconvert #include "sql_show.h" // append_identifier() +#include "sql_db.h" // drop_database_objects() #include <mysys_err.h> // EE_LINK @@ -88,13 +89,16 @@ const char *ddl_log_action_name[DDL_LOG_LAST_ACTION]= "partitioning replace", "partitioning exchange", "rename table", "rename view", "initialize drop table", "drop table", - "initialize drop view", "drop view", "drop trigger", + "drop view", "drop trigger", "drop db", }; /* Number of phases per entry */ const uchar ddl_log_entry_phases[DDL_LOG_LAST_ACTION]= { - 0, 1, 1, 2, 3, 4, 1, 1, 3, 1, 1, 1 + 0, 1, 1, 2, + (uchar) EXCH_PHASE_END, (uchar) DDL_RENAME_PHASE_END, 1, 1, + (uchar) DDL_DROP_PHASE_END, 1, 1, + (uchar) DDL_DROP_DB_PHASE_END }; @@ -111,8 +115,17 @@ struct st_global_ddl_log bool open; }; -st_global_ddl_log global_ddl_log; -String ddl_drop_query; // Used during startup recovery +/* The following structure is only used during startup recovery */ +class st_ddl_recovery { +public: + String drop_table; + String drop_view; + size_t drop_table_init_length, drop_view_init_length; + char current_db[NAME_LEN]; +}; + +static st_global_ddl_log global_ddl_log; +static st_ddl_recovery recovery_state; mysql_mutex_t LOCK_gdl; @@ -786,6 +799,8 @@ static bool ddl_log_increment_phase_no_lock(uint entry_pos) Ignore errors from the file system about: - Non existing tables or file (from drop table or delete file) - Error about tables files that already exists. + - Error from delete table (from Drop_table_error_handler) + - Wrong trigger definer (from Drop_table_error_handler) */ class ddl_log_error_handler : public Internal_error_handler @@ -793,8 +808,10 @@ class ddl_log_error_handler : public Internal_error_handler public: int handled_errors; int unhandled_errors; + int first_error; - ddl_log_error_handler() : handled_errors(0), unhandled_errors(0) + ddl_log_error_handler() : handled_errors(0), unhandled_errors(0), + first_error(0) {} bool handle_condition(THD *thd, @@ -805,11 +822,14 @@ public: Sql_condition ** cond_hdl) { *cond_hdl= NULL; - if (non_existing_table_error(sql_errno) || sql_errno == EE_LINK) + if (non_existing_table_error(sql_errno) || sql_errno == EE_LINK || + sql_errno == EE_DELETE || sql_errno == ER_TRG_NO_DEFINER) { handled_errors++; return TRUE; } + if (!first_error) + first_error= sql_errno; if (*level == Sql_condition::WARN_LEVEL_ERROR) unhandled_errors++; @@ -848,6 +868,77 @@ static bool build_filename_and_delete_tmp_file(char *path, size_t path_length, } +static LEX_CSTRING end_comment= +{ STRING_WITH_LEN(" /* generated by ddl recovery */")}; + + +/** + Log DROP query to binary log with comment + + This function is only run during recovery +*/ + +static void ddl_log_to_binary_log(THD *thd, String *query) +{ + LEX_CSTRING thd_db= thd->db; + + lex_string_set(&thd->db, recovery_state.current_db); + query->length(query->length()-1); // Removed end ',' + query->append(&end_comment); + mysql_mutex_unlock(&LOCK_gdl); + (void) thd->binlog_query(THD::STMT_QUERY_TYPE, + query->ptr(), query->length(), + TRUE, FALSE, FALSE, 0); + mysql_mutex_lock(&LOCK_gdl); + thd->db= thd_db; +} + + +/** + Log DROP TABLE/VIEW to binary log when needed + + @result 0 Nothing was done + @result 1 Query was logged to binary log & query was reset + + Logging happens in the following cases + - This is the last DROP entry + - The query could be longer than max_packet_length if we would add another + table name to the query + + When we log, we always log all found tables and views at the same time. This + is done to simply the exceute code as otherwise we would have to keep + information of what was logged. +*/ + +static bool ddl_log_drop_to_binary_log(THD *thd, DDL_LOG_ENTRY *ddl_log_entry, + String *query) +{ + DBUG_ENTER("ddl_log_binary_log"); + if (mysql_bin_log.is_open()) + { + if (!ddl_log_entry->next_entry || + query->length() + end_comment.length + NAME_LEN + 100 > + thd->variables.max_allowed_packet) + { + if (recovery_state.drop_table.length() > + recovery_state.drop_table_init_length) + { + ddl_log_to_binary_log(thd, &recovery_state.drop_table); + recovery_state.drop_table.length(recovery_state.drop_table_init_length); + } + if (recovery_state.drop_view.length() > + recovery_state.drop_view_init_length) + { + ddl_log_to_binary_log(thd, &recovery_state.drop_view); + recovery_state.drop_view.length(recovery_state.drop_view_init_length); + } + DBUG_RETURN(1); + } + } + DBUG_RETURN(0); +} + + /** Execute one action in a ddl log entry @@ -858,10 +949,6 @@ static bool build_filename_and_delete_tmp_file(char *path, size_t path_length, @retval FALSE Success */ -static LEX_CSTRING end_comment= -{ STRING_WITH_LEN(" /* generated by ddl log */")}; - - static int ddl_log_execute_action(THD *thd, MEM_ROOT *mem_root, DDL_LOG_ENTRY *ddl_log_entry) { @@ -877,10 +964,11 @@ static int ddl_log_execute_action(THD *thd, MEM_ROOT *mem_root, mysql_mutex_assert_owner(&LOCK_gdl); DBUG_PRINT("ddl_log", - ("entry type: %u action type: %u phase: %u next: %u " + ("entry type: %u action type: %u (%s) phase: %u next: %u " "handler: '%s' name: '%s' from_name: '%s' tmp_name: '%s'", (uint) ddl_log_entry->entry_type, (uint) ddl_log_entry->action_type, + ddl_log_action_name[ddl_log_entry->action_type], (uint) ddl_log_entry->phase, ddl_log_entry->next_entry, ddl_log_entry->handler_name.str, @@ -1176,17 +1264,32 @@ static int ddl_log_execute_action(THD *thd, MEM_ROOT *mem_root, (void) update_phase(entry_pos, DDL_LOG_FINAL_PHASE); } break; - case DDL_LOG_DROP_TABLE_INIT_ACTION: + /* + Initialize variables for DROP TABLE and DROP VIEW + In normal cases a query only contains one action. However in case of + DROP DATABASE we may get a mix of both and we have to keep these + separate. + */ + case DDL_LOG_DROP_INIT_ACTION: { LEX_CSTRING *comment= &ddl_log_entry->tmp_name; - ddl_drop_query.length(0); - ddl_drop_query.set_charset(system_charset_info); - ddl_drop_query.append(STRING_WITH_LEN("DROP TABLE IF EXISTS ")); + recovery_state.drop_table.length(0); + recovery_state.drop_table.set_charset(system_charset_info); + recovery_state.drop_table.append(STRING_WITH_LEN("DROP TABLE IF EXISTS ")); if (comment->length) { - ddl_drop_query.append(comment); - ddl_drop_query.append(' '); + recovery_state.drop_table.append(comment); + recovery_state.drop_table.append(' '); } + recovery_state.drop_table_init_length= recovery_state.drop_table.length(); + + recovery_state.drop_view.length(0); + recovery_state.drop_view.set_charset(system_charset_info); + recovery_state.drop_view.append(STRING_WITH_LEN("DROP VIEW IF EXISTS ")); + recovery_state.drop_view_init_length= recovery_state.drop_view.length(); + + strmake(recovery_state.current_db, + ddl_log_entry->from_db.str, sizeof(recovery_state.current_db)-1); /* We don't increment phase as we want to retry this in case of crash */ break; } @@ -1230,36 +1333,30 @@ static int ddl_log_execute_action(THD *thd, MEM_ROOT *mem_root, /* Fall through */ case DDL_DROP_PHASE_BINLOG: - append_identifier(thd, &ddl_drop_query, &db); - ddl_drop_query.append('.'); - append_identifier(thd, &ddl_drop_query, &table); - ddl_drop_query.append(','); + if (strcmp(recovery_state.current_db, db.str)) + { + append_identifier(thd, &recovery_state.drop_table, &db); + recovery_state.drop_table.append('.'); + } + append_identifier(thd, &recovery_state.drop_table, &table); + recovery_state.drop_table.append(','); /* We don't increment phase as we want to retry this in case of crash */ - if (!ddl_log_entry->next_entry && mysql_bin_log.is_open()) + if (ddl_log_drop_to_binary_log(thd, ddl_log_entry, + &recovery_state.drop_table)) { - /* Last drop table. Write query to binlog */ - ddl_drop_query.length(ddl_drop_query.length()-1); - ddl_drop_query.append(&end_comment); - - mysql_mutex_unlock(&LOCK_gdl); - (void) thd->binlog_query(THD::STMT_QUERY_TYPE, ddl_drop_query.ptr(), - ddl_drop_query.length(), TRUE, FALSE, - FALSE, 0); - mysql_mutex_lock(&LOCK_gdl); + if (ddl_log_increment_phase_no_lock(entry_pos)) + break; } break; + case DDL_DROP_PHASE_RESET: + /* We have already logged all previous drop's. Clear the query */ + recovery_state.drop_table.length(recovery_state.drop_table_init_length); + recovery_state.drop_view.length(recovery_state.drop_view_init_length); + break; } break; } - case DDL_LOG_DROP_VIEW_INIT_ACTION: - { - ddl_drop_query.length(0); - ddl_drop_query.set_charset(system_charset_info); - ddl_drop_query.append(STRING_WITH_LEN("DROP VIEW IF EXISTS ")); - /* We don't increment phase as we want to retry this in case of crash */ - break; - } case DDL_LOG_DROP_VIEW_ACTION: { LEX_CSTRING db, table, path; @@ -1268,23 +1365,29 @@ static int ddl_log_execute_action(THD *thd, MEM_ROOT *mem_root, /* Note that for views path is WITH .frm extension */ path= ddl_log_entry->tmp_name; - mysql_file_delete(key_file_frm, path.str, MYF(MY_WME|MY_IGNORE_ENOENT)); - append_identifier(thd, &ddl_drop_query, &db); - ddl_drop_query.append('.'); - append_identifier(thd, &ddl_drop_query, &table); - ddl_drop_query.append(','); + if (ddl_log_entry->phase == 0) + { + mysql_file_delete(key_file_frm, path.str, MYF(MY_WME|MY_IGNORE_ENOENT)); + if (strcmp(recovery_state.current_db, db.str)) + { + append_identifier(thd, &recovery_state.drop_view, &db); + recovery_state.drop_view.append('.'); + } + append_identifier(thd, &recovery_state.drop_view, &table); + recovery_state.drop_view.append(','); - if (!ddl_log_entry->next_entry && mysql_bin_log.is_open()) + if (ddl_log_drop_to_binary_log(thd, ddl_log_entry, + &recovery_state.drop_view)) + { + if (ddl_log_increment_phase_no_lock(entry_pos)) + break; + } + } + else { - /* Last drop view. Write query to binlog */ - ddl_drop_query.length(ddl_drop_query.length()-1); - ddl_drop_query.append(&end_comment); - - mysql_mutex_unlock(&LOCK_gdl); - (void) thd->binlog_query(THD::STMT_QUERY_TYPE, ddl_drop_query.ptr(), - ddl_drop_query.length(), TRUE, FALSE, - FALSE, 0); - mysql_mutex_lock(&LOCK_gdl); + /* We have already logged all previous drop's. Clear the query */ + recovery_state.drop_table.length(recovery_state.drop_table_init_length); + recovery_state.drop_view.length(recovery_state.drop_table_init_length); } break; } @@ -1323,27 +1426,29 @@ static int ddl_log_execute_action(THD *thd, MEM_ROOT *mem_root, &ddl_log_entry->from_name, MYF(0)); - ddl_drop_query.length(0); - ddl_drop_query.set_charset(system_charset_info); + recovery_state.drop_table.length(0); + recovery_state.drop_table.set_charset(system_charset_info); if (ddl_log_entry->tmp_name.length) { /* We can use the original query */ - ddl_drop_query.append(&ddl_log_entry->tmp_name); + recovery_state.drop_table.append(&ddl_log_entry->tmp_name); } else { /* Generate new query */ - ddl_drop_query.append(STRING_WITH_LEN("DROP TRIGGER IF EXISTS ")); - append_identifier(thd, &ddl_drop_query, &ddl_log_entry->from_name); - ddl_drop_query.append(&end_comment); + recovery_state.drop_table.append(STRING_WITH_LEN("DROP TRIGGER IF " + "EXISTS ")); + append_identifier(thd, &recovery_state.drop_table, + &ddl_log_entry->from_name); + recovery_state.drop_table.append(&end_comment); } if (mysql_bin_log.is_open()) { mysql_mutex_unlock(&LOCK_gdl); thd->db= ddl_log_entry->db; (void) thd->binlog_query(THD::STMT_QUERY_TYPE, - ddl_drop_query.ptr(), - ddl_drop_query.length(), TRUE, FALSE, + recovery_state.drop_table.ptr(), + recovery_state.drop_table.length(), TRUE, FALSE, FALSE, 0); thd->db= thd_db; mysql_mutex_lock(&LOCK_gdl); @@ -1352,6 +1457,48 @@ static int ddl_log_execute_action(THD *thd, MEM_ROOT *mem_root, (void) update_phase(entry_pos, DDL_LOG_FINAL_PHASE); break; } + case DDL_LOG_DROP_DB_ACTION: + { + LEX_CSTRING db, path; + db= ddl_log_entry->db; + path= ddl_log_entry->tmp_name; + + switch (ddl_log_entry->phase) { + case DDL_DROP_DB_PHASE_INIT: + drop_database_objects(thd, &path, &db, + !my_strcasecmp(system_charset_info, + MYSQL_SCHEMA_NAME.str, db.str)); + + strxnmov(to_path, sizeof(to_path)-1, path.str, MY_DB_OPT_FILE, NullS); + mysql_file_delete_with_symlink(key_file_misc, to_path, "", MYF(0)); + + (void) rm_dir_w_symlink(path.str, 0); + if (ddl_log_increment_phase_no_lock(entry_pos)) + break; + /* Fall through */ + case DDL_DROP_DB_PHASE_LOG: + { + String *query= &recovery_state.drop_table; + + query->length(0); + query->append(STRING_WITH_LEN("DROP DATABASE IF EXISTS ")); + append_identifier(thd, query, &db); + query->append(&end_comment); + + if (mysql_bin_log.is_open()) + { + mysql_mutex_unlock(&LOCK_gdl); + (void) thd->binlog_query(THD::STMT_QUERY_TYPE, + query->ptr(), query->length(), + TRUE, FALSE, FALSE, 0); + mysql_mutex_lock(&LOCK_gdl); + } + (void) update_phase(entry_pos, DDL_LOG_FINAL_PHASE); + break; + } + } + break; + } default: DBUG_ASSERT(0); break; @@ -1359,7 +1506,8 @@ static int ddl_log_execute_action(THD *thd, MEM_ROOT *mem_root, end: delete file; - error= no_such_table_handler.unhandled_errors > 0; + if ((error= no_such_table_handler.unhandled_errors > 0)) + my_errno= no_such_table_handler.first_error; thd->pop_internal_handler(); DBUG_RETURN(error); } @@ -1517,7 +1665,6 @@ bool ddl_log_write_entry(DDL_LOG_ENTRY *ddl_log_entry, DDL_LOG_MEMORY_ENTRY **active_entry) { bool error; - uchar *pos, *end; DBUG_ENTER("ddl_log_write_entry"); *active_entry= 0; @@ -1535,20 +1682,18 @@ bool ddl_log_write_entry(DDL_LOG_ENTRY *ddl_log_entry, DBUG_RETURN(TRUE); error= FALSE; - pos= global_ddl_log.file_entry_buf + global_ddl_log.name_pos; - end= global_ddl_log.file_entry_buf + global_ddl_log.io_size; DBUG_PRINT("ddl_log", - ("type: %c next: %u handler: %s " - "to_name: '%s.%s' from_name: '%s.%s' " - "tmp_name: '%s'", - (char) global_ddl_log.file_entry_buf[DDL_LOG_ACTION_TYPE_POS], + ("entry type: %u action type: %u (%s) phase: %u next: %u " + "handler: '%s' name: '%s' from_name: '%s' tmp_name: '%s'", + (uint) ddl_log_entry->entry_type, + (uint) ddl_log_entry->action_type, + ddl_log_action_name[ddl_log_entry->action_type], + (uint) ddl_log_entry->phase, ddl_log_entry->next_entry, - get_string(&pos, end).str, // Handler - get_string(&pos, end).str, // to db.table - get_string(&pos, end).str, - get_string(&pos, end).str, // From db.table - get_string(&pos, end).str, - get_string(&pos, end).str)); // Tmp name + ddl_log_entry->handler_name.str, + ddl_log_entry->name.str, + ddl_log_entry->from_name.str, + ddl_log_entry->tmp_name.str)); if (unlikely(write_ddl_log_file_entry((*active_entry)->entry_pos))) { @@ -1626,8 +1771,9 @@ bool ddl_log_write_execute_entry(uint first_entry, DBUG_RETURN(FALSE); } + /** - Increment phase for enty. Will deactivate entry after all phases are done + Increment phase for entry. Will deactivate entry after all phases are done @details see ddl_log_increment_phase_no_lock. @@ -1790,7 +1936,8 @@ int ddl_log_execute_recovery() thd->store_globals(); thd->init(); // Needed for error messages thd->log_all_errors= (global_system_variables.log_warnings >= 3); - ddl_drop_query.free(); + recovery_state.drop_table.free(); + recovery_state.drop_view.free(); thd->set_query(recover_query_string, strlen(recover_query_string)); @@ -1828,7 +1975,8 @@ int ddl_log_execute_recovery() count++; } } - ddl_drop_query.free(); + recovery_state.drop_table.free(); + recovery_state.drop_view.free(); close_ddl_log(); mysql_mutex_unlock(&LOCK_gdl); thd->reset_query(); @@ -1943,12 +2091,13 @@ void ddl_log_complete(DDL_LOG_STATE *state) ddl_log_disable_execute_entry(&state->execute_entry); ddl_log_release_entries(state); mysql_mutex_unlock(&LOCK_gdl); + state->list= 0; DBUG_VOID_RETURN; }; /** - Revert all entries in the ddl log + Revert (execute) all entries in the ddl log */ void ddl_log_revert(THD *thd, DDL_LOG_STATE *state) @@ -1966,6 +2115,7 @@ void ddl_log_revert(THD *thd, DDL_LOG_STATE *state) } ddl_log_release_entries(state); mysql_mutex_unlock(&LOCK_gdl); + state->list= 0; DBUG_VOID_RETURN; } @@ -2103,6 +2253,7 @@ bool ddl_log_rename_view(THD *thd, DDL_LOG_STATE *ddl_state, static bool ddl_log_drop_init(THD *thd, DDL_LOG_STATE *ddl_state, ddl_log_action_code action_code, + const LEX_CSTRING *db, const LEX_CSTRING *comment) { DDL_LOG_ENTRY ddl_log_entry; @@ -2111,26 +2262,26 @@ static bool ddl_log_drop_init(THD *thd, DDL_LOG_STATE *ddl_state, bzero(&ddl_log_entry, sizeof(ddl_log_entry)); ddl_log_entry.action_type= action_code; - ddl_log_entry.next_entry= 0; + ddl_log_entry.from_db= *const_cast<LEX_CSTRING*>(db); ddl_log_entry.tmp_name= *const_cast<LEX_CSTRING*>(comment); - ddl_log_entry.phase= 0; DBUG_RETURN(ddl_log_write(ddl_state, &ddl_log_entry)); } bool ddl_log_drop_table_init(THD *thd, DDL_LOG_STATE *ddl_state, + const LEX_CSTRING *db, const LEX_CSTRING *comment) { - return ddl_log_drop_init(thd, ddl_state, DDL_LOG_DROP_TABLE_INIT_ACTION, - comment); + return ddl_log_drop_init(thd, ddl_state, DDL_LOG_DROP_INIT_ACTION, + db, comment); } -bool ddl_log_drop_view_init(THD *thd, DDL_LOG_STATE *ddl_state) +bool ddl_log_drop_view_init(THD *thd, DDL_LOG_STATE *ddl_state, + const LEX_CSTRING *db) { - LEX_CSTRING comment= {0,0}; - return ddl_log_drop_init(thd, ddl_state, DDL_LOG_DROP_VIEW_INIT_ACTION, - &comment); + return ddl_log_drop_init(thd, ddl_state, DDL_LOG_DROP_INIT_ACTION, + db, &empty_clex_str); } static bool ddl_log_drop(THD *thd, DDL_LOG_STATE *ddl_state, @@ -2215,7 +2366,7 @@ bool ddl_log_drop_trigger(THD *thd, DDL_LOG_STATE *ddl_state, char path[FN_REFLEN+1]; off_t frm_length= 0; size_t max_query_length; - DBUG_ENTER("ddl_log_drop"); + DBUG_ENTER("ddl_log_drop_trigger"); build_table_filename(path, sizeof(path)-1, db->str, table->str, TRG_EXT, 0); @@ -2242,3 +2393,27 @@ bool ddl_log_drop_trigger(THD *thd, DDL_LOG_STATE *ddl_state, DBUG_RETURN(ddl_log_write(ddl_state, &ddl_log_entry)); } + + +/** + Log DROP DATABASE + + This is logged after all DROP TABLE's for the database. + As now know we are going to log DROP DATABASE to the binary log, we want + to ignore want to ignore all preceding DROP TABLE entries. We do that by + linking this entry directly after the execute entry and forgetting the + link to the previous entries (not setting ddl_log_entry.next_entry) +*/ + +bool ddl_log_drop_db(THD *thd, DDL_LOG_STATE *ddl_state, + const LEX_CSTRING *db, const LEX_CSTRING *path) +{ + DDL_LOG_ENTRY ddl_log_entry; + DBUG_ENTER("ddl_log_drop_db"); + + bzero(&ddl_log_entry, sizeof(ddl_log_entry)); + ddl_log_entry.action_type= DDL_LOG_DROP_DB_ACTION; + ddl_log_entry.db= *const_cast<LEX_CSTRING*>(db); + ddl_log_entry.tmp_name= *const_cast<LEX_CSTRING*>(path); + DBUG_RETURN(ddl_log_write(ddl_state, &ddl_log_entry)); +} diff --git a/sql/ddl_log.h b/sql/ddl_log.h index c3272789638..5aa039f46a5 100644 --- a/sql/ddl_log.h +++ b/sql/ddl_log.h @@ -77,11 +77,11 @@ enum ddl_log_action_code */ DDL_LOG_RENAME_TABLE_ACTION= 5, DDL_LOG_RENAME_VIEW_ACTION= 6, - DDL_LOG_DROP_TABLE_INIT_ACTION= 7, + DDL_LOG_DROP_INIT_ACTION= 7, DDL_LOG_DROP_TABLE_ACTION= 8, - DDL_LOG_DROP_VIEW_INIT_ACTION= 9, - DDL_LOG_DROP_VIEW_ACTION= 10, - DDL_LOG_DROP_TRIGGER_ACTION= 11, + DDL_LOG_DROP_VIEW_ACTION= 9, + DDL_LOG_DROP_TRIGGER_ACTION= 10, + DDL_LOG_DROP_DB_ACTION=11, DDL_LOG_LAST_ACTION /* End marker */ }; @@ -93,13 +93,15 @@ extern const uchar ddl_log_entry_phases[DDL_LOG_LAST_ACTION]; enum enum_ddl_log_exchange_phase { EXCH_PHASE_NAME_TO_TEMP= 0, EXCH_PHASE_FROM_TO_NAME= 1, - EXCH_PHASE_TEMP_TO_FROM= 2 + EXCH_PHASE_TEMP_TO_FROM= 2, + EXCH_PHASE_END }; enum enum_ddl_log_rename_table_phase { DDL_RENAME_PHASE_TRIGGER= 0, DDL_RENAME_PHASE_STAT, DDL_RENAME_PHASE_TABLE, + DDL_RENAME_PHASE_END }; enum enum_ddl_log_drop_table_phase { @@ -110,6 +112,12 @@ enum enum_ddl_log_drop_table_phase { DDL_DROP_PHASE_END }; +enum enum_ddl_log_drop_db_phase { + DDL_DROP_DB_PHASE_INIT=0, + DDL_DROP_DB_PHASE_LOG, + DDL_DROP_DB_PHASE_END +}; + /* Setting ddl_log_entry.phase to this has the same effect as setting the phase to the maximum phase (..PHASE_END) for an entry. @@ -218,8 +226,10 @@ bool ddl_log_rename_view(THD *thd, DDL_LOG_STATE *ddl_state, const LEX_CSTRING *new_db, const LEX_CSTRING *new_alias); bool ddl_log_drop_table_init(THD *thd, DDL_LOG_STATE *ddl_state, + const LEX_CSTRING *db, const LEX_CSTRING *comment); -bool ddl_log_drop_view_init(THD *thd, DDL_LOG_STATE *ddl_state); +bool ddl_log_drop_view_init(THD *thd, DDL_LOG_STATE *ddl_state, + const LEX_CSTRING *db); bool ddl_log_drop_table(THD *thd, DDL_LOG_STATE *ddl_state, handlerton *hton, const LEX_CSTRING *path, @@ -234,5 +244,13 @@ bool ddl_log_drop_trigger(THD *thd, DDL_LOG_STATE *ddl_state, const LEX_CSTRING *table, const LEX_CSTRING *trigger_name, const LEX_CSTRING *query); +bool ddl_log_drop_view(THD *thd, DDL_LOG_STATE *ddl_state, + const LEX_CSTRING *path, + const LEX_CSTRING *db, + const LEX_CSTRING *table); +bool ddl_log_drop_view(THD *thd, DDL_LOG_STATE *ddl_state, + const LEX_CSTRING *db); +bool ddl_log_drop_db(THD *thd, DDL_LOG_STATE *ddl_state, + const LEX_CSTRING *db, const LEX_CSTRING *path); extern mysql_mutex_t LOCK_gdl; #endif /* DDL_LOG_INCLUDED */ diff --git a/sql/handler.cc b/sql/handler.cc index 107523f1280..0f2533e6f11 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -839,9 +839,10 @@ static my_bool dropdb_handlerton(THD *unused1, plugin_ref plugin, } -void ha_drop_database(char* path) +void ha_drop_database(const char* path) { - plugin_foreach(NULL, dropdb_handlerton, MYSQL_STORAGE_ENGINE_PLUGIN, path); + plugin_foreach(NULL, dropdb_handlerton, MYSQL_STORAGE_ENGINE_PLUGIN, + (char*) path); } diff --git a/sql/handler.h b/sql/handler.h index 3187c5d749f..029e981c038 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -5180,7 +5180,7 @@ int ha_panic(enum ha_panic_function flag); void ha_close_connection(THD* thd); void ha_kill_query(THD* thd, enum thd_kill_levels level); bool ha_flush_logs(); -void ha_drop_database(char* path); +void ha_drop_database(const char* path); void ha_checkpoint_state(bool disable); void ha_commit_checkpoint_request(void *cookie, void (*pre_hook)(void *)); int ha_create_table(THD *thd, const char *path, const char *db, diff --git a/sql/sql_db.cc b/sql/sql_db.cc index da232526cd1..6a493ed725f 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -40,6 +40,7 @@ #include "events.h" #include "sql_handler.h" #include "sql_statistics.h" +#include "ddl_log.h" // ddl_log functions #include <my_dir.h> #include <m_ctype.h> #include "log.h" @@ -58,7 +59,7 @@ static bool find_db_tables_and_rm_known_files(THD *, MY_DIR *, const char *, const char *, TABLE_LIST **); long mysql_rm_arc_files(THD *thd, MY_DIR *dirp, const char *org_path); -static my_bool rm_dir_w_symlink(const char *org_path, my_bool send_error); +my_bool rm_dir_w_symlink(const char *org_path, my_bool send_error); static void mysql_change_db_impl(THD *thd, LEX_CSTRING *new_db_name, privilege_t new_db_access, @@ -952,6 +953,56 @@ bool mysql_alter_db(THD *thd, const LEX_CSTRING *db, /** + Drop database objects + + @param thd THD object + @param path Path to database (for ha_drop_database) + @param db Normalized database name + @param rm_mysql_schema If the schema is 'mysql', in which case we don't + log the query to binary log or delete related + routines or events. +*/ + +void drop_database_objects(THD *thd, const LEX_CSTRING *path, + const LEX_CSTRING *db, + bool rm_mysql_schema) +{ + debug_crash_here("ddl_log_drop_before_ha_drop_database"); + + ha_drop_database(path->str); + + /* + We temporarily disable the binary log while dropping the objects + in the database. Since the DROP DATABASE statement is always + replicated as a statement, execution of it will drop all objects + in the database on the slave as well, so there is no need to + replicate the removal of the individual objects in the database + as well. + + This is more of a safety precaution, since normally no objects + should be dropped while the database is being cleaned, but in + the event that a change in the code to remove other objects is + made, these drops should still not be logged. + */ + + debug_crash_here("ddl_log_drop_before_drop_db_routines"); + + query_cache_invalidate1(thd, db->str); + + if (!rm_mysql_schema) + { + tmp_disable_binlog(thd); + (void) sp_drop_db_routines(thd, db->str); /* @todo Do not ignore errors */ +#ifdef HAVE_EVENT_SCHEDULER + Events::drop_schema_events(thd, db->str); +#endif + reenable_binlog(thd); + } + debug_crash_here("ddl_log_drop_after_drop_db_routines"); +} + + +/** Drop all tables, routines and events in a database and the database itself. @param thd Thread handle @@ -967,41 +1018,31 @@ bool mysql_alter_db(THD *thd, const LEX_CSTRING *db, */ static bool -mysql_rm_db_internal(THD *thd, const LEX_CSTRING *db, bool if_exists, bool silent) +mysql_rm_db_internal(THD *thd, const LEX_CSTRING *db, bool if_exists, + bool silent) { ulong deleted_tables= 0; bool error= true, rm_mysql_schema; char path[FN_REFLEN + 16]; MY_DIR *dirp; - uint length; + uint path_length; TABLE_LIST *tables= NULL; TABLE_LIST *table; + DDL_LOG_STATE ddl_log_state; Drop_table_error_handler err_handler; + LEX_CSTRING rm_db; + char db_tmp[SAFE_NAME_LEN+1]; + const char *dbnorm; DBUG_ENTER("mysql_rm_db"); - char db_tmp[SAFE_NAME_LEN+1]; - const char *dbnorm= normalize_db_name(db->str, db_tmp, sizeof(db_tmp)); + dbnorm= normalize_db_name(db->str, db_tmp, sizeof(db_tmp)); + lex_string_set(&rm_db, dbnorm); + bzero(&ddl_log_state, sizeof(ddl_log_state)); if (lock_schema_name(thd, dbnorm)) DBUG_RETURN(true); - length= build_table_filename(path, sizeof(path) - 1, db->str, "", "", 0); - strmov(path+length, MY_DB_OPT_FILE); // Append db option file name - del_dbopt(path); // Remove dboption hash entry - /* - Now remove the db.opt file. - The 'find_db_tables_and_rm_known_files' doesn't remove this file - if there exists a table with the name 'db', so let's just do it - separately. We know this file exists and needs to be deleted anyway. - */ - if (mysql_file_delete_with_symlink(key_file_misc, path, "", MYF(0)) && - my_errno != ENOENT) - { - my_error(EE_DELETE, MYF(0), path, my_errno); - DBUG_RETURN(true); - } - - path[length]= '\0'; // Remove file name + path_length= build_table_filename(path, sizeof(path) - 1, db->str, "", "", 0); /* See if the directory exists */ if (!(dirp= my_dir(path,MYF(MY_DONT_SORT)))) @@ -1052,7 +1093,10 @@ mysql_rm_db_internal(THD *thd, const LEX_CSTRING *db, bool if_exists, bool silen } } - /* mysql_ha_rm_tables() requires a non-null TABLE_LIST. */ + /* + Close active HANDLER's for tables in the database. + Note that mysql_ha_rm_tables() requires a non-null TABLE_LIST. + */ if (tables) mysql_ha_rm_tables(thd, tables); @@ -1062,41 +1106,45 @@ mysql_rm_db_internal(THD *thd, const LEX_CSTRING *db, bool if_exists, bool silen thd->push_internal_handler(&err_handler); if (!thd->killed && !(tables && - mysql_rm_table_no_locks(thd, tables, true, false, true, false, true, - false))) + mysql_rm_table_no_locks(thd, tables, &rm_db, &ddl_log_state, true, false, + true, false, true, false))) { + debug_crash_here("ddl_log_drop_after_drop_tables"); + + LEX_CSTRING cpath{ path, path_length}; + ddl_log_drop_db(thd, &ddl_log_state, &rm_db, &cpath); + + drop_database_objects(thd, &cpath, &rm_db, rm_mysql_schema); + /* - We temporarily disable the binary log while dropping the objects - in the database. Since the DROP DATABASE statement is always - replicated as a statement, execution of it will drop all objects - in the database on the slave as well, so there is no need to - replicate the removal of the individual objects in the database - as well. - - This is more of a safety precaution, since normally no objects - should be dropped while the database is being cleaned, but in - the event that a change in the code to remove other objects is - made, these drops should still not be logged. + Now remove the db.opt file. + The 'find_db_tables_and_rm_known_files' doesn't remove this file + if there exists a table with the name 'db', so let's just do it + separately. We know this file exists and needs to be deleted anyway. */ - - ha_drop_database(path); - tmp_disable_binlog(thd); - query_cache_invalidate1(thd, dbnorm); - if (!rm_mysql_schema) + debug_crash_here("ddl_log_drop_before_drop_option_file"); + strmov(path+path_length, MY_DB_OPT_FILE); // Append db option file name + if (mysql_file_delete_with_symlink(key_file_misc, path, "", MYF(0)) && + my_errno != ENOENT) { - (void) sp_drop_db_routines(thd, dbnorm); /* @todo Do not ignore errors */ -#ifdef HAVE_EVENT_SCHEDULER - Events::drop_schema_events(thd, dbnorm); -#endif + thd->pop_internal_handler(); + my_error(EE_DELETE, MYF(0), path, my_errno); + error= true; + ddl_log_complete(&ddl_log_state); + goto end; } - reenable_binlog(thd); + del_dbopt(path); // Remove dboption hash entry + path[path_length]= '\0'; // Remove file name /* If the directory is a symbolic link, remove the link first, then remove the directory the symbolic link pointed at */ + debug_crash_here("ddl_log_drop_before_drop_dir"); error= rm_dir_w_symlink(path, true); + debug_crash_here("ddl_log_drop_after_drop_dir"); } + thd->pop_internal_handler(); update_binlog: @@ -1112,6 +1160,7 @@ update_binlog: if (mysql_bin_log.is_open()) { int errcode= query_error_code(thd, TRUE); + int res; Query_log_event qinfo(thd, query, query_length, FALSE, TRUE, /* suppress_use */ TRUE, errcode); /* @@ -1126,7 +1175,14 @@ update_binlog: These DDL methods and logging are protected with the exclusive metadata lock on the schema. */ - if (mysql_bin_log.write(&qinfo)) + debug_crash_here("ddl_log_drop_before_binlog"); + thd->binlog_xid= thd->query_id; + ddl_log_update_xid(&ddl_log_state, thd->binlog_xid); + res= mysql_bin_log.write(&qinfo); + thd->binlog_xid= 0; + debug_crash_here("ddl_log_drop_after_binlog"); + + if (res) { error= true; goto exit; @@ -1176,13 +1232,21 @@ update_binlog: *query_pos++ = ','; } - if (query_pos != query_data_start) + if (query_pos != query_data_start) // If database was not empty { + int res; /* These DDL methods and logging are protected with the exclusive metadata lock on the schema. */ - if (write_to_binlog(thd, query, (uint)(query_pos -1 - query), db->str, db->length)) + debug_crash_here("ddl_log_drop_before_binlog"); + thd->binlog_xid= thd->query_id; + ddl_log_update_xid(&ddl_log_state, thd->binlog_xid); + res= write_to_binlog(thd, query, (uint)(query_pos -1 - query), db->str, + db->length); + thd->binlog_xid= 0; + debug_crash_here("ddl_log_drop_after_binlog"); + if (res) { error= true; goto exit; @@ -1191,6 +1255,7 @@ update_binlog: } exit: + ddl_log_complete(&ddl_log_state); /* If this database was the client's selected database, we silently change the client's selected database to nothing (to have an empty @@ -1202,6 +1267,7 @@ exit: mysql_change_db_impl(thd, NULL, NO_ACL, thd->variables.collation_server); thd->session_tracker.current_schema.mark_as_changed(thd); } +end: my_dirend(dirp); DBUG_RETURN(error); } @@ -1332,22 +1398,24 @@ static bool find_db_tables_and_rm_known_files(THD *thd, MY_DIR *dirp, 1 ERROR */ -static my_bool rm_dir_w_symlink(const char *org_path, my_bool send_error) +my_bool rm_dir_w_symlink(const char *org_path, my_bool send_error) { char tmp_path[FN_REFLEN], *pos; char *path= tmp_path; DBUG_ENTER("rm_dir_w_symlink"); unpack_filename(tmp_path, org_path); -#ifdef HAVE_READLINK - int error; - char tmp2_path[FN_REFLEN]; - /* Remove end FN_LIBCHAR as this causes problem on Linux in readlink */ + /* Remove end FN_LIBCHAR as this causes problem on Linux and OS/2 */ pos= strend(path); if (pos > path && pos[-1] == FN_LIBCHAR) *--pos=0; - if (unlikely((error= my_readlink(tmp2_path, path, MYF(MY_WME))) < 0)) +#ifdef HAVE_READLINK + int error; + char tmp2_path[FN_REFLEN]; + + if (unlikely((error= my_readlink(tmp2_path, path, + MYF(send_error ? MY_WME : 0))) < 0)) DBUG_RETURN(1); if (likely(!error)) { @@ -1359,11 +1427,7 @@ static my_bool rm_dir_w_symlink(const char *org_path, my_bool send_error) path= tmp2_path; } #endif - /* Remove last FN_LIBCHAR to not cause a problem on OS/2 */ - pos= strend(path); - if (pos > path && pos[-1] == FN_LIBCHAR) - *--pos=0; if (unlikely(my_rmdir(path) < 0 && send_error)) { my_error(ER_DB_DROP_RMDIR, MYF(0), path, errno); diff --git a/sql/sql_db.h b/sql/sql_db.h index c9f1ed068e6..3c037d668e0 100644 --- a/sql/sql_db.h +++ b/sql/sql_db.h @@ -47,6 +47,10 @@ void my_dbopt_cleanup(void); const char *normalize_db_name(const char *db, char *buffer, size_t buffer_size); +void drop_database_objects(THD *thd, const LEX_CSTRING *path, + const LEX_CSTRING *db, + bool rm_mysql_schema); +my_bool rm_dir_w_symlink(const char *org_path, my_bool send_error); #define MY_DB_OPT_FILE "db.opt" #endif /* SQL_DB_INCLUDED */ diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 9c50ecdb15b..12d387bc817 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -1041,7 +1041,9 @@ bool mysql_rm_table(THD *thd,TABLE_LIST *tables, bool if_exists, /* mark for close and remove all cached entries */ thd->push_internal_handler(&err_handler); - error= mysql_rm_table_no_locks(thd, tables, if_exists, drop_temporary, + error= mysql_rm_table_no_locks(thd, tables, &thd->db, (DDL_LOG_STATE*) 0, + if_exists, + drop_temporary, false, drop_sequence, dont_log_query, false); thd->pop_internal_handler(); @@ -1102,6 +1104,11 @@ static uint32 get_comment(THD *thd, uint32 comment_pos, @param thd Thread handler @param tables Tables to drop + @param current_db Current database, used for ddl logs + @param ddl_log_state DDL log state, for global ddl logging (used by + DROP DATABASE. If not set, an internal ddl log state + will be used. If set then the caller must call + ddl_log_complete(ddl_log_state); @param if_exists If set, don't give an error if table doesn't exists. In this case we give an warning of level 'NOTE' @param drop_temporary Only drop temporary tables @@ -1130,7 +1137,10 @@ static uint32 get_comment(THD *thd, uint32 comment_pos, not all. */ -int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, +int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, + const LEX_CSTRING *current_db, + DDL_LOG_STATE *ddl_log_state, + bool if_exists, bool drop_temporary, bool drop_view, bool drop_sequence, bool dont_log_query, @@ -1140,7 +1150,7 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, char path[FN_REFLEN + 1]; LEX_CSTRING alias= null_clex_str; StringBuffer<160> unknown_tables(system_charset_info); - DDL_LOG_STATE ddl_log_state; + DDL_LOG_STATE local_ddl_log_state; const char *comment_start; uint not_found_errors= 0, table_count= 0, non_temp_tables_count= 0; int error= 0; @@ -1156,6 +1166,12 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, String built_trans_tmp_query, built_non_trans_tmp_query; DBUG_ENTER("mysql_rm_table_no_locks"); + if (!ddl_log_state) + { + ddl_log_state= &local_ddl_log_state; + bzero(ddl_log_state, sizeof(*ddl_log_state)); + } + unknown_tables.length(0); comment_len= get_comment(thd, if_exists ? 17:9, &comment_start); @@ -1209,14 +1225,13 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, built_non_trans_tmp_query.set_charset(system_charset_info); built_non_trans_tmp_query.copy(built_trans_tmp_query); } - bzero(&ddl_log_state, sizeof(ddl_log_state)); for (table= tables; table; table= table->next_local) { bool is_trans= 0, temporary_table_was_dropped= 0; bool table_creation_was_logged= 0; bool wrong_drop_sequence= 0; - bool table_dropped= 0; + bool table_dropped= 0, res; const LEX_CSTRING db= table->db; const LEX_CSTRING table_name= table->table_name; LEX_CSTRING cpath= {0,0}; @@ -1376,7 +1391,7 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, if (!table_count++) { LEX_CSTRING comment= {comment_start, (size_t) comment_len}; - if (ddl_log_drop_table_init(thd, &ddl_log_state, &comment)) + if (ddl_log_drop_table_init(thd, ddl_log_state, current_db, &comment)) { error= 1; goto err; @@ -1434,12 +1449,18 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, bool enoent_warning= !dont_log_query && !(hton && hton->discover_table); - if (ddl_log_drop_table(thd, &ddl_log_state, hton, &cpath, &db, - &table_name)) + if (was_view) + res= ddl_log_drop_view(thd, ddl_log_state, &cpath, &db, + &table_name); + else + res= ddl_log_drop_table(thd, ddl_log_state, hton, &cpath, &db, + &table_name); + if (res) { - error= -1; - goto err; + error= -1; + goto err; } + debug_crash_here("ddl_log_drop_before_delete_table"); error= ha_delete_table(thd, hton, path, &db, &table_name, enoent_warning); @@ -1510,7 +1531,7 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, int ferror= 0; DBUG_ASSERT(!was_view); - if (ddl_log_drop_table(thd, &ddl_log_state, hton, &cpath, &db, + if (ddl_log_drop_table(thd, ddl_log_state, 0, &cpath, &db, &table_name)) { error= -1; @@ -1548,9 +1569,12 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, if (thd->replication_flags & OPTION_IF_EXISTS) log_if_exists= 1; - debug_crash_here("ddl_log_drop_before_drop_trigger"); - ddl_log_update_phase(&ddl_log_state, DDL_DROP_PHASE_TRIGGER); - debug_crash_here("ddl_log_drop_before_drop_trigger2"); + if (!was_view) + { + debug_crash_here("ddl_log_drop_before_drop_trigger"); + ddl_log_update_phase(ddl_log_state, DDL_DROP_PHASE_TRIGGER); + debug_crash_here("ddl_log_drop_before_drop_trigger2"); + } if (likely(!error) || non_existing_table_error(error)) { @@ -1610,7 +1634,8 @@ report_error: table_name.str, (uint)table_name.length); mysql_audit_drop_table(thd, table); } - ddl_log_update_phase(&ddl_log_state, DDL_DROP_PHASE_BINLOG); + if (!was_view) + ddl_log_update_phase(ddl_log_state, DDL_DROP_PHASE_BINLOG); if (!dont_log_query && (!error || table_dropped || non_existing_table_error(error))) @@ -1715,7 +1740,7 @@ err: built_query.append(normal_tables.ptr(), normal_tables.length()); built_query.append(generated_by_server); thd->binlog_xid= thd->query_id; - ddl_log_update_xid(&ddl_log_state, thd->binlog_xid); + ddl_log_update_xid(ddl_log_state, thd->binlog_xid); error |= (thd->binlog_query(THD::STMT_QUERY_TYPE, built_query.ptr(), built_query.length(), @@ -1725,7 +1750,8 @@ err: debug_crash_here("ddl_log_drop_after_binlog"); } } - ddl_log_complete(&ddl_log_state); + if (ddl_log_state == &local_ddl_log_state) + ddl_log_complete(ddl_log_state); if (!drop_temporary) { @@ -4234,7 +4260,9 @@ int create_table_impl(THD *thd, const LEX_CSTRING &orig_db, */ (void) trans_rollback_stmt(thd); /* Remove normal table without logging. Keep tables locked */ - if (mysql_rm_table_no_locks(thd, &table_list, 0, 0, 0, 0, 1, 1)) + if (mysql_rm_table_no_locks(thd, &table_list, &thd->db, + (DDL_LOG_STATE*) 0, + 0, 0, 0, 0, 1, 1)) goto err; /* @@ -4453,7 +4481,9 @@ int mysql_create_table_no_lock(THD *thd, const LEX_CSTRING *db, { DBUG_ASSERT(thd->is_error()); /* Drop the table as it wasn't completely done */ - if (!mysql_rm_table_no_locks(thd, table_list, 1, + if (!mysql_rm_table_no_locks(thd, table_list, &thd->db, + (DDL_LOG_STATE*) 0, + 1, create_info->tmp_table(), false, true /* Sequence*/, true /* Don't log_query */, diff --git a/sql/sql_table.h b/sql/sql_table.h index 720643d7851..a625461f621 100644 --- a/sql/sql_table.h +++ b/sql/sql_table.h @@ -37,6 +37,7 @@ typedef struct st_key KEY; typedef struct st_key_cache KEY_CACHE; typedef struct st_lock_param_type ALTER_PARTITION_PARAM_TYPE; typedef struct st_order ORDER; +typedef struct st_ddl_log_state DDL_LOG_STATE; enum enum_explain_filename_mode { @@ -177,7 +178,10 @@ bool mysql_checksum_table(THD* thd, TABLE_LIST* table_list, bool mysql_rm_table(THD *thd,TABLE_LIST *tables, bool if_exists, bool drop_temporary, bool drop_sequence, bool dont_log_query); -int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, +int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, + const LEX_CSTRING *db, + DDL_LOG_STATE *ddl_log_state, + bool if_exists, bool drop_temporary, bool drop_view, bool drop_sequence, bool dont_log_query, bool dont_free_locks); diff --git a/sql/sql_view.cc b/sql/sql_view.cc index e1d9ffb9cdc..36e85bc2ea0 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -1870,7 +1870,7 @@ bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode) } if (!view_count++) { - if (ddl_log_drop_view_init(thd, &ddl_log_state)) + if (ddl_log_drop_view_init(thd, &ddl_log_state, &thd->db)) DBUG_RETURN(TRUE); } if (ddl_log_drop_view(thd, &ddl_log_state, &cpath, &view->db, |