mirror of
https://github.com/MariaDB/server.git
synced 2025-07-29 05:21:33 +03:00
1) undoing my fix for BUG#8055 "Trouble with replication from temporary tables and ignores"
and fixing it another way (per Monty; a simpler solution which does not increase the number of binlog events is to always execute DROP TEMPORARY TABLE IF EXISTS on slave). A new test rpl_drop_temp. 2) fixing BUG#8436 "Multiple "stacked" SQL statements cause replication to stop" by setting thd->query_length to the length of the query being executed, not counting the next queries if this is a multi-query. Should also improve display of SHOW PROCESSLIST. A new test rpl_multi_query. mysql-test/r/drop_temp_table.result: back to one single DROP sql/sql_base.cc: undoing the fix I had made some days ago: we are back to one single DROP TEMPORARY TABLE for all temp tables. sql/sql_parse.cc: 1) set thd->query_length to the length of the query being executed, excluding the other next queries if this is a multi-query. The setting happens ASAP, ie. just after we know it's a multi-query, ie. just after yyparse(). Don't include the ';' in thd->query_length, because this is not good for storage in binlog. 2) always execute a DROP TEMPORARY TABLE IF EXISTS on slave, don't skip it even if --replicate-ignore-table
This commit is contained in:
@ -17,8 +17,6 @@ master-bin.000001 # Query 1 # create database `drop-temp+table-test`
|
||||
master-bin.000001 # Query 1 # use `drop-temp+table-test`; create temporary table shortn1 (a int)
|
||||
master-bin.000001 # Query 1 # 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 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`; DROP /*!40005 TEMPORARY */ TABLE IF EXISTS `drop-temp+table-test`.`shortn2`,`drop-temp+table-test`.`table:name`,`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`;
|
||||
|
12
mysql-test/r/rpl_drop_temp.result
Normal file
12
mysql-test/r/rpl_drop_temp.result
Normal file
@ -0,0 +1,12 @@
|
||||
stop slave;
|
||||
drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9;
|
||||
reset master;
|
||||
reset slave;
|
||||
drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9;
|
||||
start slave;
|
||||
create database if not exists mysqltest;
|
||||
create temporary table mysqltest.t1 (n int);
|
||||
create temporary table mysqltest.t2 (n int);
|
||||
show status like 'Slave_open_temp_tables';
|
||||
Variable_name Value
|
||||
Slave_open_temp_tables 0
|
32
mysql-test/r/rpl_multi_query.result
Normal file
32
mysql-test/r/rpl_multi_query.result
Normal file
@ -0,0 +1,32 @@
|
||||
stop slave;
|
||||
drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9;
|
||||
reset master;
|
||||
reset slave;
|
||||
drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9;
|
||||
start slave;
|
||||
drop database if exists mysqltest;
|
||||
create database mysqltest;
|
||||
create table mysqltest.t1 ( n int);
|
||||
insert into mysqltest.t1 values(1)/
|
||||
insert into mysqltest.t1 values(2);
|
||||
insert into mysqltest.t1 values(3);
|
||||
insert into mysqltest.t1 values(4);
|
||||
insert into mysqltest.t1 values(5)/
|
||||
select * from mysqltest.t1;
|
||||
n
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
5
|
||||
show binlog events from 79;
|
||||
Log_name Pos Event_type Server_id Orig_log_pos Info
|
||||
master-bin.000001 # Query 1 # drop database if exists mysqltest
|
||||
master-bin.000001 # Query 1 # create database mysqltest
|
||||
master-bin.000001 # Query 1 # use `test`; create table mysqltest.t1 ( n int)
|
||||
master-bin.000001 # Query 1 # use `test`; insert into mysqltest.t1 values(1)
|
||||
master-bin.000001 # Query 1 # use `test`; insert into mysqltest.t1 values(2)
|
||||
master-bin.000001 # Query 1 # use `test`; insert into mysqltest.t1 values(3)
|
||||
master-bin.000001 # Query 1 # use `test`; insert into mysqltest.t1 values(4)
|
||||
master-bin.000001 # Query 1 # use `test`; insert into mysqltest.t1 values(5)
|
||||
drop database mysqltest;
|
2
mysql-test/t/rpl_drop_temp-slave.opt
Normal file
2
mysql-test/t/rpl_drop_temp-slave.opt
Normal file
@ -0,0 +1,2 @@
|
||||
--replicate-ignore-table=mysqltest.t2
|
||||
|
13
mysql-test/t/rpl_drop_temp.test
Normal file
13
mysql-test/t/rpl_drop_temp.test
Normal file
@ -0,0 +1,13 @@
|
||||
source include/master-slave.inc;
|
||||
--disable_warnings
|
||||
create database if not exists mysqltest;
|
||||
--enable_warnings
|
||||
|
||||
create temporary table mysqltest.t1 (n int);
|
||||
create temporary table mysqltest.t2 (n int);
|
||||
sync_slave_with_master;
|
||||
connection master;
|
||||
disconnect master;
|
||||
connection slave;
|
||||
--real_sleep 3; # time for DROP to be written
|
||||
show status like 'Slave_open_temp_tables';
|
26
mysql-test/t/rpl_multi_query.test
Normal file
26
mysql-test/t/rpl_multi_query.test
Normal file
@ -0,0 +1,26 @@
|
||||
# Test for BUG#8436: verify that a multi-query (i.e. one query
|
||||
# containing several queries (assuming client has
|
||||
# CLIENT_MULTI_STATEMENTS) will be binlogged ONE-query-per-event (not
|
||||
# one binlog event containing all queries)
|
||||
|
||||
source include/master-slave.inc;
|
||||
--disable_warnings
|
||||
drop database if exists mysqltest;
|
||||
--enable_warnings
|
||||
create database mysqltest;
|
||||
|
||||
delimiter /;
|
||||
create table mysqltest.t1 ( n int);
|
||||
insert into mysqltest.t1 values(1)/
|
||||
insert into mysqltest.t1 values(2);
|
||||
insert into mysqltest.t1 values(3);
|
||||
insert into mysqltest.t1 values(4);
|
||||
insert into mysqltest.t1 values(5)/
|
||||
delimiter ;/
|
||||
sync_slave_with_master;
|
||||
select * from mysqltest.t1;
|
||||
connection master;
|
||||
--replace_column 2 # 5 #
|
||||
show binlog events from 79;
|
||||
drop database mysqltest;
|
||||
sync_slave_with_master;
|
@ -485,58 +485,62 @@ void close_temporary(TABLE *table,bool delete_table)
|
||||
void close_temporary_tables(THD *thd)
|
||||
{
|
||||
TABLE *table,*next;
|
||||
char *query, *name_in_query, *end;
|
||||
uint greatest_key_length= 0;
|
||||
char *query, *end;
|
||||
uint query_buf_size;
|
||||
bool found_user_tables = 0;
|
||||
|
||||
if (!thd->temporary_tables)
|
||||
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);
|
||||
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)
|
||||
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, greatest_key_length+50)))
|
||||
if ((query = alloc_root(thd->mem_root, query_buf_size)))
|
||||
// Better add "if exists", in case a RESET MASTER has been done
|
||||
name_in_query= 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)
|
||||
{
|
||||
/*
|
||||
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] != '#'))
|
||||
if (query) // we might be out of memory, but this is not fatal
|
||||
{
|
||||
// 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
|
||||
with \0 terminated db name
|
||||
*/
|
||||
end = strxmov(name_in_query, table->table_cache_key, "`.`",
|
||||
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);
|
||||
end = strxmov(end,"`",table->table_cache_key,"`.`",
|
||||
table->real_name,"`,", NullS);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -1472,6 +1472,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
|
||||
{
|
||||
if (alloc_query(thd, packet, packet_length))
|
||||
break; // fatal error is set
|
||||
char *packet_end= thd->query + thd->query_length;
|
||||
mysql_log.write(thd,command,"%s",thd->query);
|
||||
DBUG_PRINT("query",("%-.4096s",thd->query));
|
||||
mysql_parse(thd,thd->query, thd->query_length);
|
||||
@ -1487,7 +1488,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
|
||||
if (thd->lock || thd->open_tables || thd->derived_tables)
|
||||
close_thread_tables(thd);
|
||||
#endif
|
||||
ulong length= thd->query_length-(ulong)(packet-thd->query);
|
||||
ulong length= (ulong)(packet_end-packet);
|
||||
|
||||
/* Remove garbage at start of query */
|
||||
while (my_isspace(thd->charset(), *packet) && length > 0)
|
||||
@ -1924,9 +1925,14 @@ mysql_execute_command(THD *thd)
|
||||
}
|
||||
/*
|
||||
Skip if we are in the slave thread, some table rules have been
|
||||
given and the table list says the query should not be replicated
|
||||
given and the table list says the query should not be replicated.
|
||||
Exception is DROP TEMPORARY TABLE IF EXISTS: we always execute it
|
||||
(otherwise we have stale files on slave caused by exclusion of one tmp
|
||||
table).
|
||||
*/
|
||||
if (all_tables_not_ok(thd,tables))
|
||||
if (!(lex->sql_command == SQLCOM_DROP_TABLE &&
|
||||
lex->drop_temporary && lex->drop_if_exists) &&
|
||||
all_tables_not_ok(thd,tables))
|
||||
{
|
||||
/* we warn the slave SQL thread */
|
||||
my_error(ER_SLAVE_IGNORED_TABLE, MYF(0));
|
||||
@ -4118,6 +4124,20 @@ void mysql_parse(THD *thd, char *inBuf, uint length)
|
||||
send_error(thd, 0, NullS);
|
||||
else
|
||||
{
|
||||
/*
|
||||
Binlog logs a string starting from thd->query and having length
|
||||
thd->query_length; so we set thd->query_length correctly (to not
|
||||
log several statements in one event, when we executed only first).
|
||||
We set it to not see the ';' (otherwise it would get into binlog
|
||||
and Query_log_event::print() would give ';;' output).
|
||||
This also helps display only the current query in SHOW
|
||||
PROCESSLIST.
|
||||
Note that we don't need LOCK_thread_count to modify query_length.
|
||||
*/
|
||||
if (lex->found_colon &&
|
||||
(thd->query_length= (ulong)(lex->found_colon - thd->query)))
|
||||
thd->query_length--;
|
||||
/* Actually execute the query */
|
||||
mysql_execute_command(thd);
|
||||
query_cache_end_of_result(thd);
|
||||
}
|
||||
|
Reference in New Issue
Block a user