summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOleksandr Byelkin <sanja@mariadb.com>2020-02-03 16:02:05 +0100
committerAndrei Elkin <andrei.elkin@mariadb.com>2020-03-09 22:16:43 +0200
commit980108ceebdca5c4f6c9e3a167e9ad40cb062ac8 (patch)
treee9e5a5d18c842a52079b7afd56c1d4f724a9ec2b
parent1f5a8e1f779ebd6cedfde726166da6c52bfe1959 (diff)
downloadmariadb-git-980108ceebdca5c4f6c9e3a167e9ad40cb062ac8.tar.gz
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.
-rw-r--r--mysql-test/main/mysqld--help.result16
-rw-r--r--mysql-test/suite/rpl/r/rpl_row_triggers.result132
-rw-r--r--mysql-test/suite/rpl/t/rpl_row_triggers.test116
-rw-r--r--mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result4
-rw-r--r--sql/log_event.h6
-rw-r--r--sql/log_event_server.cc17
-rw-r--r--sql/sql_class.h3
-rw-r--r--sql/sys_vars.cc7
8 files changed, 278 insertions, 23 deletions
diff --git a/mysql-test/main/mysqld--help.result b/mysql-test/main/mysqld--help.result
index 8013b6075d0..20f40b94337 100644
--- a/mysql-test/main/mysqld--help.result
+++ b/mysql-test/main/mysqld--help.result
@@ -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
diff --git a/mysql-test/suite/rpl/r/rpl_row_triggers.result b/mysql-test/suite/rpl/r/rpl_row_triggers.result
index ad78c33e2b9..07a66d13bcf 100644
--- a/mysql-test/suite/rpl/r/rpl_row_triggers.result
+++ b/mysql-test/suite/rpl/r/rpl_row_triggers.result
@@ -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
diff --git a/mysql-test/suite/rpl/t/rpl_row_triggers.test b/mysql-test/suite/rpl/t/rpl_row_triggers.test
index d5f29b9207d..68002dbe7e2 100644
--- a/mysql-test/suite/rpl/t/rpl_row_triggers.test
+++ b/mysql-test/suite/rpl/t/rpl_row_triggers.test
@@ -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
diff --git a/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result b/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result
index 1d28a9853e4..d0e07e54bc9 100644
--- a/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result
+++ b/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result
@@ -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
diff --git a/sql/log_event.h b/sql/log_event.h
index 15442bd5a97..f2d27282a6f 100644
--- a/sql/log_event.h
+++ b/sql/log_event.h
@@ -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:
diff --git a/sql/log_event_server.cc b/sql/log_event_server.cc
index 202a41c2837..d371992c6c0 100644
--- a/sql/log_event_server.cc
+++ b/sql/log_event_server.cc
@@ -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);
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 123bf0c0583..939dc918003 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -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};
diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc
index 7f61da63d60..0d67c00bdbb 100644
--- a/sql/sys_vars.cc
+++ b/sql/sys_vars.cc
@@ -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));