# ==== Purpose ====
#
# Verifies that empty transactions are generated when they are
# supposed to. There are three cases:
#
#  1. Transaction is filtered out on slave
#
#  2. Transaction does not have any effect, gets logged and assigned a
#     GTID on master because master uses binlog_format=statement, but
#     does not get logged on slave because slave uses binlog_format=row.
#
#  3. Transaction is explicitly made empty on master by setting
#     gtid_next and executing a COMMIT.
#
# Moreover, verify that no empty transaction is generated for rolled
# back transactions.
#
# ==== Implementation ====
#
# For case 1, we use a filter (specified in the .cnf file) to filter
# out table t_ignore, and verify that empty transaction is generated
# on slave.  Do this both for (1A) base tables and for (1B) temporary
# tables.
#
# For case 2, we use binlog_format=statement on master and
# binlog_format=row on the first slave.  We test two cases of
# transactions that are not logged in row format: (2A) temporary table
# DDL and DML and (2B) an UPDATE statement that matches no row.  We
# use a second slave where the effect of the statement would be seen,
# if it was not logged as an empty statement.  In case (2A) we check
# that slave_open_temp_tables is still 0 on the second slave. In case
# (2B) we do an UPDATE on master that matches no rows on master, but
# that would match a row on the second slave.
#
# For case 3, we just execute an explicit empty transaction on master
# and verify that it gets assigned a GTID and gets replicated to the
# slaves.
#
# To verify nothing is logged for rollback transactions, we execute a
# DDL statement that generates an error, a DML statement that
# generates an error, and an explicit ROLLBACK transaction, and verify
# that nothing got logged for the three statements.
#
# ==== References ====
#
# WL#3584 - Global Transaction Identifiers (GTIDs)
# - The base worklog implementing empty transactions.
# BUG#18145032 - NO EMPTY TRANSACTION IS CREATED FOR A FILTERED CREATE TEMPORARY TABLE WITH GTIDS
# - Addressing the special cases for CREATE/ALTER/DROP TEMPORARY.
# BUG#18095502 - RESTART OF SLAVE INSTANCE FAIL IN GTID REPLICATION IF WE USE REPLICATE-IGNORE-DB
# - Addressing database filters in the applier thread.

--let $rpl_topology= 1->2->3
--let $rpl_gtid_utils= 1
--source include/rpl_init.inc
--source include/rpl_default_connections.inc
--source include/not_embedded.inc
--source include/have_binlog_format_mixed.inc
--source include/have_gtid.inc

--source include/rpl_reset.inc

--echo ==== Case 1A: Transaction filtered out ====

--source include/gtid_step_reset.inc

CREATE TABLE t_ignore(id INT);
INSERT INTO t_ignore VALUES (1);
DROP TABLE t_ignore;

CREATE TABLE t_ignore_wild(id INT);
INSERT INTO t_ignore_wild VALUES (1);
DROP TABLE t_ignore_wild;

# Verify that 6 GTIDs were generated.
--let $gtid_step_count= 6
--source include/gtid_step_assert.inc

# Verify that the GTIDs make it to every slave
--source include/rpl_sync.inc

--echo ==== Case 1B: CREATE/ALTER/DROP TEMPORARY filtered out ====

CREATE TEMPORARY TABLE t_ignore(a INT);
ALTER TABLE t_ignore ADD COLUMN b INT;
INSERT INTO t_ignore VALUES (1, 2);
DROP TEMPORARY TABLE t_ignore;

CREATE TEMPORARY TABLE t_ignore_wild(a INT);
ALTER TABLE t_ignore_wild ADD COLUMN b INT;
INSERT INTO t_ignore_wild VALUES (1, 2);
DROP TEMPORARY TABLE t_ignore_wild;

# Verify that 8 GTIDs were generated.
--let $gtid_step_count= 8
--source include/gtid_step_assert.inc

# Verify that the GTIDs make it to every slave.
--source include/rpl_sync.inc

--echo ==== Case 1C: database filters on slave applier ====

--source include/gtid_step_reset.inc

# These two will be ignored on first slave.
CREATE DATABASE db_ignore;
USE db_ignore;
CREATE TABLE t1 (a INT);
INSERT INTO t1 VALUES (1);
USE test;

# Verify 2 GTIDs were generated
--let $gtid_step_count= 3
--source include/gtid_step_assert.inc

--source include/rpl_sync.inc

--let $assert_text= db_ignore should not be created on slave
--let $assert_cond= "[slave:SHOW DATABASES LIKE "db_ignore"]" = ""
--source include/assert.inc

--let $assert_text= db_ignore should not be created on second slave
--let $assert_cond= "[server_3:SHOW DATABASES LIKE "db_ignore"]" = ""
--source include/assert.inc

DROP DATABASE db_ignore;
--source include/rpl_sync.inc

--echo ==== Case 1D: database filters on slave binary log ====

--source include/gtid_step_reset.inc

# These two will be ignored on first slave.
CREATE DATABASE db_binlog_ignore;
USE db_binlog_ignore;
CREATE TABLE t1 (a INT);
INSERT INTO t1 VALUES (1);
USE test;

# Verify 2 GTIDs were generated
--let $gtid_step_count= 3
--source include/gtid_step_assert.inc

--source include/rpl_sync.inc

--let $assert_text= db_binlog_ignore should not be created on slave
--let $assert_cond= "[slave:SHOW DATABASES LIKE "db_binlog_ignore"]" = "db_binlog_ignore"
--source include/assert.inc

--let $assert_text= db_binlog_ignore should not be created on second slave
--let $assert_cond= "[server_3:SHOW DATABASES LIKE "db_binlog_ignore"]" = ""
--source include/assert.inc

DROP DATABASE db_binlog_ignore;
--source include/rpl_sync.inc

--echo ==== Case 2A: temp table transaction not logged in row format ====

--echo ---- Initialize ----

--connection slave
SET @save.binlog_format= @@global.binlog_format;
SET @@global.binlog_format= row;
source include/restart_slave_sql.inc;

--echo ---- Test ----

--source include/gtid_step_reset.inc


# - Master will log these statements and assign them GTIDs, since
#   master uses binlog_format=statement.
#
# - First slave will not log any change since it uses row format, but
#   it should log empty transactions with GTIDs.
#
# - Second slave should also not log any change, since it receives an
#   empty transaction from the first slave.  Thus
#   Slave_open_temp_tables should remain 0 on the second slave.
CREATE TEMPORARY TABLE t2 (a INT);
ALTER TABLE t2 ADD COLUMN b INT;
INSERT INTO t2 VALUES (1, 2);

# assert exactly 3 GTIDs were generated
--let $gtid_step_count= 3
--source include/gtid_step_assert.inc

# Verify that the GTID gets replicated everywhere.
--let $use_gtids= 1
--source include/rpl_sync.inc

# Verify temp tables on first slave but not on second slave
--let $assert_text= First slave should not have created any temp table
--let $assert_cond= [slave:SHOW GLOBAL STATUS LIKE 'Slave_open_temp_tables'] = 1

--let $assert_text= Second slave should not have created any temp table
--let $assert_cond= [server_3:SHOW GLOBAL STATUS LIKE 'Slave_open_temp_tables'] = 0

--echo ---- Clean up ----

DROP TEMPORARY TABLE t2;
--source include/rpl_sync.inc

--connection slave
SET @@global.binlog_format= @save.binlog_format;
--connection master

--echo ==== Case 2B: transaction with no effect not logged in row format ====

--echo ---- Initialize ----

--connection slave
SET @save.binlog_format= @@global.binlog_format;
SET @@global.binlog_format= row;
source include/restart_slave_sql.inc;

--connection master
SET @@session.binlog_format= statement;
CREATE TABLE t1(id INT) ENGINE = InnoDB;
--source include/rpl_sync.inc
--connection server_3
INSERT INTO t1 VALUES (0);
--connection master

--echo ---- Test ----

# - Master will log this and assign it a GTID despite it has no
#   effect, since master uses binlog_format=statement.
#
# - First slave will not log any change since it uses row format, but
#   it should log an empty transaction with a GTID.
#
# - Second slave should also not log any change, since it receives an
#   empty transaction from the first slave.  It should also not update
#   the table despite the original SQL statement would match a row on
#   the second slave, since it only receives the empty transaction
#   from the first slave.
UPDATE t1 SET id= 1 WHERE id = 0;

# Verify that the GTID gets replicated everywhere.
--let $use_gtids= 1
--source include/rpl_sync.inc

--let $assert_text= Second slave should not have done any update
--let $assert_cond= [server_3:SELECT * FROM t1] = 0

-- echo ---- Clean up ----

DROP TABLE t1;
--source include/rpl_sync.inc

--connection slave
SET @@global.binlog_format= @save.binlog_format;
--connection master

--echo ==== Case 3: explicit empty transaction on master ====

--source include/gtid_step_reset.inc
eval SET @@SESSION.GTID_NEXT = '$uuida:1';
COMMIT;
SET @@SESSION.GTID_NEXT = 'AUTOMATIC';

# Verify exactly one GTID was generated
--let $gtid_step_count= 1
--let $gtid_step_only_count= 1
--source include/gtid_step_assert.inc
--let $gtid_step_only_count= 0

# Verify the GTID makes it to every slave.
--source include/rpl_sync.inc

--echo ==== Case 4: Nothing logged for ROLLBACK transactions ====

# Test that ROLLBACK does not generate any GTID

--echo ---- Initialize ----

CREATE TABLE t1 (a INT) ENGINE = InnoDB;

--echo ---- Test ----

--source include/gtid_step_reset.inc

--error ER_BAD_TABLE_ERROR
DROP TABLE t2;

--error ER_WRONG_VALUE_COUNT_ON_ROW
INSERT INTO t1 VALUES (1, 1);

BEGIN;
INSERT INTO t1 VALUES (2);
ROLLBACK;

--let $gtid_step_count= 0
--source include/gtid_step_assert.inc

--echo ---- Clean Up ----

DROP TABLE t1;

--source include/rpl_end.inc
