[Commits] b3473961810: MDEV-11094: Blackhole table updates on slave fail when row annotation is enabled

sujatha sujatha.sivakumar at mariadb.com
Wed May 29 14:07:27 EEST 2019


revision-id: b347396181018cedc946450cb49891f1a0aa4575 (mariadb-10.1.39-48-gb3473961810)
parent(s): 8358c6f03edb941be488f009c2bd0eb9df47e8c5
author: Sujatha
committer: Sujatha
timestamp: 2019-05-29 15:18:52 +0530
message:

MDEV-11094: Blackhole table updates on slave fail when row annotation is enabled

Problem:
=======
rpl_blackhole.test fails when executed with following options
mysqld=--binlog_annotate_row_events=1, mysqld=--replicate_annotate_row_events=1

Test output:
------------
worker[1] Using MTR_BUILD_THREAD 300, with reserved ports 16000..16019
rpl.rpl_blackhole_bug 'mix'              [ pass ]    791
rpl.rpl_blackhole_bug 'row'              [ fail ]
Replicate_Wild_Ignore_Table
Last_Errno	1032
Last_Error	Could not execute Update_rows_v1 event on table test.t1; Can't find
record in 't1', Error_code: 1032; handler error HA_ERR_END_OF_FILE; the event's
master log master-bin.000001, end_log_pos 1510

Analysis:
=========
Enabling "replicate_annotate_row_events" on slave, Tells the slave to write
annotate rows events received from the master to its own binary log. The
received annotate events are applied after the Gtid event as shown below.
thd->query() will be set to the actual query received from the master, through
annotate event. Annotate_rows event should not be deleted after the event is
applied as the thd->query will be used to generate new Annotate_rows event
during applying the subsequent Rows events. After the last Rows event has been
applied, the saved Annotate_rows event (if any) will be deleted.

In balckhole engine all the DML operations are noops as they donot store any
data. They simply return success without doing any operation. But the existing
strictly expects thd->query() to be 'NULL' to identify that row based
replication is in use. This assumption will fail when row annotations are
enabled as the query is not 'NULL'. Hence various row based operations like
'update', 'delete', 'index lookup' will fail when row annotations are enabled.

Fix:
===
Extend the row based replication check to include row annotations as well.
i.e Either the thd->query() is NULL or thd->query() points to query and row
annotations are in use.

---
 mysql-test/extra/rpl_tests/rpl_blackhole.test      |   2 +-
 .../extra/rpl_tests/rpl_blackhole_basic.test       |  97 +++++
 .../suite/rpl/r/rpl_blackhole_row_annotate.result  | 434 +++++++++++++++++++++
 mysql-test/suite/rpl/t/rpl_blackhole.test          |  77 +---
 .../rpl/t/rpl_blackhole_row_annotate-master.opt    |   1 +
 .../rpl/t/rpl_blackhole_row_annotate-slave.opt     |   1 +
 .../suite/rpl/t/rpl_blackhole_row_annotate.test    |  49 +++
 storage/blackhole/ha_blackhole.cc                  |  28 +-
 8 files changed, 606 insertions(+), 83 deletions(-)

diff --git a/mysql-test/extra/rpl_tests/rpl_blackhole.test b/mysql-test/extra/rpl_tests/rpl_blackhole.test
index 1a0eeb3cf15..569a24e5252 100644
--- a/mysql-test/extra/rpl_tests/rpl_blackhole.test
+++ b/mysql-test/extra/rpl_tests/rpl_blackhole.test
@@ -11,7 +11,7 @@
 #    executing statement. If difference is >0, then something was
 #    written to the binary log on the slave.
 
-connection slave;
+# On Connection Slave
 let $before = query_get_value("SHOW MASTER STATUS", Position, 1);
 
 --echo [on master]
diff --git a/mysql-test/extra/rpl_tests/rpl_blackhole_basic.test b/mysql-test/extra/rpl_tests/rpl_blackhole_basic.test
new file mode 100644
index 00000000000..f3fdc915080
--- /dev/null
+++ b/mysql-test/extra/rpl_tests/rpl_blackhole_basic.test
@@ -0,0 +1,97 @@
+# PURPOSE. Test that blackhole works with replication in all three
+# modes: STATEMENT, MIXED, and ROW.
+#
+# METHOD. We start by creating a table on the master and then change
+# the engine to use blackhole on the slave.
+#
+# After insert/update/delete of one or more rows, the test the
+# proceeds to check that replication is running after replicating an
+# change, that the blackhole engine does not contain anything (which
+# is just a check that the correct engine is used), and that something
+# is written to the binary log.
+#
+# Whe check INSERT, UPDATE, and DELETE statement for tables with no
+# key (forcing a range search on the slave), primary keys (using a
+# primary key lookup), and index/key with multiple matches (forcing an
+# index search).
+
+# We start with no primary key
+CREATE TABLE t1 (a INT, b INT, c INT);
+CREATE TABLE t2 (a INT, b INT, c INT);
+
+sync_slave_with_master;
+ALTER TABLE t1 ENGINE=BLACKHOLE;
+
+connection master;
+INSERT INTO t2 VALUES (1,9,1), (2,9,2), (3,9,3), (4,9,4);
+sync_slave_with_master;
+
+# Test insert, no primary key
+let $statement = INSERT INTO t1 VALUES (1,1,1),(2,1,2),(3,1,3),(4,1,4);
+source extra/rpl_tests/rpl_blackhole.test;
+
+# Test update, no primary key
+let $statement = UPDATE t1 SET c = 2*c WHERE a % 2 = 0 AND b = 1;
+source extra/rpl_tests/rpl_blackhole.test;
+
+# Test delete, no primary key
+let $statement = DELETE FROM t1 WHERE a % 2 = 0 AND b = 1;
+source extra/rpl_tests/rpl_blackhole.test;
+
+# Test INSERT-SELECT into Blackhole, no primary key
+let $statement = INSERT INTO t1 SELECT * FROM t2;
+source extra/rpl_tests/rpl_blackhole.test;
+
+#
+# The MASTER has MyISAM as the engine for both tables. The SLAVE has Blackhole
+# on t1 (transactional engine) and MyISAM on t2 (non-transactional engine).
+#
+# In MIXED mode, the command "INSERT INTO t2 SELECT * FROM t1" is logged as
+# statement on the master. On the slave, it is tagged as unsafe because the
+# statement mixes both transactional and non-transactional engines and as such
+# its changes are logged as rows. However, due to the nature of the blackhole
+# engine, no rows are returned and thus any chain replication would make the
+# next master on the chain diverge.
+#
+# Fo this reason, we have disabled the statement.
+#
+# Test INSERT-SELECT from Blackhole, no primary key
+# let $statement = INSERT INTO t2 SELECT * FROM t1;
+# source extra/rpl_tests/rpl_blackhole.test;
+#
+
+connection master;
+ALTER TABLE t1 ADD PRIMARY KEY pk_t1 (a,b);
+sync_slave_with_master;
+
+# Test insert, primary key
+let $statement = INSERT INTO t1 VALUES (1,2,1),(2,2,2),(3,2,3),(4,2,4);
+source extra/rpl_tests/rpl_blackhole.test;
+
+# Test update, primary key
+let $statement = UPDATE t1 SET c = 2*c WHERE a % 2 = 0 AND b = 2;
+source extra/rpl_tests/rpl_blackhole.test;
+
+# Test delete, primary key
+let $statement = DELETE FROM t1 WHERE a % 2 = 0 AND b = 2;
+source extra/rpl_tests/rpl_blackhole.test;
+
+connection master;
+ALTER TABLE t1 DROP PRIMARY KEY, ADD KEY key_t1 (a);
+sync_slave_with_master;
+
+# Test insert, key
+let $statement = INSERT INTO t1 VALUES (1,3,1),(2,3,2),(3,3,3),(4,3,4);
+source extra/rpl_tests/rpl_blackhole.test;
+
+# Test update, key
+let $statement = UPDATE t1 SET c = 2*c WHERE a % 2 = 0 AND b = 3;
+source extra/rpl_tests/rpl_blackhole.test;
+
+# Test delete, key
+let $statement = DELETE FROM t1 WHERE a % 2 = 0 AND b = 3;
+source extra/rpl_tests/rpl_blackhole.test;
+
+connection master;
+DROP TABLE t1,t2;
+sync_slave_with_master;
diff --git a/mysql-test/suite/rpl/r/rpl_blackhole_row_annotate.result b/mysql-test/suite/rpl/r/rpl_blackhole_row_annotate.result
new file mode 100644
index 00000000000..20ca37322dc
--- /dev/null
+++ b/mysql-test/suite/rpl/r/rpl_blackhole_row_annotate.result
@@ -0,0 +1,434 @@
+include/master-slave.inc
+[connection master]
+SET timestamp=1000000000;
+RESET MASTER;
+SET timestamp=1000000000;
+RESET MASTER;
+CREATE TABLE t1 (a INT, b INT, c INT);
+CREATE TABLE t2 (a INT, b INT, c INT);
+ALTER TABLE t1 ENGINE=BLACKHOLE;
+INSERT INTO t2 VALUES (1,9,1), (2,9,2), (3,9,3), (4,9,4);
+[on master]
+INSERT INTO t1 VALUES (1,1,1),(2,1,2),(3,1,3),(4,1,4);
+[on slave]
+# Expect 0
+SELECT COUNT(*) FROM t1;
+COUNT(*)
+0
+>>> Something was written to binary log <<<
+[on master]
+UPDATE t1 SET c = 2*c WHERE a % 2 = 0 AND b = 1;
+[on slave]
+# Expect 0
+SELECT COUNT(*) FROM t1;
+COUNT(*)
+0
+>>> Something was written to binary log <<<
+[on master]
+DELETE FROM t1 WHERE a % 2 = 0 AND b = 1;
+[on slave]
+# Expect 0
+SELECT COUNT(*) FROM t1;
+COUNT(*)
+0
+>>> Something was written to binary log <<<
+[on master]
+INSERT INTO t1 SELECT * FROM t2;
+[on slave]
+# Expect 0
+SELECT COUNT(*) FROM t1;
+COUNT(*)
+0
+>>> Something was written to binary log <<<
+ALTER TABLE t1 ADD PRIMARY KEY pk_t1 (a,b);
+[on master]
+INSERT INTO t1 VALUES (1,2,1),(2,2,2),(3,2,3),(4,2,4);
+[on slave]
+# Expect 0
+SELECT COUNT(*) FROM t1;
+COUNT(*)
+0
+>>> Something was written to binary log <<<
+[on master]
+UPDATE t1 SET c = 2*c WHERE a % 2 = 0 AND b = 2;
+[on slave]
+# Expect 0
+SELECT COUNT(*) FROM t1;
+COUNT(*)
+0
+>>> Something was written to binary log <<<
+[on master]
+DELETE FROM t1 WHERE a % 2 = 0 AND b = 2;
+[on slave]
+# Expect 0
+SELECT COUNT(*) FROM t1;
+COUNT(*)
+0
+>>> Something was written to binary log <<<
+ALTER TABLE t1 DROP PRIMARY KEY, ADD KEY key_t1 (a);
+[on master]
+INSERT INTO t1 VALUES (1,3,1),(2,3,2),(3,3,3),(4,3,4);
+[on slave]
+# Expect 0
+SELECT COUNT(*) FROM t1;
+COUNT(*)
+0
+>>> Something was written to binary log <<<
+[on master]
+UPDATE t1 SET c = 2*c WHERE a % 2 = 0 AND b = 3;
+[on slave]
+# Expect 0
+SELECT COUNT(*) FROM t1;
+COUNT(*)
+0
+>>> Something was written to binary log <<<
+[on master]
+DELETE FROM t1 WHERE a % 2 = 0 AND b = 3;
+[on slave]
+# Expect 0
+SELECT COUNT(*) FROM t1;
+COUNT(*)
+0
+>>> Something was written to binary log <<<
+DROP TABLE t1,t2;
+FLUSH LOGS;
+show binlog events in 'slave-bin.000001' from <start_pos>;
+Log_name	Pos	Event_type	Server_id	End_log_pos	Info
+slave-bin.000001	#	Gtid_list	2	#	[]
+slave-bin.000001	#	Binlog_checkpoint	2	#	slave-bin.000001
+slave-bin.000001	#	Gtid	1	#	GTID 0-1-1
+slave-bin.000001	#	Query	1	#	use `test`; CREATE TABLE t1 (a INT, b INT, c INT)
+slave-bin.000001	#	Gtid	1	#	GTID 0-1-2
+slave-bin.000001	#	Query	1	#	use `test`; CREATE TABLE t2 (a INT, b INT, c INT)
+slave-bin.000001	#	Gtid	2	#	GTID 0-2-3
+slave-bin.000001	#	Query	2	#	use `test`; ALTER TABLE t1 ENGINE=BLACKHOLE
+slave-bin.000001	#	Gtid	1	#	BEGIN GTID 0-1-3
+slave-bin.000001	#	Annotate_rows	1	#	INSERT INTO t2 VALUES (1,9,1), (2,9,2), (3,9,3), (4,9,4)
+slave-bin.000001	#	Table_map	1	#	table_id: # (test.t2)
+slave-bin.000001	#	Write_rows_v1	1	#	table_id: # flags: STMT_END_F
+slave-bin.000001	#	Query	1	#	COMMIT
+slave-bin.000001	#	Gtid	1	#	BEGIN GTID 0-1-4
+slave-bin.000001	#	Annotate_rows	1	#	INSERT INTO t1 VALUES (1,1,1),(2,1,2),(3,1,3),(4,1,4)
+slave-bin.000001	#	Table_map	1	#	table_id: # (test.t1)
+slave-bin.000001	#	Write_rows_v1	1	#	table_id: # flags: STMT_END_F
+slave-bin.000001	#	Query	1	#	COMMIT
+slave-bin.000001	#	Gtid	1	#	BEGIN GTID 0-1-5
+slave-bin.000001	#	Annotate_rows	1	#	UPDATE t1 SET c = 2*c WHERE a % 2 = 0 AND b = 1
+slave-bin.000001	#	Table_map	1	#	table_id: # (test.t1)
+slave-bin.000001	#	Update_rows_v1	1	#	table_id: # flags: STMT_END_F
+slave-bin.000001	#	Query	1	#	COMMIT
+slave-bin.000001	#	Gtid	1	#	BEGIN GTID 0-1-6
+slave-bin.000001	#	Annotate_rows	1	#	DELETE FROM t1 WHERE a % 2 = 0 AND b = 1
+slave-bin.000001	#	Table_map	1	#	table_id: # (test.t1)
+slave-bin.000001	#	Delete_rows_v1	1	#	table_id: # flags: STMT_END_F
+slave-bin.000001	#	Query	1	#	COMMIT
+slave-bin.000001	#	Gtid	1	#	BEGIN GTID 0-1-7
+slave-bin.000001	#	Annotate_rows	1	#	INSERT INTO t1 SELECT * FROM t2
+slave-bin.000001	#	Table_map	1	#	table_id: # (test.t1)
+slave-bin.000001	#	Write_rows_v1	1	#	table_id: # flags: STMT_END_F
+slave-bin.000001	#	Query	1	#	COMMIT
+slave-bin.000001	#	Gtid	1	#	GTID 0-1-8
+slave-bin.000001	#	Query	1	#	use `test`; ALTER TABLE t1 ADD PRIMARY KEY pk_t1 (a,b)
+slave-bin.000001	#	Gtid	1	#	BEGIN GTID 0-1-9
+slave-bin.000001	#	Annotate_rows	1	#	INSERT INTO t1 VALUES (1,2,1),(2,2,2),(3,2,3),(4,2,4)
+slave-bin.000001	#	Table_map	1	#	table_id: # (test.t1)
+slave-bin.000001	#	Write_rows_v1	1	#	table_id: # flags: STMT_END_F
+slave-bin.000001	#	Query	1	#	COMMIT
+slave-bin.000001	#	Gtid	1	#	BEGIN GTID 0-1-10
+slave-bin.000001	#	Annotate_rows	1	#	UPDATE t1 SET c = 2*c WHERE a % 2 = 0 AND b = 2
+slave-bin.000001	#	Table_map	1	#	table_id: # (test.t1)
+slave-bin.000001	#	Update_rows_v1	1	#	table_id: # flags: STMT_END_F
+slave-bin.000001	#	Query	1	#	COMMIT
+slave-bin.000001	#	Gtid	1	#	BEGIN GTID 0-1-11
+slave-bin.000001	#	Annotate_rows	1	#	DELETE FROM t1 WHERE a % 2 = 0 AND b = 2
+slave-bin.000001	#	Table_map	1	#	table_id: # (test.t1)
+slave-bin.000001	#	Delete_rows_v1	1	#	table_id: # flags: STMT_END_F
+slave-bin.000001	#	Query	1	#	COMMIT
+slave-bin.000001	#	Gtid	1	#	GTID 0-1-12
+slave-bin.000001	#	Query	1	#	use `test`; ALTER TABLE t1 DROP PRIMARY KEY, ADD KEY key_t1 (a)
+slave-bin.000001	#	Gtid	1	#	BEGIN GTID 0-1-13
+slave-bin.000001	#	Annotate_rows	1	#	INSERT INTO t1 VALUES (1,3,1),(2,3,2),(3,3,3),(4,3,4)
+slave-bin.000001	#	Table_map	1	#	table_id: # (test.t1)
+slave-bin.000001	#	Write_rows_v1	1	#	table_id: # flags: STMT_END_F
+slave-bin.000001	#	Query	1	#	COMMIT
+slave-bin.000001	#	Gtid	1	#	BEGIN GTID 0-1-14
+slave-bin.000001	#	Annotate_rows	1	#	UPDATE t1 SET c = 2*c WHERE a % 2 = 0 AND b = 3
+slave-bin.000001	#	Table_map	1	#	table_id: # (test.t1)
+slave-bin.000001	#	Update_rows_v1	1	#	table_id: # flags: STMT_END_F
+slave-bin.000001	#	Query	1	#	COMMIT
+slave-bin.000001	#	Gtid	1	#	BEGIN GTID 0-1-15
+slave-bin.000001	#	Annotate_rows	1	#	DELETE FROM t1 WHERE a % 2 = 0 AND b = 3
+slave-bin.000001	#	Table_map	1	#	table_id: # (test.t1)
+slave-bin.000001	#	Delete_rows_v1	1	#	table_id: # flags: STMT_END_F
+slave-bin.000001	#	Query	1	#	COMMIT
+slave-bin.000001	#	Gtid	1	#	GTID 0-1-16
+slave-bin.000001	#	Query	1	#	use `test`; DROP TABLE IF EXISTS `t1`,`t2` /* generated by server */
+slave-bin.000001	#	Rotate	2	#	slave-bin.000002;pos=4
+/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=1*/;
+/*!40019 SET @@session.max_insert_delayed_threads=0*/;
+/*!50003 SET @OLD_COMPLETION_TYPE=@@COMPLETION_TYPE,COMPLETION_TYPE=0*/;
+DELIMITER /*!*/;
+# at #
+#010909  4:46:40 server id #  end_log_pos # 	Start: binlog v 4, server v #.##.## created 010909  4:46:40 at startup
+ROLLBACK/*!*/;
+# at #
+#010909  4:46:40 server id #  end_log_pos # 	Gtid list []
+# at #
+#010909  4:46:40 server id #  end_log_pos # 	Binlog checkpoint slave-bin.000001
+# at #
+#010909  4:46:40 server id #  end_log_pos # 	GTID 0-1-1 ddl
+/*!100101 SET @@session.skip_parallel_replication=0*//*!*/;
+/*!100001 SET @@session.gtid_domain_id=0*//*!*/;
+/*!100001 SET @@session.server_id=1*//*!*/;
+/*!100001 SET @@session.gtid_seq_no=1*//*!*/;
+# at #
+#010909  4:46:40 server id #  end_log_pos # 	Query	thread_id=#	exec_time=#	error_code=0
+use `test`/*!*/;
+SET TIMESTAMP=1000000000/*!*/;
+SET @@session.pseudo_thread_id=#/*!*/;
+SET @@session.foreign_key_checks=1, @@session.sql_auto_is_null=0, @@session.unique_checks=1, @@session.autocommit=1/*!*/;
+SET @@session.sql_mode=1342177280/*!*/;
+SET @@session.auto_increment_increment=1, @@session.auto_increment_offset=1/*!*/;
+/*!\C latin1 *//*!*/;
+SET @@session.character_set_client=8,@@session.collation_connection=8,@@session.collation_server=8/*!*/;
+SET @@session.lc_time_names=0/*!*/;
+SET @@session.collation_database=DEFAULT/*!*/;
+CREATE TABLE t1 (a INT, b INT, c INT)
+/*!*/;
+# at #
+#010909  4:46:40 server id #  end_log_pos # 	GTID 0-1-2 ddl
+/*!100001 SET @@session.gtid_seq_no=2*//*!*/;
+# at #
+#010909  4:46:40 server id #  end_log_pos # 	Query	thread_id=#	exec_time=#	error_code=0
+SET TIMESTAMP=1000000000/*!*/;
+CREATE TABLE t2 (a INT, b INT, c INT)
+/*!*/;
+# at #
+#010909  4:46:40 server id #  end_log_pos # 	GTID 0-2-3 ddl
+/*!100001 SET @@session.server_id=2*//*!*/;
+/*!100001 SET @@session.gtid_seq_no=3*//*!*/;
+# at #
+#010909  4:46:40 server id #  end_log_pos # 	Query	thread_id=#	exec_time=#	error_code=0
+SET TIMESTAMP=1000000000/*!*/;
+ALTER TABLE t1 ENGINE=BLACKHOLE
+/*!*/;
+# at #
+#010909  4:46:40 server id #  end_log_pos # 	GTID 0-1-3 trans
+/*!100001 SET @@session.server_id=1*//*!*/;
+/*!100001 SET @@session.gtid_seq_no=3*//*!*/;
+BEGIN
+/*!*/;
+# at #
+# at #
+#010909  4:46:40 server id #  end_log_pos # 	Annotate_rows:
+#Q> INSERT INTO t2 VALUES (1,9,1), (2,9,2), (3,9,3), (4,9,4)
+#010909  4:46:40 server id #  end_log_pos # 	Table_map: `test`.`t2` mapped to number #
+# at #
+#010909  4:46:40 server id #  end_log_pos # 	Write_rows: table id # flags: STMT_END_F
+# at #
+#010909  4:46:40 server id #  end_log_pos # 	Query	thread_id=#	exec_time=#	error_code=0
+SET TIMESTAMP=1000000000/*!*/;
+COMMIT
+/*!*/;
+# at #
+#010909  4:46:40 server id #  end_log_pos # 	GTID 0-1-4 trans
+/*!100001 SET @@session.gtid_seq_no=4*//*!*/;
+BEGIN
+/*!*/;
+# at #
+# at #
+#010909  4:46:40 server id #  end_log_pos # 	Annotate_rows:
+#Q> INSERT INTO t1 VALUES (1,1,1),(2,1,2),(3,1,3),(4,1,4)
+#010909  4:46:40 server id #  end_log_pos # 	Table_map: `test`.`t1` mapped to number #
+# at #
+#010909  4:46:40 server id #  end_log_pos # 	Write_rows: table id # flags: STMT_END_F
+# at #
+#010909  4:46:40 server id #  end_log_pos # 	Query	thread_id=#	exec_time=#	error_code=0
+SET TIMESTAMP=1000000000/*!*/;
+COMMIT
+/*!*/;
+# at #
+#010909  4:46:40 server id #  end_log_pos # 	GTID 0-1-5 trans
+/*!100001 SET @@session.gtid_seq_no=5*//*!*/;
+BEGIN
+/*!*/;
+# at #
+# at #
+#010909  4:46:40 server id #  end_log_pos # 	Annotate_rows:
+#Q> UPDATE t1 SET c = 2*c WHERE a % 2 = 0 AND b = 1
+#010909  4:46:40 server id #  end_log_pos # 	Table_map: `test`.`t1` mapped to number #
+# at #
+#010909  4:46:40 server id #  end_log_pos # 	Update_rows: table id # flags: STMT_END_F
+# at #
+#010909  4:46:40 server id #  end_log_pos # 	Query	thread_id=#	exec_time=#	error_code=0
+SET TIMESTAMP=1000000000/*!*/;
+COMMIT
+/*!*/;
+# at #
+#010909  4:46:40 server id #  end_log_pos # 	GTID 0-1-6 trans
+/*!100001 SET @@session.gtid_seq_no=6*//*!*/;
+BEGIN
+/*!*/;
+# at #
+# at #
+#010909  4:46:40 server id #  end_log_pos # 	Annotate_rows:
+#Q> DELETE FROM t1 WHERE a % 2 = 0 AND b = 1
+#010909  4:46:40 server id #  end_log_pos # 	Table_map: `test`.`t1` mapped to number #
+# at #
+#010909  4:46:40 server id #  end_log_pos # 	Delete_rows: table id # flags: STMT_END_F
+# at #
+#010909  4:46:40 server id #  end_log_pos # 	Query	thread_id=#	exec_time=#	error_code=0
+SET TIMESTAMP=1000000000/*!*/;
+COMMIT
+/*!*/;
+# at #
+#010909  4:46:40 server id #  end_log_pos # 	GTID 0-1-7 trans
+/*!100001 SET @@session.gtid_seq_no=7*//*!*/;
+BEGIN
+/*!*/;
+# at #
+# at #
+#010909  4:46:40 server id #  end_log_pos # 	Annotate_rows:
+#Q> INSERT INTO t1 SELECT * FROM t2
+#010909  4:46:40 server id #  end_log_pos # 	Table_map: `test`.`t1` mapped to number #
+# at #
+#010909  4:46:40 server id #  end_log_pos # 	Write_rows: table id # flags: STMT_END_F
+# at #
+#010909  4:46:40 server id #  end_log_pos # 	Query	thread_id=#	exec_time=#	error_code=0
+SET TIMESTAMP=1000000000/*!*/;
+COMMIT
+/*!*/;
+# at #
+#010909  4:46:40 server id #  end_log_pos # 	GTID 0-1-8 ddl
+/*!100001 SET @@session.gtid_seq_no=8*//*!*/;
+# at #
+#010909  4:46:40 server id #  end_log_pos # 	Query	thread_id=#	exec_time=#	error_code=0
+SET TIMESTAMP=1000000000/*!*/;
+ALTER TABLE t1 ADD PRIMARY KEY pk_t1 (a,b)
+/*!*/;
+# at #
+#010909  4:46:40 server id #  end_log_pos # 	GTID 0-1-9 trans
+/*!100001 SET @@session.gtid_seq_no=9*//*!*/;
+BEGIN
+/*!*/;
+# at #
+# at #
+#010909  4:46:40 server id #  end_log_pos # 	Annotate_rows:
+#Q> INSERT INTO t1 VALUES (1,2,1),(2,2,2),(3,2,3),(4,2,4)
+#010909  4:46:40 server id #  end_log_pos # 	Table_map: `test`.`t1` mapped to number #
+# at #
+#010909  4:46:40 server id #  end_log_pos # 	Write_rows: table id # flags: STMT_END_F
+# at #
+#010909  4:46:40 server id #  end_log_pos # 	Query	thread_id=#	exec_time=#	error_code=0
+SET TIMESTAMP=1000000000/*!*/;
+COMMIT
+/*!*/;
+# at #
+#010909  4:46:40 server id #  end_log_pos # 	GTID 0-1-10 trans
+/*!100001 SET @@session.gtid_seq_no=10*//*!*/;
+BEGIN
+/*!*/;
+# at #
+# at #
+#010909  4:46:40 server id #  end_log_pos # 	Annotate_rows:
+#Q> UPDATE t1 SET c = 2*c WHERE a % 2 = 0 AND b = 2
+#010909  4:46:40 server id #  end_log_pos # 	Table_map: `test`.`t1` mapped to number #
+# at #
+#010909  4:46:40 server id #  end_log_pos # 	Update_rows: table id # flags: STMT_END_F
+# at #
+#010909  4:46:40 server id #  end_log_pos # 	Query	thread_id=#	exec_time=#	error_code=0
+SET TIMESTAMP=1000000000/*!*/;
+COMMIT
+/*!*/;
+# at #
+#010909  4:46:40 server id #  end_log_pos # 	GTID 0-1-11 trans
+/*!100001 SET @@session.gtid_seq_no=11*//*!*/;
+BEGIN
+/*!*/;
+# at #
+# at #
+#010909  4:46:40 server id #  end_log_pos # 	Annotate_rows:
+#Q> DELETE FROM t1 WHERE a % 2 = 0 AND b = 2
+#010909  4:46:40 server id #  end_log_pos # 	Table_map: `test`.`t1` mapped to number #
+# at #
+#010909  4:46:40 server id #  end_log_pos # 	Delete_rows: table id # flags: STMT_END_F
+# at #
+#010909  4:46:40 server id #  end_log_pos # 	Query	thread_id=#	exec_time=#	error_code=0
+SET TIMESTAMP=1000000000/*!*/;
+COMMIT
+/*!*/;
+# at #
+#010909  4:46:40 server id #  end_log_pos # 	GTID 0-1-12 ddl
+/*!100001 SET @@session.gtid_seq_no=12*//*!*/;
+# at #
+#010909  4:46:40 server id #  end_log_pos # 	Query	thread_id=#	exec_time=#	error_code=0
+SET TIMESTAMP=1000000000/*!*/;
+ALTER TABLE t1 DROP PRIMARY KEY, ADD KEY key_t1 (a)
+/*!*/;
+# at #
+#010909  4:46:40 server id #  end_log_pos # 	GTID 0-1-13 trans
+/*!100001 SET @@session.gtid_seq_no=13*//*!*/;
+BEGIN
+/*!*/;
+# at #
+# at #
+#010909  4:46:40 server id #  end_log_pos # 	Annotate_rows:
+#Q> INSERT INTO t1 VALUES (1,3,1),(2,3,2),(3,3,3),(4,3,4)
+#010909  4:46:40 server id #  end_log_pos # 	Table_map: `test`.`t1` mapped to number #
+# at #
+#010909  4:46:40 server id #  end_log_pos # 	Write_rows: table id # flags: STMT_END_F
+# at #
+#010909  4:46:40 server id #  end_log_pos # 	Query	thread_id=#	exec_time=#	error_code=0
+SET TIMESTAMP=1000000000/*!*/;
+COMMIT
+/*!*/;
+# at #
+#010909  4:46:40 server id #  end_log_pos # 	GTID 0-1-14 trans
+/*!100001 SET @@session.gtid_seq_no=14*//*!*/;
+BEGIN
+/*!*/;
+# at #
+# at #
+#010909  4:46:40 server id #  end_log_pos # 	Annotate_rows:
+#Q> UPDATE t1 SET c = 2*c WHERE a % 2 = 0 AND b = 3
+#010909  4:46:40 server id #  end_log_pos # 	Table_map: `test`.`t1` mapped to number #
+# at #
+#010909  4:46:40 server id #  end_log_pos # 	Update_rows: table id # flags: STMT_END_F
+# at #
+#010909  4:46:40 server id #  end_log_pos # 	Query	thread_id=#	exec_time=#	error_code=0
+SET TIMESTAMP=1000000000/*!*/;
+COMMIT
+/*!*/;
+# at #
+#010909  4:46:40 server id #  end_log_pos # 	GTID 0-1-15 trans
+/*!100001 SET @@session.gtid_seq_no=15*//*!*/;
+BEGIN
+/*!*/;
+# at #
+# at #
+#010909  4:46:40 server id #  end_log_pos # 	Annotate_rows:
+#Q> DELETE FROM t1 WHERE a % 2 = 0 AND b = 3
+#010909  4:46:40 server id #  end_log_pos # 	Table_map: `test`.`t1` mapped to number #
+# at #
+#010909  4:46:40 server id #  end_log_pos # 	Delete_rows: table id # flags: STMT_END_F
+# at #
+#010909  4:46:40 server id #  end_log_pos # 	Query	thread_id=#	exec_time=#	error_code=0
+SET TIMESTAMP=1000000000/*!*/;
+COMMIT
+/*!*/;
+# at #
+#010909  4:46:40 server id #  end_log_pos # 	GTID 0-1-16 ddl
+/*!100001 SET @@session.gtid_seq_no=16*//*!*/;
+# at #
+#010909  4:46:40 server id #  end_log_pos # 	Query	thread_id=#	exec_time=#	error_code=0
+SET TIMESTAMP=1000000000/*!*/;
+DROP TABLE IF EXISTS `t1`,`t2` /* generated by server */
+/*!*/;
+# at #
+#010909  4:46:40 server id #  end_log_pos # 	Rotate to slave-bin.000002  pos: 4
+DELIMITER ;
+# End of log file
+ROLLBACK /* added by mysqlbinlog */;
+/*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/;
+/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/;
+include/rpl_end.inc
diff --git a/mysql-test/suite/rpl/t/rpl_blackhole.test b/mysql-test/suite/rpl/t/rpl_blackhole.test
index 76b2e2421c9..9128382d12b 100644
--- a/mysql-test/suite/rpl/t/rpl_blackhole.test
+++ b/mysql-test/suite/rpl/t/rpl_blackhole.test
@@ -20,81 +20,6 @@ source include/master-slave.inc;
 
 call mtr.add_suppression("Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT");
 
-# We start with no primary key
-CREATE TABLE t1 (a INT, b INT, c INT);
-CREATE TABLE t2 (a INT, b INT, c INT);
+source extra/rpl_tests/rpl_blackhole_basic.test;
 
-sync_slave_with_master;
-ALTER TABLE t1 ENGINE=BLACKHOLE;
-
-connection master;
-INSERT INTO t2 VALUES (1,9,1), (2,9,2), (3,9,3), (4,9,4);
-sync_slave_with_master;
-
-# Test insert, no primary key
-let $statement = INSERT INTO t1 VALUES (1,1,1),(2,1,2),(3,1,3),(4,1,4);
-source extra/rpl_tests/rpl_blackhole.test;
-
-# Test update, no primary key
-let $statement = UPDATE t1 SET c = 2*c WHERE a % 2 = 0 AND b = 1;
-source extra/rpl_tests/rpl_blackhole.test;
-
-# Test delete, no primary key
-let $statement = DELETE FROM t1 WHERE a % 2 = 0 AND b = 1;
-source extra/rpl_tests/rpl_blackhole.test;
-
-# Test INSERT-SELECT into Blackhole, no primary key
-let $statement = INSERT INTO t1 SELECT * FROM t2;
-source extra/rpl_tests/rpl_blackhole.test;
-
-# 
-# The MASTER has MyISAM as the engine for both tables. The SLAVE has Blackhole
-# on t1 (transactional engine) and MyISAM on t2 (non-transactional engine). 
-#
-# In MIXED mode, the command "INSERT INTO t2 SELECT * FROM t1" is logged as
-# statement on the master. On the slave, it is tagged as unsafe because the
-# statement mixes both transactional and non-transactional engines and as such
-# its changes are logged as rows. However, due to the nature of the blackhole
-# engine, no rows are returned and thus any chain replication would make the
-# next master on the chain diverge.
-#
-# Fo this reason, we have disabled the statement.
-#
-# Test INSERT-SELECT from Blackhole, no primary key
-# let $statement = INSERT INTO t2 SELECT * FROM t1;
-# source extra/rpl_tests/rpl_blackhole.test;
-#
-
-connection master;
-ALTER TABLE t1 ADD PRIMARY KEY pk_t1 (a,b);
-
-# Test insert, primary key
-let $statement = INSERT INTO t1 VALUES (1,2,1),(2,2,2),(3,2,3),(4,2,4);
-source extra/rpl_tests/rpl_blackhole.test;
-
-# Test update, primary key
-let $statement = UPDATE t1 SET c = 2*c WHERE a % 2 = 0 AND b = 2;
-source extra/rpl_tests/rpl_blackhole.test;
-
-# Test delete, primary key
-let $statement = DELETE FROM t1 WHERE a % 2 = 0 AND b = 2;
-source extra/rpl_tests/rpl_blackhole.test;
-
-connection master;
-ALTER TABLE t1 DROP PRIMARY KEY, ADD KEY key_t1 (a);
-
-# Test insert, key
-let $statement = INSERT INTO t1 VALUES (1,3,1),(2,3,2),(3,3,3),(4,3,4);
-source extra/rpl_tests/rpl_blackhole.test;
-
-# Test update, key
-let $statement = UPDATE t1 SET c = 2*c WHERE a % 2 = 0 AND b = 3;
-source extra/rpl_tests/rpl_blackhole.test;
-
-# Test delete, key
-let $statement = DELETE FROM t1 WHERE a % 2 = 0 AND b = 3;
-source extra/rpl_tests/rpl_blackhole.test;
-
-connection master;
-DROP TABLE t1,t2;
 --source include/rpl_end.inc
diff --git a/mysql-test/suite/rpl/t/rpl_blackhole_row_annotate-master.opt b/mysql-test/suite/rpl/t/rpl_blackhole_row_annotate-master.opt
new file mode 100644
index 00000000000..91302791099
--- /dev/null
+++ b/mysql-test/suite/rpl/t/rpl_blackhole_row_annotate-master.opt
@@ -0,0 +1 @@
+--binlog_annotate_row_events --timezone=GMT-3
diff --git a/mysql-test/suite/rpl/t/rpl_blackhole_row_annotate-slave.opt b/mysql-test/suite/rpl/t/rpl_blackhole_row_annotate-slave.opt
new file mode 100644
index 00000000000..7ac6a84faa7
--- /dev/null
+++ b/mysql-test/suite/rpl/t/rpl_blackhole_row_annotate-slave.opt
@@ -0,0 +1 @@
+--binlog_annotate_row_events --replicate_annotate_row_events
diff --git a/mysql-test/suite/rpl/t/rpl_blackhole_row_annotate.test b/mysql-test/suite/rpl/t/rpl_blackhole_row_annotate.test
new file mode 100644
index 00000000000..77384d91475
--- /dev/null
+++ b/mysql-test/suite/rpl/t/rpl_blackhole_row_annotate.test
@@ -0,0 +1,49 @@
+# ==== Purpose ====
+#
+# Test verifies that when "replicate_annotate_row_events" are enabled on slave
+# the DML operations on blackhole engine will be successful. It also ensures
+# that Annotate events are logged into slave's binary log.
+#
+# ==== Implementation ====
+#
+# Steps:
+#    0 - Enable "replicate_annotate_row_events" on slave and do DML operations
+#    on master.
+#    1 - Slave server will successfully apply the DML operations and it is in
+#    sync with master.
+#    2 - Verify that the "show binlog events" prints all annotate events.
+#    3 - Stream the slave's binary log using "mysqlbinlog" tool and verify
+#    that the Annotate events are being displayed.
+#
+# ==== References ====
+#
+# MDEV-11094: Blackhole table updates on slave fail when row annotation is
+# enabled
+
+source include/have_blackhole.inc;
+source include/have_binlog_format_row.inc;
+source include/binlog_start_pos.inc;
+source include/master-slave.inc;
+
+SET timestamp=1000000000;
+RESET MASTER;
+connection slave;
+SET timestamp=1000000000;
+RESET MASTER;
+
+connection master;
+source extra/rpl_tests/rpl_blackhole_basic.test;
+
+# Verify on slave.
+connection slave;
+FLUSH LOGS;
+--replace_column 2 # 5 #
+--replace_result $binlog_start_pos <start_pos>
+--replace_regex /table_id: [0-9]+/table_id: #/ /\/\* xid=.* \*\//\/* xid= *\//
+--eval show binlog events in 'slave-bin.000001' from $binlog_start_pos
+
+let $MYSQLD_DATADIR= `select @@datadir`;
+--replace_regex /server id [0-9]*/server id #/ /server v [^ ]*/server v #.##.##/ /exec_time=[0-9]*/exec_time=#/ /thread_id=[0-9]*/thread_id=#/ /table id [0-9]*/table id #/ /mapped to number [0-9]*/mapped to number #/ /end_log_pos [0-9]*/end_log_pos #/ /# at [0-9]*/# at #/
+--exec $MYSQL_BINLOG --base64-output=decode-rows $MYSQLD_DATADIR/slave-bin.000001
+
+source include/rpl_end.inc;
diff --git a/storage/blackhole/ha_blackhole.cc b/storage/blackhole/ha_blackhole.cc
index 01aaa9ea15f..43bcdc541a1 100644
--- a/storage/blackhole/ha_blackhole.cc
+++ b/storage/blackhole/ha_blackhole.cc
@@ -25,6 +25,16 @@
 #include "ha_blackhole.h"
 #include "sql_class.h"                          // THD, SYSTEM_THREAD_SLAVE_SQL
 
+static bool is_row_based_replication(THD *thd)
+{
+  /*
+    A row event which has its thd->query() == NULL or a row event which has
+    replicate_annotate_row_events enabled. In the later case the thd->query()
+    will be pointing to the query, received through replicated annotate event
+    from master.
+  */
+  return ((thd->query() == NULL) || thd->variables.binlog_annotate_row_events);
+}
 /* Static declarations for handlerton */
 
 static handler *blackhole_create_handler(handlerton *hton,
@@ -109,7 +119,8 @@ int ha_blackhole::update_row(const uchar *old_data, uchar *new_data)
 {
   DBUG_ENTER("ha_blackhole::update_row");
   THD *thd= ha_thd();
-  if (thd->system_thread == SYSTEM_THREAD_SLAVE_SQL && thd->query() == NULL)
+  if (thd->system_thread == SYSTEM_THREAD_SLAVE_SQL &&
+      is_row_based_replication(thd))
     DBUG_RETURN(0);
   DBUG_RETURN(HA_ERR_WRONG_COMMAND);
 }
@@ -118,7 +129,8 @@ int ha_blackhole::delete_row(const uchar *buf)
 {
   DBUG_ENTER("ha_blackhole::delete_row");
   THD *thd= ha_thd();
-  if (thd->system_thread == SYSTEM_THREAD_SLAVE_SQL && thd->query() == NULL)
+  if (thd->system_thread == SYSTEM_THREAD_SLAVE_SQL &&
+      is_row_based_replication(thd))
     DBUG_RETURN(0);
   DBUG_RETURN(HA_ERR_WRONG_COMMAND);
 }
@@ -135,7 +147,8 @@ int ha_blackhole::rnd_next(uchar *buf)
   int rc;
   DBUG_ENTER("ha_blackhole::rnd_next");
   THD *thd= ha_thd();
-  if (thd->system_thread == SYSTEM_THREAD_SLAVE_SQL && thd->query() == NULL)
+  if (thd->system_thread == SYSTEM_THREAD_SLAVE_SQL &&
+      is_row_based_replication(thd))
     rc= 0;
   else
     rc= HA_ERR_END_OF_FILE;
@@ -220,7 +233,8 @@ int ha_blackhole::index_read_map(uchar * buf, const uchar * key,
   int rc;
   DBUG_ENTER("ha_blackhole::index_read");
   THD *thd= ha_thd();
-  if (thd->system_thread == SYSTEM_THREAD_SLAVE_SQL && thd->query() == NULL)
+  if (thd->system_thread == SYSTEM_THREAD_SLAVE_SQL &&
+      is_row_based_replication(thd))
     rc= 0;
   else
     rc= HA_ERR_END_OF_FILE;
@@ -235,7 +249,8 @@ int ha_blackhole::index_read_idx_map(uchar * buf, uint idx, const uchar * key,
   int rc;
   DBUG_ENTER("ha_blackhole::index_read_idx");
   THD *thd= ha_thd();
-  if (thd->system_thread == SYSTEM_THREAD_SLAVE_SQL && thd->query() == NULL)
+  if (thd->system_thread == SYSTEM_THREAD_SLAVE_SQL &&
+      is_row_based_replication(thd))
     rc= 0;
   else
     rc= HA_ERR_END_OF_FILE;
@@ -249,7 +264,8 @@ int ha_blackhole::index_read_last_map(uchar * buf, const uchar * key,
   int rc;
   DBUG_ENTER("ha_blackhole::index_read_last");
   THD *thd= ha_thd();
-  if (thd->system_thread == SYSTEM_THREAD_SLAVE_SQL && thd->query() == NULL)
+  if (thd->system_thread == SYSTEM_THREAD_SLAVE_SQL &&
+      is_row_based_replication(thd))
     rc= 0;
   else
     rc= HA_ERR_END_OF_FILE;


More information about the commits mailing list