mirror of
https://github.com/MariaDB/server.git
synced 2025-08-08 11:22:35 +03:00
MDEV-20348: 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: ========= During the execution of DROP TABLE command "ha_delete_table" call is invoked to delete the table. If the query is killed at this point, the kill command is not handled within the code. This results in two issues. 1) The table which is not dropped also gets written into the binary log. 2) The code continues further upon receiving 'KILL QUERY'. Fix: === Upon receiving the KILL command the query should stop its current execution. Tables which were successfully dropped prior to KILL command should be included in the binary log.
This commit is contained in:
32
mysql-test/suite/rpl/r/rpl_failed_drop_tbl_binlog.result
Normal file
32
mysql-test/suite/rpl/r/rpl_failed_drop_tbl_binlog.result
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
include/master-slave.inc
|
||||||
|
[connection master]
|
||||||
|
create table t1 (a int) engine=innodb;
|
||||||
|
create table t2 (b longblob) engine=innodb;
|
||||||
|
create table t3 (c int) engine=innodb;
|
||||||
|
insert into t2 values (repeat('b',1024*1024));
|
||||||
|
insert into t2 select * from t2;
|
||||||
|
insert into t2 select * from t2;
|
||||||
|
insert into t2 select * from t2;
|
||||||
|
insert into t2 select * from t2;
|
||||||
|
set debug_sync='rm_table_no_locks_before_delete_table SIGNAL nogo WAIT_FOR go EXECUTE 2';
|
||||||
|
drop table t1, t2, t3;
|
||||||
|
connect foo,localhost,root;
|
||||||
|
set debug_sync='now SIGNAL go';
|
||||||
|
kill query CONNECTION_ID;
|
||||||
|
connection master;
|
||||||
|
ERROR 70100: Query execution was interrupted
|
||||||
|
"Tables t2 and t3 should be listed"
|
||||||
|
SHOW TABLES;
|
||||||
|
Tables_in_test
|
||||||
|
t2
|
||||||
|
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`; DROP TABLE `t1` /* generated by server */
|
||||||
|
connection slave;
|
||||||
|
drop table t2, t3;
|
||||||
|
connection master;
|
||||||
|
set debug_sync='RESET';
|
||||||
|
drop table t2, t3;
|
||||||
|
include/rpl_end.inc
|
64
mysql-test/suite/rpl/t/rpl_failed_drop_tbl_binlog.test
Normal file
64
mysql-test/suite/rpl/t/rpl_failed_drop_tbl_binlog.test
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
# ==== 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 tables named t1, t2, t3
|
||||||
|
# 1 - Execute DROP TABLE t1,t2,t3 command.
|
||||||
|
# 2 - Kill the DROP TABLE command while it is trying to drop table 't2'.
|
||||||
|
# 3 - Verify that tables t2,t3 are present after the DROP command execution
|
||||||
|
# was interrupted.
|
||||||
|
# 4 - Check that table 't1' is present in binary log as part of DROP
|
||||||
|
# command.
|
||||||
|
#
|
||||||
|
# ==== References ====
|
||||||
|
#
|
||||||
|
# MDEV-20348: DROP TABLE IF EXISTS killed on master but was replicated.
|
||||||
|
#
|
||||||
|
|
||||||
|
--source include/have_innodb.inc
|
||||||
|
--source include/have_debug_sync.inc
|
||||||
|
--source include/have_binlog_format_statement.inc
|
||||||
|
--source include/master-slave.inc
|
||||||
|
|
||||||
|
create table t1 (a int) engine=innodb;
|
||||||
|
create table t2 (b longblob) engine=innodb;
|
||||||
|
create table t3 (c int) engine=innodb;
|
||||||
|
insert into t2 values (repeat('b',1024*1024));
|
||||||
|
insert into t2 select * from t2;
|
||||||
|
insert into t2 select * from t2;
|
||||||
|
insert into t2 select * from t2;
|
||||||
|
insert into t2 select * from t2;
|
||||||
|
let $binlog_start= query_get_value(SHOW MASTER STATUS, Position, 1);
|
||||||
|
|
||||||
|
let $id=`select connection_id()`;
|
||||||
|
set debug_sync='rm_table_no_locks_before_delete_table SIGNAL nogo WAIT_FOR go EXECUTE 2';
|
||||||
|
send drop table t1, t2, t3;
|
||||||
|
|
||||||
|
connect foo,localhost,root;
|
||||||
|
set debug_sync='now SIGNAL go';
|
||||||
|
let $wait_condition=select 1 from information_schema.processlist where state like 'debug sync point:%';
|
||||||
|
source include/wait_condition.inc;
|
||||||
|
--replace_result $id CONNECTION_ID
|
||||||
|
eval kill query $id;
|
||||||
|
|
||||||
|
connection master;
|
||||||
|
error ER_QUERY_INTERRUPTED;
|
||||||
|
reap;
|
||||||
|
|
||||||
|
--echo "Tables t2 and t3 should be listed"
|
||||||
|
SHOW TABLES;
|
||||||
|
--source include/show_binlog_events.inc
|
||||||
|
--sync_slave_with_master
|
||||||
|
drop table t2, t3;
|
||||||
|
|
||||||
|
connection master;
|
||||||
|
set debug_sync='RESET';
|
||||||
|
drop table t2, t3;
|
||||||
|
|
||||||
|
source include/rpl_end.inc;
|
@@ -2362,35 +2362,6 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
|
|||||||
path_length= build_table_filename(path, sizeof(path) - 1, db, alias,
|
path_length= build_table_filename(path, sizeof(path) - 1, db, alias,
|
||||||
reg_ext, 0);
|
reg_ext, 0);
|
||||||
|
|
||||||
/*
|
|
||||||
This handles the case where a "DROP" was executed and a regular
|
|
||||||
table "may be" dropped as drop_temporary is FALSE and error is
|
|
||||||
TRUE. If the error was FALSE a temporary table was dropped and
|
|
||||||
regardless of the status of drop_temporary a "DROP TEMPORARY"
|
|
||||||
must be used.
|
|
||||||
*/
|
|
||||||
if (!dont_log_query)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
Note that unless if_exists is TRUE or a temporary table was deleted,
|
|
||||||
there is no means to know if the statement should be written to the
|
|
||||||
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");
|
DEBUG_SYNC(thd, "rm_table_no_locks_before_delete_table");
|
||||||
error= 0;
|
error= 0;
|
||||||
@@ -2464,10 +2435,16 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
|
|||||||
// Remove extension for delete
|
// Remove extension for delete
|
||||||
*(end= path + path_length - reg_ext_length)= '\0';
|
*(end= path + path_length - reg_ext_length)= '\0';
|
||||||
|
|
||||||
error= ha_delete_table(thd, table_type, path, db, table->table_name,
|
if ((error= ha_delete_table(thd, table_type, path, db, table->table_name,
|
||||||
!dont_log_query);
|
!dont_log_query)))
|
||||||
|
{
|
||||||
if (!error)
|
if (thd->is_killed())
|
||||||
|
{
|
||||||
|
error= -1;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
int frm_delete_error, trigger_drop_error= 0;
|
int frm_delete_error, trigger_drop_error= 0;
|
||||||
/* Delete the table definition file */
|
/* Delete the table definition file */
|
||||||
@@ -2518,6 +2495,24 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
|
|||||||
mysql_audit_drop_table(thd, table);
|
mysql_audit_drop_table(thd, table);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!dont_log_query && !drop_temporary)
|
||||||
|
{
|
||||||
|
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(",");
|
||||||
|
}
|
||||||
|
|
||||||
DBUG_PRINT("table", ("table: %p s: %p", table->table,
|
DBUG_PRINT("table", ("table: %p s: %p", table->table,
|
||||||
table->table ? table->table->s : NULL));
|
table->table ? table->table->s : NULL));
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user