Skip to content

Commit

Permalink
MDEV-21833 Make slave_run_triggers_for_rbr enforce triggers to run on…
Browse files Browse the repository at this point in the history
… 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.
  • Loading branch information
sanja-byelkin authored and andrelkin committed Mar 9, 2020
1 parent 1f5a8e1 commit 980108c
Show file tree
Hide file tree
Showing 8 changed files with 278 additions and 23 deletions.
16 changes: 9 additions & 7 deletions mysql-test/main/mysqld--help.result
Expand Up @@ -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
Expand Down
132 changes: 132 additions & 0 deletions mysql-test/suite/rpl/r/rpl_row_triggers.result
Expand Up @@ -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
116 changes: 116 additions & 0 deletions mysql-test/suite/rpl/t/rpl_row_triggers.test
Expand Up @@ -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
4 changes: 2 additions & 2 deletions mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result
Expand Up @@ -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
Expand Down
6 changes: 6 additions & 0 deletions sql/log_event.h
Expand Up @@ -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:
Expand Down
17 changes: 7 additions & 10 deletions sql/log_event_server.cc
Expand Up @@ -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 */
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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();
Expand All @@ -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
Expand Down Expand Up @@ -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;
Expand All @@ -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);
Expand Down
3 changes: 2 additions & 1 deletion sql/sql_class.h
Expand Up @@ -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};

Expand Down
7 changes: 4 additions & 3 deletions sql/sys_vars.cc
Expand Up @@ -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));
Expand Down

0 comments on commit 980108c

Please sign in to comment.