[Commits] 3b9ce7937e2: MENT-325: DROP TABLE IF EXISTS killed on master but was replicated
revision-id: 3b9ce7937e2422dc7bae26218b63b27ac68ba057 (mariadb-10.2.26-25-g3b9ce7937e2) parent(s): 65296123d0fcaeb122bc3b9d9e387468052c06b6 author: Sujatha committer: Sujatha timestamp: 2019-08-14 12:06:27 +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. Fix: === When DROP TABLE command execution is interuppted because of kill command the replicated DROP TABLE command should include only the list of tables which were dropped succesfully. --- .../suite/rpl/r/rpl_failed_drop_tbl_binlog.result | 61 +++++++++++++++ .../suite/rpl/t/rpl_failed_drop_tbl_binlog.test | 88 ++++++++++++++++++++++ sql/sql_table.cc | 33 ++++---- 3 files changed, 168 insertions(+), 14 deletions(-) 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..7d02b4624e3 --- /dev/null +++ b/mysql-test/suite/rpl/r/rpl_failed_drop_tbl_binlog.result @@ -0,0 +1,61 @@ +include/master-slave.inc +[connection master] +connect master2,localhost,root,,; +connection master; +CREATE TABLE t1 (A INT); +CREATE TABLE t2 (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) +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # use `test`; CREATE TABLE t2 (A INT) +disconnect master2; +"Table t1 should be listed" +SHOW TABLES; +Tables_in_test +t1 +t2 +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,t3; +ERROR 42S02: Unknown table 'test.t3' +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`; CREATE TABLE t2 (A INT) +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # use `test`; DROP TABLE `t1`,`t3` /* generated by server */ +# Testing DROP TABLE command with a non existing table and an existing table. Both should be writtent to binary log. +DROP TABLE t3,t2; +ERROR 42S02: Unknown table 'test.t3' +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`; CREATE TABLE t2 (A INT) +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # use `test`; DROP TABLE `t1`,`t3` /* generated by server */ +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # use `test`; DROP TABLE `t3`,`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..9612848cc62 --- /dev/null +++ b/mysql-test/suite/rpl/t/rpl_failed_drop_tbl_binlog.test @@ -0,0 +1,88 @@ +# ==== 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); +CREATE TABLE t2 (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,t3; +--source include/show_binlog_events.inc + +--echo # Testing DROP TABLE command with a non existing table and an existing table. Both should be writtent to binary log. +--error ER_BAD_TABLE_ERROR +DROP TABLE t3,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 9d7e03727d3..f1185845d59 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -2185,6 +2185,7 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, int error= 0; int non_temp_tables_count= 0; bool non_tmp_error= 0; + bool at_least_one_table_dropped= false; bool trans_tmp_table_deleted= 0, non_trans_tmp_table_deleted= 0; bool non_tmp_table_deleted= 0; bool is_drop_tmp_if_exists_added= 0; @@ -2377,19 +2378,6 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, binary log. See further information on this variable in what follows. */ non_tmp_table_deleted= (if_exists ? TRUE : non_tmp_table_deleted); - /* - Don't write the database name if it is the current one (or if - thd->db is NULL). - */ - if (thd->db == NULL || strcmp(db,thd->db) != 0) - { - append_identifier(thd, &built_query, db, db_length); - built_query.append("."); - } - - append_identifier(thd, &built_query, table->table_name, - table->table_name_length); - built_query.append(","); } } DEBUG_SYNC(thd, "rm_table_no_locks_before_delete_table"); @@ -2518,6 +2506,22 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, mysql_audit_drop_table(thd, table); } + /* + Add table name to "DROP TABLE command" only after successful deletion. + Don't write the database name if it is the current one (or if + thd->db is NULL). + */ + if (thd->db == NULL || strcmp(db,thd->db) != 0) + { + append_identifier(thd, &built_query, db, db_length); + built_query.append("."); + } + + append_identifier(thd, &built_query, table->table_name, + table->table_name_length); + built_query.append(","); + at_least_one_table_dropped= true; + DBUG_PRINT("table", ("table: %p s: %p", table->table, table->table ? table->table->s : NULL)); } @@ -2586,7 +2590,8 @@ 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) + if (non_tmp_table_deleted && + (at_least_one_table_dropped == true)) { /* Chop of the last comma */ built_query.chop();
participants (1)
-
sujatha