[Commits] Rev 4046: MDEV-9095: Executing triggers on slave in row-based replication in file:///home/bell/maria/bzr/work-maria-10.0-MDEV-5095/

sanja at montyprogram.com sanja at montyprogram.com
Tue Mar 11 11:10:46 EET 2014


At file:///home/bell/maria/bzr/work-maria-10.0-MDEV-5095/

------------------------------------------------------------
revno: 4046
revision-id: sanja at montyprogram.com-20140311091044-lqdv8zuzxpg28993
parent: svoj at mariadb.org-20140307074306-blf3begv8ee0tb5j
committer: sanja at montyprogram.com
branch nick: work-maria-10.0-MDEV-5095
timestamp: Tue 2014-03-11 11:10:44 +0200
message:
  MDEV-9095: Executing triggers on slave in row-based replication
-------------- next part --------------
=== added file 'mysql-test/include/have_rbr_triggers.inc'
--- a/mysql-test/include/have_rbr_triggers.inc	1970-01-01 00:00:00 +0000
+++ b/mysql-test/include/have_rbr_triggers.inc	2014-03-11 09:10:44 +0000
@@ -0,0 +1,5 @@
+if (`select count(*) = 0 from information_schema.session_variables where variable_name = 'slave_run_triggers_for_rbr'`)
+{
+  skip RBR triggers are not available;
+}
+

=== modified file 'mysql-test/r/mysqld--help.result'
--- a/mysql-test/r/mysqld--help.result	2014-03-05 22:20:10 +0000
+++ b/mysql-test/r/mysqld--help.result	2014-03-11 09:10:44 +0000
@@ -904,6 +904,15 @@ The following options may be given as th
  in parallel events on the slave that were group-committed
  on the master or were logged with GTID in different
  replication domains.
+ --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.
  --slave-skip-errors=name 
  Tells the slave thread to continue replication when a
  query event returns an error from the provided list
@@ -1289,6 +1298,7 @@ slave-max-allowed-packet 1073741824
 slave-net-timeout 3600
 slave-parallel-max-queued 131072
 slave-parallel-threads 0
+slave-run-triggers-for-rbr NO
 slave-skip-errors (No default value)
 slave-sql-verify-checksum TRUE
 slave-transaction-retries 10

=== added file 'mysql-test/suite/rpl/r/rpl_row_triggers.result'
--- a/mysql-test/suite/rpl/r/rpl_row_triggers.result	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/rpl/r/rpl_row_triggers.result	2014-03-11 09:10:44 +0000
@@ -0,0 +1,240 @@
+include/master-slave.inc
+[connection master]
+# Test of row replication with triggers on the slave side
+CREATE TABLE t1 (C1 CHAR(1) primary key, C2 CHAR(1)) engine=innodb;
+SELECT * FROM t1;
+C1	C2
+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= YES;
+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_db before delete on t1 for each row
+update t2 set cnt=cnt+1, o=old.C1, n=' ' where id = 'd0';
+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		
+# INSERT triggers test
+insert into t1 values ('a','b');
+SELECT * FROM t2 order by id;
+id	cnt	o	n
+d0	0		
+d1	0		
+i0	1		a
+i1	1		a
+u0	0		
+u1	0		
+# UPDATE triggers test
+update t1 set C1= 'd';
+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
+# DELETE triggers test
+delete from t1 where C1='d';
+SELECT * FROM t2 order by id;
+id	cnt	o	n
+d0	1	d	
+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	1	d	
+d1	1	d	
+i0	2		0
+i1	2		0
+u0	1	a	d
+u1	1	a	d
+insert into t1 values ('0','1');
+SELECT * FROM t2 order by id;
+id	cnt	o	n
+d0	1	d	
+d1	1	d	
+i0	3		0
+i1	3		0
+u0	2	0	0
+u1	2	0	0
+# INSERT triggers which cause also DELETE test
+# (insert duplicate row in table referenced by foreign key)
+insert into t1 values ('1','1');
+CREATE TABLE t3 (C1 CHAR(1) primary key, FOREIGN KEY (C1) REFERENCES t1(C1) ) engine=innodb;
+insert into t1 values ('1','1');
+SELECT * FROM t2 order by id;
+id	cnt	o	n
+d0	2	1	
+d1	2	1	
+i0	5		1
+i1	5		1
+u0	2	0	0
+u1	2	0	0
+drop table t3,t1;
+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;
+CREATE TABLE t1 (i INT) ENGINE=InnoDB;
+CREATE TABLE t2 (i INT) ENGINE=InnoDB;
+SET @old_slave_run_triggers_for_rbr= @@global.slave_run_triggers_for_rbr;
+SET GLOBAL slave_run_triggers_for_rbr=YES;
+CREATE TRIGGER tr AFTER INSERT ON t1 FOR EACH ROW
+INSERT INTO t2 VALUES (new.i);
+BEGIN;
+INSERT INTO t1 VALUES (1);
+INSERT INTO t1 VALUES (2);
+COMMIT;
+select * from t2;
+i
+1
+2
+SET @@global.slave_run_triggers_for_rbr= @old_slave_run_triggers_for_rbr;
+drop tables t2,t1;
+# Triggers on slave do not work if master has some
+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;
+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= YES;
+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		
+# INSERT triggers test
+insert into t1 values ('a','b');
+SELECT * FROM t2 order by id;
+id	cnt	o	n
+d0	0		
+d1	0		
+i0	0		
+i1	0		
+u0	0		
+u1	0		
+# UPDATE triggers test
+update t1 set C1= 'd';
+SELECT * FROM t2 order by id;
+id	cnt	o	n
+d0	0		
+d1	0		
+i0	0		
+i1	0		
+u0	0		
+u1	0		
+# DELETE triggers test
+delete from t1 where C1='d';
+SELECT * FROM t2 order by id;
+id	cnt	o	n
+d0	0		
+d1	0		
+i0	0		
+i1	0		
+u0	0		
+u1	0		
+# 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	0		
+i0	1		0
+i1	1		0
+u0	0		
+u1	0		
+insert into t1 values ('0','1');
+SELECT * FROM t2 order by id;
+id	cnt	o	n
+d0	0		
+d1	0		
+i0	1		0
+i1	1		0
+u0	0		
+u1	0		
+# INSERT triggers which cause also DELETE test
+# (insert duplicate row in table referenced by foreign key)
+insert into t1 values ('1','1');
+CREATE TABLE t3 (C1 CHAR(1) primary key, FOREIGN KEY (C1) REFERENCES t1(C1) ) engine=innodb;
+insert into t1 values ('1','1');
+SELECT * FROM t2 order by id;
+id	cnt	o	n
+d0	0		
+d1	0		
+i0	2		1
+i1	2		1
+u0	0		
+u1	0		
+drop table t3,t1;
+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;
+#
+# MDEV-5513: Trigger is applied to the rows after first one
+#
+create table t1 (a int, b int);
+create table tlog (a int);
+set sql_log_bin=0;
+create trigger tr1 after insert on t1 for each row insert into tlog values (1);
+set sql_log_bin=1;
+set @slave_run_triggers_for_rbr.saved = @@slave_run_triggers_for_rbr;
+set global slave_run_triggers_for_rbr=1;
+create trigger tr2 before insert on t1 for each row set new.b = new.a;
+insert into t1 values (1,10),(2,20),(3,30);
+select * from t1;
+a	b
+1	10
+2	20
+3	30
+set global slave_run_triggers_for_rbr = @slave_run_triggers_for_rbr.saved;
+drop table t1, tlog;
+include/rpl_end.inc

=== added file 'mysql-test/suite/rpl/r/rpl_row_triggers_sbr.result'
--- a/mysql-test/suite/rpl/r/rpl_row_triggers_sbr.result	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/rpl/r/rpl_row_triggers_sbr.result	2014-03-11 09:10:44 +0000
@@ -0,0 +1,14 @@
+include/master-slave.inc
+[connection master]
+set binlog_format = row;
+create table t1 (i int);
+create table t2 (i int);
+SET @old_slave_run_triggers_for_rbr= @@global.slave_run_triggers_for_rbr;
+set global slave_run_triggers_for_rbr=YES;
+create trigger tr_before before insert on t1 for each row
+insert into t2 values (1);
+insert into t1 values (1);
+include/wait_for_slave_sql_error_and_skip.inc [errno=1666]
+drop tables t1,t2;
+SET @@global.slave_run_triggers_for_rbr= @old_slave_run_triggers_for_rbr;
+include/rpl_end.inc

=== added file 'mysql-test/suite/rpl/t/rpl_row_triggers.test'
--- a/mysql-test/suite/rpl/t/rpl_row_triggers.test	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/rpl/t/rpl_row_triggers.test	2014-03-11 09:10:44 +0000
@@ -0,0 +1,280 @@
+-- source include/have_binlog_format_row.inc
+-- source include/have_rbr_triggers.inc
+-- source include/master-slave.inc
+-- source include/have_innodb.inc
+
+-- echo # Test of row replication with triggers on the slave side
+
+connection master;
+CREATE TABLE t1 (C1 CHAR(1) primary key, C2 CHAR(1)) engine=innodb;
+SELECT * FROM t1;
+
+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= YES;
+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_db before delete on t1 for each row
+  update t2 set cnt=cnt+1, o=old.C1, n=' ' where id = 'd0';
+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;
+
+--connection master
+
+CREATE TABLE t1 (i INT) ENGINE=InnoDB;
+CREATE TABLE t2 (i INT) ENGINE=InnoDB;
+
+--sync_slave_with_master
+
+SET @old_slave_run_triggers_for_rbr= @@global.slave_run_triggers_for_rbr;
+SET GLOBAL slave_run_triggers_for_rbr=YES;
+
+CREATE TRIGGER tr AFTER INSERT ON t1 FOR EACH ROW
+  INSERT INTO t2 VALUES (new.i);
+
+--connection master
+
+BEGIN;
+INSERT INTO t1 VALUES (1);
+INSERT INTO t1 VALUES (2);
+COMMIT;
+
+--sync_slave_with_master
+select * from t2;
+SET @@global.slave_run_triggers_for_rbr= @old_slave_run_triggers_for_rbr;
+
+--connection master
+drop tables t2,t1;
+
+--sync_slave_with_master
+
+-- echo # Triggers on slave do not work 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= YES;
+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 # MDEV-5513: Trigger is applied to the rows after first one
+--echo #
+
+--connection master
+
+create table t1 (a int, b int);
+create table tlog (a int);
+
+set sql_log_bin=0;
+create trigger tr1 after insert on t1 for each row insert into tlog values (1);
+set sql_log_bin=1;
+
+sync_slave_with_master;
+--connection slave
+
+set @slave_run_triggers_for_rbr.saved = @@slave_run_triggers_for_rbr;
+set global slave_run_triggers_for_rbr=1;
+create trigger tr2 before insert on t1 for each row set new.b = new.a;
+
+--connection master
+
+insert into t1 values (1,10),(2,20),(3,30);
+
+--sync_slave_with_master
+
+select * from t1;
+
+# Cleanup 
+
+set global slave_run_triggers_for_rbr = @slave_run_triggers_for_rbr.saved;
+
+--connection master
+
+drop table t1, tlog;
+
+sync_slave_with_master;
+
+
+
+--source include/rpl_end.inc

=== added file 'mysql-test/suite/rpl/t/rpl_row_triggers_sbr.test'
--- a/mysql-test/suite/rpl/t/rpl_row_triggers_sbr.test	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/rpl/t/rpl_row_triggers_sbr.test	2014-03-11 09:10:44 +0000
@@ -0,0 +1,43 @@
+--source include/master-slave.inc
+--source include/have_binlog_format_statement.inc
+--source include/have_rbr_triggers.inc
+
+--disable_query_log
+CALL mtr.add_suppression("Cannot execute statement: impossible to write to binary log since statement is in row format and BINLOG_FORMAT = STATEMENT");
+--enable_query_log
+
+set binlog_format = row;
+
+create table t1 (i int);
+create table t2 (i int);
+
+--sync_slave_with_master
+--disable_query_log
+CALL mtr.add_suppression("impossible to write to binary log since statement is in row format and BINLOG_FORMAT = STATEMENT");
+--enable_query_log
+
+SET @old_slave_run_triggers_for_rbr= @@global.slave_run_triggers_for_rbr;
+set global slave_run_triggers_for_rbr=YES;
+
+create trigger tr_before before insert on t1 for each row
+  insert into t2 values (1);
+
+--connection master
+
+insert into t1 values (1);
+
+--connection slave
+
+--let $slave_sql_errno= 1666
+--source include/wait_for_slave_sql_error_and_skip.inc
+
+--connection master
+
+drop tables t1,t2;
+
+--sync_slave_with_master
+SET @@global.slave_run_triggers_for_rbr= @old_slave_run_triggers_for_rbr;
+
+--connection master
+
+--source include/rpl_end.inc

=== modified file 'sql/log_event.cc'
--- a/sql/log_event.cc	2014-03-05 22:20:10 +0000
+++ b/sql/log_event.cc	2014-03-11 09:10:44 +0000
@@ -9168,7 +9168,8 @@ Rows_log_event::Rows_log_event(THD *thd_
     m_type(event_type), m_extra_row_data(0)
 #ifdef HAVE_REPLICATION
     , m_curr_row(NULL), m_curr_row_end(NULL),
-    m_key(NULL), m_key_info(NULL), m_key_nr(0)
+    m_key(NULL), m_key_info(NULL), m_key_nr(0),
+    master_had_triggers(0)
 #endif
 {
   /*
@@ -9217,7 +9218,8 @@ Rows_log_event::Rows_log_event(const cha
     m_extra_row_data(0)
 #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
     , m_curr_row(NULL), m_curr_row_end(NULL),
-    m_key(NULL), m_key_info(NULL), m_key_nr(0)
+    m_key(NULL), m_key_info(NULL), m_key_nr(0),
+    master_had_triggers(0)
 #endif
 {
   DBUG_ENTER("Rows_log_event::Rows_log_event(const char*,...)");
@@ -9472,10 +9474,28 @@ int Rows_log_event::do_add_row_data(ucha
 }
 #endif
 
-#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
+
+/**
+  Restores empty table list as it was before trigger processing.
+
+  @note We have a lot of ASSERTS that check the lists when we close tables.
+  There was the same problem with MERGE MYISAM tables and so here we try to
+  go the same way.
+*/
+static void restore_empty_query_table_list(LEX *lex)
+{
+  if (lex->first_not_own_table())
+      (*lex->first_not_own_table()->prev_global)= NULL;
+  lex->query_tables= NULL;
+  lex->query_tables_last= &lex->query_tables;
+}
+
+
 int Rows_log_event::do_apply_event(rpl_group_info *rgi)
 {
   Relay_log_info const *rli= rgi->rli;
+  TABLE* table;
   DBUG_ENTER("Rows_log_event::do_apply_event(Relay_log_info*)");
   int error= 0;
   /*
@@ -9555,6 +9575,28 @@ int Rows_log_event::do_apply_event(rpl_g
     /* A small test to verify that objects have consistent types */
     DBUG_ASSERT(sizeof(thd->variables.option_bits) == sizeof(OPTION_RELAXED_UNIQUE_CHECKS));
 
+    if (slave_run_triggers_for_rbr)
+    {
+      LEX *lex= thd->lex;
+      uint8 new_trg_event_map= get_trg_event_map();
+
+      /*
+        Trigger's procedures work with global table list. So we have to add
+        rgi->tables_to_lock content there to get trigger's in the list.
+
+        Then restore_empty_query_table_list() restore the list as it was
+      */
+      DBUG_ASSERT(lex->query_tables == NULL);
+      if ((lex->query_tables= rgi->tables_to_lock))
+        rgi->tables_to_lock->prev_global= &lex->query_tables;
+
+      for (TABLE_LIST *tables= rgi->tables_to_lock; tables;
+           tables= tables->next_global)
+      {
+        tables->trg_event_map= new_trg_event_map;
+        lex->query_tables_last= &tables->next_global;
+      }
+    }
     if (open_and_lock_tables(thd, rgi->tables_to_lock, FALSE, 0))
     {
       uint actual_error= thd->get_stmt_da()->sql_errno();
@@ -9572,8 +9614,9 @@ int Rows_log_event::do_apply_event(rpl_g
                      "unexpected success or fatal error"));
         thd->is_slave_error= 1;
       }
-      rgi->slave_close_thread_tables(thd);
-      DBUG_RETURN(actual_error);
+      /* remove trigger's tables */
+      error= actual_error;
+      goto err;
     }
 
     /*
@@ -9617,8 +9660,9 @@ int Rows_log_event::do_apply_event(rpl_g
             having severe errors which should not be skiped.
           */
           thd->is_slave_error= 1;
-          rgi->slave_close_thread_tables(thd);
-          DBUG_RETURN(ERR_BAD_TABLE_DEF);
+          /* remove trigger's tables */
+          error= ERR_BAD_TABLE_DEF;
+          goto err;
         }
         DBUG_PRINT("debug", ("Table: %s.%s is compatible with master"
                              " - conv_table: %p",
@@ -9644,21 +9688,35 @@ int Rows_log_event::do_apply_event(rpl_g
      */
     TABLE_LIST *ptr= rgi->tables_to_lock;
     for (uint i=0 ;  ptr && (i < rgi->tables_to_lock_count); ptr= ptr->next_global, i++)
+    {
       rgi->m_table_map.set_table(ptr->table_id, ptr->table);
+      /*
+        Following is passing flag about triggers on the server. The problem was
+        to pass it between table map event and row event. I do it via extended
+        TABLE_LIST (RPL_TABLE_LIST) but row event uses only TABLE so I need to
+        find somehow the corresponding TABLE_LIST.
+      */
+      if (m_table_id == ptr->table_id)
+      {
+        ptr->table->master_had_triggers=
+          ((RPL_TABLE_LIST*)ptr)->master_had_triggers;
+      }
+    }
 
 #ifdef HAVE_QUERY_CACHE
     query_cache.invalidate_locked_for_write(thd, rgi->tables_to_lock);
 #endif
   }
 
-  TABLE* 
-    table= 
-    m_table= rgi->m_table_map.get_table(m_table_id);
-
-  DBUG_PRINT("debug", ("m_table: 0x%lx, m_table_id: %lu", (ulong) m_table, m_table_id));
+  table= m_table= rgi->m_table_map.get_table(m_table_id);
 
+  DBUG_PRINT("debug", ("m_table: 0x%lx, m_table_id: %lu%s",
+                       (ulong) m_table, m_table_id,
+                       table && master_had_triggers ?
+                       " (master had triggers)" : ""));
   if (table)
   {
+    master_had_triggers= table->master_had_triggers;
     bool transactional_table= table->file->has_transactions();
     /*
       table == NULL means that this table should not be replicated
@@ -9822,9 +9880,13 @@ int Rows_log_event::do_apply_event(rpl_g
     */
     thd->reset_current_stmt_binlog_format_row();
     thd->is_slave_error= 1;
-    DBUG_RETURN(error);
+    /* remove trigger's tables */
+    goto err;
   }
 
+  /* remove trigger's tables */
+  if (slave_run_triggers_for_rbr)
+    restore_empty_query_table_list(thd->lex);
   if (get_flags(STMT_END_F) && (error= rows_event_stmt_cleanup(rgi, thd)))
     slave_rows_error_report(ERROR_LEVEL,
                             thd->is_error() ? 0 : error,
@@ -9832,6 +9894,12 @@ int Rows_log_event::do_apply_event(rpl_g
                             get_type_str(),
                             RPL_LOG_NAME, (ulong) log_pos);
   DBUG_RETURN(error);
+
+err:
+  if (slave_run_triggers_for_rbr)
+    restore_empty_query_table_list(thd->lex);
+  rgi->slave_close_thread_tables(thd);
+  DBUG_RETURN(error);
 }
 
 Log_event::enum_skip_reason
@@ -10305,6 +10373,7 @@ Table_map_log_event::Table_map_log_event
 {
   uchar cbuf[MAX_INT_WIDTH];
   uchar *cbuf_end;
+  DBUG_ENTER("Table_map_log_event::Table_map_log_event(TABLE)");
   DBUG_ASSERT(m_table_id != ~0UL);
   /*
     In TABLE_SHARE, "db" and "table_name" are 0-terminated (see this comment in
@@ -10325,6 +10394,9 @@ Table_map_log_event::Table_map_log_event
   DBUG_ASSERT(static_cast<size_t>(cbuf_end - cbuf) <= sizeof(cbuf));
   m_data_size+= (cbuf_end - cbuf) + m_colcnt;	// COLCNT and column types
 
+  if (tbl->triggers)
+    m_flags|= TM_BIT_HAS_TRIGGERS_F;
+
   /* If malloc fails, caught in is_valid() */
   if ((m_memory= (uchar*) my_malloc(m_colcnt, MYF(MY_WME))))
   {
@@ -10369,6 +10441,7 @@ Table_map_log_event::Table_map_log_event
     if (m_table->field[i]->maybe_null())
       m_null_bits[(i / 8)]+= 1 << (i % 8);
 
+  DBUG_VOID_RETURN;
 }
 #endif /* !defined(MYSQL_CLIENT) */
 
@@ -10731,7 +10804,11 @@ int Table_map_log_event::do_apply_event(
   table_list->table_id= DBUG_EVALUATE_IF("inject_tblmap_same_id_maps_diff_table", 0, m_table_id);
   table_list->updating= 1;
   table_list->required_type= FRMTYPE_TABLE;
-  DBUG_PRINT("debug", ("table: %s is mapped to %u", table_list->table_name, table_list->table_id));
+  table_list->master_had_triggers= ((m_flags & TM_BIT_HAS_TRIGGERS_F) ? 1 : 0);
+  DBUG_PRINT("debug", ("table: %s is mapped to %u%s",
+                       table_list->table_name, table_list->table_id,
+                       (table_list->master_had_triggers ?
+                        " (master had triggers)" : "")));
   enum_tbl_map_status tblmap_status= check_table_map(rgi, table_list);
   if (tblmap_status == OK_TO_PROCESS)
   {
@@ -10903,8 +10980,10 @@ void Table_map_log_event::print(FILE *,
   {
     print_header(&print_event_info->head_cache, print_event_info, TRUE);
     my_b_printf(&print_event_info->head_cache,
-                "\tTable_map: %`s.%`s mapped to number %lu\n",
-                m_dbnam, m_tblnam, (ulong) m_table_id);
+                "\tTable_map: %`s.%`s mapped to number %lu%s\n",
+                m_dbnam, m_tblnam, m_table_id,
+                ((m_flags & TM_BIT_HAS_TRIGGERS_F) ?
+                 " (has triggers)" : ""));
     print_base64(&print_event_info->body_cache, print_event_info, TRUE);
   }
 }
@@ -10980,6 +11059,8 @@ Write_rows_log_event::do_before_row_oper
     /* 
        NDB specific: update from ndb master wrapped as Write_rows
        so that the event should be applied to replace slave's row
+
+       Also following is needed in case if we have AFTER DELETE triggers.
     */
     m_table->file->extra(HA_EXTRA_WRITE_CAN_REPLACE);
     /* 
@@ -10994,6 +11075,8 @@ Write_rows_log_event::do_before_row_oper
       from the start.
     */
   }
+  if (slave_run_triggers_for_rbr && !master_had_triggers && m_table->triggers )
+    m_table->prepare_triggers_for_insert_stmt_or_event();
 
   /* Honor next number column if present */
   m_table->next_number_field= m_table->found_next_number_field;
@@ -11039,6 +11122,25 @@ Write_rows_log_event::do_after_row_opera
 
 #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
 
+bool Rows_log_event::process_triggers(trg_event_type event,
+                                      trg_action_time_type time_type,
+                                      bool old_row_is_record1)
+{
+  bool result;
+  DBUG_ENTER("Rows_log_event::process_triggers");
+  if (slave_run_triggers_for_rbr == SLAVE_RUN_TRIGGERS_FOR_RBR_YES)
+  {
+    tmp_disable_binlog(thd); /* Do not replicate the low-level changes. */
+    result= m_table->triggers->process_triggers(thd, event,
+                                              time_type, old_row_is_record1);
+    reenable_binlog(thd);
+  }
+  else
+    result= m_table->triggers->process_triggers(thd, event,
+                                              time_type, old_row_is_record1);
+
+  DBUG_RETURN(result);
+}
 /*
   Check if there are more UNIQUE keys after the given key.
 */
@@ -11121,6 +11223,8 @@ Rows_log_event::write_row(rpl_group_info
   TABLE *table= m_table;  // pointer to event's table
   int error;
   int UNINIT_VAR(keynum);
+  bool invoke_triggers=
+    slave_run_triggers_for_rbr && !master_had_triggers && table->triggers;
   auto_afree_ptr<char> key(NULL);
 
   prepare_record(table, m_width,
@@ -11130,13 +11234,17 @@ Rows_log_event::write_row(rpl_group_info
   if ((error= unpack_current_row(rgi)))
     DBUG_RETURN(error);
 
-  if (m_curr_row == m_rows_buf)
+  if (m_curr_row == m_rows_buf && !invoke_triggers)
   {
-    /* this is the first row to be inserted, we estimate the rows with
+    /*
+       This table has no triggers so we can do bulk insert.
+
+       This is the first row to be inserted, we estimate the rows with
        the size of the first row and use that value to initialize
-       storage engine for bulk insertion */
+       storage engine for bulk insertion.
+    */
     ulong estimated_rows= (m_rows_end - m_curr_row) / (m_curr_row_end - m_curr_row);
-    m_table->file->ha_start_bulk_insert(estimated_rows);
+    table->file->ha_start_bulk_insert(estimated_rows);
   }
   
   
@@ -11146,6 +11254,12 @@ Rows_log_event::write_row(rpl_group_info
   DBUG_PRINT_BITSET("debug", "read_set = %s", table->read_set);
 #endif
 
+  if (invoke_triggers &&
+      process_triggers(TRG_EVENT_INSERT, TRG_ACTION_BEFORE, TRUE))
+  {
+      DBUG_RETURN(HA_ERR_GENERIC); // in case if error is not set yet
+  }
+
   /* 
     Try to write record. If a corresponding record already exists in the table,
     we try to change it using ha_update_row() if possible. Otherwise we delete
@@ -11272,38 +11386,61 @@ Rows_log_event::write_row(rpl_group_info
         !table->file->referenced_by_foreign_key())
     {
       DBUG_PRINT("info",("Updating row using ha_update_row()"));
-      error=table->file->ha_update_row(table->record[1],
-                                       table->record[0]);
-      switch (error) {
-                
-      case HA_ERR_RECORD_IS_THE_SAME:
-        DBUG_PRINT("info",("ignoring HA_ERR_RECORD_IS_THE_SAME error from"
-                           " ha_update_row()"));
-        error= 0;
-      
-      case 0:
-        break;
-        
-      default:    
-        DBUG_PRINT("info",("ha_update_row() returns error %d",error));
-        table->file->print_error(error, MYF(0));
+      if (invoke_triggers &&
+          process_triggers(TRG_EVENT_UPDATE, TRG_ACTION_BEFORE, FALSE))
+        error= HA_ERR_GENERIC; // in case if error is not set yet
+      else
+      {
+        error= table->file->ha_update_row(table->record[1],
+                                         table->record[0]);
+        switch (error) {
+
+        case HA_ERR_RECORD_IS_THE_SAME:
+          DBUG_PRINT("info",("ignoring HA_ERR_RECORD_IS_THE_SAME error from"
+                             " ha_update_row()"));
+          error= 0;
+
+        case 0:
+          break;
+
+        default:
+          DBUG_PRINT("info",("ha_update_row() returns error %d",error));
+          table->file->print_error(error, MYF(0));
+        }
+        if (invoke_triggers && !error &&
+            (process_triggers(TRG_EVENT_UPDATE, TRG_ACTION_AFTER, TRUE) ||
+             process_triggers(TRG_EVENT_INSERT, TRG_ACTION_AFTER, TRUE)))
+          error= HA_ERR_GENERIC; // in case if error is not set yet
       }
-      
+
       DBUG_RETURN(error);
     }
     else
     {
       DBUG_PRINT("info",("Deleting offending row and trying to write new one again"));
-      if ((error= table->file->ha_delete_row(table->record[1])))
+      if (invoke_triggers &&
+          process_triggers(TRG_EVENT_DELETE, TRG_ACTION_BEFORE, TRUE))
+        error= HA_ERR_GENERIC; // in case if error is not set yet
+      else
       {
-        DBUG_PRINT("info",("ha_delete_row() returns error %d",error));
-        table->file->print_error(error, MYF(0));
-        DBUG_RETURN(error);
+        if ((error= table->file->ha_delete_row(table->record[1])))
+        {
+          DBUG_PRINT("info",("ha_delete_row() returns error %d",error));
+          table->file->print_error(error, MYF(0));
+          DBUG_RETURN(error);
+        }
+        if (invoke_triggers &&
+            process_triggers(TRG_EVENT_DELETE, TRG_ACTION_AFTER, TRUE))
+          DBUG_RETURN(HA_ERR_GENERIC); // in case if error is not set yet
       }
       /* Will retry ha_write_row() with the offending row removed. */
     }
   }
 
+  if (invoke_triggers &&
+      process_triggers(TRG_EVENT_INSERT, TRG_ACTION_AFTER, TRUE))
+    error= HA_ERR_GENERIC; // in case if error is not set yet
+
   DBUG_RETURN(error);
 }
 
@@ -11335,6 +11472,16 @@ void Write_rows_log_event::print(FILE *f
 }
 #endif
 
+
+#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
+uint8 Write_rows_log_event::get_trg_event_map()
+{
+  return (static_cast<uint8> (1 << static_cast<int>(TRG_EVENT_INSERT)) |
+          static_cast<uint8> (1 << static_cast<int>(TRG_EVENT_UPDATE)) |
+          static_cast<uint8> (1 << static_cast<int>(TRG_EVENT_DELETE)));
+}
+#endif
+
 /**************************************************************************
 	Delete_rows_log_event member functions
 **************************************************************************/
@@ -11959,6 +12106,8 @@ Delete_rows_log_event::do_before_row_ope
     */
     return 0;
   }
+  if (slave_run_triggers_for_rbr && !master_had_triggers)
+    m_table->prepare_triggers_for_delete_stmt_or_event();
 
   return find_key();
 }
@@ -11979,6 +12128,8 @@ Delete_rows_log_event::do_after_row_oper
 int Delete_rows_log_event::do_exec_row(rpl_group_info *rgi)
 {
   int error;
+  bool invoke_triggers=
+    slave_run_triggers_for_rbr && !master_had_triggers && m_table->triggers;
   DBUG_ASSERT(m_table != NULL);
 
   if (!(error= find_row(rgi))) 
@@ -11986,7 +12137,14 @@ int Delete_rows_log_event::do_exec_row(r
     /*
       Delete the record found, located in record[0]
     */
-    error= m_table->file->ha_delete_row(m_table->record[0]);
+    if (invoke_triggers &&
+        process_triggers(TRG_EVENT_DELETE, TRG_ACTION_BEFORE, FALSE))
+      error= HA_ERR_GENERIC; // in case if error is not set yet
+    if (!error)
+      error= m_table->file->ha_delete_row(m_table->record[0]);
+    if (invoke_triggers && !error &&
+        process_triggers(TRG_EVENT_DELETE, TRG_ACTION_AFTER, FALSE))
+      error= HA_ERR_GENERIC; // in case if error is not set yet
     m_table->file->ha_index_or_rnd_end();
   }
   return error;
@@ -12003,6 +12161,13 @@ void Delete_rows_log_event::print(FILE *
 #endif
 
 
+#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
+uint8 Delete_rows_log_event::get_trg_event_map()
+{
+  return static_cast<uint8> (1 << static_cast<int>(TRG_EVENT_DELETE));
+}
+#endif
+
 /**************************************************************************
 	Update_rows_log_event member functions
 **************************************************************************/
@@ -12085,6 +12250,9 @@ Update_rows_log_event::do_before_row_ope
   if ((err= find_key()))
     return err;
 
+  if (slave_run_triggers_for_rbr && !master_had_triggers)
+    m_table->prepare_triggers_for_update_stmt_or_event();
+
   return 0;
 }
 
@@ -12104,6 +12272,8 @@ Update_rows_log_event::do_after_row_oper
 int 
 Update_rows_log_event::do_exec_row(rpl_group_info *rgi)
 {
+  bool invoke_triggers=
+    slave_run_triggers_for_rbr && !master_had_triggers && m_table->triggers;
   DBUG_ASSERT(m_table != NULL);
 
   int error= find_row(rgi); 
@@ -12150,10 +12320,21 @@ Update_rows_log_event::do_exec_row(rpl_g
   DBUG_DUMP("new values", m_table->record[0], m_table->s->reclength);
 #endif
 
+  if (invoke_triggers &&
+      process_triggers(TRG_EVENT_UPDATE, TRG_ACTION_BEFORE, TRUE))
+  {
+    error= HA_ERR_GENERIC; // in case if error is not set yet
+    goto err;
+  }
+
   error= m_table->file->ha_update_row(m_table->record[1], m_table->record[0]);
   if (error == HA_ERR_RECORD_IS_THE_SAME)
     error= 0;
 
+  if (invoke_triggers && !error &&
+      process_triggers(TRG_EVENT_UPDATE, TRG_ACTION_AFTER, TRUE))
+    error= HA_ERR_GENERIC; // in case if error is not set yet
+
 err:
   m_table->file->ha_index_or_rnd_end();
   return error;
@@ -12169,6 +12350,12 @@ void Update_rows_log_event::print(FILE *
 }
 #endif
 
+#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
+uint8 Update_rows_log_event::get_trg_event_map()
+{
+  return static_cast<uint8> (1 << static_cast<int>(TRG_EVENT_UPDATE));
+}
+#endif
 
 Incident_log_event::Incident_log_event(const char *buf, uint event_len,
                                        const Format_description_log_event *descr_event)

=== modified file 'sql/log_event.h'
--- a/sql/log_event.h	2014-03-05 22:20:10 +0000
+++ b/sql/log_event.h	2014-03-11 09:10:44 +0000
@@ -4049,7 +4049,9 @@ public:
   enum 
   {
     TM_NO_FLAGS = 0U,
-    TM_BIT_LEN_EXACT_F = (1U << 0)
+    TM_BIT_LEN_EXACT_F = (1U << 0),
+    // MariaDB flags (we starts from the other end)
+    TM_BIT_HAS_TRIGGERS_F= (1U << 14)
   };
 
   flag_set get_flags(flag_set flag) const { return m_flags & flag; }
@@ -4254,6 +4256,10 @@ public:
 
   const uchar* get_extra_row_data() const   { return m_extra_row_data; }
 
+#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
+  virtual uint8 get_trg_event_map()= 0;
+#endif
+
 protected:
   /* 
      The constructors are protected since you're supposed to inherit
@@ -4307,6 +4313,7 @@ protected:
   uchar    *m_extra_row_data;   /* Pointer to extra row data if any */
                                 /* If non null, first byte is length */
 
+
   /* helper functions */
 
 #if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
@@ -4315,6 +4322,7 @@ protected:
   uchar    *m_key;      /* Buffer to keep key value during searches */
   KEY      *m_key_info; /* Pointer to KEY info for m_key_nr */
   uint      m_key_nr;   /* Key number */
+  bool master_had_triggers;     /* set after tables opening */
 
   int find_key(); // Find a best key to use in find_row()
   int find_row(rpl_group_info *);
@@ -4329,6 +4337,9 @@ protected:
     return ::unpack_row(rgi, m_table, m_width, m_curr_row, &m_cols,
                                    &m_curr_row_end, &m_master_reclength, m_rows_end);
   }
+  bool process_triggers(trg_event_type event,
+                        trg_action_time_type time_type,
+                        bool old_row_is_record1);
 #endif
 
 private:
@@ -4433,6 +4444,10 @@ public:
   }
 #endif
 
+#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
+  uint8 get_trg_event_map();
+#endif
+
 private:
   virtual Log_event_type get_general_type_code() { return (Log_event_type)TYPE_CODE; }
 
@@ -4507,6 +4522,10 @@ public:
     return Rows_log_event::is_valid() && m_cols_ai.bitmap;
   }
 
+#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
+  uint8 get_trg_event_map();
+#endif
+
 protected:
   virtual Log_event_type get_general_type_code() { return (Log_event_type)TYPE_CODE; }
 
@@ -4571,7 +4590,11 @@ public:
                                   cols, fields, before_record);
   }
 #endif
-  
+
+#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
+  uint8 get_trg_event_map();
+#endif
+
 protected:
   virtual Log_event_type get_general_type_code() { return (Log_event_type)TYPE_CODE; }
 

=== modified file 'sql/mysqld.cc'
--- a/sql/mysqld.cc	2014-03-05 22:20:10 +0000
+++ b/sql/mysqld.cc	2014-03-11 09:10:44 +0000
@@ -479,6 +479,7 @@ ulong open_files_limit, max_binlog_size;
 ulong slave_trans_retries;
 uint  slave_net_timeout;
 ulong slave_exec_mode_options;
+ulong slave_run_triggers_for_rbr= 0;
 ulong slave_ddl_exec_mode_options= SLAVE_EXEC_MODE_IDEMPOTENT;
 ulonglong slave_type_conversions_options;
 ulong thread_cache_size=0;

=== modified file 'sql/mysqld.h'
--- a/sql/mysqld.h	2014-03-05 22:20:10 +0000
+++ b/sql/mysqld.h	2014-03-11 09:10:44 +0000
@@ -98,6 +98,7 @@ extern my_bool opt_safe_show_db, opt_loc
 extern my_bool opt_slave_compressed_protocol, use_temp_pool;
 extern ulong slave_exec_mode_options, slave_ddl_exec_mode_options;
 extern ulong slave_retried_transactions;
+extern ulong slave_run_triggers_for_rbr;
 extern ulonglong slave_type_conversions_options;
 extern my_bool read_only, opt_readonly;
 extern my_bool lower_case_file_system;

=== modified file 'sql/rpl_utility.h'
--- a/sql/rpl_utility.h	2013-11-20 11:05:39 +0000
+++ b/sql/rpl_utility.h	2014-03-11 09:10:44 +0000
@@ -238,6 +238,7 @@ struct RPL_TABLE_LIST
   bool m_tabledef_valid;
   table_def m_tabledef;
   TABLE *m_conv_table;
+  bool master_had_triggers;
 };
 
 

=== modified file 'sql/sql_class.h'
--- a/sql/sql_class.h	2014-03-07 20:05:28 +0000
+++ b/sql/sql_class.h	2014-03-11 09:10:44 +0000
@@ -80,6 +80,9 @@ enum enum_delay_key_write { DELAY_KEY_WR
 enum enum_slave_exec_mode { SLAVE_EXEC_MODE_STRICT,
                             SLAVE_EXEC_MODE_IDEMPOTENT,
                             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};
 enum enum_slave_type_conversions { SLAVE_TYPE_CONVERSIONS_ALL_LOSSY,
                                    SLAVE_TYPE_CONVERSIONS_ALL_NON_LOSSY};
 enum enum_mark_columns

=== modified file 'sql/sql_delete.cc'
--- a/sql/sql_delete.cc	2014-02-19 10:05:15 +0000
+++ b/sql/sql_delete.cc	2014-03-11 09:10:44 +0000
@@ -521,16 +521,8 @@ bool mysql_delete(THD *thd, TABLE_LIST *
   init_ftfuncs(thd, select_lex, 1);
   THD_STAGE_INFO(thd, stage_updating);
 
-  if (table->triggers &&
-      table->triggers->has_triggers(TRG_EVENT_DELETE,
-                                    TRG_ACTION_AFTER))
+  if (table->prepare_triggers_for_delete_stmt_or_event())
   {
-    /*
-      The table has AFTER DELETE triggers that might access to subject table
-      and therefore might need delete to be done immediately. So we turn-off
-      the batching.
-    */
-    (void) table->file->extra(HA_EXTRA_DELETE_CANNOT_BATCH);
     will_batch= FALSE;
   }
   else
@@ -938,17 +930,7 @@ multi_delete::initialize_tables(JOIN *jo
 	transactional_tables= 1;
       else
 	normal_tables= 1;
-      if (tbl->triggers &&
-          tbl->triggers->has_triggers(TRG_EVENT_DELETE,
-                                      TRG_ACTION_AFTER))
-      {
-	/*
-          The table has AFTER DELETE triggers that might access to subject 
-          table and therefore might need delete to be done immediately. 
-          So we turn-off the batching.
-        */
-	(void) tbl->file->extra(HA_EXTRA_DELETE_CANNOT_BATCH);
-      }
+      tbl->prepare_triggers_for_delete_stmt_or_event();
       tbl->prepare_for_position();
       tbl->mark_columns_needed_for_delete();
     }

=== modified file 'sql/sql_insert.cc'
--- a/sql/sql_insert.cc	2014-02-19 10:05:15 +0000
+++ b/sql/sql_insert.cc	2014-03-11 09:10:44 +0000
@@ -373,48 +373,6 @@ static int check_update_fields(THD *thd,
   return 0;
 }
 
-/*
-  Prepare triggers  for INSERT-like statement.
-
-  SYNOPSIS
-    prepare_triggers_for_insert_stmt()
-      table   Table to which insert will happen
-
-  NOTE
-    Prepare triggers for INSERT-like statement by marking fields
-    used by triggers and inform handlers that batching of UPDATE/DELETE 
-    cannot be done if there are BEFORE UPDATE/DELETE triggers.
-*/
-
-void prepare_triggers_for_insert_stmt(TABLE *table)
-{
-  if (table->triggers)
-  {
-    if (table->triggers->has_triggers(TRG_EVENT_DELETE,
-                                      TRG_ACTION_AFTER))
-    {
-      /*
-        The table has AFTER DELETE triggers that might access to 
-        subject table and therefore might need delete to be done 
-        immediately. So we turn-off the batching.
-      */ 
-      (void) table->file->extra(HA_EXTRA_DELETE_CANNOT_BATCH);
-    }
-    if (table->triggers->has_triggers(TRG_EVENT_UPDATE,
-                                      TRG_ACTION_AFTER))
-    {
-      /*
-        The table has AFTER UPDATE triggers that might access to subject 
-        table and therefore might need update to be done immediately. 
-        So we turn-off the batching.
-      */ 
-      (void) table->file->extra(HA_EXTRA_UPDATE_CANNOT_BATCH);
-    }
-  }
-  table->mark_columns_needed_for_insert();
-}
-
-
 /**
   Upgrade table-level lock of INSERT statement to TL_WRITE if
   a more concurrent lock is infeasible for some reason. This is
@@ -902,7 +860,8 @@ bool mysql_insert(THD *thd,TABLE_LIST *t
 
   thd->abort_on_warning= !ignore && thd->is_strict_mode();
 
-  prepare_triggers_for_insert_stmt(table);
+  table->prepare_triggers_for_insert_stmt_or_event();
+  table->mark_columns_needed_for_insert();
 
 
   if (table_list->prepare_where(thd, 0, TRUE) ||
@@ -3537,7 +3496,10 @@ select_insert::prepare(List<Item> &value
         table_list->prepare_check_option(thd));
 
   if (!res)
-     prepare_triggers_for_insert_stmt(table);
+  {
+     table->prepare_triggers_for_insert_stmt_or_event();
+     table->mark_columns_needed_for_insert();
+  }
 
   DBUG_RETURN(res);
 }

=== modified file 'sql/sql_insert.h'
--- a/sql/sql_insert.h	2011-06-30 15:46:53 +0000
+++ b/sql/sql_insert.h	2014-03-11 09:10:44 +0000
@@ -38,7 +38,6 @@ void upgrade_lock_type_for_insert(THD *t
                                   bool is_multi_insert);
 int check_that_all_fields_are_given_values(THD *thd, TABLE *entry,
                                            TABLE_LIST *table_list);
-void prepare_triggers_for_insert_stmt(TABLE *table);
 int write_record(THD *thd, TABLE *table, COPY_INFO *info);
 void kill_delayed_threads(void);
 

=== modified file 'sql/sql_load.cc'
--- a/sql/sql_load.cc	2014-02-19 10:05:15 +0000
+++ b/sql/sql_load.cc	2014-03-11 09:10:44 +0000
@@ -28,7 +28,6 @@
 #include <my_dir.h>
 #include "sql_view.h"                           // check_key_in_view
 #include "sql_insert.h" // check_that_all_fields_are_given_values,
-                        // prepare_triggers_for_insert_stmt,
                         // write_record
 #include "sql_acl.h"    // INSERT_ACL, UPDATE_ACL
 #include "log_event.h"  // Delete_file_log_event,
@@ -298,7 +297,8 @@ int mysql_load(THD *thd,sql_exchange *ex
       DBUG_RETURN(TRUE);
   }
 
-  prepare_triggers_for_insert_stmt(table);
+  table->prepare_triggers_for_insert_stmt_or_event();
+  table->mark_columns_needed_for_insert();
 
   uint tot_length=0;
   bool use_blobs= 0, use_vars= 0;

=== modified file 'sql/sql_update.cc'
--- a/sql/sql_update.cc	2014-02-19 10:05:15 +0000
+++ b/sql/sql_update.cc	2014-03-11 09:10:44 +0000
@@ -703,16 +703,8 @@ int mysql_update(THD *thd,
 
   transactional_table= table->file->has_transactions();
   thd->abort_on_warning= !ignore && thd->is_strict_mode();
-  if (table->triggers &&
-      table->triggers->has_triggers(TRG_EVENT_UPDATE,
-                                    TRG_ACTION_AFTER))
+  if (table->prepare_triggers_for_update_stmt_or_event())
   {
-    /*
-      The table has AFTER UPDATE triggers that might access to subject 
-      table and therefore might need update to be done immediately. 
-      So we turn-off the batching.
-    */ 
-    (void) table->file->extra(HA_EXTRA_UPDATE_CANNOT_BATCH);
     will_batch= FALSE;
   }
   else
@@ -1610,17 +1602,7 @@ int multi_update::prepare(List<Item> &no
       table->no_keyread=1;
       table->covering_keys.clear_all();
       table->pos_in_table_list= tl;
-      if (table->triggers &&
-          table->triggers->has_triggers(TRG_EVENT_UPDATE,
-                                        TRG_ACTION_AFTER))
-      {
-	/*
-           The table has AFTER UPDATE triggers that might access to subject 
-           table and therefore might need update to be done immediately. 
-           So we turn-off the batching.
-	*/ 
-	(void) table->file->extra(HA_EXTRA_UPDATE_CANNOT_BATCH);
-      }
+      table->prepare_triggers_for_update_stmt_or_event();
     }
   }
 

=== modified file 'sql/sys_vars.cc'
--- a/sql/sys_vars.cc	2014-03-07 20:05:28 +0000
+++ b/sql/sys_vars.cc	2014-03-11 09:10:44 +0000
@@ -2692,6 +2692,21 @@ static Sys_var_enum Slave_ddl_exec_mode(
        GLOBAL_VAR(slave_ddl_exec_mode_options), CMD_LINE(REQUIRED_ARG),
        slave_exec_mode_names, DEFAULT(SLAVE_EXEC_MODE_IDEMPOTENT));
 
+static const char *slave_run_triggers_for_rbr_names[]=
+  {"NO", "YES", "LOGGING", 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 "
+       "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.",
+       GLOBAL_VAR(slave_run_triggers_for_rbr), CMD_LINE(REQUIRED_ARG),
+       slave_run_triggers_for_rbr_names,
+       DEFAULT(SLAVE_RUN_TRIGGERS_FOR_RBR_NO));
+
 static const char *slave_type_conversions_name[]= {"ALL_LOSSY", "ALL_NON_LOSSY", 0};
 static Sys_var_set Slave_type_conversions(
        "slave_type_conversions",

=== modified file 'sql/table.cc'
--- a/sql/table.cc	2014-02-26 14:28:07 +0000
+++ b/sql/table.cc	2014-03-11 09:10:44 +0000
@@ -3994,6 +3994,10 @@ void TABLE::init(THD *thd, TABLE_LIST *t
   created= TRUE;
   cond_selectivity= 1.0;
   cond_selectivity_sampling_explain= NULL;
+#ifdef HAVE_REPLICATION
+  /* used in RBR Triggers */
+  master_had_triggers= 0;
+#endif
 
   /* Catch wrong handling of the auto_increment_field_not_null. */
   DBUG_ASSERT(!auto_increment_field_not_null);
@@ -6657,6 +6661,81 @@ int TABLE::update_default_fields()
 
 
 /*
+  Prepare triggers  for INSERT-like statement.
+
+  SYNOPSIS
+    prepare_triggers_for_insert_stmt_or_event()
+
+  NOTE
+    Prepare triggers for INSERT-like statement by marking fields
+    used by triggers and inform handlers that batching of UPDATE/DELETE 
+    cannot be done if there are BEFORE UPDATE/DELETE triggers.
+*/
+
+void TABLE::prepare_triggers_for_insert_stmt_or_event()
+{
+  if (triggers)
+  {
+    if (triggers->has_triggers(TRG_EVENT_DELETE,
+                               TRG_ACTION_AFTER))
+    {
+      /*
+        The table has AFTER DELETE triggers that might access to
+        subject table and therefore might need delete to be done
+        immediately. So we turn-off the batching.
+      */
+      (void) file->extra(HA_EXTRA_DELETE_CANNOT_BATCH);
+    }
+    if (triggers->has_triggers(TRG_EVENT_UPDATE,
+                               TRG_ACTION_AFTER))
+    {
+      /*
+        The table has AFTER UPDATE triggers that might access to subject
+        table and therefore might need update to be done immediately.
+        So we turn-off the batching.
+      */
+      (void) file->extra(HA_EXTRA_UPDATE_CANNOT_BATCH);
+    }
+  }
+}
+
+
+bool TABLE::prepare_triggers_for_delete_stmt_or_event()
+{
+  if (triggers &&
+      triggers->has_triggers(TRG_EVENT_DELETE,
+                             TRG_ACTION_AFTER))
+  {
+    /*
+      The table has AFTER DELETE triggers that might access to subject table
+      and therefore might need delete to be done immediately. So we turn-off
+      the batching.
+    */
+    (void) file->extra(HA_EXTRA_DELETE_CANNOT_BATCH);
+    return TRUE;
+  }
+  return FALSE;
+}
+
+
+bool TABLE::prepare_triggers_for_update_stmt_or_event()
+{
+  if (triggers &&
+      triggers->has_triggers(TRG_EVENT_UPDATE,
+                             TRG_ACTION_AFTER))
+  {
+    /*
+      The table has AFTER UPDATE triggers that might access to subject
+      table and therefore might need update to be done immediately.
+      So we turn-off the batching.
+    */ 
+    (void) file->extra(HA_EXTRA_UPDATE_CANNOT_BATCH);
+    return TRUE;
+  }
+  return FALSE;
+}
+
+/*
   @brief Reset const_table flag
 
   @detail

=== modified file 'sql/table.h'
--- a/sql/table.h	2014-02-28 09:00:31 +0000
+++ b/sql/table.h	2014-03-11 09:10:44 +0000
@@ -1234,6 +1234,10 @@ public:
   bool get_fields_in_item_tree;      /* Signal to fix_field */
   bool m_needs_reopen;
   bool created;    /* For tmp tables. TRUE <=> tmp table was actually created.*/
+#ifdef HAVE_REPLICATION
+  /* used in RBR Triggers */
+  bool master_had_triggers;
+#endif
 
   REGINFO reginfo;			/* field connections */
   MEM_ROOT mem_root;
@@ -1362,6 +1366,10 @@ public:
   ulong actual_key_flags(KEY *keyinfo);
   int update_default_fields();
   inline ha_rows stat_records() { return used_stat_records; }
+
+  void prepare_triggers_for_insert_stmt_or_event();
+  bool prepare_triggers_for_delete_stmt_or_event();
+  bool prepare_triggers_for_update_stmt_or_event();
 };
 
 



More information about the commits mailing list