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 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 `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`; 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`.`shortn2`,`drop-temp+table-test`.`table:name`,`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`.`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`; DO RELEASE_LOCK("a")
|
master-bin.000001 # Query 1 # use `drop-temp+table-test`; DO RELEASE_LOCK("a")
|
||||||
drop database `drop-temp+table-test`;
|
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)
|
void close_temporary_tables(THD *thd)
|
||||||
{
|
{
|
||||||
TABLE *table,*next;
|
TABLE *table,*next;
|
||||||
char *query, *name_in_query, *end;
|
char *query, *end;
|
||||||
uint greatest_key_length= 0;
|
uint query_buf_size;
|
||||||
|
bool found_user_tables = 0;
|
||||||
|
|
||||||
if (!thd->temporary_tables)
|
if (!thd->temporary_tables)
|
||||||
return;
|
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);
|
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)
|
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
|
// 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)
|
for (table=thd->temporary_tables ; table ; table=next)
|
||||||
{
|
{
|
||||||
/*
|
if (query) // we might be out of memory, but this is not fatal
|
||||||
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] != '#'))
|
|
||||||
{
|
{
|
||||||
|
// 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
|
Here we assume table_cache_key always starts
|
||||||
with \0 terminated db name
|
with \0 terminated db name
|
||||||
*/
|
*/
|
||||||
end = strxmov(name_in_query, table->table_cache_key, "`.`",
|
end = strxmov(end,"`",table->table_cache_key,"`.`",
|
||||||
table->real_name, "`", NullS);
|
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);
|
|
||||||
}
|
}
|
||||||
next=table->next;
|
next=table->next;
|
||||||
close_temporary(table);
|
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;
|
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))
|
if (alloc_query(thd, packet, packet_length))
|
||||||
break; // fatal error is set
|
break; // fatal error is set
|
||||||
|
char *packet_end= thd->query + thd->query_length;
|
||||||
mysql_log.write(thd,command,"%s",thd->query);
|
mysql_log.write(thd,command,"%s",thd->query);
|
||||||
DBUG_PRINT("query",("%-.4096s",thd->query));
|
DBUG_PRINT("query",("%-.4096s",thd->query));
|
||||||
mysql_parse(thd,thd->query, thd->query_length);
|
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)
|
if (thd->lock || thd->open_tables || thd->derived_tables)
|
||||||
close_thread_tables(thd);
|
close_thread_tables(thd);
|
||||||
#endif
|
#endif
|
||||||
ulong length= thd->query_length-(ulong)(packet-thd->query);
|
ulong length= (ulong)(packet_end-packet);
|
||||||
|
|
||||||
/* Remove garbage at start of query */
|
/* Remove garbage at start of query */
|
||||||
while (my_isspace(thd->charset(), *packet) && length > 0)
|
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
|
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 */
|
/* we warn the slave SQL thread */
|
||||||
my_error(ER_SLAVE_IGNORED_TABLE, MYF(0));
|
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);
|
send_error(thd, 0, NullS);
|
||||||
else
|
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);
|
mysql_execute_command(thd);
|
||||||
query_cache_end_of_result(thd);
|
query_cache_end_of_result(thd);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user