mirror of
https://github.com/MariaDB/server.git
synced 2025-07-29 05:21:33 +03:00
patch for BUG#4680 - drop database breaking replication if there were extra files
in the database directory on the master
This commit is contained in:
30
mysql-test/r/rpl_drop_db.result
Normal file
30
mysql-test/r/rpl_drop_db.result
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
stop slave;
|
||||||
|
drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9;
|
||||||
|
reset master;
|
||||||
|
reset slave;
|
||||||
|
drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9;
|
||||||
|
start slave;
|
||||||
|
drop database if exists d1;
|
||||||
|
create database d1;
|
||||||
|
create table d1.t1 (n int);
|
||||||
|
insert into d1.t1 values (1);
|
||||||
|
select * from d1.t1 into outfile 'd1/f1.txt';
|
||||||
|
create table d1.t2 (n int);
|
||||||
|
create table d1.t3 (n int);
|
||||||
|
drop database d1;
|
||||||
|
ERROR HY000: Error dropping database (can't rmdir './d1/', errno: 17)
|
||||||
|
use d1;
|
||||||
|
show tables;
|
||||||
|
Tables_in_d1
|
||||||
|
use test;
|
||||||
|
create table t1 (n int);
|
||||||
|
insert into t1 values (1234);
|
||||||
|
use d1;
|
||||||
|
show tables;
|
||||||
|
Tables_in_d1
|
||||||
|
use test;
|
||||||
|
select * from t1;
|
||||||
|
n
|
||||||
|
1234
|
||||||
|
drop table t1;
|
||||||
|
stop slave;
|
39
mysql-test/t/rpl_drop_db.test
Normal file
39
mysql-test/t/rpl_drop_db.test
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
# test case for BUG#4680 -- if there are extra files in the db directory
|
||||||
|
# dropping the db on the master causes replication problems
|
||||||
|
|
||||||
|
-- source include/master-slave.inc
|
||||||
|
connection master;
|
||||||
|
|
||||||
|
--disable_warnings
|
||||||
|
drop database if exists d1;
|
||||||
|
--enable_warnings
|
||||||
|
create database d1;
|
||||||
|
create table d1.t1 (n int);
|
||||||
|
insert into d1.t1 values (1);
|
||||||
|
select * from d1.t1 into outfile 'd1/f1.txt';
|
||||||
|
create table d1.t2 (n int);
|
||||||
|
create table d1.t3 (n int);
|
||||||
|
--error 1010
|
||||||
|
drop database d1;
|
||||||
|
use d1;
|
||||||
|
show tables;
|
||||||
|
use test;
|
||||||
|
create table t1 (n int);
|
||||||
|
insert into t1 values (1234);
|
||||||
|
sync_slave_with_master;
|
||||||
|
|
||||||
|
connection slave;
|
||||||
|
use d1;
|
||||||
|
show tables;
|
||||||
|
use test;
|
||||||
|
select * from t1;
|
||||||
|
|
||||||
|
connection master;
|
||||||
|
drop table t1;
|
||||||
|
sync_slave_with_master;
|
||||||
|
|
||||||
|
#cleanup
|
||||||
|
connection slave;
|
||||||
|
stop slave;
|
||||||
|
system rm -rf var/master-data/d1;
|
||||||
|
|
@ -441,7 +441,7 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
|
|||||||
bool drop_temporary, bool log_query);
|
bool drop_temporary, bool log_query);
|
||||||
int mysql_rm_table_part2_with_lock(THD *thd, TABLE_LIST *tables,
|
int mysql_rm_table_part2_with_lock(THD *thd, TABLE_LIST *tables,
|
||||||
bool if_exists, bool drop_temporary,
|
bool if_exists, bool drop_temporary,
|
||||||
bool log_query);
|
bool log_query, List<String> *dropped_tables);
|
||||||
int quick_rm_table(enum db_type base,const char *db,
|
int quick_rm_table(enum db_type base,const char *db,
|
||||||
const char *table_name);
|
const char *table_name);
|
||||||
bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list);
|
bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list);
|
||||||
|
@ -31,7 +31,7 @@ static TYPELIB deletable_extentions=
|
|||||||
|
|
||||||
static long mysql_rm_known_files(THD *thd, MY_DIR *dirp,
|
static long mysql_rm_known_files(THD *thd, MY_DIR *dirp,
|
||||||
const char *db, const char *path,
|
const char *db, const char *path,
|
||||||
uint level);
|
uint level, List<String> *dropped_tables);
|
||||||
|
|
||||||
/* Database options hash */
|
/* Database options hash */
|
||||||
static HASH dboptions;
|
static HASH dboptions;
|
||||||
@ -584,6 +584,7 @@ int mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
|
|||||||
int error= 0;
|
int error= 0;
|
||||||
char path[FN_REFLEN+16], tmp_db[NAME_LEN+1];
|
char path[FN_REFLEN+16], tmp_db[NAME_LEN+1];
|
||||||
MY_DIR *dirp;
|
MY_DIR *dirp;
|
||||||
|
List<String> dropped_tables;
|
||||||
uint length;
|
uint length;
|
||||||
DBUG_ENTER("mysql_rm_db");
|
DBUG_ENTER("mysql_rm_db");
|
||||||
|
|
||||||
@ -621,8 +622,10 @@ int mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
|
|||||||
remove_db_from_cache(db);
|
remove_db_from_cache(db);
|
||||||
pthread_mutex_unlock(&LOCK_open);
|
pthread_mutex_unlock(&LOCK_open);
|
||||||
|
|
||||||
|
|
||||||
error= -1;
|
error= -1;
|
||||||
if ((deleted= mysql_rm_known_files(thd, dirp, db, path, 0)) >= 0)
|
if ((deleted= mysql_rm_known_files(thd, dirp, db, path, 0,
|
||||||
|
&dropped_tables)) >= 0)
|
||||||
{
|
{
|
||||||
ha_drop_database(path);
|
ha_drop_database(path);
|
||||||
query_cache_invalidate1(db);
|
query_cache_invalidate1(db);
|
||||||
@ -672,6 +675,37 @@ int mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
|
|||||||
send_ok(thd, (ulong) deleted);
|
send_ok(thd, (ulong) deleted);
|
||||||
thd->server_status&= ~SERVER_STATUS_DB_DROPPED;
|
thd->server_status&= ~SERVER_STATUS_DB_DROPPED;
|
||||||
}
|
}
|
||||||
|
else if (!dropped_tables.is_empty() && mysql_bin_log.is_open())
|
||||||
|
{
|
||||||
|
List_iterator<String> it(dropped_tables);
|
||||||
|
String* dropped_table;
|
||||||
|
int q_len= 11; /* drop table */
|
||||||
|
int db_len= strlen(db);
|
||||||
|
|
||||||
|
for (;(dropped_table= it++);)
|
||||||
|
{
|
||||||
|
q_len += dropped_table->length() + 2 + db_len;
|
||||||
|
}
|
||||||
|
q_len--; /* no last comma */
|
||||||
|
|
||||||
|
char* query= thd->alloc(q_len);
|
||||||
|
if (!query)
|
||||||
|
goto exit; /* not much else we can do */
|
||||||
|
char* p= strmov(query,"drop table ");
|
||||||
|
it.rewind();
|
||||||
|
|
||||||
|
for (;(dropped_table= it++);)
|
||||||
|
{
|
||||||
|
p= strmov(p,db);
|
||||||
|
*p++ = '.';
|
||||||
|
p= strnmov(p,dropped_table->ptr(),dropped_table->length());
|
||||||
|
*p++ = ',';
|
||||||
|
}
|
||||||
|
*--p= 0;
|
||||||
|
Query_log_event qinfo(thd, query, q_len, 0, 0);
|
||||||
|
qinfo.error_code= 0;
|
||||||
|
mysql_bin_log.write(&qinfo);
|
||||||
|
}
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
start_waiting_global_read_lock(thd);
|
start_waiting_global_read_lock(thd);
|
||||||
@ -716,7 +750,7 @@ exit2:
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db,
|
static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db,
|
||||||
const char *org_path, uint level)
|
const char *org_path, uint level, List<String> *dropped_tables)
|
||||||
{
|
{
|
||||||
long deleted=0;
|
long deleted=0;
|
||||||
ulong found_other_files=0;
|
ulong found_other_files=0;
|
||||||
@ -758,7 +792,7 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db,
|
|||||||
if ((new_dirp = my_dir(newpath,MYF(MY_DONT_SORT))))
|
if ((new_dirp = my_dir(newpath,MYF(MY_DONT_SORT))))
|
||||||
{
|
{
|
||||||
DBUG_PRINT("my",("New subdir found: %s", newpath));
|
DBUG_PRINT("my",("New subdir found: %s", newpath));
|
||||||
if ((mysql_rm_known_files(thd, new_dirp, NullS, newpath,1)) < 0)
|
if ((mysql_rm_known_files(thd, new_dirp, NullS, newpath,1,0)) < 0)
|
||||||
goto err;
|
goto err;
|
||||||
if (!(copy_of_path= thd->memdup(newpath, length+1)) ||
|
if (!(copy_of_path= thd->memdup(newpath, length+1)) ||
|
||||||
!(dir= new (thd->mem_root) String(copy_of_path, length,
|
!(dir= new (thd->mem_root) String(copy_of_path, length,
|
||||||
@ -805,7 +839,8 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (thd->killed ||
|
if (thd->killed ||
|
||||||
(tot_list && mysql_rm_table_part2_with_lock(thd, tot_list, 1, 0, 1)))
|
(tot_list && mysql_rm_table_part2_with_lock(thd, tot_list, 1, 0,
|
||||||
|
1,dropped_tables)))
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
/* Remove RAID directories */
|
/* Remove RAID directories */
|
||||||
|
@ -156,7 +156,8 @@ int mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists,
|
|||||||
|
|
||||||
int mysql_rm_table_part2_with_lock(THD *thd,
|
int mysql_rm_table_part2_with_lock(THD *thd,
|
||||||
TABLE_LIST *tables, bool if_exists,
|
TABLE_LIST *tables, bool if_exists,
|
||||||
bool drop_temporary, bool dont_log_query)
|
bool drop_temporary, bool dont_log_query,
|
||||||
|
List<String>* dropped_tables)
|
||||||
{
|
{
|
||||||
int error;
|
int error;
|
||||||
thd->mysys_var->current_mutex= &LOCK_open;
|
thd->mysys_var->current_mutex= &LOCK_open;
|
||||||
@ -165,6 +166,23 @@ int mysql_rm_table_part2_with_lock(THD *thd,
|
|||||||
|
|
||||||
error=mysql_rm_table_part2(thd,tables, if_exists, drop_temporary,
|
error=mysql_rm_table_part2(thd,tables, if_exists, drop_temporary,
|
||||||
dont_log_query);
|
dont_log_query);
|
||||||
|
/*
|
||||||
|
For now we assume that if we got success all the tables in the list
|
||||||
|
were actually dropped, otherwise, assume none were dropped.
|
||||||
|
TODO: fix it to work with a partial drop - extremely rare case, but
|
||||||
|
can happen.
|
||||||
|
*/
|
||||||
|
if (!error && dropped_tables)
|
||||||
|
{
|
||||||
|
TABLE_LIST* tbl;
|
||||||
|
|
||||||
|
for (tbl= tables; tbl; tbl= tbl->next)
|
||||||
|
{
|
||||||
|
String *dropped_table= new (thd->mem_root)
|
||||||
|
String(tbl->real_name,&my_charset_latin1);
|
||||||
|
dropped_tables->push_back(dropped_table);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pthread_mutex_unlock(&LOCK_open);
|
pthread_mutex_unlock(&LOCK_open);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user