Current Path : /home/usr.opt/mysql57/mysql-test/extra/rpl_tests/ |
FreeBSD hs32.drive.ne.jp 9.1-RELEASE FreeBSD 9.1-RELEASE #1: Wed Jan 14 12:18:08 JST 2015 root@hs32.drive.ne.jp:/sys/amd64/compile/hs32 amd64 |
Current File : //home/usr.opt/mysql57/mysql-test/extra/rpl_tests/rpl_split_statements.test |
# ==== 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, a separate statement for all dropped transactional # temporary tables, and a separate statement for all dropped # non-transactional tables. # # 3. DROP DATABASE: this statement will first drop all non-temporary # tables in the database, and then try to remove the database # directory. If removing the directory fails (e.g., because there # is an unrelated file in the directory), then the statement will # be logged as a single DROP TABLE statement listing all tables. # If there are many tables, the statement becomes big. To avoid # too big statements, the statement is split into multiple DROP # TABLE statements, each with length at most 1024 bytes. # # If GTID_NEXT='UUID:NUMBER', the statement will not be logged and # the gtid will not be added to GTID_EXECUTED, if the statement # fails. This is regardless of whether it would result in multiple # DROP statements or just one. # # 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. 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. # # 3. DROP DATABASE. We try five different error scenarios: # # 3.1. Database exists and is empty. # 3.2. Database exists and contains one table. # 3.3. Database exists and contains many tables with long names, so # that the statement gets split into multiple transactions in # the binary log. # 3.4. Database has not been created, but the directory exists. # 3.5. Database has not created and the directory does not exist. # # We run these five scenarios in three ways: # # - On master with GTID_MODE='AUTOMATIC' # - On master with GTID_MODE!='AUTOMATIC' ('ANONYMOUS'/'UUID:NUMBER', # depending on GTID_MODE). # - On slave. # # In scenarios 3.1-3.4, we create an extra file in the database # directory to trigger the error: to trigger the error on master # we create the file in master's database directory; to trigger on # slave we create it in slave's database directory. In scenario # 3.5 we create the directory on master # # 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_GROUP 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 if ($gtid_mode_on) { --let $expect_slave_error= 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 3: DROP DATABASE ==== --echo ---- Initialize ---- --echo # db1, db2, db3: no tables. CREATE DATABASE db1; CREATE DATABASE db2; CREATE DATABASE db3; --source include/rpl_sync.inc --write_file $server_1_datadir/db1/file.txt EOF --write_file $server_1_datadir/db2/file.txt EOF --write_file $server_2_datadir/db3/file.txt EOF --echo # db4, db5, db6: one table. CREATE DATABASE db4; CREATE DATABASE db5; CREATE DATABASE db6; CREATE TABLE db4.t1 (a INT); CREATE TABLE db5.t1 (a INT); CREATE TABLE db6.t1 (a INT); --source include/rpl_sync.inc --write_file $server_1_datadir/db4/file.txt EOF --write_file $server_1_datadir/db5/file.txt EOF --write_file $server_2_datadir/db6/file.txt EOF --echo # db7, db8, db9: many tables with long names. CREATE DATABASE db7; CREATE DATABASE db8; CREATE DATABASE db9; let $i= 0; --let $long_text= `SELECT REPEAT('a', 60)` --disable_query_log while ($i < 20) { # This is very slow for some reason, so do three tables in parallel. eval CREATE TABLE db7.$long_text$i (a INT); eval CREATE TABLE db8.$long_text$i (a INT); eval CREATE TABLE db9.$long_text$i (a INT); --inc $i } --enable_query_log --source include/rpl_sync.inc --write_file $server_1_datadir/db7/file.txt EOF --write_file $server_1_datadir/db8/file.txt EOF --write_file $server_2_datadir/db9/file.txt EOF --echo # db10, db11, db12: not a database, but the directory exists. --mkdir $server_1_datadir/db10 --mkdir $server_1_datadir/db11 --mkdir $server_1_datadir/db12 --mkdir $server_2_datadir/db12 --write_file $server_1_datadir/db10/file.txt EOF --write_file $server_1_datadir/db11/file.txt EOF --write_file $server_2_datadir/db12/file.txt EOF --echo # db13, db14, db15: not a database. db15 is a database on master. --mkdir $server_1_datadir/db15 --source include/rpl_sync.inc --echo ---- DROP DATABASE is split on master; GTID_NEXT=AUTOMATIC ---- --echo # db1: no table. --source include/save_binlog_position.inc SET GTID_NEXT = 'AUTOMATIC'; --replace_result \\ / --error ER_DB_DROP_RMDIR DROP DATABASE db1; --let $event_sequence= () --source include/assert_binlog_events.inc --echo # db4: one table. --source include/save_binlog_position.inc SET GTID_NEXT = 'AUTOMATIC'; --replace_result \\ / --error ER_DB_DROP_RMDIR DROP DATABASE db4; if ($gtid_mode_on) { --let $event_sequence= Gtid # !Q(DROP TABLE.*) } if (!$gtid_mode_on) { --let $event_sequence= Anonymous_Gtid # !Q(DROP TABLE.*) } --source include/assert_binlog_events.inc --echo # db7: many tables with long names. --source include/save_binlog_position.inc SET GTID_NEXT = 'AUTOMATIC'; --replace_result \\ / --error ER_DB_DROP_RMDIR DROP DATABASE db7; if ($gtid_mode_on) { --let $event_sequence= Gtid # !Q(DROP TABLE.*) # Gtid # !Q(DROP TABLE.*) } if (!$gtid_mode_on) { --let $event_sequence= Anonymous_Gtid # !Q(DROP TABLE.*) # Anonymous_Gtid # !Q(DROP TABLE.*) } --source include/assert_binlog_events.inc --echo # db10: not a database, but directory exists. --source include/save_binlog_position.inc SET GTID_NEXT = 'AUTOMATIC'; --replace_result \\ / --error ER_DB_DROP_RMDIR DROP DATABASE db10; --let $event_sequence= () --source include/assert_binlog_events.inc --echo # db13: not a database. --source include/save_binlog_position.inc SET GTID_NEXT = 'AUTOMATIC'; --replace_result \\ / --error ER_DB_DROP_EXISTS DROP DATABASE db13; --let $event_sequence= () --source include/assert_binlog_events.inc --echo ---- DROP DATABASE is split on master; GTID_NEXT=non-automatic ---- --echo # db2: no table. --source include/save_binlog_position.inc --source include/set_gtid_next_gtid_mode_agnostic.inc if (!$gtid_mode_on) { --replace_result \\ / --error ER_DB_DROP_RMDIR DROP DATABASE db2; --let $event_sequence= () --source include/assert_binlog_events.inc } if ($gtid_mode_on) { --replace_result $server_1_uuid MASTER_UUID \\ / --error ER_CANNOT_LOG_PARTIAL_DROP_DATABASE_WITH_GTID DROP DATABASE db2; --let $event_sequence= () --source include/assert_binlog_events.inc } SET GTID_NEXT = 'AUTOMATIC'; --echo # db5: one table. --source include/save_binlog_position.inc --source include/set_gtid_next_gtid_mode_agnostic.inc if (!$gtid_mode_on) { --replace_result \\ / --error ER_DB_DROP_RMDIR DROP DATABASE db5; --let $event_sequence= Anonymous_Gtid # !Q(DROP TABLE.*) --source include/assert_binlog_events.inc } if ($gtid_mode_on) { --replace_result $server_1_uuid MASTER_UUID \\ / --error ER_CANNOT_LOG_PARTIAL_DROP_DATABASE_WITH_GTID DROP DATABASE db5; --let $event_sequence= () --source include/assert_binlog_events.inc } SET GTID_NEXT = 'AUTOMATIC'; --echo # db8: many tables with long names. --source include/save_binlog_position.inc --source include/set_gtid_next_gtid_mode_agnostic.inc if (!$gtid_mode_on) { --replace_result \\ / --error ER_DB_DROP_RMDIR DROP DATABASE db8; --let $event_sequence= Anonymous_Gtid # !Q(DROP TABLE.*) # Anonymous_Gtid # !Q(DROP TABLE.*) --source include/assert_binlog_events.inc } if ($gtid_mode_on) { --replace_result $server_1_uuid MASTER_UUID \\ / --error ER_CANNOT_LOG_PARTIAL_DROP_DATABASE_WITH_GTID DROP DATABASE db8; --let $event_sequence= () --source include/assert_binlog_events.inc } SET GTID_NEXT = 'AUTOMATIC'; --echo # db11: not a database, but directory exists. --source include/save_binlog_position.inc --source include/set_gtid_next_gtid_mode_agnostic.inc if (!$gtid_mode_on) { --replace_result \\ / --error ER_DB_DROP_RMDIR DROP DATABASE db11; } if ($gtid_mode_on) { --replace_result $server_1_uuid MASTER_UUID \\ / --error ER_CANNOT_LOG_PARTIAL_DROP_DATABASE_WITH_GTID DROP DATABASE db11; } --let $event_sequence= () --source include/assert_binlog_events.inc SET GTID_NEXT = 'AUTOMATIC'; --echo # db14: not a database. --source include/save_binlog_position.inc --source include/set_gtid_next_gtid_mode_agnostic.inc --error ER_DB_DROP_EXISTS DROP DATABASE db14; --let $event_sequence= () --source include/assert_binlog_events.inc SET GTID_NEXT = 'AUTOMATIC'; --echo ---- DROP DATABASE is split on slave ---- SET GTID_NEXT = 'AUTOMATIC'; if (!$gtid_mode_on) { --let $slave_sql_errno= convert_error(ER_DB_DROP_RMDIR) } if ($gtid_mode_on) { --let $slave_sql_errno= convert_error(ER_CANNOT_LOG_PARTIAL_DROP_DATABASE_WITH_GTID) } --echo # db3: no table. DROP DATABASE db3; --source include/sync_slave_io_with_master.inc --source include/wait_for_slave_sql_error.inc --source include/rpl_skip_to_end_of_relay_log.inc --let $rpl_connection_name= master --source include/rpl_connection.inc --echo # db6: one table. DROP DATABASE db6; --source include/sync_slave_io_with_master.inc --source include/wait_for_slave_sql_error.inc --source include/rpl_skip_to_end_of_relay_log.inc --let $rpl_connection_name= master --source include/rpl_connection.inc --echo # db9: many tables with long names. DROP DATABASE db9; --source include/sync_slave_io_with_master.inc --source include/wait_for_slave_sql_error.inc --source include/rpl_skip_to_end_of_relay_log.inc --let $rpl_connection_name= master --source include/rpl_connection.inc --echo # db12: not a database, but directory exists. DROP DATABASE db12; --source include/sync_slave_io_with_master.inc --source include/wait_for_slave_sql_error.inc --source include/rpl_skip_to_end_of_relay_log.inc --let $rpl_connection_name= master --source include/rpl_connection.inc --echo # db15: not a database (on slave). --let $slave_sql_errno= convert_error(ER_DB_DROP_EXISTS) DROP DATABASE db15; --source include/sync_slave_io_with_master.inc --source include/wait_for_slave_sql_error.inc --source include/rpl_skip_to_end_of_relay_log.inc --let $rpl_connection_name= master --source include/rpl_connection.inc --echo ---- Clean up ---- --remove_file $server_1_datadir/db1/file.txt --remove_file $server_1_datadir/db2/file.txt --remove_file $server_2_datadir/db3/file.txt --remove_file $server_1_datadir/db4/file.txt --remove_file $server_1_datadir/db5/file.txt --remove_file $server_2_datadir/db6/file.txt --remove_file $server_1_datadir/db7/file.txt --remove_file $server_1_datadir/db8/file.txt --remove_file $server_2_datadir/db9/file.txt --remove_file $server_1_datadir/db10/file.txt --remove_file $server_1_datadir/db11/file.txt --remove_file $server_2_datadir/db12/file.txt DROP DATABASE db1; DROP DATABASE db2; DROP DATABASE IF EXISTS db3; DROP DATABASE db4; DROP DATABASE db5; DROP DATABASE IF EXISTS db6; DROP DATABASE db7; DROP DATABASE db8; DROP DATABASE IF EXISTS db9; DROP DATABASE IF EXISTS db10; DROP DATABASE IF EXISTS db11; DROP DATABASE IF EXISTS db12; DROP DATABASE IF EXISTS db15; --source include/rpl_sync.inc --echo ==== Case 4: CREATE TABLE ... SELECT ==== --echo See rpl_gtid_create_select.test --source include/rpl_end.inc