From 5da083ef67eabd92aa316ead7fc3a0af69655977 Mon Sep 17 00:00:00 2001 From: "s.sujatha" Date: Mon, 29 Dec 2014 12:17:55 +0530 Subject: [PATCH] Bug#20041860: SLAVE ERROR WHEN DROP DATABASE Fix: === Backport Bug#11756194 to mysql-5.5. slave breaks if 'drop database' fails on master and mismatched tables on slave. 'DROP TABLE ' was binlogged when 'DROP DATABASE' failed and at least one table was deleted from the database. The log event would lead slave SQL thread stop if some of the tables did not exist on slave. After this patch, It is always binlogged with 'IF EXISTS' option. --- mysql-test/extra/binlog_tests/database.test | 35 ++++++++++ .../suite/binlog/r/binlog_database.result | 69 ++++++++++++++++++- .../suite/rpl/r/rpl_drop_db_fail.result | 16 +++++ mysql-test/suite/rpl/t/rpl_drop_db_fail.test | 31 +++++++++ sql/sql_db.cc | 4 +- 5 files changed, 150 insertions(+), 5 deletions(-) create mode 100644 mysql-test/suite/rpl/r/rpl_drop_db_fail.result create mode 100644 mysql-test/suite/rpl/t/rpl_drop_db_fail.test diff --git a/mysql-test/extra/binlog_tests/database.test b/mysql-test/extra/binlog_tests/database.test index d071415bf65..61e8b594185 100644 --- a/mysql-test/extra/binlog_tests/database.test +++ b/mysql-test/extra/binlog_tests/database.test @@ -31,12 +31,47 @@ source include/show_binlog_events.inc; FLUSH STATUS; +--echo +--echo # 'DROP TABLE IF EXISTS ' is binlogged +--echo # when 'DROP DATABASE' fails and at least one table is deleted +--echo # from the database. +RESET MASTER; +CREATE DATABASE testing_1; +USE testing_1; +CREATE TABLE t1(c1 INT); +CREATE TABLE t2(c1 INT); + +let $prefix= `SELECT UUID()`; +--echo # Create a file in the database directory +--replace_result $prefix FAKE_FILE +eval SELECT 'hello' INTO OUTFILE 'fake_file.$prefix'; + +--echo +--echo # 'DROP DATABASE' will fail if there is any other file in the the +--echo # database directory + +# Use '/' instead of '\' in the error message. On windows platform, dir is +# formed with '\'. +--replace_regex /\\testing_1\\*/\/testing_1\// +--error 1010 +DROP DATABASE testing_1; +let $wait_binlog_event= DROP TABLE IF EXIST; +source include/wait_for_binlog_event.inc; +let $MYSQLD_DATADIR= `SELECT @@datadir`; + +--echo +--echo # Remove the fake file. +--remove_file $MYSQLD_DATADIR/testing_1/fake_file.$prefix +--echo # Now we can drop the database. +DROP DATABASE testing_1; + --echo # --echo # Bug#11765416 58381: FAILED DROP DATABASE CAN BREAK STATEMENT --echo # BASED REPLICATION --echo # +USE test; --disable_warnings DROP DATABASE IF EXISTS db1; DROP TABLE IF EXISTS t3; diff --git a/mysql-test/suite/binlog/r/binlog_database.result b/mysql-test/suite/binlog/r/binlog_database.result index 556a23cd7b9..b04d2a09ec6 100644 --- a/mysql-test/suite/binlog/r/binlog_database.result +++ b/mysql-test/suite/binlog/r/binlog_database.result @@ -39,10 +39,31 @@ master-bin.000001 # Query # # COMMIT master-bin.000001 # Query # # use `test`; DROP TEMPORARY TABLE `tt1` /* generated by server */ master-bin.000001 # Query # # use `test`; DROP TABLE `t1` /* generated by server */ FLUSH STATUS; + +# 'DROP TABLE IF EXISTS ' is binlogged +# when 'DROP DATABASE' fails and at least one table is deleted +# from the database. +RESET MASTER; +CREATE DATABASE testing_1; +USE testing_1; +CREATE TABLE t1(c1 INT); +CREATE TABLE t2(c1 INT); +# Create a file in the database directory +SELECT 'hello' INTO OUTFILE 'fake_file.FAKE_FILE'; + +# 'DROP DATABASE' will fail if there is any other file in the the +# database directory +DROP DATABASE testing_1; +ERROR HY000: Error dropping database (can't rmdir './testing_1/', errno: 17) + +# Remove the fake file. +# Now we can drop the database. +DROP DATABASE testing_1; # # Bug#11765416 58381: FAILED DROP DATABASE CAN BREAK STATEMENT # BASED REPLICATION # +USE test; DROP DATABASE IF EXISTS db1; DROP TABLE IF EXISTS t3; CREATE DATABASE db1; @@ -58,7 +79,7 @@ Tables_in_db1 t2 show binlog events from ; Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 # Query # # use `db1`; DROP TABLE `t1` +master-bin.000001 # Query # # use `db1`; DROP TABLE IF EXISTS `t1` DROP TABLE t3; DROP DATABASE db1; set binlog_format=mixed; @@ -102,10 +123,31 @@ master-bin.000001 # Query # # COMMIT master-bin.000001 # Query # # use `test`; DROP TEMPORARY TABLE `tt1` /* generated by server */ master-bin.000001 # Query # # use `test`; DROP TABLE `t1` /* generated by server */ FLUSH STATUS; + +# 'DROP TABLE IF EXISTS ' is binlogged +# when 'DROP DATABASE' fails and at least one table is deleted +# from the database. +RESET MASTER; +CREATE DATABASE testing_1; +USE testing_1; +CREATE TABLE t1(c1 INT); +CREATE TABLE t2(c1 INT); +# Create a file in the database directory +SELECT 'hello' INTO OUTFILE 'fake_file.FAKE_FILE'; + +# 'DROP DATABASE' will fail if there is any other file in the the +# database directory +DROP DATABASE testing_1; +ERROR HY000: Error dropping database (can't rmdir './testing_1/', errno: 17) + +# Remove the fake file. +# Now we can drop the database. +DROP DATABASE testing_1; # # Bug#11765416 58381: FAILED DROP DATABASE CAN BREAK STATEMENT # BASED REPLICATION # +USE test; DROP DATABASE IF EXISTS db1; DROP TABLE IF EXISTS t3; CREATE DATABASE db1; @@ -121,7 +163,7 @@ Tables_in_db1 t2 show binlog events from ; Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 # Query # # use `db1`; DROP TABLE `t1` +master-bin.000001 # Query # # use `db1`; DROP TABLE IF EXISTS `t1` DROP TABLE t3; DROP DATABASE db1; set binlog_format=row; @@ -166,10 +208,31 @@ master-bin.000001 # Query # # COMMIT master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt1` /* generated by server */ master-bin.000001 # Query # # use `test`; DROP TABLE `t1` /* generated by server */ FLUSH STATUS; + +# 'DROP TABLE IF EXISTS ' is binlogged +# when 'DROP DATABASE' fails and at least one table is deleted +# from the database. +RESET MASTER; +CREATE DATABASE testing_1; +USE testing_1; +CREATE TABLE t1(c1 INT); +CREATE TABLE t2(c1 INT); +# Create a file in the database directory +SELECT 'hello' INTO OUTFILE 'fake_file.FAKE_FILE'; + +# 'DROP DATABASE' will fail if there is any other file in the the +# database directory +DROP DATABASE testing_1; +ERROR HY000: Error dropping database (can't rmdir './testing_1/', errno: 17) + +# Remove the fake file. +# Now we can drop the database. +DROP DATABASE testing_1; # # Bug#11765416 58381: FAILED DROP DATABASE CAN BREAK STATEMENT # BASED REPLICATION # +USE test; DROP DATABASE IF EXISTS db1; DROP TABLE IF EXISTS t3; CREATE DATABASE db1; @@ -185,7 +248,7 @@ Tables_in_db1 t2 show binlog events from ; Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 # Query # # use `db1`; DROP TABLE `t1` +master-bin.000001 # Query # # use `db1`; DROP TABLE IF EXISTS `t1` DROP TABLE t3; DROP DATABASE db1; show databases; diff --git a/mysql-test/suite/rpl/r/rpl_drop_db_fail.result b/mysql-test/suite/rpl/r/rpl_drop_db_fail.result new file mode 100644 index 00000000000..1be90338493 --- /dev/null +++ b/mysql-test/suite/rpl/r/rpl_drop_db_fail.result @@ -0,0 +1,16 @@ +include/master-slave.inc +[connection master] +CREATE DATABASE IF NOT EXISTS db1; +CREATE DATABASE IF NOT EXISTS db2; +use db1; +CREATE TABLE a(id INT); +CREATE VIEW v AS SELECT * FROM a; +CREATE TABLE table_father(id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(20)) ENGINE=INNODB; +use db2; +CREATE TABLE table_child(id INT PRIMARY KEY, info VARCHAR(20), father_id INT) ENGINE=INNODB; +ALTER TABLE table_child ADD CONSTRAINT aaa FOREIGN KEY (father_id) REFERENCES db1.table_father(id); +DROP DATABASE db1; +ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails +DROP DATABASE db2; +DROP DATABASE db1; +include/rpl_end.inc diff --git a/mysql-test/suite/rpl/t/rpl_drop_db_fail.test b/mysql-test/suite/rpl/t/rpl_drop_db_fail.test new file mode 100644 index 00000000000..9cf60cf2adf --- /dev/null +++ b/mysql-test/suite/rpl/t/rpl_drop_db_fail.test @@ -0,0 +1,31 @@ +############################################################################### +# Bug#20041860: SLAVE ERROR WHEN DROP DATABASE +# +# Test: +# ===== +# Create two databases such that one database has a dependency over the other +# database and try to drop the database which has a dependency. This should +# not cause slave to break. +############################################################################### +--source include/master-slave.inc + +connection master; +CREATE DATABASE IF NOT EXISTS db1; +CREATE DATABASE IF NOT EXISTS db2; + +use db1; +CREATE TABLE a(id INT); +CREATE VIEW v AS SELECT * FROM a; +CREATE TABLE table_father(id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(20)) ENGINE=INNODB; + +use db2; +CREATE TABLE table_child(id INT PRIMARY KEY, info VARCHAR(20), father_id INT) ENGINE=INNODB; +ALTER TABLE table_child ADD CONSTRAINT aaa FOREIGN KEY (father_id) REFERENCES db1.table_father(id); + +--error ER_ROW_IS_REFERENCED +DROP DATABASE db1; +DROP DATABASE db2; +--sync_slave_with_master +--connection master +DROP DATABASE db1; +--source include/rpl_end.inc diff --git a/sql/sql_db.cc b/sql/sql_db.cc index f4ac6fbbef7..e24d87e849f 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -1,5 +1,5 @@ /* - Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -942,7 +942,7 @@ update_binlog: if (!(query= (char*) thd->alloc(MAX_DROP_TABLE_Q_LEN))) goto exit; /* not much else we can do */ - query_pos= query_data_start= strmov(query,"DROP TABLE "); + query_pos= query_data_start= strmov(query,"DROP TABLE IF EXISTS "); query_end= query + MAX_DROP_TABLE_Q_LEN; db_len= strlen(db);