summaryrefslogtreecommitdiff
path: root/sql/sql_base.cc
diff options
context:
space:
mode:
authorunknown <aelkin@mysql.com>2006-04-23 12:18:57 +0300
committerunknown <aelkin@mysql.com>2006-04-23 12:18:57 +0300
commit329ebbd14dc562a1206997cd855c29c6291c3854 (patch)
tree29a91d7d3a9b215d5596a3a5509eeb69ffaace4a /sql/sql_base.cc
parent8ed06b840e2aaac5d01ca2731f9abd53c339d968 (diff)
downloadmariadb-git-329ebbd14dc562a1206997cd855c29c6291c3854.tar.gz
Bug#17263 temporary tables and replication
Backporting a changeset made for 5.0. Comments from there: 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. mysql-test/r/rpl_temporary.result: results changed mysql-test/t/rpl_temporary.test: test to generate problematic drop in binlog to feed it to restarting slave to see no stop. sql/sql_base.cc: change in drop temprorary tables alg in close_temporary_tables.
Diffstat (limited to 'sql/sql_base.cc')
-rw-r--r--sql/sql_base.cc161
1 files changed, 121 insertions, 40 deletions
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 223e8482f49..756fb8189dc 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -483,13 +483,23 @@ void close_temporary(TABLE *table,bool delete_table)
DBUG_VOID_RETURN;
}
+/* close_temporary_tables' internal */
+static inline uint tmpkeyval(THD *thd, TABLE *table)
+{
+ return uint4korr(table->table_cache_key + table->key_length -
+ sizeof(thd->variables.pseudo_thread_id));
+}
+
+/* Creates one DROP TEMPORARY TABLE binlog event for each pseudo-thread */
void close_temporary_tables(THD *thd)
{
- TABLE *table,*next;
- char *query, *end;
- uint query_buf_size;
- bool found_user_tables = 0;
+ TABLE *next,
+ *prev_table /* prev link is not maintained in TABLE's double-linked list */,
+ *table;
+ char *query= (gptr) 0, *end;
+ uint query_buf_size, max_names_len;
+ bool found_user_tables;
if (!thd->temporary_tables)
return;
@@ -497,51 +507,122 @@ void close_temporary_tables(THD *thd)
LINT_INIT(end);
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->table_name[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
- enough; indeed it is enough, because table->key_length is greater (by 8,
- because of server_id and thread_id) than db||table.
+ 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 `#'
*/
- query_buf_size+= table->key_length+1;
-
- if ((query = alloc_root(thd->mem_root, query_buf_size)))
+ if (table->real_name[0] == '#')
+ continue;
+ else
+ {
+ found_user_tables = 1;
+ }
+ for (prev_sorted= NULL, sorted= thd->temporary_tables; sorted != table;
+ prev_sorted= sorted, sorted= sorted->next)
+ {
+ if (sorted->real_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->real_name[0] != '#';
+ table=table->next)
+ {
+ uint tmp_names_len;
+ for (tmp_names_len= table->key_length + 1;
+ table->next && table->real_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->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 ");
+ end= strmov(query, "DROP /*!40005 TEMPORARY */ TABLE IF EXISTS ");
- for (table=thd->temporary_tables ; table ; table=next)
+ /* 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
+ if (query // we might be out of memory, but this is not fatal
+ && table->real_name[0] != '#')
{
- // skip temporary tables not created directly by the user
- if (table->real_name[0] != '#')
- found_user_tables = 1;
+ 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->real_name[0] != '#' &&
+ tmpkeyval(thd, table) == thd->variables.pseudo_thread_id;
+ table= next)
+ {
+ end_cur= strxmov(end_cur, "`", table->table_cache_key, "`.`",
+ table->real_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);
/*
- Here we assume table_cache_key always starts
- with \0 terminated db name
+ 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.
*/
- end = strxmov(end,"`",table->table_cache_key,"`.`",
- table->real_name,"`,", NullS);
+ qinfo.error_code= 0;
+ mysql_bin_log.write(&qinfo);
+ }
+ else
+ {
+ next= table->next;
+ close_temporary(table, 1);
}
- next=table->next;
- 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;
}