#
# Purpose:
#   This test ensures consistency in binlogging behavior for XA transactions
# that have all statements error and rollback, effectively leaving an "empty"
# transaction. In such cases, an empty XA transaction should be binlogged. The
# bug reported by MDEV-25616 revealed that an "empty" XA transaction would
# binlog an XA ROLLBACK or XA COMMIT event without a preceding setup, i.e.
# XA START through XA PREPARE. The bug presented differently for XA
# transactions consisting of transactional and non-transactional statements.
# Therefore, this test validates that an entire XA transaction is binlogged
# for different combinations of transactional or non-transactional statements.
# Note that the behavior changes when binlogging empty XA transactions
# depending on the binlog_row_format variables. That is, when the content of
# the transaction consists of errored transactional statements, in row format,
# an empty XA transaction will be binlogged; however, in mixed and statement
# formats, nothing will be written into the binary log.
#
# Methodology:
#   Create XA transactions with various combinations of erroring transactional
# or non-transactional statements. The binary log is examined to ensure all
# XA components are written. Chain replication is used, i.e.
# (primary->replica->replica), to ensure replica binlogging is consistent with
# manual execution. The transactional and non-transactional tables use InnoDB
# and MyISAM, respectively.
#
# Parameters
#   $expect_transactional_xa_binlog : Boolean indicating whether or not an
#       errored transactional statement should result in XA statements written
#       into the binary log.
#
# References:
#   MDEV-25616: Binlog event for XA COMMIT is generated without matching
#               XA START, replication aborts
#
#   MDEV-33921: Replication fails when XA transactions are used where the slave
#               has replicate_do_db set and the client has touched a different
#               database when running DML such as inserts.
#
--source include/have_log_bin.inc

--let $rpl_server_count= 3
--let $rpl_topology= 1->2->3
--source include/rpl_init.inc

--connection server_1
-- source include/have_innodb.inc
--connection server_2
-- source include/have_innodb.inc
--connection server_3
-- source include/have_innodb.inc
--connection server_1

--echo #
--echo # Test Case 1: An XA transaction without any statements should not be
--echo # binlogged
--let $trx_statements=

--let $xa_completion_action= COMMIT
--source include/rpl_xa_empty_transaction.inc

--let $xa_completion_action= ROLLBACK
--source include/rpl_xa_empty_transaction.inc


--echo #
--echo # Test Case 2: An XA transaction consisting of a successfully rolled back
--echo # statement should not be binlogged
--let $trx_statements= T

--let $xa_completion_action= COMMIT
--source include/rpl_xa_empty_transaction.inc

--let $xa_completion_action= ROLLBACK
--source include/rpl_xa_empty_transaction.inc


--echo #
--echo # Test Case 3: An XA transaction with a statement that cannot be rolled
--echo # back should be binlogged

# TODO: remove work-around MDEV-24654 when fixed.
--connection server_1
set @sav_binlog_format = @@binlog_format;
set @@binlog_format = row;
set @@global.binlog_format = row;
--let $trx_statements= N

--let $xa_completion_action= COMMIT
--source include/rpl_xa_empty_transaction.inc

--let $xa_completion_action= ROLLBACK
--source include/rpl_xa_empty_transaction.inc

--connection server_1
set @@binlog_format = @sav_binlog_format;
set @@global.binlog_format = @sav_binlog_format;


--echo #
--echo # Test Case 4: An XA transaction with multiple statements that can all
--echo # be rolled back should not be binlogged
--let $trx_statements= T,T

--let $xa_completion_action= COMMIT
--source include/rpl_xa_empty_transaction.inc

--let $xa_completion_action= ROLLBACK
--source include/rpl_xa_empty_transaction.inc


--echo #
--echo # Test Case 5: A mixed XA transaction consisting of one statement that
--echo # can successfully be rolled back (first statement), and another that
--echo # can not (second statement) should be binlogged

--connection server_1
set @sav_binlog_format = @@binlog_format;
set @@binlog_format = row;
set @@global.binlog_format = row;
--let $trx_statements= T,N

--let $xa_completion_action= COMMIT
--source include/rpl_xa_empty_transaction.inc

--let $xa_completion_action= ROLLBACK
--source include/rpl_xa_empty_transaction.inc

--connection server_1
set @@binlog_format = @sav_binlog_format;
set @@global.binlog_format = @sav_binlog_format;


--echo #
--echo # Test Case 6: A mixed XA transaction consisting of one statement that
--echo # cannot successfully be rolled back (first statement), and another that
--echo # can (second statement) should be binlogged

--connection server_1
set @sav_binlog_format = @@binlog_format;
set @@binlog_format = row;
set @@global.binlog_format = row;
--let $trx_statements= N,T

--let $xa_completion_action= COMMIT
--source include/rpl_xa_empty_transaction.inc

--let $xa_completion_action= ROLLBACK
--source include/rpl_xa_empty_transaction.inc

--connection server_1
set @@binlog_format = @sav_binlog_format;
set @@global.binlog_format = @sav_binlog_format;

--echo #
--echo # Test Case 7: An XA transaction consisting of two failed
--echo # non-transactional statements should be binlogged

--connection server_1
set @sav_binlog_format = @@binlog_format;
set @@binlog_format = row;
set @@global.binlog_format = row;
--let $trx_statements= N,N

--let $xa_completion_action= COMMIT
--source include/rpl_xa_empty_transaction.inc

--let $xa_completion_action= ROLLBACK
--source include/rpl_xa_empty_transaction.inc

--connection server_1
set @@binlog_format = @sav_binlog_format;
set @@global.binlog_format = @sav_binlog_format;


--echo #
--echo # MDEV-33921.1: If a slave's replication of an XA transaction results in
--echo # an empty transaction, e.g. due to replication filters, the slave
--echo # should not binlog any part of the XA transaction.
#
#    Note that the MDEV-33921 report is actually about that XA END is filtered
# out (not executed), and then its corresponding XA PREPARE errors because the
# XA state of the transaction is incorrect. This test case inherently tests
# both bugs.

--connection server_1
create database db1;
create database db2;
create table db1.t1 (a int) engine=innodb;
--source include/save_master_gtid.inc
--connection server_3
--source include/sync_with_master_gtid.inc
--source include/stop_slave.inc
change master to master_use_gtid=slave_pos;

--connection server_2
--source include/stop_slave.inc
SET @@GLOBAL.replicate_ignore_db= "";
SET @@GLOBAL.replicate_do_db= "db2";
change master to master_use_gtid=slave_pos;
--source include/start_slave.inc

--connection server_1
--let $pre_xa_gtid= `SELECT @@global.gtid_binlog_pos`
use db1;
XA START "x1";
insert into db1.t1 values (1);
XA END "x1";
XA PREPARE "x1";
XA COMMIT "x1";
--source include/save_master_gtid.inc

--connection server_2
--source include/sync_with_master_gtid.inc
--let $slave_binlogged_gtid= `SELECT @@global.gtid_binlog_pos`
if (`SELECT strcmp("$slave_binlogged_gtid","$pre_xa_gtid")`)
{
  --die Slave binlogged an empty XA transaction yet should not have
}

--connection server_2
--source include/save_master_gtid.inc

--connection server_3
--source include/start_slave.inc
--source include/sync_with_master_gtid.inc

--echo #
--echo # 33921.2: If the slave shuts down after "preparing" a filtered-to-empty
--echo # XA transaction (and not completing it), then when the respective
--echo # XA completion (COMMIT in this test) command is replicated, the slave
--echo # should not throw ER_XAER_NOTA. Note that internally, the error is
--echo # thrown, but it is ignored because the target db is filtered.

--connection server_3
--source include/stop_slave.inc

--connection server_1
--let $pre_xa_gtid= `SELECT @@global.gtid_binlog_pos`

#  Used by mysqlbinlog in part 3
--let $pre_xa_pos  = query_get_value(SHOW MASTER STATUS, Position, 1)

use db1;
XA START "x2";
insert into db1.t1 values (2);
XA END "x2";
XA PREPARE "x2";
--source include/save_master_gtid.inc

--connection server_2
--source include/sync_with_master_gtid.inc
--let $rpl_server_number= 2
--echo # Connection named slave is needed for reconnection
--connect(slave,localhost,root,,)
--connect(slave1,localhost,root,,)
--source include/rpl_restart_server.inc

--connection server_2
--source include/stop_slave.inc
SET @@GLOBAL.replicate_do_db= "db2";
change master to master_use_gtid=slave_pos;
--source include/start_slave.inc

--connection server_1
XA COMMIT "x2";
--source include/save_master_gtid.inc

--connection server_2
--source include/sync_with_master_gtid.inc
--source include/save_master_gtid.inc

--connection server_3
--source include/start_slave.inc
--source include/sync_with_master_gtid.inc

--echo #
--echo # 33921.3: Ensure XA commands are not considered by mysqlbinlog's
--echo # --database filter
--connection server_1
--let $datadir= `select @@datadir`
--let $binlog_file= query_get_value(SHOW MASTER STATUS, File, 1)
--let assert_file= $MYSQLTEST_VARDIR/tmp/binlog_decoded.out
--echo # MYSQL_BINLOG datadir/binlog_file --start-position=pre_xa_pos --database=db2 --result-file=assert_file
--exec $MYSQL_BINLOG $datadir/$binlog_file --start-position=$pre_xa_pos --database=db2 --result-file=$assert_file

--let assert_text= Mysqlbinlog should output all XA commands from the filtered transaction
--let assert_count= 4
--let assert_select= XA START|XA END|XA PREPARE|XA COMMIT
--source include/assert_grep.inc

--connection server_2
--source include/stop_slave.inc
SET @@GLOBAL.replicate_do_db="";
--source include/start_slave.inc

--connection server_1
drop database db1;
drop database db2;


#
# Cleanup
--connection server_1
--source include/rpl_end.inc

--echo # End of rpl_xa_empty_transaction.test
