Commit 44c2dcff authored by guilhem@mysql.com's avatar guilhem@mysql.com

Fix for BUG#8055 "Trouble with replication from temporary tables and ignores":

when we close the session's temp tables at session end, we automatically write to binlog *one* DROP TEMPORARY TABLE *per tmp table*.
parent b0bf1950
reset master; reset master;
create database `drop-temp+table-test`; create database `drop-temp+table-test`;
use `drop-temp+table-test`; use `drop-temp+table-test`;
create temporary table shortn1 (a int);
create temporary table `table:name` (a int); create temporary table `table:name` (a int);
create temporary table shortn2 (a int);
select get_lock("a",10); select get_lock("a",10);
get_lock("a",10) get_lock("a",10)
1 1
...@@ -10,9 +12,13 @@ get_lock("a",10) ...@@ -10,9 +12,13 @@ get_lock("a",10)
1 1
show binlog events; show binlog events;
Log_name Pos Event_type Server_id Orig_log_pos Info Log_name Pos Event_type Server_id Orig_log_pos Info
master-bin.000001 4 Start 1 4 Server ver: VERSION, Binlog ver: 3 master-bin.000001 # Start 1 # Server ver: VERSION, Binlog ver: 3
master-bin.000001 79 Query 1 79 create database `drop-temp+table-test` master-bin.000001 # Query 1 # create database `drop-temp+table-test`
master-bin.000001 168 Query 1 168 use `drop-temp+table-test`; create temporary table `table:name` (a int) master-bin.000001 # Query 1 # use `drop-temp+table-test`; create temporary table shortn1 (a int)
master-bin.000001 262 Query 1 262 use `drop-temp+table-test`; DROP /*!40005 TEMPORARY */ TABLE IF EXISTS `drop-temp+table-test`.`table:name` master-bin.000001 # Query 1 # use `drop-temp+table-test`; create temporary table `table:name` (a int)
master-bin.000001 391 Query 1 391 use `drop-temp+table-test`; DO RELEASE_LOCK("a") master-bin.000001 # Query 1 # use `drop-temp+table-test`; create temporary table shortn2 (a int)
master-bin.000001 # Query 1 # use `drop-temp+table-test`; DROP /*!40005 TEMPORARY */ TABLE IF EXISTS `drop-temp+table-test`.`shortn2`
master-bin.000001 # Query 1 # use `drop-temp+table-test`; DROP /*!40005 TEMPORARY */ TABLE IF EXISTS `drop-temp+table-test`.`table:name`
master-bin.000001 # Query 1 # use `drop-temp+table-test`; DROP /*!40005 TEMPORARY */ TABLE IF EXISTS `drop-temp+table-test`.`shortn1`
master-bin.000001 # Query 1 # use `drop-temp+table-test`; DO RELEASE_LOCK("a")
drop database `drop-temp+table-test`; drop database `drop-temp+table-test`;
...@@ -4,7 +4,9 @@ connection con1; ...@@ -4,7 +4,9 @@ connection con1;
reset master; reset master;
create database `drop-temp+table-test`; create database `drop-temp+table-test`;
use `drop-temp+table-test`; use `drop-temp+table-test`;
create temporary table shortn1 (a int);
create temporary table `table:name` (a int); create temporary table `table:name` (a int);
create temporary table shortn2 (a int);
select get_lock("a",10); select get_lock("a",10);
disconnect con1; disconnect con1;
...@@ -15,5 +17,6 @@ connection con2; ...@@ -15,5 +17,6 @@ connection con2;
select get_lock("a",10); select get_lock("a",10);
let $VERSION=`select version()`; let $VERSION=`select version()`;
--replace_result $VERSION VERSION --replace_result $VERSION VERSION
--replace_column 2 # 5 #
show binlog events; show binlog events;
drop database `drop-temp+table-test`; drop database `drop-temp+table-test`;
...@@ -485,62 +485,58 @@ void close_temporary(TABLE *table,bool delete_table) ...@@ -485,62 +485,58 @@ void close_temporary(TABLE *table,bool delete_table)
void close_temporary_tables(THD *thd) void close_temporary_tables(THD *thd)
{ {
TABLE *table,*next; TABLE *table,*next;
char *query, *end; char *query, *name_in_query, *end;
uint query_buf_size; uint greatest_key_length= 0;
bool found_user_tables = 0;
if (!thd->temporary_tables) if (!thd->temporary_tables)
return; return;
/*
We write a DROP TEMPORARY TABLE for each temp table left, so that our
replication slave can clean them up. Not one multi-table DROP TABLE binlog
event: this would cause problems if slave uses --replicate-*-table.
*/
LINT_INIT(end); LINT_INIT(end);
query_buf_size= 50; // Enough for DROP ... TABLE IF EXISTS
/* We'll re-use always same buffer so make it big enough for longest name */
for (table=thd->temporary_tables ; table ; table=table->next) for (table=thd->temporary_tables ; table ; table=table->next)
/* greatest_key_length= max(greatest_key_length, table->key_length);
We are going to add 4 ` around the db/table names, so 1 does not look
enough; indeed it is enough, because table->key_length is greater (by 8,
because of server_id and thread_id) than db||table.
*/
query_buf_size+= table->key_length+1;
if ((query = alloc_root(thd->mem_root, query_buf_size))) if ((query = alloc_root(thd->mem_root, greatest_key_length+50)))
// Better add "if exists", in case a RESET MASTER has been done // Better add "if exists", in case a RESET MASTER has been done
end=strmov(query, "DROP /*!40005 TEMPORARY */ TABLE IF EXISTS "); name_in_query= strmov(query, "DROP /*!40005 TEMPORARY */ TABLE IF EXISTS `");
for (table=thd->temporary_tables ; table ; table=next) for (table=thd->temporary_tables ; table ; table=next)
{ {
if (query) // we might be out of memory, but this is not fatal /*
In we are OOM for 'query' this is not fatal. We skip temporary tables
not created directly by the user.
*/
if (query && mysql_bin_log.is_open() && (table->real_name[0] != '#'))
{ {
// skip temporary tables not created directly by the user
if (table->real_name[0] != '#')
found_user_tables = 1;
/* /*
Here we assume table_cache_key always starts Here we assume table_cache_key always starts
with \0 terminated db name with \0 terminated db name
*/ */
end = strxmov(end,"`",table->table_cache_key,"`.`", end = strxmov(name_in_query, table->table_cache_key, "`.`",
table->real_name,"`,", NullS); table->real_name, "`", NullS);
Query_log_event qinfo(thd, query, (ulong)(end-query), 0, FALSE);
/*
Imagine the thread had created a temp table, then was doing a SELECT, and
the SELECT was killed. Then it's not clever to mark the statement above as
"killed", because it's not really a statement updating data, and there
are 99.99% chances it will succeed on slave. And, if thread is
killed now, it's not clever either.
If a real update (one updating a persistent table) was killed on the
master, then this real update will be logged with error_code=killed,
rightfully causing the slave to stop.
*/
qinfo.error_code= 0;
mysql_bin_log.write(&qinfo);
} }
next=table->next; next=table->next;
close_temporary(table); close_temporary(table);
} }
if (query && found_user_tables && mysql_bin_log.is_open())
{
/* The -1 is to remove last ',' */
thd->clear_error();
Query_log_event qinfo(thd, query, (ulong)(end-query)-1, 0, FALSE);
/*
Imagine the thread had created a temp table, then was doing a SELECT, and
the SELECT was killed. Then it's not clever to mark the statement above as
"killed", because it's not really a statement updating data, and there
are 99.99% chances it will succeed on slave.
If a real update (one updating a persistent table) was killed on the
master, then this real update will be logged with error_code=killed,
rightfully causing the slave to stop.
*/
qinfo.error_code= 0;
mysql_bin_log.write(&qinfo);
}
thd->temporary_tables=0; thd->temporary_tables=0;
} }
......
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