diff options
-rw-r--r-- | mysql-test/suite/rpl/r/rpl_gtid_misc.result | 25 | ||||
-rw-r--r-- | mysql-test/suite/rpl/t/rpl_gtid_misc.test | 50 | ||||
-rw-r--r-- | sql/rpl_gtid.cc | 25 | ||||
-rw-r--r-- | sql/rpl_gtid.h | 1 | ||||
-rw-r--r-- | sql/sql_repl.cc | 25 |
5 files changed, 126 insertions, 0 deletions
diff --git a/mysql-test/suite/rpl/r/rpl_gtid_misc.result b/mysql-test/suite/rpl/r/rpl_gtid_misc.result new file mode 100644 index 00000000000..cdaac1b1d34 --- /dev/null +++ b/mysql-test/suite/rpl/r/rpl_gtid_misc.result @@ -0,0 +1,25 @@ +include/master-slave.inc +[connection master] +*** MDEV-6403: Temporary tables lost at STOP SLAVE in GTID mode if master has not rotated binlog since restart *** +CREATE TABLE t1 (a INT PRIMARY KEY); +include/stop_slave.inc +SET sql_log_bin= 0; +INSERT INTO t1 VALUES (1); +SET sql_log_bin= 1; +CHANGE MASTER TO master_use_gtid= current_pos; +CREATE TEMPORARY TABLE t2 LIKE t1; +INSERT INTO t2 VALUE (1); +INSERT INTO t1 SELECT * FROM t2; +DROP TEMPORARY TABLE t2; +START SLAVE; +include/wait_for_slave_sql_error.inc [errno=1062] +STOP SLAVE IO_THREAD; +SET sql_log_bin= 0; +DELETE FROM t1 WHERE a=1; +SET sql_log_bin= 1; +include/start_slave.inc +SELECT * FROM t1 ORDER BY a; +a +1 +DROP TABLE t1; +include/rpl_end.inc diff --git a/mysql-test/suite/rpl/t/rpl_gtid_misc.test b/mysql-test/suite/rpl/t/rpl_gtid_misc.test new file mode 100644 index 00000000000..66d98ec8025 --- /dev/null +++ b/mysql-test/suite/rpl/t/rpl_gtid_misc.test @@ -0,0 +1,50 @@ +--source include/master-slave.inc + +--echo *** MDEV-6403: Temporary tables lost at STOP SLAVE in GTID mode if master has not rotated binlog since restart *** + +--connection master +CREATE TABLE t1 (a INT PRIMARY KEY); +--sync_slave_with_master + +--connection slave +--source include/stop_slave.inc +# Inject a duplicate key error that will make the slave stop in the middle of +# a sequence of transactions that use a temporary table. +SET sql_log_bin= 0; +INSERT INTO t1 VALUES (1); +SET sql_log_bin= 1; +CHANGE MASTER TO master_use_gtid= current_pos; + +--connection master + +# Make some queries that use a temporary table. +CREATE TEMPORARY TABLE t2 LIKE t1; +INSERT INTO t2 VALUE (1); +INSERT INTO t1 SELECT * FROM t2; +DROP TEMPORARY TABLE t2; +--save_master_pos + +--connection slave +START SLAVE; +--let $slave_sql_errno=1062 +--source include/wait_for_slave_sql_error.inc + +# Restart the slave. +# The bug was that the IO thread would receive again the restart +# format_description event at the start of the master's binlog, and this +# event would cause the SQL thread to discard all active temporary tables. + +STOP SLAVE IO_THREAD; + +SET sql_log_bin= 0; +DELETE FROM t1 WHERE a=1; +SET sql_log_bin= 1; + +--source include/start_slave.inc +--sync_with_master +SELECT * FROM t1 ORDER BY a; + +--connection master +DROP TABLE t1; + +--source include/rpl_end.inc diff --git a/sql/rpl_gtid.cc b/sql/rpl_gtid.cc index d4c7b2c3fbf..f17ece298d3 100644 --- a/sql/rpl_gtid.cc +++ b/sql/rpl_gtid.cc @@ -1870,6 +1870,31 @@ slave_connection_state::get_gtid_list(rpl_gtid *gtid_list, uint32 list_size) /* + Check if the GTID position has been reached, for mysql_binlog_send(). + + The position has not been reached if we have anything in the state, unless + it has either the START_ON_EMPTY_DOMAIN flag set (which means it does not + belong to this master at all), or the START_OWN_SLAVE_POS (which means that + we start on an old position from when the server was a slave with + --log-slave-updates=0). +*/ +bool +slave_connection_state::is_pos_reached() +{ + uint32 i; + + for (i= 0; i < hash.records; ++i) + { + entry *e= (entry *)my_hash_element(&hash, i); + if (!(e->flags & (START_OWN_SLAVE_POS|START_ON_EMPTY_DOMAIN))) + return false; + } + + return true; +} + + +/* Execute a MASTER_GTID_WAIT(). The position to wait for is in gtid_str in string form. The timeout in microseconds is in timeout_us, zero means no timeout. diff --git a/sql/rpl_gtid.h b/sql/rpl_gtid.h index 22771833845..997540728a5 100644 --- a/sql/rpl_gtid.h +++ b/sql/rpl_gtid.h @@ -288,6 +288,7 @@ struct slave_connection_state int to_string(String *out_str); int append_to_string(String *out_str); int get_gtid_list(rpl_gtid *gtid_list, uint32 list_size); + bool is_pos_reached(); }; diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index 68535938484..2b5572609d8 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -2375,6 +2375,31 @@ impossible position"; info.fdev= tmp; (*packet)[FLAGS_OFFSET+ev_offset] &= ~LOG_EVENT_BINLOG_IN_USE_F; + + if (info.using_gtid_state) + { + /* + If this event has the field `created' set, then it will cause the + slave to delete all active temporary tables. This must not happen + if the slave received any later GTIDs in a previous connect, as + those GTIDs might have created new temporary tables that are still + needed. + + So here, we check if the starting GTID position was already + reached before this format description event. If not, we clear the + `created' flag to preserve temporary tables on the slave. (If the + slave connects at a position past this event, it means that it + already received and handled it in a previous connect). + */ + if (!info.gtid_state.is_pos_reached()) + { + int4store((char*) packet->ptr()+LOG_EVENT_MINIMAL_HEADER_LEN+ + ST_CREATED_OFFSET+ev_offset, (ulong) 0); + if (info.current_checksum_alg != BINLOG_CHECKSUM_ALG_OFF && + info.current_checksum_alg != BINLOG_CHECKSUM_ALG_UNDEF) + fix_checksum(packet, ev_offset); + } + } } #ifndef DBUG_OFF |