Commit 980108ce authored by Oleksandr Byelkin's avatar Oleksandr Byelkin Committed by Andrei Elkin

MDEV-21833 Make slave_run_triggers_for_rbr enforce triggers to run on slave,...

MDEV-21833 Make slave_run_triggers_for_rbr enforce triggers to run on slave, even when there are triggers on the master

A bit changed patch of Anders Karlsson with examples added.

New parameters "ENFORCE" to slave-run-triggers-for-rbr added.
parent 1f5a8e1f
......@@ -1187,13 +1187,15 @@ The following specify which files/extra groups are read (specified before remain
Alias for slave_parallel_threads
--slave-run-triggers-for-rbr=name
Modes for how triggers in row-base replication on slave
side will be executed. Legal values are NO (default), YES
and LOGGING. NO means that trigger for RBR will not be
running on slave. YES and LOGGING means that triggers
will be running on slave, if there was not triggers
running on the master for the statement. LOGGING also
means results of that the executed triggers work will be
written to the binlog.
side will be executed. Legal values are NO (default),
YES, LOGGING and ENFORCE. NO means that trigger for RBR
will not be running on slave. YES and LOGGING means that
triggers will be running on slave, if there was not
triggers running on the master for the statement. LOGGING
also means results of that the executed triggers work
will be written to the binlog. ENFORCE means that
triggers will always be run on the slave, even if there
are triggers on the master. ENFORCE implies LOGGING.
--slave-skip-errors=name
Tells the slave thread to continue replication when a
query event returns an error from the provided list
......
......@@ -338,4 +338,136 @@ connection master;
set binlog_row_image = @binlog_row_image.saved;
drop table t1;
connection slave;
#
# enterprise 10.4 tests start
#
#
# MENT-607 : Make slave_run_triggers_for_rbr enforce triggers to run
# on slave, even when there are triggers on the master
#
# Triggers on slave WILL work (with ENFORCE) if master has some
connection master;
CREATE TABLE t1 (C1 CHAR(1) primary key, C2 CHAR(1)) engine=innodb;
SELECT * FROM t1;
C1 C2
create trigger t1_dummy before delete on t1 for each row
set @dummy= 1;
connection slave;
connection slave;
SET @old_slave_exec_mode= @@global.slave_exec_mode;
SET @old_slave_run_triggers_for_rbr= @@global.slave_run_triggers_for_rbr;
SET @@global.slave_exec_mode= IDEMPOTENT;
SET @@global.slave_run_triggers_for_rbr= ENFORCE;
SELECT * FROM t1;
C1 C2
create table t2 (id char(2) primary key, cnt int, o char(1), n char(1));
insert into t2 values
('u0', 0, ' ', ' '),('u1', 0, ' ', ' '),
('d0', 0, ' ', ' '),('d1', 0, ' ', ' '),
('i0', 0, ' ', ' '),('i1', 0, ' ', ' ');
create trigger t1_cnt_b before update on t1 for each row
update t2 set cnt=cnt+1, o=old.C1, n=new.C1 where id = 'u0';
create trigger t1_cnt_ib before insert on t1 for each row
update t2 set cnt=cnt+1, n=new.C1, o=' ' where id = 'i0';
create trigger t1_cnt_a after update on t1 for each row
update t2 set cnt=cnt+1, o=old.C1, n=new.C1 where id = 'u1';
create trigger t1_cnt_da after delete on t1 for each row
update t2 set cnt=cnt+1, o=old.C1, n=' ' where id = 'd1';
create trigger t1_cnt_ia after insert on t1 for each row
update t2 set cnt=cnt+1, n=new.C1, o=' ' where id = 'i1';
SELECT * FROM t2 order by id;
id cnt o n
d0 0
d1 0
i0 0
i1 0
u0 0
u1 0
connection master;
# INSERT triggers test
insert into t1 values ('a','b');
connection slave;
connection slave;
SELECT * FROM t2 order by id;
id cnt o n
d0 0
d1 0
i0 1 a
i1 1 a
u0 0
u1 0
connection master;
# UPDATE triggers test
update t1 set C1= 'd';
connection slave;
connection slave;
SELECT * FROM t2 order by id;
id cnt o n
d0 0
d1 0
i0 1 a
i1 1 a
u0 1 a d
u1 1 a d
connection master;
# DELETE triggers test
delete from t1 where C1='d';
connection slave;
connection slave;
SELECT * FROM t2 order by id;
id cnt o n
d0 0
d1 1 d
i0 1 a
i1 1 a
u0 1 a d
u1 1 a d
# INSERT triggers which cause also UPDATE test (insert duplicate row)
insert into t1 values ('0','1');
SELECT * FROM t2 order by id;
id cnt o n
d0 0
d1 1 d
i0 2 0
i1 2 0
u0 1 a d
u1 1 a d
connection master;
insert into t1 values ('0','1');
connection slave;
connection slave;
SELECT * FROM t2 order by id;
id cnt o n
d0 0
d1 2 0
i0 3 0
i1 3 0
u0 1 a d
u1 1 a d
# INSERT triggers which cause also DELETE test
# (insert duplicate row in table referenced by foreign key)
insert into t1 values ('1','1');
connection master;
CREATE TABLE t3 (C1 CHAR(1) primary key, FOREIGN KEY (C1) REFERENCES t1(C1) ) engine=innodb;
insert into t1 values ('1','1');
connection slave;
connection slave;
SELECT * FROM t2 order by id;
id cnt o n
d0 0
d1 3 1
i0 5 1
i1 5 1
u0 1 a d
u1 1 a d
connection master;
drop table t3,t1;
connection slave;
connection slave;
SET @@global.slave_exec_mode= @old_slave_exec_mode;
SET @@global.slave_run_triggers_for_rbr= @old_slave_run_triggers_for_rbr;
drop table t2;
#
# enterprise 10.4 tests end
#
include/rpl_end.inc
......@@ -323,4 +323,120 @@ drop table t1;
--sync_slave_with_master
--echo #
--echo # enterprise 10.4 tests start
--echo #
--echo #
--echo # MENT-607 : Make slave_run_triggers_for_rbr enforce triggers to run
--echo # on slave, even when there are triggers on the master
--echo #
--echo # Triggers on slave WILL work (with ENFORCE) if master has some
connection master;
CREATE TABLE t1 (C1 CHAR(1) primary key, C2 CHAR(1)) engine=innodb;
SELECT * FROM t1;
create trigger t1_dummy before delete on t1 for each row
set @dummy= 1;
sync_slave_with_master;
connection slave;
SET @old_slave_exec_mode= @@global.slave_exec_mode;
SET @old_slave_run_triggers_for_rbr= @@global.slave_run_triggers_for_rbr;
SET @@global.slave_exec_mode= IDEMPOTENT;
SET @@global.slave_run_triggers_for_rbr= ENFORCE;
SELECT * FROM t1;
create table t2 (id char(2) primary key, cnt int, o char(1), n char(1));
insert into t2 values
('u0', 0, ' ', ' '),('u1', 0, ' ', ' '),
('d0', 0, ' ', ' '),('d1', 0, ' ', ' '),
('i0', 0, ' ', ' '),('i1', 0, ' ', ' ');
create trigger t1_cnt_b before update on t1 for each row
update t2 set cnt=cnt+1, o=old.C1, n=new.C1 where id = 'u0';
create trigger t1_cnt_ib before insert on t1 for each row
update t2 set cnt=cnt+1, n=new.C1, o=' ' where id = 'i0';
create trigger t1_cnt_a after update on t1 for each row
update t2 set cnt=cnt+1, o=old.C1, n=new.C1 where id = 'u1';
create trigger t1_cnt_da after delete on t1 for each row
update t2 set cnt=cnt+1, o=old.C1, n=' ' where id = 'd1';
create trigger t1_cnt_ia after insert on t1 for each row
update t2 set cnt=cnt+1, n=new.C1, o=' ' where id = 'i1';
SELECT * FROM t2 order by id;
connection master;
--echo # INSERT triggers test
insert into t1 values ('a','b');
sync_slave_with_master;
connection slave;
SELECT * FROM t2 order by id;
connection master;
--echo # UPDATE triggers test
update t1 set C1= 'd';
sync_slave_with_master;
connection slave;
SELECT * FROM t2 order by id;
connection master;
--echo # DELETE triggers test
delete from t1 where C1='d';
sync_slave_with_master;
connection slave;
SELECT * FROM t2 order by id;
--echo # INSERT triggers which cause also UPDATE test (insert duplicate row)
insert into t1 values ('0','1');
SELECT * FROM t2 order by id;
connection master;
insert into t1 values ('0','1');
sync_slave_with_master;
connection slave;
SELECT * FROM t2 order by id;
--echo # INSERT triggers which cause also DELETE test
--echo # (insert duplicate row in table referenced by foreign key)
insert into t1 values ('1','1');
connection master;
CREATE TABLE t3 (C1 CHAR(1) primary key, FOREIGN KEY (C1) REFERENCES t1(C1) ) engine=innodb;
insert into t1 values ('1','1');
sync_slave_with_master;
connection slave;
SELECT * FROM t2 order by id;
connection master;
drop table t3,t1;
sync_slave_with_master;
connection slave;
SET @@global.slave_exec_mode= @old_slave_exec_mode;
SET @@global.slave_run_triggers_for_rbr= @old_slave_run_triggers_for_rbr;
drop table t2;
--echo #
--echo # enterprise 10.4 tests end
--echo #
--source include/rpl_end.inc
......@@ -3616,11 +3616,11 @@ COMMAND_LINE_ARGUMENT REQUIRED
VARIABLE_NAME SLAVE_RUN_TRIGGERS_FOR_RBR
VARIABLE_SCOPE GLOBAL
VARIABLE_TYPE ENUM
VARIABLE_COMMENT Modes for how triggers in row-base replication on slave side will be executed. Legal values are NO (default), YES and LOGGING. NO means that trigger for RBR will not be running on slave. YES and LOGGING means that triggers will be running on slave, if there was not triggers running on the master for the statement. LOGGING also means results of that the executed triggers work will be written to the binlog.
VARIABLE_COMMENT Modes for how triggers in row-base replication on slave side will be executed. Legal values are NO (default), YES, LOGGING and ENFORCE. NO means that trigger for RBR will not be running on slave. YES and LOGGING means that triggers will be running on slave, if there was not triggers running on the master for the statement. LOGGING also means results of that the executed triggers work will be written to the binlog. ENFORCE means that triggers will always be run on the slave, even if there are triggers on the master. ENFORCE implies LOGGING.
NUMERIC_MIN_VALUE NULL
NUMERIC_MAX_VALUE NULL
NUMERIC_BLOCK_SIZE NULL
ENUM_VALUE_LIST NO,YES,LOGGING
ENUM_VALUE_LIST NO,YES,LOGGING,ENFORCE
READ_ONLY NO
COMMAND_LINE_ARGUMENT REQUIRED
VARIABLE_NAME SLAVE_SKIP_ERRORS
......
......@@ -4880,6 +4880,12 @@ class Rows_log_event : public Log_event
#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
virtual uint8 get_trg_event_map()= 0;
inline bool do_invoke_trigger()
{
return (slave_run_triggers_for_rbr && !master_had_triggers) ||
slave_run_triggers_for_rbr == SLAVE_RUN_TRIGGERS_FOR_RBR_ENFORCE;
}
#endif
protected:
......
......@@ -6809,7 +6809,7 @@ Write_rows_log_event::do_before_row_operations(const Slave_reporting_capability
m_table->file->extra(HA_EXTRA_WRITE_CAN_REPLACE);
m_table->file->extra(HA_EXTRA_IGNORE_NO_KEY);
}
if (slave_run_triggers_for_rbr && !master_had_triggers && m_table->triggers )
if (m_table->triggers && do_invoke_trigger())
m_table->prepare_triggers_for_insert_stmt_or_event();
/* Honor next number column if present */
......@@ -6989,8 +6989,7 @@ Rows_log_event::write_row(rpl_group_info *rgi,
TABLE *table= m_table; // pointer to event's table
int error;
int UNINIT_VAR(keynum);
const bool invoke_triggers=
slave_run_triggers_for_rbr && !master_had_triggers && table->triggers;
const bool invoke_triggers= (m_table->triggers && do_invoke_trigger());
auto_afree_ptr<char> key(NULL);
prepare_record(table, m_width, true);
......@@ -7866,7 +7865,7 @@ Delete_rows_log_event::do_before_row_operations(const Slave_reporting_capability
*/
return 0;
}
if (slave_run_triggers_for_rbr && !master_had_triggers)
if (do_invoke_trigger())
m_table->prepare_triggers_for_delete_stmt_or_event();
return find_key();
......@@ -7889,8 +7888,7 @@ int Delete_rows_log_event::do_exec_row(rpl_group_info *rgi)
int error;
const char *tmp= thd->get_proc_info();
const char *message= "Delete_rows_log_event::find_row()";
const bool invoke_triggers=
slave_run_triggers_for_rbr && !master_had_triggers && m_table->triggers;
const bool invoke_triggers= (m_table->triggers && do_invoke_trigger());
DBUG_ASSERT(m_table != NULL);
#ifdef WSREP_PROC_INFO
......@@ -8016,7 +8014,7 @@ Update_rows_log_event::do_before_row_operations(const Slave_reporting_capability
if ((err= find_key()))
return err;
if (slave_run_triggers_for_rbr && !master_had_triggers)
if (do_invoke_trigger())
m_table->prepare_triggers_for_update_stmt_or_event();
return 0;
......@@ -8035,11 +8033,10 @@ Update_rows_log_event::do_after_row_operations(const Slave_reporting_capability
return error;
}
int
int
Update_rows_log_event::do_exec_row(rpl_group_info *rgi)
{
const bool invoke_triggers=
slave_run_triggers_for_rbr && !master_had_triggers && m_table->triggers;
const bool invoke_triggers= (m_table->triggers && do_invoke_trigger());
const char *tmp= thd->get_proc_info();
const char *message= "Update_rows_log_event::find_row()";
DBUG_ASSERT(m_table != NULL);
......
......@@ -106,7 +106,8 @@ enum enum_slave_exec_mode { SLAVE_EXEC_MODE_STRICT,
SLAVE_EXEC_MODE_LAST_BIT };
enum enum_slave_run_triggers_for_rbr { SLAVE_RUN_TRIGGERS_FOR_RBR_NO,
SLAVE_RUN_TRIGGERS_FOR_RBR_YES,
SLAVE_RUN_TRIGGERS_FOR_RBR_LOGGING};
SLAVE_RUN_TRIGGERS_FOR_RBR_LOGGING,
SLAVE_RUN_TRIGGERS_FOR_RBR_ENFORCE};
enum enum_slave_type_conversions { SLAVE_TYPE_CONVERSIONS_ALL_LOSSY,
SLAVE_TYPE_CONVERSIONS_ALL_NON_LOSSY};
......
......@@ -3102,16 +3102,17 @@ static Sys_var_enum Slave_ddl_exec_mode(
slave_exec_mode_names, DEFAULT(SLAVE_EXEC_MODE_IDEMPOTENT));
static const char *slave_run_triggers_for_rbr_names[]=
{"NO", "YES", "LOGGING", 0};
{"NO", "YES", "LOGGING", "ENFORCE", 0};
static Sys_var_enum Slave_run_triggers_for_rbr(
"slave_run_triggers_for_rbr",
"Modes for how triggers in row-base replication on slave side will be "
"executed. Legal values are NO (default), YES and LOGGING. NO means "
"executed. Legal values are NO (default), YES, LOGGING and ENFORCE. NO means "
"that trigger for RBR will not be running on slave. YES and LOGGING "
"means that triggers will be running on slave, if there was not "
"triggers running on the master for the statement. LOGGING also means "
"results of that the executed triggers work will be written to "
"the binlog.",
"the binlog. ENFORCE means that triggers will always be run on the slave, "
"even if there are triggers on the master. ENFORCE implies LOGGING.",
GLOBAL_VAR(slave_run_triggers_for_rbr), CMD_LINE(REQUIRED_ARG),
slave_run_triggers_for_rbr_names,
DEFAULT(SLAVE_RUN_TRIGGERS_FOR_RBR_NO));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment