1
0
mirror of https://github.com/MariaDB/server.git synced 2025-08-07 00:04:31 +03:00

BUG#17263: incorrect DROP query in temporary tables replication

the fix from 5.0 with necessary modifications applied.
This commit is contained in:
aelkin@mysql.com
2006-04-25 22:53:50 +03:00
parent 3363effa52
commit 61e41ac212

View File

@@ -1167,16 +1167,26 @@ bool close_thread_table(THD *thd, TABLE **table_ptr)
} }
/* 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.str + table->s->table_cache_key.length - 4);
}
/* /*
Close all temporary tables created by 'CREATE TEMPORARY TABLE' for thread Close all temporary tables created by 'CREATE TEMPORARY TABLE' for thread
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;
@@ -1184,50 +1194,123 @@ 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
of sublists of equal pseudo_thread_id
*/
for (prev_table= thd->temporary_tables,
table= prev_table->next,
found_user_tables= (prev_table->s->table_name.str[0] != '#');
table;
prev_table= table, table= table->next)
{ {
TABLE *prev_sorted /* same as for prev_table */,
*sorted;
/* /*
We are going to add 4 ` around the db/table names, so 1 does not look table not created directly by the user is moved to the tail.
enough; indeed it is enough, because table->table_cache_key.length is Fixme/todo: nothing (I checked the manual) prevents user to create temp
greater (by 8, because of server_id and thread_id) than db||table. with `#'
*/ */
query_buf_size+= table->s->table_cache_key.length+1; if (table->s->table_name.str[0] == '#')
} continue;
else
if ((query = alloc_root(thd->mem_root, query_buf_size)))
// 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
{ {
// skip temporary tables not created directly by the user found_user_tables = 1;
if (table->s->table_name.str[0] != '#')
found_user_tables = 1;
end= strxmov(end, "`",table->s->db.str, "`.`",
table->s->table_name.str, "`,", NullS);
} }
next=table->next; for (prev_sorted= NULL, sorted= thd->temporary_tables; sorted != table;
close_temporary(table, 1, 1); prev_sorted= sorted, sorted= sorted->next)
} {
if (query && found_user_tables && mysql_bin_log.is_open() && if (sorted->s->table_name.str[0] == '#' || tmpkeyval(thd, sorted) > tmpkeyval(thd, table))
!thd->current_stmt_binlog_row_based) // CREATE TEMP TABLE not binlogged if row-based {
/* 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.str[0] != '#';
table=table->next)
{ {
/* The -1 is to remove last ',' */ uint tmp_names_len;
thd->clear_error(); for (tmp_names_len= table->s->table_cache_key.length + 1;
Query_log_event qinfo(thd, query, (ulong)(end-query)-1, 0, FALSE); table->next && table->s->table_name.str[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->s->table_cache_key.length is
master, then this real update will be logged with error_code=killed, greater (by 8, because of server_id and thread_id) than db||table.
rightfully causing the slave to stop. */
*/ tmp_names_len += table->next->s->table_cache_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() &&
!thd->current_stmt_binlog_row_based &&
(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.str[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.str[0] != '#' &&
tmpkeyval(thd, table) == thd->variables.pseudo_thread_id;
table= next)
{
end_cur= strxmov(end_cur, "`", table->s->db.str, "`.`",
table->s->table_name.str, "`,", NullS);
next= table->next;
close_temporary(table, 1, 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, 1);
}
} }
thd->temporary_tables=0; thd->temporary_tables=0;
} }