1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338
|
# ==== Purpose ====
#
# In order to avoid problems with GTIDs, MySQL server should refuse to execute
# any statements that will be split before sending to binlog if
# @@SESSION.GTID_NEXT is set to 'UUID:NUMBER'.
#
# In MySQL server, DROP TABLE statements can be split into up to three
# distinct binlog events: one containing the regular tables, one containing
# transactional temporary tables and one containing non-transactional
# temporary tables.
#
# In the first part, this test will verify if a splittable DROP TABLE
# statement is being refused to execute throwing the correct error to the
# client session.
#
# In the second part, this test will verify an issue with inexistent temporary
# tables being assumed as transactional on the slave side. This was making
# the slave to split a DROP statement that the master logged as a single DROP
# statement.
#
# In the third part, this test will verify that a slave having to drop
# temporary tables because of the master have restarted will split the DROP
# TABLE statement also based on table types (transactional or not).
#
# The details about the implementation of this test is in the beginning of
# each part.
#
# ==== Related Bugs and Worklogs ====
#
# BUG#17620053: SET GTID_NEXT AND DROP TABLE WITH BOTH REGULAR AND TEMPORARY
# TABLES
#
--let $rpl_topology= 1->2->3
--source include/rpl_init.inc
--source include/rpl_default_connections.inc
--source include/have_myisam.inc
--echo #
--echo # First part
--echo #
#
# This part verifies the splittable DROP TABLE behavior in client sessions by
# creating three tables (one regular and two temporary), setting session
# GTID_NEXT to a specific GTID and then trying to issue a DROP TABLE
# statement containing the combinations of at least two tables.
#
# This part only applies to statement binary log format, as otherwise temporary
# table operations are completely invisible to the binary log.
#
# As DROP statements with both regular and temporary tables or with
# transactional and non transactional temporary tables are split before
# sending to binlog, the following DROP statements must return an error to the
# client session because of being considered unsafe for GTID enforcement as it
# would be split into two (or three) statements to be sent to binlog.
#
# In order to improve the test coverage, we will verify DROP TABLE statements
# with all combinations of the following:
# 1) Regular transactional table;
# 2) Regular non-transactional table;
# 3) Temporary transactional table;
# 4) Temporary non-transactional table;
# 5) Nonexistent table.
#
# With the above set we could make up to 32 possibilities (including none of
# above). But we will test the behavior only for the following combinations:
# - 18 error cases (ER_GTID_UNSAFE_BINLOG_SPLITTABLE_STATEMENT_AND_ASSIGNED_GTID):
# (1 | 2 | 1+2) + (3 | 4 | 3+4) + (nothing | 5)
# (3+4) | (3+4+5)
# (3 | 4) + 5 /* as this it not DROP TEMPORARY, nonexistent tables will be
# assumed as regular tables, so the DROP will be split */
# - 3 error cases (ER_BAD_TABLE_ERROR):
# (1 | 2 | 1+2) + 5
# - 1 non-error case:
# 1+2
# Note: for ER_GTID_UNSAFE_BINLOG_SPLITTABLE_STATEMENT_AND_ASSIGNED_GTID cases, the
# statement will not be executed and no DROP will be performed. For
# ER_BAD_TABLE_ERROR cases, the existent tables will be dropped regardless the
# error.
# Create two regular tables and two temporary tables with distinct storage engines
--source include/rpl_connection_master.inc
CREATE TABLE trans_t1 (c1 INT) ENGINE=InnoDB;
CREATE TABLE non_trans_t1 (c1 INT) ENGINE=MyISAM;
CREATE TEMPORARY TABLE temp_trans_t1 (c1 INT) ENGINE=InnoDB;
CREATE TEMPORARY TABLE temp_non_trans_t1 (c1 INT) ENGINE=MyISAM;
--let $binlog_format= `SELECT @@GLOBAL.binlog_format`
if ($binlog_format == "STATEMENT")
{
--echo # Error cases ER_GTID_UNSAFE_BINLOG_SPLITTABLE_STATEMENT_AND_ASSIGNED_GTID
--let $next_gtid= '11111111-1111-1111-1111-111111111111:1'
--let $expected_error= ER_GTID_UNSAFE_BINLOG_SPLITTABLE_STATEMENT_AND_ASSIGNED_GTID
# reg trans + reg non trans + temp trans
--let $statement= DROP TABLE trans_t1, non_trans_t1, temp_trans_t1
--source extra/rpl_tests/rpl_gtid_drop_table.inc
# reg trans + reg non trans + temp trans + temp non trans
--let $statement= DROP TABLE trans_t1, non_trans_t1, temp_trans_t1, temp_non_trans_t1
--source extra/rpl_tests/rpl_gtid_drop_table.inc
# reg trans + reg non trans + temp trans + temp non trans + inexistent
--let $statement= DROP TABLE trans_t1, non_trans_t1, temp_trans_t1, temp_non_trans_t1, non_existent
--source extra/rpl_tests/rpl_gtid_drop_table.inc
# reg trans + reg non trans + temp trans + inexistent
--let $statement= DROP TABLE trans_t1, non_trans_t1, temp_trans_t1, non_existent
--source extra/rpl_tests/rpl_gtid_drop_table.inc
# reg trans + reg non trans + temp non trans
--let $statement= DROP TABLE trans_t1, non_trans_t1, temp_non_trans_t1
--source extra/rpl_tests/rpl_gtid_drop_table.inc
# reg trans + reg non trans + temp non trans + inexistent
--let $statement= DROP TABLE trans_t1, non_trans_t1, temp_non_trans_t1, non_existent
--source extra/rpl_tests/rpl_gtid_drop_table.inc
# reg trans + temp trans
--let $statement= DROP TABLE trans_t1, temp_trans_t1
--source extra/rpl_tests/rpl_gtid_drop_table.inc
# reg trans + temp trans + temp non trans
--let $statement= DROP TABLE trans_t1, temp_trans_t1, temp_non_trans_t1
--source extra/rpl_tests/rpl_gtid_drop_table.inc
# reg trans + temp trans + temp non trans + inexistent
--let $statement= DROP TABLE trans_t1, temp_trans_t1, temp_non_trans_t1, non_existent
--source extra/rpl_tests/rpl_gtid_drop_table.inc
# reg trans + temp trans + inexistent
--let $statement= DROP TABLE trans_t1, temp_trans_t1, non_existent
--source extra/rpl_tests/rpl_gtid_drop_table.inc
# reg trans + temp non trans
--let $statement= DROP TABLE trans_t1, temp_non_trans_t1
--source extra/rpl_tests/rpl_gtid_drop_table.inc
# reg trans + temp non trans + inexistent
--let $statement= DROP TABLE trans_t1, temp_non_trans_t1, non_existent
--source extra/rpl_tests/rpl_gtid_drop_table.inc
# reg non trans + temp trans
--let $statement= DROP TABLE non_trans_t1, temp_trans_t1
--source extra/rpl_tests/rpl_gtid_drop_table.inc
# reg non trans + temp trans + temp non trans
--let $statement= DROP TABLE non_trans_t1, temp_trans_t1, temp_non_trans_t1
--source extra/rpl_tests/rpl_gtid_drop_table.inc
# reg non trans + temp trans + temp non trans + inexistent
--let $statement= DROP TABLE non_trans_t1, temp_trans_t1, temp_non_trans_t1, non_existent
--source extra/rpl_tests/rpl_gtid_drop_table.inc
# reg non trans + temp trans + inexistent
--let $statement= DROP TABLE non_trans_t1, temp_trans_t1, non_existent
--source extra/rpl_tests/rpl_gtid_drop_table.inc
# reg non trans + temp non trans
--let $statement= DROP TABLE non_trans_t1, temp_non_trans_t1
--source extra/rpl_tests/rpl_gtid_drop_table.inc
# reg non trans + temp non trans + inexistent
--let $statement= DROP TABLE non_trans_t1, temp_non_trans_t1, non_existent
--source extra/rpl_tests/rpl_gtid_drop_table.inc
# temp trans + temp non trans
--let $statement= DROP TABLE temp_trans_t1, temp_non_trans_t1
--source extra/rpl_tests/rpl_gtid_drop_table.inc
# temp trans + temp non trans + inexistent
--let $statement= DROP TABLE temp_trans_t1, temp_non_trans_t1, non_existent
--source extra/rpl_tests/rpl_gtid_drop_table.inc
# temp trans + inexistent
--let $statement= DROP TABLE temp_trans_t1, non_existent
--source extra/rpl_tests/rpl_gtid_drop_table.inc
# temp non trans + inexistent
--let $statement= DROP TABLE temp_non_trans_t1, non_existent
--source extra/rpl_tests/rpl_gtid_drop_table.inc
}
--echo # Error cases ER_BAD_TABLE_ERROR.
# All error cases below are no-ops.
--let $expected_error= ER_BAD_TABLE_ERROR
# reg trans + reg non trans + inexistent
--let $next_gtid= '11111111-1111-1111-1111-111111111111:1'
--let $statement= DROP TABLE trans_t1, non_trans_t1, non_existent
--source extra/rpl_tests/rpl_gtid_drop_table.inc
# reg trans + inexistent
--let $next_gtid= '11111111-1111-1111-1111-111111111111:2'
--let $statement= DROP TABLE trans_t1, non_existent
--source extra/rpl_tests/rpl_gtid_drop_table.inc
# reg non trans + inexistent
--let $next_gtid= '11111111-1111-1111-1111-111111111111:3'
--let $statement= DROP TABLE non_trans_t1, non_existent
--source extra/rpl_tests/rpl_gtid_drop_table.inc
--echo # Non-error cases
--let $expected_error=
# reg trans + reg non trans
--let $next_gtid= '11111111-1111-1111-1111-111111111111:4'
--let $statement= DROP TABLE trans_t1, non_trans_t1
--source extra/rpl_tests/rpl_gtid_drop_table.inc
SET GTID_NEXT= AUTOMATIC;
CREATE TABLE trans_t1 (c1 INT) ENGINE=InnoDB;
--echo #
--echo # Second part
--echo #
#
# The MySQL server cannot evaluate if an inexistent temporary table is
# transactional or not. Before the fix for BUG#17620053, the inexistent
# temporary tables were assumed to be transactional.
#
# This assumption could lead to a split if, for example, you issue a DROP
# TEMPORARY TABLE for two existent non-transactional temporary tables in the
# master (will binlog only one statement) but a filter made one of the
# temporary tables inexistent on the slave side (will binlog two statements:
# one with the existent non-transactional table and other with the inexistent
# table assumed to be transactional).
#
# As this test has a filter in its slave configuration like this:
# '--replicate-ignore-table=test.temp_ignore', all statements containing the
# 'test.temp_ignore' temporary table alone wont be executed by the SQL slave
# thread. So, the CREATE TABLE statements for 'temp_ignore' table will be
# executed only on the master.
#
# Issuing a DROP TEMPORARY TABLE statement in the master containing the two
# non-transaction temporary tables would result in a single binlog statement,
# as the two tables are non-transactional ones.
#
# In the slave, because of the filter, the non-replicated table will be
# assumed unknown. With the fix for BUG#17620053, the unknown tables will
# be assumed as transactional only if at least one transactional table is
# dropped. If the DROP recognizes only non-transactional tables, the unknown
# temporary tables will be assumed non-transactional, avoiding splitting the
# statement.
# Create an additional non-transactional temporary table that will not be
# replicated due to the replication filter, only t1 will be replicated.
CREATE TEMPORARY TABLE temp_ignore (c1 INT) ENGINE=MyISAM;
# Drop the two non-transactional temp tables
DROP TEMPORARY TABLE IF EXISTS temp_ignore, temp_non_trans_t1;
# Sync to know that everything was replicated
--source include/sync_slave_sql_with_master.inc
# Back to the 'master' connection
--source include/rpl_connection_master.inc
# Create temp_ignore table again, but with InnoDB storage engine
CREATE TEMPORARY TABLE temp_ignore (c1 INT) ENGINE=InnoDB;
# Drop the two transactional temp tables
DROP TEMPORARY TABLE IF EXISTS temp_ignore, temp_trans_t1;
# Sync to know that everything was replicated
--source include/sync_slave_sql_with_master.inc
--echo #
--echo # Third part
--echo #
#
# This part was adapted from rpl_create_drop_temp_table.test
#
# As we use three server topology, when the slave (mysqld 2) detects the
# master (mysqld 1) has restarted it will drop the temporary tables and
# will binlog the DROP TABLE statements that will be replicated to
# the third server (mysqld 3).
#
# As the DROP of temporary table in the slave side groups the tables
# by pseudo-threadid and by database, we will use two sessions on master,
# creating three sets of temporary tables, each set on a distinct database.
#
# The first set contains two tables with distinct storage engines, the
# second set contains a single table and the third set contains two tables
# with the same storage engine.
#
--source include/rpl_connection_master.inc
SET SESSION sql_log_bin= 0;
call mtr.add_suppression("Error: table .* does not exist in the InnoDB internal");
SET SESSION sql_log_bin= 1;
CREATE DATABASE test2;
CREATE DATABASE test3;
# Create two temporary tables with distinct storage engines in 'test'
# This should generate two DROP statements
use test;
CREATE TEMPORARY TABLE temp_trans_t1 (c1 INT) ENGINE=InnoDB;
CREATE TEMPORARY TABLE temp_non_trans_t1 (c1 INT) ENGINE=MyISAM;
# Create one temporary table with transactional storage engine in 'test2'
# This should generate only one DROP statement
USE test2;
CREATE TEMPORARY TABLE temp_trans_t1 (c1 INT) ENGINE=InnoDB;
# Create two temporary tables with transactional storage engine in 'test3'
# This should generate only one DROP statement with the two tables
USE test3;
CREATE TEMPORARY TABLE temp_trans_t1 (c1 INT) ENGINE=InnoDB;
CREATE TEMPORARY TABLE temp_trans_t2 (c1 INT) ENGINE=InnoDB;
# Create two temporary tables with distinct storage engines in 'test'
# in another client session. This should generate two DROP statements
--source include/rpl_connection_master1.inc
use test;
CREATE TEMPORARY TABLE temp_trans_t1 (c1 INT) ENGINE=InnoDB;
CREATE TEMPORARY TABLE temp_non_trans_t1 (c1 INT) ENGINE=MyISAM;
# Create one temporary table with non-transactional storage engine in 'test2'
# This should generate only one DROP statement
USE test2;
CREATE TEMPORARY TABLE temp_non_trans_t1 (c1 INT) ENGINE=MyISAM;
# Create two temporary tables with non-transactional storage engine in 'test3'
# This should generate only one DROP statement with the two tables
USE test3;
CREATE TEMPORARY TABLE temp_non_trans_t1 (c1 INT) ENGINE=MyISAM;
CREATE TEMPORARY TABLE temp_non_trans_t2 (c1 INT) ENGINE=MyISAM;
# Sync to know that everything was replicated
--source include/sync_slave_sql_with_master.inc
# Stop slave io thread
--disable_warnings
--source include/stop_slave_io.inc
--enable_warnings
# Kill Master so that it does not go through THD::cleanup logic. Hence it does
# not generate "drop temporary" query for 'temp' tables.
--let $rpl_server_number= 1
--let $rpl_force_stop=1
--source include/rpl_stop_server.inc
# Restart Master (generates Format Description event which tells slave to
# drop all temporary tables)
--source include/rpl_start_server.inc
# Start and sync slave IO thread
--source include/rpl_connection_slave.inc
--source include/start_slave_io.inc
--source include/rpl_connection_master.inc
--source include/sync_slave_io_with_master.inc
# Wait for slave thread to apply all events (including the newly generated
# FormatDescription event which tells slave SQL thread to drop all temporary
--let $show_statement= SHOW PROCESSLIST
--let $field= State
--let $condition= 'Replica has read all relay log; waiting for the slave I/O thread to update it';
--source include/wait_show_condition.inc
# Now we verify replica_open_temp_tables, it should be '0'
--let $assert_text= Replica_open_temp_tables should be 0
--let $assert_cond= [SHOW STATUS LIKE "Replica_open_temp_tables", Value, 1] = 0
--source include/assert.inc
# Cleanup replication
--source include/rpl_connection_master.inc
USE test;
DROP TABLE trans_t1;
DROP DATABASE test2;
DROP DATABASE test3;
--source include/rpl_end.inc
|