mirror of
https://github.com/MariaDB/server.git
synced 2025-01-16 03:52:35 +01:00
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.
This commit is contained in:
parent
1f5a8e1f77
commit
980108ceeb
8 changed files with 278 additions and 23 deletions
|
@ -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 @@ public:
|
|||
|
||||
#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));
|
||||
|
|
Loading…
Reference in a new issue