# ==== Purpose ====
#
# This test script serves as the functionality testing for the table
# performance_schema.replication_applier_status_by_worker. Test
# for ddl and dml operations is a part of the perfschema suite.
# The ddl/dml tests are named:
# 1) ddl_replication_applier_status_by_worker.test and
# 2) dml_replication_applier_status_by_worker.test.
#
# This test script does the following:

#  - Verify that SELECT works for every field in the table.
#  - The SELECT per field produces an output similar to the corresponding field
#    in SHOW SLAVE STATUS(SSS), if there is one.
#  - If there is no matching field in SSS, we resort to other method of testing
#    those fields.
#  - We perform all the testing on connection "slave". On master, the table
#    returns an empty set.
#
# The follwing scenarios are tested in this test script:
#
#  - Test each field on a fresh replication setup.
#  - Introduce error in worker thread and check for the correctness of error
#    error number, message and timestamp.
#  - Verify that, the change in values are correctly shown by the table.
#  - Verify that the values are preserved after STOP SLAVE.
#  - Set up replication in gtid-mode=on and test 'Last_Seen_Transaction' field.
#  - Verify that the value in 'Last_Seen_Transaction' field is preserved after
#    STOP SLAVE.
#
#  ==== Related Bugs and Worklogs ====
#
#  WL#3656: PERFORMANCE SCHEMA table for SHOW SLAVE STATUS
#  Bug#18358253: RPL.RPL_PERFSCHEMA_EXECUTE_STATUS_BY_WORKER HAS SPORADIC FAILURES ON PB2
#
#  Bug #18360716 ERRORS IN MTS WORKER THREADS REPORTED WRONGLY FOR ANONYMOUS TRANSACTIONS
#  - The result file was updated after fixing this bug.

--source include/not_group_replication_plugin.inc
source include/not_gtid_enabled.inc;
source include/have_binlog_format_mixed.inc;
source include/master-slave.inc;

call mtr.add_suppression("Error 'Table 'test.t' doesn't exist' on query.");
call mtr.add_suppression("The slave coordinator and worker threads are stopped, possibly leaving data in inconsistent state.");
call mtr.add_suppression("Request to stop slave SQL Thread received while applying a group that has non-transactional changes;");

let $assert_text= On master, the table should return an empty set.;
let $assert_cond= [select count(*) from performance_schema.replication_applier_status_by_worker] = 0;
source include/assert.inc;

--echo
--echo # Setup MTS and perform testing on a fresh slave.
--echo

--source include/sync_slave_sql_with_master.inc
--connection slave

# PB2 runs the tests with slave-parallel-worker=4 in a combination.
# Reset it to 0 beacause the following test for empty set is valid only
# in non-MTS slave.

source include/stop_slave.inc;
SET @save_slave_parallel_workers=@@global.slave_parallel_workers;
SET @@global.slave_parallel_workers=0;
# to avoid warnings
set @save_slave_transaction_retries= @@global.slave_transaction_retries;
source include/start_slave.inc;

# Post Bug#20001173 fix in case of Single Threaded Slave(STS) mode SQL
# thread's staus will be reported as part of
# performance_schema.replication_applier_status_by_worker table.
let $assert_text= In non-MTS mode(SQL thread), the table should return one row as Single SQL applier thread will be part of replication_applier_status_by_worker table.;
let $assert_cond= [select count(*) from performance_schema.replication_applier_status_by_worker] = 1;
source include/assert.inc;

let $assert_text= In non-MTS mode(SQL thread), the table should return empty result set;
let $assert_cond= [select count(*) from performance_schema.replication_applier_status_by_coordinator] = 0;
source include/assert.inc;
source include/stop_slave.inc;

SET @@global.slave_parallel_workers=1;
# to avoid warnings
set @save_slave_transaction_retries= @@global.slave_transaction_retries;
source include/start_slave.inc;

let $ps_value= query_get_value(select Worker_Id from performance_schema.replication_applier_status_by_worker, Worker_Id, 1);
let $assert_text= Worker_Id should be 1;
let $assert_cond= "$ps_value"= 1;
source include/assert.inc;

# To verify the correctness of thread_id field, we check for the name of
# the thread.

let $thread_name= `select name from performance_schema.threads where thread_id= (select Thread_Id from performance_schema.replication_applier_status_by_worker)`;
let $assert_text= thread_name should should indicate worker thread.;
let $assert_cond= "$thread_name" = "thread/sql/slave_worker";
source include/assert.inc;

let $ps_value= query_get_value(select Service_State from performance_schema.replication_applier_status_by_worker, Service_State, 1);
let $assert_text= Service_State should be "ON" on a fresh slave server.;
let $assert_cond= "$ps_value"= "ON";
source include/assert.inc;

let $ps_value= query_get_value(select Last_Seen_Transaction from performance_schema.replication_applier_status_by_worker, Last_Seen_Transaction, 1);
let $assert_text= Last_Seen_Transaction should show "" if no transaction applierd;
let $assert_cond= "$ps_value" = "";
source include/assert.inc;

--connection master
CREATE TABLE t1 (a INT);
DROP TABLE t1;
--sync_slave_with_master

let $ps_value= query_get_value(select Last_Seen_Transaction from performance_schema.replication_applier_status_by_worker, Last_Seen_Transaction, 1);
let $assert_text= Last_Seen_Transaction should show "ANONYMOUS" if last transaction was anonymous.;
let $assert_cond= "$ps_value" = "ANONYMOUS";
source include/assert.inc;

let $sss_value= query_get_value(SHOW SLAVE STATUS, Last_SQL_Errno, 1);
let $ps_value= query_get_value(select Last_Error_Number from performance_schema.replication_applier_status_by_worker, Last_Error_Number, 1);
let $assert_text= Value returned by SSS and PS table for Last_Error_Number should be same.;
let $assert_cond= "$sss_value" = "$ps_value";
source include/assert.inc;

let $sss_value= query_get_value(SHOW SLAVE STATUS, Last_SQL_Error, 1);
let $ps_value= query_get_value(select Last_Error_Message from performance_schema.replication_applier_status_by_worker, Last_Error_Message, 1);
let $assert_text= Value returned by SSS and PS table for Last_Error_Message should both be empty.;
let $assert_cond= "$sss_value" = "$ps_value";
source include/assert.inc;

let $sss_value= query_get_value(SHOW SLAVE STATUS, Last_SQL_Error_Timestamp, 1);
let $ps_value= query_get_value(select Last_Error_Timestamp from performance_schema.replication_applier_status_by_worker, Last_Error_Timestamp, 1);
let $assert_text= Value returned by PS table for Last_Error_Timestamp should be 0000-00-00 00:00:00.;
let $assert_cond= "$ps_value" = "0000-00-00 00:00:00";
source include/assert.inc;

--echo
--echo # Introduce an error in the worker thread and check for the correctness
--echo # of error number, message and timestamp fields.
--echo

# Cause an error in Worker thread.
# 1) Create a table 't' at master, replicate at slave.
# 2) Drop table 't' at slave only.
# 3) Insert a value in table 't' on master and replicate on slave.
# Since slave doesnt have table 't' anymore, worker thread will report an error.

--connection master
use test;
create table t(a int primary key);
sync_slave_with_master;
drop table t;
--connection master
insert into t values(1);
--connection slave
let $slave_sql_errno=1146;
source include/wait_for_slave_sql_error.inc;

--echo
--echo # Extract the error related fields from SSS and PS table and compare
--echo # them for correctness.
--echo

let $sss_value= query_get_value(SHOW SLAVE STATUS, Last_SQL_Errno, 1);
let $ps_value= query_get_value(select Last_Error_Number from performance_schema.replication_applier_status_by_worker, Last_Error_Number, 1);
let $assert_text= Value returned by SSS and PS table for Last_Error_Number should be same.;
let $assert_cond= "$sss_value" = "$ps_value";
source include/assert.inc;

--disable_query_log
--replace_regex /master-bin.[0-9]+/FILENAME/ /end_log_pos [0-9]+/end_log_pos POSITION/
select Last_Error_Message from performance_schema.replication_applier_status_by_worker;
--enable_query_log

# The timestamp format is slightly different in SSS and PS.
# SSS => YYMMDD HH:MM:SS
# PS  => YYYY-MM-DD HH:MM:SS
# To match the two, we get rid of hyphons from PS output and first two digits
# the year field so that it can be matched directly.

let $sss_value= query_get_value(SHOW SLAVE STATUS, Last_SQL_Error_Timestamp, 1);
let $ps_value= query_get_value(select Last_Error_Timestamp from performance_schema.replication_applier_status_by_worker, Last_Error_Timestamp, 1);
let $ps_value_without_hyphons= `SELECT REPLACE("$ps_value", '-', '')`;
let $ps_value_in_sss_format= `select substring("$ps_value_without_hyphons", 3)`;
let $assert_text= Value returned by SSS and PS table for Last_Error_Timestamp should be same.;
let $assert_cond= "$sss_value" = "$ps_value_in_sss_format";
source include/assert.inc;

--echo
--echo # Verify that the error fields are preserved after STOP SLAVE.
--echo

--echo
--echo # 1. Verify that thread_id changes to NULL and service_state to "off" on
--echo #    STOP SLAVE.
--echo

let $ps_value= query_get_value(select thread_id from performance_schema.replication_applier_status_by_worker, thread_id, 1);
let $assert_text= After STOP SLAVE, thread_id should be NULL;
let $assert_cond= "$ps_value" = "NULL";
source include/assert.inc;

let $ps_value= query_get_value(select Service_State from performance_schema.replication_applier_status_by_coordinator, Service_State, 1);
let $assert_text= So, Service_State after STOP SLAVE should be "OFF".;
let $assert_cond= "$ps_value"= "OFF";
source include/assert.inc;

--echo
--echo # 2. Extract the worker_id and the error related fields from SSS and PS
--echo #    table and compare them. These fields should preserve their values.
--echo

let $ps_value= query_get_value(select Worker_Id from performance_schema.replication_applier_status_by_worker, Worker_Id, 1);
let $assert_text= Worker_Id should be 1;
let $assert_cond= "$ps_value"= 1;
source include/assert.inc;

let $sss_value= query_get_value(SHOW SLAVE STATUS, Last_SQL_Errno, 1);
let $ps_value= query_get_value(select Last_Error_Number from performance_schema.replication_applier_status_by_worker, Last_Error_Number, 1);
let $assert_text= Value returned by SSS and PS table for Last_Error_Number should be same.;
let $assert_cond= "$sss_value" = "$ps_value";
source include/assert.inc;

--disable_query_log
--replace_regex /master-bin.[0-9]+/FILENAME/ /end_log_pos [0-9]+/end_log_pos POSITION/
select Last_Error_Message from performance_schema.replication_applier_status_by_worker;
--enable_query_log

# The timestamp format is slightly different in SSS and PS.
# SSS => YYMMDD HH:MM:SS
# PS  => YYYY-MM-DD HH:MM:SS
# To match the two, we get rid of hyphons from PS output and first two digits
# the year field so that it can be matched directly.

let $sss_value= query_get_value(SHOW SLAVE STATUS, Last_SQL_Error_Timestamp, 1);
let $ps_value= query_get_value(select Last_Error_Timestamp from performance_schema.replication_applier_status_by_worker, Last_Error_Timestamp, 1);
let $ps_value_without_hyphons= `SELECT REPLACE("$ps_value", '-', '')`;
let $ps_value_in_sss_format= `select substring("$ps_value_without_hyphons", 3)`;
let $assert_text= Value returned by SSS and PS table for Last_Error_Timestamp should be same.;
let $assert_cond= "$sss_value" = "$ps_value_in_sss_format";
source include/assert.inc;

stop slave;
reset slave;
--connection master
drop table t;
reset master;

--echo
--echo # Set up replication in gtid-mode=on and test 'Last_Seen_Transaction'
--echo # field.
--echo

--let $rpl_skip_sync= 1
--let $rpl_set_enforce_gtid_consistency= 1
--source include/rpl_set_gtid_mode.inc
--let $rpl_skip_sync= 0

--connection slave
stop slave;
replace_result $MASTER_MYPORT MASTER_MYPORT;
replace_column 2 ####;
eval change master to master_host='127.0.0.1',master_port=$MASTER_MYPORT, master_user='root';
SET @@global.slave_parallel_workers=1;
# to avoid warnings
set @save_slave_transaction_retries= @@global.slave_transaction_retries;
start slave;

--connection master
create database d1;
create table d1.t1(a int);
insert into d1.t1 values(1);
let $MASTER_UUID= query_get_value(`SHOW VARIABLES LIKE '%server_uuid%'`, Value, 1);

sync_slave_with_master;

replace_result $MASTER_UUID MASTER_UUID;
query_vertical select Last_Seen_Transaction from performance_schema.replication_applier_status_by_worker;

--echo
--echo # Verify that the value in 'Last_Seen_Transaction' field is preserved
--echo # after STOP SLAVE.
--echo

source include/stop_slave.inc;
replace_result $MASTER_UUID MASTER_UUID;
query_vertical select Last_Seen_Transaction from performance_schema.replication_applier_status_by_worker;

--echo #
--echo # BUG#20513547 REPLICATION_APPLIER_STATUS_BY_WORKER SHOWS
--echo #              WRONG WORKER_ID WHEN SLAVE STOPS
--echo #
SET @@GLOBAL.slave_parallel_workers = 3;
--source include/start_slave.inc
SELECT worker_id from performance_schema.replication_applier_status_by_worker;
--source include/stop_slave.inc
SELECT worker_id from performance_schema.replication_applier_status_by_worker;

--echo
--echo # Cleanup.
--echo

--connection master
DROP DATABASE d1;

--connection slave
set @@global.slave_parallel_workers= @save_slave_parallel_workers;
set @@global.slave_transaction_retries= @save_slave_transaction_retries;
source include/start_slave.inc;

--let $rpl_gtid_mode= OFF
--let $rpl_set_enforce_gtid_consistency= 0
--source include/rpl_set_gtid_mode.inc

source include/rpl_end.inc;
