[Commits] 3a8d8b5e3b2: MENT-325: DROP TABLE IF EXISTS killed on master but was replicated
revision-id: 3a8d8b5e3b2ec0c4f23349c3ab1864fc866f1361 (mariadb-10.2.26-13-g3a8d8b5e3b2) parent(s): 6765cc607764c920bc5fbf4128bb526b73f15c2e author: Sujatha committer: Sujatha timestamp: 2019-08-09 15:06:55 +0530 message: MENT-325: DROP TABLE IF EXISTS killed on master but was replicated Problem: ======= DROP TABLE IF EXISTS was killed. The table still exists on the master but the DDL was still logged. Analysis and Fix: ================ Implemented upstream fix. commit id: 0b5b1aed3a1ec0fcf833cf9ee69dcff04ed874ae We do binlog failed queries when the query can generate 'partial results'. When a list of tables are dropped as part of a single 'DROP TABLE' command and if it fails, it can generate partial results and in such cases we log it with an expected error into the binary log so that slave's state is also the same as that of the master. But when the DROP TABLE command is used to DROP a single table and if that command fails then the query cannot generate 'partial results'. In that case the query will not be written to the binary log. --- .../suite/rpl/r/rpl_failed_drop_tbl_binlog.result | 42 +++++++++++ .../suite/rpl/t/rpl_failed_drop_tbl_binlog.test | 81 ++++++++++++++++++++++ sql/sql_table.cc | 8 ++- 3 files changed, 130 insertions(+), 1 deletion(-) diff --git a/mysql-test/suite/rpl/r/rpl_failed_drop_tbl_binlog.result b/mysql-test/suite/rpl/r/rpl_failed_drop_tbl_binlog.result new file mode 100644 index 00000000000..de166047bed --- /dev/null +++ b/mysql-test/suite/rpl/r/rpl_failed_drop_tbl_binlog.result @@ -0,0 +1,42 @@ +include/master-slave.inc +[connection master] +connect master2,localhost,root,,; +connection master; +CREATE TABLE t1 (A INT); +connection slave; +connection master; +LOCK TABLE t1 WRITE; +SET debug_sync='rm_table_no_locks_before_delete_table WAIT_FOR go'; +DROP TABLE IF EXISTS t1; +connection master1; +SET debug_sync='open_tables_after_open_and_process_table SIGNAL go WAIT_FOR go2'; +PREPARE x FROM "INSERT t1 VALUES (10)"; +connection master2; +KILL QUERY 9; +SET debug_sync='now SIGNAL go2'; +connection master; +ERROR 70100: Query execution was interrupted +include/assert.inc [Drop with single table should not be written to the binary log if the query execution fails] +include/show_binlog_events.inc +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # use `test`; CREATE TABLE t1 (A INT) +disconnect master2; +"Table t1 should be listed" +SHOW TABLES; +Tables_in_test +t1 +connection master; +UNLOCK TABLES; +SET debug_sync='reset'; +# DROP TABLE command with multiple tables fails the command should be written into the binary log. +DROP TABLE t1,t2; +ERROR 42S02: Unknown table 'test.t2' +include/show_binlog_events.inc +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # use `test`; CREATE TABLE t1 (A INT) +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # use `test`; DROP TABLE `t1`,`t2` /* generated by server */ +connection slave; +include/rpl_end.inc diff --git a/mysql-test/suite/rpl/t/rpl_failed_drop_tbl_binlog.test b/mysql-test/suite/rpl/t/rpl_failed_drop_tbl_binlog.test new file mode 100644 index 00000000000..e8eaeb493c7 --- /dev/null +++ b/mysql-test/suite/rpl/t/rpl_failed_drop_tbl_binlog.test @@ -0,0 +1,81 @@ +# ==== Purpose ==== +# +# Check that when the execution of a DROP TABLE command with single table +# fails it should not be written to the binary log. Also test that when the +# execution of DROP TABLE command with multiple tables fails the command +# should be written into the binary log. +# +# ==== Implementation ==== +# +# Steps: +# 0 - Create table named t1. +# 1 - Simulate a scenario where DROP TABLE command waits for an exclusive +# MDL lock to drop the table. +# 2 - Kill the query from another session. +# 3 - The DROP TABLE query execution will be interrupted. Ensure that the +# failed DROP TABLE command is not written to the binary log. +# 4 - Execute DROP TABLE t1, t2 statement. It will fail as 't2' table is +# not present. It should error out with unknown table error. +# 5 - The command should be written to binary log as it has already dropped +# table t1. +# +# ==== References ==== +# +# MENT-325: DROP TABLE IF EXISTS killed on master but was replicated. +# + +--source include/have_debug_sync.inc +--source include/have_binlog_format_statement.inc +--source include/master-slave.inc + +connect (master2,localhost,root,,); + +--connection master +CREATE TABLE t1 (A INT); +# Save master position +--let $saved_master_pos=query_get_value('SHOW MASTER STATUS', Position, 1) +--sync_slave_with_master + +--connection master +--let id=`select connection_id()` +LOCK TABLE t1 WRITE; +SET debug_sync='rm_table_no_locks_before_delete_table WAIT_FOR go'; +--send DROP TABLE IF EXISTS t1 + +--connection master1 +--let $wait_condition= SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.PROCESSLIST WHERE STATE like "debug sync point: rm_table_no_locks_before_delete_table%" +--source include/wait_condition.inc +SET debug_sync='open_tables_after_open_and_process_table SIGNAL go WAIT_FOR go2'; +--send PREPARE x FROM "INSERT t1 VALUES (10)" + +--connection master2 +--let $wait_condition= SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.PROCESSLIST WHERE STATE like "debug sync point: open_tables_after_open_and_process_table%" +--source include/wait_condition.inc +eval KILL QUERY $id; +SET debug_sync='now SIGNAL go2'; + +--connection master +--error ER_QUERY_INTERRUPTED +reap; + +--let $current_master_pos=query_get_value('SHOW MASTER STATUS', Position, 1) +--let $assert_text= Drop with single table should not be written to the binary log if the query execution fails +--let $assert_cond= $current_master_pos = $saved_master_pos +--source include/assert.inc + +--source include/show_binlog_events.inc +disconnect master2; + +--echo "Table t1 should be listed" +SHOW TABLES; + +--connection master +UNLOCK TABLES; +SET debug_sync='reset'; +--echo # DROP TABLE command with multiple tables fails the command should be written into the binary log. +--error ER_BAD_TABLE_ERROR +DROP TABLE t1,t2; +--source include/show_binlog_events.inc +--sync_slave_with_master + +--source include/rpl_end.inc diff --git a/sql/sql_table.cc b/sql/sql_table.cc index fbd6622b1b2..65ac14f8541 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -2586,7 +2586,13 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, is_drop_tmp_if_exists_added, 0); } - if (non_tmp_table_deleted) + /* + When the DROP TABLE command is used to drop a single table and if that + command fails then the query cannot generate 'partial results'. In that + case the query will not be written to the binary log. + */ + if (non_tmp_table_deleted && + (thd->lex->select_lex.table_list.elements > 1 || !error)) { /* Chop of the last comma */ built_query.chop();
participants (1)
-
sujatha