mirror of
https://github.com/MariaDB/server.git
synced 2025-07-30 16:24:05 +03:00
Merge mysql.com:/net/nb/home/elkin/MySQL/FIXES/5.0-bug17263-temp_drop
into mysql.com:/usr_rh9/home/elkin.rh9/MySQL/Merge/5.0 mysql-test/r/rpl_temporary.result: Auto merged mysql-test/t/rpl_temporary.test: Auto merged sql/sql_base.cc: Auto merged
This commit is contained in:
@ -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;
|
|||||||
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;
|
|||||||
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
|
||||||
|
166
sql/sql_base.cc
166
sql/sql_base.cc
@ -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,47 +630,122 @@ 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)
|
/*
|
||||||
/*
|
insertion sort of temp tables by pseudo_thread_id to build ordered list
|
||||||
We are going to add 4 ` around the db/table names, so 1 does not look
|
of sublists of equal pseudo_thread_id
|
||||||
enough; indeed it is enough, because table->key_length is greater (by 8,
|
*/
|
||||||
because of server_id and thread_id) than db||table.
|
for (prev_table= thd->temporary_tables,
|
||||||
*/
|
table= prev_table->next,
|
||||||
query_buf_size+= table->s->key_length+1;
|
found_user_tables= (prev_table->s->table_name[0] != '#');
|
||||||
|
table;
|
||||||
if ((query = alloc_root(thd->mem_root, query_buf_size)))
|
prev_table= table, table= table->next)
|
||||||
// Better add "if exists", in case a RESET MASTER has been done
|
|
||||||
end=strmov(query, "DROP /*!40005 TEMPORARY */ TABLE IF EXISTS ");
|
|
||||||
|
|
||||||
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
|
found_user_tables = 1;
|
||||||
if (table->s->table_name[0] != '#')
|
|
||||||
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 (query && found_user_tables && mysql_bin_log.is_open())
|
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;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
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)
|
||||||
{
|
{
|
||||||
/* The -1 is to remove last ',' */
|
uint tmp_names_len;
|
||||||
thd->clear_error();
|
for (tmp_names_len= table->s->key_length + 1;
|
||||||
Query_log_event qinfo(thd, query, (ulong)(end-query)-1, 0, FALSE);
|
table->next && table->s->table_name[0] != '#' &&
|
||||||
/*
|
tmpkeyval(thd, table) == tmpkeyval(thd, table->next);
|
||||||
Imagine the thread had created a temp table, then was doing a SELECT, and
|
table=table->next)
|
||||||
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.
|
We are going to add 4 ` around the db/table names, so 1 might not look
|
||||||
If a real update (one updating a persistent table) was killed on the
|
enough; indeed it is enough, because table->key_length is greater (by 8,
|
||||||
master, then this real update will be logged with error_code=killed,
|
because of server_id and thread_id) than db||table.
|
||||||
rightfully causing the slave to stop.
|
*/
|
||||||
*/
|
tmp_names_len += table->next->s->key_length + 1;
|
||||||
qinfo.error_code= 0;
|
}
|
||||||
mysql_bin_log.write(&qinfo);
|
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();
|
||||||
|
/* 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
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
next= table->next;
|
||||||
|
close_temporary(table, 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
thd->temporary_tables=0;
|
thd->temporary_tables=0;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user