Commit 30c55ea4 authored by guilhem@gbichot2's avatar guilhem@gbichot2

A fix of the fix for BUG#1209 (was: SELECT DATABASE() still shows dropped database).

Don't free the client's selected db (thd->db) when it has been DROPped,
if this is a slave thread; the x_free() was causing a lot
of various bugs in rpl_loaddata_rule_m (garbage characters, segfault, hangs in
other threads).
A small post-merge fix (rli->inside_transaction exists in 4.0 only).
parent a5cc38fa
...@@ -1876,19 +1876,22 @@ int Rotate_log_event::exec_event(struct st_relay_log_info* rli) ...@@ -1876,19 +1876,22 @@ int Rotate_log_event::exec_event(struct st_relay_log_info* rli)
pthread_mutex_lock(&rli->data_lock); pthread_mutex_lock(&rli->data_lock);
#ifdef TO_BE_CHECKED_BY_GUILHEM if (thd->options & OPTION_BEGIN)
if (rli->inside_transaction)
{ {
slave_print_error(rli, 0, slave_print_error(rli, 0,
opt_using_transactions ?
"\ "\
There is an unfinished transaction in the relay log (could find neither \ There is an unfinished transaction in the relay log (could find neither \
COMMIT nor ROLLBACK in the relay log); It could be that the master died while \ COMMIT nor ROLLBACK in the relay log); It could be that the master died while \
writing the transaction to its binary log. Now the slave is rolling back the \ writing the transaction to its binary log. Now the slave is rolling back the \
transaction."); transaction." :
"\
There is an unfinished transaction in the relay log (could find neither \
COMMIT nor ROLLBACK in the relay log); It could be that the master died while \
writing the transaction to its binary log.");
pthread_mutex_unlock(&rli->data_lock); pthread_mutex_unlock(&rli->data_lock);
DBUG_RETURN(1); DBUG_RETURN(1);
} }
#endif
memcpy(log_name, new_log_ident, ident_len+1); memcpy(log_name, new_log_ident, ident_len+1);
rli->notify_group_master_log_name_update(); rli->notify_group_master_log_name_update();
......
...@@ -391,10 +391,32 @@ int mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent) ...@@ -391,10 +391,32 @@ int mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
exit: exit:
start_waiting_global_read_lock(thd); start_waiting_global_read_lock(thd);
/*
If this database was the client's selected database, we silently change the
client's selected database to nothing (to have an empty SELECT DATABASE() in
the future). For this we free() thd->db and set it to 0. But we don't do
free() for the slave thread. Indeed, doing a x_free() on it leads to nasty
problems (i.e. long painful debugging) because in this thread, thd->db is
the same as data_buf and db of the Query_log_event which is dropping the
database. So if you free() thd->db, you're freeing data_buf. You set thd->db
to 0 but not data_buf (thd->db and data_buf are two distinct pointers which
point to the same place). Then in ~Query_log_event(), we have
'if (data_buf) free(data_buf)'
data_buf is !=0 so this makes a DOUBLE free().
Side effects of this double free() are, randomly (depends on the machine),
when the slave is replicating a DROP DATABASE:
- garbage characters in the error message:
"Error 'Can't drop database 'test2'; database doesn't exist' on query
'h4zI'"
- segfault
- hang in "free(vio)" (yes!) in the I/O or SQL slave threads (so slave
server hangs at shutdown etc).
*/
if (thd->db && !strcmp(thd->db, db)) if (thd->db && !strcmp(thd->db, db))
{ {
x_free(thd->db); if (!(thd->slave_thread)) /* a slave thread will free it itself */
thd->db= 0; x_free(thd->db);
thd->db= 0;
} }
exit2: exit2:
VOID(pthread_mutex_unlock(&LOCK_mysql_create_db)); VOID(pthread_mutex_unlock(&LOCK_mysql_create_db));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment