348 lines
11 KiB
Plaintext
348 lines
11 KiB
Plaintext
# ==== 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/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("Slave SQL for channel '': ... The slave 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 MASTER_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
|