polardbxengine/mysql-test/suite/xengine_rpl_gtid/include/rpl_split_statements.inc

348 lines
11 KiB
PHP

# ==== 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);
CREATE TEMPORARY TABLE temp_t (a INT);
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);
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);
CREATE TABLE base_4_a (a INT);
--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 XENGINE 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);
CREATE TABLE base_4_a (a INT);
--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