Commit b56d36dd authored by aelkin@mysql.com's avatar aelkin@mysql.com

Bug#17263 temporary tables and replication

The fix refines the algorithm of generating DROPs for binlog. 
Temp tables with common pseudo_thread_id are clustered into one query. 
Consequently one replication event per pseudo_thread_id is generated.
parent 34b2ed5f
...@@ -103,3 +103,17 @@ f ...@@ -103,3 +103,17 @@ f
1 1
drop temporary table t4; drop temporary table t4;
drop table t5; drop table t5;
set @session.pseudo_thread_id=100;
create temporary table t101 (id int);
create temporary table t102 (id int);
set @session.pseudo_thread_id=200;
create temporary table t201 (id int);
create temporary table `#not_user_table_prefixed_with_hash_sign_no_harm` (id int);
set @con1_id=connection_id();
kill @con1_id;
create table t1(f int);
insert into t1 values (1);
select * from t1 /* must be 1 */;
f
1
drop table t1;
...@@ -129,6 +129,8 @@ drop table t1,t2; ...@@ -129,6 +129,8 @@ drop table t1,t2;
create temporary table t3 (f int); create temporary table t3 (f int);
sync_with_master; sync_with_master;
# The server will now close done
# #
# Bug#17284 erroneous temp table cleanup on slave # Bug#17284 erroneous temp table cleanup on slave
# #
...@@ -154,6 +156,32 @@ connection master; ...@@ -154,6 +156,32 @@ connection master;
drop temporary table t4; drop temporary table t4;
drop table t5; drop table t5;
# The server will now close done #
# BUG#17263 incorrect generation DROP temp tables
# Temporary tables of connection are dropped in batches
# where a batch correspond to pseudo_thread_id
# value was set up at the moment of temp table creation
#
connection con1;
set @session.pseudo_thread_id=100;
create temporary table t101 (id int);
create temporary table t102 (id int);
set @session.pseudo_thread_id=200;
create temporary table t201 (id int);
create temporary table `#not_user_table_prefixed_with_hash_sign_no_harm` (id int);
set @con1_id=connection_id();
kill @con1_id;
#now do something to show that slave is ok after DROP temp tables
connection master;
create table t1(f int);
insert into t1 values (1);
sync_slave_with_master;
#connection slave;
select * from t1 /* must be 1 */;
connection master;
drop table t1;
# End of 5.0 tests # End of 5.0 tests
...@@ -607,13 +607,22 @@ void close_temporary(TABLE *table,bool delete_table) ...@@ -607,13 +607,22 @@ void close_temporary(TABLE *table,bool delete_table)
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
/* close_temporary_tables' internal, 4 is due to uint4korr definition */
static inline uint tmpkeyval(THD *thd, TABLE *table)
{
return uint4korr(table->s->table_cache_key + table->s->key_length - 4);
}
/* Creates one DROP TEMPORARY TABLE binlog event for each pseudo-thread */
void close_temporary_tables(THD *thd) void close_temporary_tables(THD *thd)
{ {
TABLE *table,*next; TABLE *next,
char *query, *end; *prev_table /* prev link is not maintained in TABLE's double-linked list */,
uint query_buf_size; *table;
bool found_user_tables = 0; char *query= (gptr) 0, *end;
uint query_buf_size, max_names_len;
bool found_user_tables;
if (!thd->temporary_tables) if (!thd->temporary_tables)
return; return;
...@@ -621,36 +630,105 @@ void close_temporary_tables(THD *thd) ...@@ -621,36 +630,105 @@ void close_temporary_tables(THD *thd)
LINT_INIT(end); LINT_INIT(end);
query_buf_size= 50; // Enough for DROP ... TABLE IF EXISTS query_buf_size= 50; // Enough for DROP ... TABLE IF EXISTS
for (table=thd->temporary_tables ; table ; table=table->next)
/* /*
We are going to add 4 ` around the db/table names, so 1 does not look insertion sort of temp tables by pseudo_thread_id to build ordered list
enough; indeed it is enough, because table->key_length is greater (by 8, of sublists of equal pseudo_thread_id
because of server_id and thread_id) than db||table.
*/ */
query_buf_size+= table->s->key_length+1; for (prev_table= thd->temporary_tables,
table= prev_table->next,
if ((query = alloc_root(thd->mem_root, query_buf_size))) found_user_tables= (prev_table->s->table_name[0] != '#');
// Better add "if exists", in case a RESET MASTER has been done table;
end=strmov(query, "DROP /*!40005 TEMPORARY */ TABLE IF EXISTS "); prev_table= table, table= table->next)
for (table=thd->temporary_tables ; table ; table=next)
{ {
if (query) // we might be out of memory, but this is not fatal TABLE *prev_sorted /* same as for prev_table */,
*sorted;
/*
table not created directly by the user is moved to the tail.
Fixme/todo: nothing (I checked the manual) prevents user to create temp
with `#'
*/
if (table->s->table_name[0] == '#')
continue;
else
{ {
// skip temporary tables not created directly by the user
if (table->s->table_name[0] != '#')
found_user_tables = 1; found_user_tables = 1;
end = strxmov(end,"`",table->s->db,"`.`",
table->s->table_name,"`,", NullS);
} }
next=table->next; for (prev_sorted= NULL, sorted= thd->temporary_tables; sorted != table;
close_temporary(table, 1); prev_sorted= sorted, sorted= sorted->next)
{
if (sorted->s->table_name[0] == '#' || tmpkeyval(thd, sorted) > tmpkeyval(thd, table))
{
/* move into the sorted part of the list from the unsorted */
prev_table->next= table->next;
table->next= sorted;
if (prev_sorted)
{
prev_sorted->next= table;
} }
if (query && found_user_tables && mysql_bin_log.is_open()) else
{ {
/* The -1 is to remove last ',' */ thd->temporary_tables= table;
}
table= prev_table;
break;
}
}
}
/*
calc query_buf_size as max per sublists, one sublist per pseudo thread id.
Also stop at first occurence of `#'-named table that starts
all implicitly created temp tables
*/
for (max_names_len= 0, table=thd->temporary_tables;
table && table->s->table_name[0] != '#';
table=table->next)
{
uint tmp_names_len;
for (tmp_names_len= table->s->key_length + 1;
table->next && table->s->table_name[0] != '#' &&
tmpkeyval(thd, table) == tmpkeyval(thd, table->next);
table=table->next)
{
/*
We are going to add 4 ` around the db/table names, so 1 might 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.
*/
tmp_names_len += table->next->s->key_length + 1;
}
if (tmp_names_len > max_names_len) max_names_len= tmp_names_len;
}
/* allocate */
if (found_user_tables && mysql_bin_log.is_open() &&
(query = alloc_root(thd->mem_root, query_buf_size+= max_names_len)))
// Better add "if exists", in case a RESET MASTER has been done
end= strmov(query, "DROP /*!40005 TEMPORARY */ TABLE IF EXISTS ");
/* scan sorted tmps to generate sequence of DROP */
for (table=thd->temporary_tables; table; table= next)
{
if (query // we might be out of memory, but this is not fatal
&& table->s->table_name[0] != '#')
{
char *end_cur;
/* Set pseudo_thread_id to be that of the processed table */
thd->variables.pseudo_thread_id= tmpkeyval(thd, table);
/* Loop forward through all tables within the sublist of
common pseudo_thread_id to create single DROP query */
for (end_cur= end;
table && table->s->table_name[0] != '#' &&
tmpkeyval(thd, table) == thd->variables.pseudo_thread_id;
table= next)
{
end_cur= strxmov(end_cur, "`", table->s->db, "`.`",
table->s->table_name, "`,", NullS);
next= table->next;
close_temporary(table, 1);
}
thd->clear_error(); thd->clear_error();
Query_log_event qinfo(thd, query, (ulong)(end-query)-1, 0, FALSE); /* The -1 is to remove last ',' */
Query_log_event qinfo(thd, query, (ulong)(end_cur - query) - 1, 0, FALSE);
/* /*
Imagine the thread had created a temp table, then was doing a SELECT, and 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 the SELECT was killed. Then it's not clever to mark the statement above as
...@@ -663,6 +741,12 @@ void close_temporary_tables(THD *thd) ...@@ -663,6 +741,12 @@ void close_temporary_tables(THD *thd)
qinfo.error_code= 0; qinfo.error_code= 0;
mysql_bin_log.write(&qinfo); mysql_bin_log.write(&qinfo);
} }
else
{
next= table->next;
close_temporary(table, 1);
}
}
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