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 339 340 341 342 343 344 345 346 347 348
|
# ==== Purpose ====
#
# A few special SQL statements/constructs can generate multiple
# transactions in the binary log. This poses interesting problems
# especially when it comes to generating and preserving GTIDs.
#
# There are four cases to consider:
#
# - When GTID_MODE=ON/ON_PERMISSIVE and GTID_NEXT=AUTOMATIC, a
# separate GTID should be generated for each of the statements.
#
# - When GTID_MODE=ON and GTID_NEXT=UUID:NUMBER, an error should be
# generated since it is impossible to log the statement using just
# the given GTID. The exact point when error can be generated
# (before starting to execute, after executing the first transaction
# of the statement, or after executing all transactions of the
# statement) depends on the statement.
#
# - When GTID_MODE=OFF/OFF_PERMISSIVE and GTID_NEXT=AUTOMATIC, an
# Anonymous_gtid_log_event should be generated for each of the
# statements.
#
# - When GTID_MODE=OFF and GTID_NEXT=ANONYMOUS, an
# Anonymous_gtid_log_event should be generated for each of the
# statements. Moreover, anonymous ownership should not be released
# until the last transaction generated by the statement is written
# to the binary log.
#
# The following statements can generate multiple transactions in the
# binary log:
#
# 1. CALL: when a stored procedure executes multiple statements in
# autocommit mode, each statement will be logged as a separate
# transaction.
#
# 2. DROP TABLE: when a single statement drops multiple tables, then
# there will be a separate statement for all dropped non-temporary
# tables, with statement binlog format a separate statement for all
# dropped transactional temporary tables, and a separate statement
# for all dropped non-transactional temporary tables. Moreover,
# statement for all dropped non-temporary tables can be split
# further. If no GTID is assigned then there will be individual
# DROP TABLE statements for each dropped table in SEs which don't
# support atomic DDL and single statement for all dropped tables in
# SEs supporting atomic DDL. If original DROP TABLE statement is
# supposed to be executed under single GTID then all dropped base
# tables (both supporting atomic DDL and not) are combined in a
# single statement. Execution of statements which drop both
# temporary and non-temporary tables or two kinds of temporary
# tables under single GTID is not allowed.
#
# 3. DROP DATABASE: See details in rpl_split_statements_debug.test
#
# 4. CREATE TABLE ... SELECT: this statement contains both DDL and
# DML. When binlog_format='ROW', all DML must be logged in row
# format, but at the same time DDL can only be logged in statement
# format. Therefore this statement is logged as a CREATE TABLE
# followed by row events. This statement is not allowed when
# GTID_MODE = ON.
#
# This test verifies that all these statements work, for all values of
# GTID_MODE/GTID_NEXT, and both on a client connection and in a slave
# connection.
#
# ==== Implementation ====
#
# 1. CALL. We execute a multi-transaction CALL:
#
# - On master with GTID_MODE='AUTOMATIC';
# - On master with GTID_MODE!='AUTOMATIC' ('ANONYMOUS'/'UUID:NUMBER',
# depending on GTID_MODE)
# - Not on slave. Since CALL does not get logged as CALL, there is
# no way to get it in the binary log.
#
# 2. DROP TABLE.
#
# First, we drop all combinations of two or three tables, from the
# different categories non-temporary, temporary transactional, and
# temporary non-transactional. This is done in three ways:
#
# - On master with GTID_MODE='AUTOMATIC'
# - On master with GTID_MODE!='AUTOMATIC' ('ANONYMOUS'/'UUID:NUMBER',
# depending on GTID_MODE)
# - On slave. This can be done using different table definitions
# on master and slave. On master we use only non-temporary
# tables, so that any DROP TABLE statement will succeed and make
# it to the binary log. In order to make the tables temporary
# on slave, we first create the non-temporary table on master,
# then sync it to slave, then drop it on slave, and then use a
# stored procedure on master that creates a temporary table with
# the same name *only* when it executes on the slave.
#
# Second, we drop group of 4 tables, where 2 tables are in SE which
# do not support atomic DDL and 2 in SE which support it. We do it
# in both GTID_MODE='AUTOMATIC' and GTID_MODE='UUID:NUMBER' to see
# if they are split into several/combined into single statement
# according to GTID_MODE.
#
# Third, we drop group of 4 tables, where 2 tables are in SE which
# do not support atomic DDL and 2 in SE which support it and then
# fail. This triggers code path in DROP TABLE implementation
# which differs from case when there is no failure. This test has
# been moved into rpl_split_statements_debug.test.
#
# Note that two last tests also provide coverage for changes to binary
# logging and GTID handling for normal and failed DROP TABLES, which
# were implemented as part of WL#7743 "New data dictionary: changes to
# DDL-related parts of SE API".
#
# 3. DROP DATABASE. Tests for this statement were moved into
# rpl_split_statements_debug.test.
#
# 4. CREATE TABLE ... SELECT. This is tested in a different test
# (rpl_gtid_create_select.test), since the statement cannot be
# executed if gtid_mode=on.
#
# ==== Reference ====
#
# WL#7592: GTIDs: Generate Gtid_log_event and Previous_gtids_log_event always
# - Test was introduced by this worklog.
# It suffices to test one binlog_format.
--source include/have_binlog_format_statement.inc
--let $rpl_gtid_utils= 1
--source include/set_privilege_checks_user_as_system_user.inc
--source include/master-slave.inc
CALL mtr.add_suppression('Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT.');
if ($gtid_mode_on)
{
CALL mtr.add_suppression('Cannot execute statement because it needs to be written to the binary log as multiple statements');
CALL mtr.add_suppression('DROP DATABASE failed; some tables may have been dropped but the database directory remains.');
}
--connection slave
CALL mtr.add_suppression("Error dropping database");
CALL mtr.add_suppression("Can't drop database '.*'; database doesn't exist");
CALL mtr.add_suppression("Replica SQL for channel '': ... The replica coordinator and worker threads are stopped, possibly leaving data in inconsistent state. .* Error_code: 1756");
--connection master
--let $gtid_step_gtid_mode_agnostic= 1
--echo ==== Case 1: CALL is split on master ====
# Note: CALL cannot be executed on a slave, since CALL statements are
# not written as CALL in the binary log.
--echo ---- Initialize ----
CREATE TABLE t (a INT);
--delimiter |
CREATE PROCEDURE proc ()
BEGIN
INSERT INTO t VALUES (1);
INSERT INTO t VALUES (2);
END|
--delimiter ;
--echo ---- GTID_NEXT=AUTOMATIC ----
--source include/gtid_step_reset.inc
CALL proc();
--let $gtid_step_count= 2
--source include/gtid_step_assert.inc
--let $assert_cond= COUNT(*) = 2 FROM t
--let $assert_text= Both rows were inserted
--source include/assert.inc
DELETE FROM t;
--echo ---- GTID_NEXT=non-automatic ----
--source include/gtid_step_reset.inc
--source include/set_gtid_next_gtid_mode_agnostic.inc
if ($gtid_mode_on)
{
--replace_result $server_1_uuid SOURCE_UUID
--error ER_GTID_NEXT_TYPE_UNDEFINED_GTID
CALL proc();
}
if (!$gtid_mode_on)
{
CALL proc();
}
SET GTID_NEXT= 'AUTOMATIC';
--let $gtid_step_count= 1
--source include/gtid_step_assert.inc
if ($gtid_mode_on)
{
--let $assert_cond= COUNT(*) = 1 FROM t
}
if (!$gtid_mode_on)
{
--let $assert_cond= COUNT(*) = 2 FROM t
}
--let $assert_text= One row inserted if GTID_MODE=ON, two if GTID_MODE=OFF
--source include/assert.inc
DROP TABLE t;
DROP PROCEDURE proc;
--echo ==== Case 2A: DROP TABLE is split on master ====
--echo ---- Initialize ----
--delimiter |
CREATE PROCEDURE create_tables()
BEGIN
CREATE TABLE base (a INT) ENGINE = InnoDB;
CREATE TEMPORARY TABLE temp_t (a INT) ENGINE = InnoDB;
CREATE TEMPORARY TABLE temp_n (a INT) ENGINE = MyISAM;
END|
CREATE PROCEDURE drop_tables()
BEGIN
DROP TABLE IF EXISTS base;
DROP TABLE IF EXISTS temp_t;
DROP TABLE IF EXISTS temp_n;
END|
--delimiter ;
--source include/rpl_sync.inc
--echo ---- GTID_MODE=AUTOMATIC ----
--let $automatic= 1
--source extra/rpl_tests/rpl_drop_multiple_tables_in_multiple_ways.inc
--echo ---- GTID_MODE=non-automatic ----
--let $automatic= 0
--source extra/rpl_tests/rpl_drop_multiple_tables_in_multiple_ways.inc
--echo ==== Case 2B: DROP TABLE is split on slave ====
--echo ---- Initialize ----
CREATE TABLE dummy (a INT);
DROP PROCEDURE create_tables;
--delimiter |
CREATE FUNCTION create_tables_func() RETURNS INT
BEGIN
IF @@GLOBAL.SERVER_ID = 2 THEN
CREATE TEMPORARY TABLE temp_t (a INT) ENGINE = InnoDB;
CREATE TEMPORARY TABLE temp_n (a INT) ENGINE = MyISAM;
END IF;
RETURN 0;
END|
CREATE PROCEDURE create_tables()
BEGIN
CREATE TABLE base (a INT);
SET @@SESSION.SQL_LOG_BIN = 0;
CREATE TABLE temp_t (a INT);
CREATE TABLE temp_n (a INT);
SET @@SESSION.SQL_LOG_BIN = 1;
INSERT INTO dummy VALUES (create_tables_func());
END|
--delimiter ;
--source include/rpl_sync.inc
--echo ---- GTID_MODE=AUTOMATIC ----
--let $automatic= 1
--let $transaction_count= 1
--source extra/rpl_tests/rpl_drop_multiple_tables_in_multiple_ways.inc
--echo ---- Clean up ----
DROP FUNCTION create_tables_func;
DROP PROCEDURE create_tables;
DROP PROCEDURE drop_tables;
DROP TABLE dummy;
--echo ==== Case 2C: DROP TABLES for base tables in engines with and without atomic DDL support ====
CREATE TABLE base_1_n (a INT) ENGINE = MyISAM;
CREATE TABLE base_2_n (a INT) ENGINE = MyISAM;
CREATE TABLE base_3_a (a INT) ENGINE = InnoDB;
CREATE TABLE base_4_a (a INT) ENGINE = InnoDB;
--source include/rpl_sync.inc
--echo ---- GTID_MODE=AUTOMATIC ----
--source include/gtid_step_reset.inc
DROP TABLES base_1_n, base_2_n, base_3_a, base_4_a;
--echo # In AUTOMATIC mode the above statement should be split into three:
--echo # two statements for each of MyISAM tables and single statement
--echo # dropping both of InnoDB tables.
--let $gtid_step_count= 3
--source include/gtid_step_assert.inc
CREATE TABLE base_1_n (a INT) ENGINE = MyISAM;
CREATE TABLE base_2_n (a INT) ENGINE = MyISAM;
CREATE TABLE base_3_a (a INT) ENGINE = InnoDB;
CREATE TABLE base_4_a (a INT) ENGINE = InnoDB;
--source include/rpl_sync.inc
--echo ---- GTID_NEXT=non-automatic ----
--source include/gtid_step_reset.inc
--source include/set_gtid_next_gtid_mode_agnostic.inc
DROP TABLES base_1_n, base_2_n, base_3_a, base_4_a;
SET GTID_NEXT= 'AUTOMATIC';
if ($gtid_mode_on)
{
--echo # With GTID assigned the above statement should not be split.
--let $gtid_step_count= 1
--source include/gtid_step_assert.inc
}
if (!$gtid_mode_on)
{
--echo # Without GTID assigned the above statement should be split into
--echo # three as in AUTOMATIC mode.
--let $gtid_step_count= 3
--source include/gtid_step_assert.inc
}
--source include/rpl_sync.inc
--echo ==== Case 2D: Failing DROP TABLES for base tables with and without atomic DDL support ====
--echo See rpl_split_statements_debug.test
--echo ==== Case 3: DROP DATABASE ====
--echo See rpl_split_statements_debug.test
--echo ==== Case 4: CREATE TABLE ... SELECT ====
--echo See rpl_gtid_create_select.test
--source include/rpl_end.inc
|