MDEV-9095: Executing triggers on slave in row-based replication
This commit is contained in:
parent
f1ca1f37c9
commit
af3180ab6f
5
mysql-test/include/have_rbr_triggers.inc
Normal file
5
mysql-test/include/have_rbr_triggers.inc
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -1301,6 +1301,7 @@ slave-max-allowed-packet 1073741824
|
|||||||
slave-net-timeout 3600
|
slave-net-timeout 3600
|
||||||
slave-parallel-max-queued 131072
|
slave-parallel-max-queued 131072
|
||||||
slave-parallel-threads 0
|
slave-parallel-threads 0
|
||||||
|
slave-run-triggers-for-rbr NO
|
||||||
slave-skip-errors (No default value)
|
slave-skip-errors (No default value)
|
||||||
slave-sql-verify-checksum TRUE
|
slave-sql-verify-checksum TRUE
|
||||||
slave-transaction-retries 10
|
slave-transaction-retries 10
|
||||||
|
240
mysql-test/suite/rpl/r/rpl_row_triggers.result
Normal file
240
mysql-test/suite/rpl/r/rpl_row_triggers.result
Normal file
@ -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
|
14
mysql-test/suite/rpl/r/rpl_row_triggers_sbr.result
Normal file
14
mysql-test/suite/rpl/r/rpl_row_triggers_sbr.result
Normal file
@ -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
|
280
mysql-test/suite/rpl/t/rpl_row_triggers.test
Normal file
280
mysql-test/suite/rpl/t/rpl_row_triggers.test
Normal file
@ -0,0 +1,280 @@
|
|||||||
|
-- source include/have_binlog_format_row.inc
|
||||||
|
-- source include/have_rbr_triggers.inc
|
||||||
|
-- source include/have_innodb.inc
|
||||||
|
-- source include/master-slave.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
|
43
mysql-test/suite/rpl/t/rpl_row_triggers_sbr.test
Normal file
43
mysql-test/suite/rpl/t/rpl_row_triggers_sbr.test
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
--source include/have_binlog_format_statement.inc
|
||||||
|
--source include/have_rbr_triggers.inc
|
||||||
|
--source include/master-slave.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
|
269
sql/log_event.cc
269
sql/log_event.cc
@ -9169,7 +9169,8 @@ Rows_log_event::Rows_log_event(THD *thd_arg, TABLE *tbl_arg, ulong tid,
|
|||||||
m_type(event_type), m_extra_row_data(0)
|
m_type(event_type), m_extra_row_data(0)
|
||||||
#ifdef HAVE_REPLICATION
|
#ifdef HAVE_REPLICATION
|
||||||
, m_curr_row(NULL), m_curr_row_end(NULL),
|
, 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
|
#endif
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
@ -9218,7 +9219,8 @@ Rows_log_event::Rows_log_event(const char *buf, uint event_len,
|
|||||||
m_extra_row_data(0)
|
m_extra_row_data(0)
|
||||||
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
|
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
|
||||||
, m_curr_row(NULL), m_curr_row_end(NULL),
|
, 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
|
#endif
|
||||||
{
|
{
|
||||||
DBUG_ENTER("Rows_log_event::Rows_log_event(const char*,...)");
|
DBUG_ENTER("Rows_log_event::Rows_log_event(const char*,...)");
|
||||||
@ -9473,10 +9475,28 @@ int Rows_log_event::do_add_row_data(uchar *row_data, size_t length)
|
|||||||
}
|
}
|
||||||
#endif
|
#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)
|
int Rows_log_event::do_apply_event(rpl_group_info *rgi)
|
||||||
{
|
{
|
||||||
Relay_log_info const *rli= rgi->rli;
|
Relay_log_info const *rli= rgi->rli;
|
||||||
|
TABLE* table;
|
||||||
DBUG_ENTER("Rows_log_event::do_apply_event(Relay_log_info*)");
|
DBUG_ENTER("Rows_log_event::do_apply_event(Relay_log_info*)");
|
||||||
int error= 0;
|
int error= 0;
|
||||||
/*
|
/*
|
||||||
@ -9556,6 +9576,28 @@ int Rows_log_event::do_apply_event(rpl_group_info *rgi)
|
|||||||
/* A small test to verify that objects have consistent types */
|
/* A small test to verify that objects have consistent types */
|
||||||
DBUG_ASSERT(sizeof(thd->variables.option_bits) == sizeof(OPTION_RELAXED_UNIQUE_CHECKS));
|
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))
|
if (open_and_lock_tables(thd, rgi->tables_to_lock, FALSE, 0))
|
||||||
{
|
{
|
||||||
uint actual_error= thd->get_stmt_da()->sql_errno();
|
uint actual_error= thd->get_stmt_da()->sql_errno();
|
||||||
@ -9573,8 +9615,9 @@ int Rows_log_event::do_apply_event(rpl_group_info *rgi)
|
|||||||
"unexpected success or fatal error"));
|
"unexpected success or fatal error"));
|
||||||
thd->is_slave_error= 1;
|
thd->is_slave_error= 1;
|
||||||
}
|
}
|
||||||
rgi->slave_close_thread_tables(thd);
|
/* remove trigger's tables */
|
||||||
DBUG_RETURN(actual_error);
|
error= actual_error;
|
||||||
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -9618,8 +9661,9 @@ int Rows_log_event::do_apply_event(rpl_group_info *rgi)
|
|||||||
having severe errors which should not be skiped.
|
having severe errors which should not be skiped.
|
||||||
*/
|
*/
|
||||||
thd->is_slave_error= 1;
|
thd->is_slave_error= 1;
|
||||||
rgi->slave_close_thread_tables(thd);
|
/* remove trigger's tables */
|
||||||
DBUG_RETURN(ERR_BAD_TABLE_DEF);
|
error= ERR_BAD_TABLE_DEF;
|
||||||
|
goto err;
|
||||||
}
|
}
|
||||||
DBUG_PRINT("debug", ("Table: %s.%s is compatible with master"
|
DBUG_PRINT("debug", ("Table: %s.%s is compatible with master"
|
||||||
" - conv_table: %p",
|
" - conv_table: %p",
|
||||||
@ -9645,21 +9689,35 @@ int Rows_log_event::do_apply_event(rpl_group_info *rgi)
|
|||||||
*/
|
*/
|
||||||
TABLE_LIST *ptr= rgi->tables_to_lock;
|
TABLE_LIST *ptr= rgi->tables_to_lock;
|
||||||
for (uint i=0 ; ptr && (i < rgi->tables_to_lock_count); ptr= ptr->next_global, i++)
|
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);
|
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
|
#ifdef HAVE_QUERY_CACHE
|
||||||
query_cache.invalidate_locked_for_write(thd, rgi->tables_to_lock);
|
query_cache.invalidate_locked_for_write(thd, rgi->tables_to_lock);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
TABLE*
|
table= m_table= rgi->m_table_map.get_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", (ulong) m_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)
|
if (table)
|
||||||
{
|
{
|
||||||
|
master_had_triggers= table->master_had_triggers;
|
||||||
bool transactional_table= table->file->has_transactions();
|
bool transactional_table= table->file->has_transactions();
|
||||||
/*
|
/*
|
||||||
table == NULL means that this table should not be replicated
|
table == NULL means that this table should not be replicated
|
||||||
@ -9823,9 +9881,13 @@ int Rows_log_event::do_apply_event(rpl_group_info *rgi)
|
|||||||
*/
|
*/
|
||||||
thd->reset_current_stmt_binlog_format_row();
|
thd->reset_current_stmt_binlog_format_row();
|
||||||
thd->is_slave_error= 1;
|
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)))
|
if (get_flags(STMT_END_F) && (error= rows_event_stmt_cleanup(rgi, thd)))
|
||||||
slave_rows_error_report(ERROR_LEVEL,
|
slave_rows_error_report(ERROR_LEVEL,
|
||||||
thd->is_error() ? 0 : error,
|
thd->is_error() ? 0 : error,
|
||||||
@ -9833,6 +9895,12 @@ int Rows_log_event::do_apply_event(rpl_group_info *rgi)
|
|||||||
get_type_str(),
|
get_type_str(),
|
||||||
RPL_LOG_NAME, (ulong) log_pos);
|
RPL_LOG_NAME, (ulong) log_pos);
|
||||||
DBUG_RETURN(error);
|
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
|
Log_event::enum_skip_reason
|
||||||
@ -10306,6 +10374,7 @@ Table_map_log_event::Table_map_log_event(THD *thd, TABLE *tbl, ulong tid,
|
|||||||
{
|
{
|
||||||
uchar cbuf[MAX_INT_WIDTH];
|
uchar cbuf[MAX_INT_WIDTH];
|
||||||
uchar *cbuf_end;
|
uchar *cbuf_end;
|
||||||
|
DBUG_ENTER("Table_map_log_event::Table_map_log_event(TABLE)");
|
||||||
DBUG_ASSERT(m_table_id != ~0UL);
|
DBUG_ASSERT(m_table_id != ~0UL);
|
||||||
/*
|
/*
|
||||||
In TABLE_SHARE, "db" and "table_name" are 0-terminated (see this comment in
|
In TABLE_SHARE, "db" and "table_name" are 0-terminated (see this comment in
|
||||||
@ -10326,6 +10395,9 @@ Table_map_log_event::Table_map_log_event(THD *thd, TABLE *tbl, ulong tid,
|
|||||||
DBUG_ASSERT(static_cast<size_t>(cbuf_end - cbuf) <= sizeof(cbuf));
|
DBUG_ASSERT(static_cast<size_t>(cbuf_end - cbuf) <= sizeof(cbuf));
|
||||||
m_data_size+= (cbuf_end - cbuf) + m_colcnt; // COLCNT and column types
|
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 malloc fails, caught in is_valid() */
|
||||||
if ((m_memory= (uchar*) my_malloc(m_colcnt, MYF(MY_WME))))
|
if ((m_memory= (uchar*) my_malloc(m_colcnt, MYF(MY_WME))))
|
||||||
{
|
{
|
||||||
@ -10370,6 +10442,7 @@ Table_map_log_event::Table_map_log_event(THD *thd, TABLE *tbl, ulong tid,
|
|||||||
if (m_table->field[i]->maybe_null())
|
if (m_table->field[i]->maybe_null())
|
||||||
m_null_bits[(i / 8)]+= 1 << (i % 8);
|
m_null_bits[(i / 8)]+= 1 << (i % 8);
|
||||||
|
|
||||||
|
DBUG_VOID_RETURN;
|
||||||
}
|
}
|
||||||
#endif /* !defined(MYSQL_CLIENT) */
|
#endif /* !defined(MYSQL_CLIENT) */
|
||||||
|
|
||||||
@ -10732,7 +10805,11 @@ int Table_map_log_event::do_apply_event(rpl_group_info *rgi)
|
|||||||
table_list->table_id= DBUG_EVALUATE_IF("inject_tblmap_same_id_maps_diff_table", 0, m_table_id);
|
table_list->table_id= DBUG_EVALUATE_IF("inject_tblmap_same_id_maps_diff_table", 0, m_table_id);
|
||||||
table_list->updating= 1;
|
table_list->updating= 1;
|
||||||
table_list->required_type= FRMTYPE_TABLE;
|
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);
|
enum_tbl_map_status tblmap_status= check_table_map(rgi, table_list);
|
||||||
if (tblmap_status == OK_TO_PROCESS)
|
if (tblmap_status == OK_TO_PROCESS)
|
||||||
{
|
{
|
||||||
@ -10904,8 +10981,10 @@ void Table_map_log_event::print(FILE *, PRINT_EVENT_INFO *print_event_info)
|
|||||||
{
|
{
|
||||||
print_header(&print_event_info->head_cache, print_event_info, TRUE);
|
print_header(&print_event_info->head_cache, print_event_info, TRUE);
|
||||||
my_b_printf(&print_event_info->head_cache,
|
my_b_printf(&print_event_info->head_cache,
|
||||||
"\tTable_map: %`s.%`s mapped to number %lu\n",
|
"\tTable_map: %`s.%`s mapped to number %lu%s\n",
|
||||||
m_dbnam, m_tblnam, (ulong) m_table_id);
|
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);
|
print_base64(&print_event_info->body_cache, print_event_info, TRUE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -10981,6 +11060,8 @@ Write_rows_log_event::do_before_row_operations(const Slave_reporting_capability
|
|||||||
/*
|
/*
|
||||||
NDB specific: update from ndb master wrapped as Write_rows
|
NDB specific: update from ndb master wrapped as Write_rows
|
||||||
so that the event should be applied to replace slave's row
|
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);
|
m_table->file->extra(HA_EXTRA_WRITE_CAN_REPLACE);
|
||||||
/*
|
/*
|
||||||
@ -10995,6 +11076,8 @@ Write_rows_log_event::do_before_row_operations(const Slave_reporting_capability
|
|||||||
from the start.
|
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 */
|
/* Honor next number column if present */
|
||||||
m_table->next_number_field= m_table->found_next_number_field;
|
m_table->next_number_field= m_table->found_next_number_field;
|
||||||
@ -11040,6 +11123,25 @@ Write_rows_log_event::do_after_row_operations(const Slave_reporting_capability *
|
|||||||
|
|
||||||
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
|
#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.
|
Check if there are more UNIQUE keys after the given key.
|
||||||
*/
|
*/
|
||||||
@ -11122,6 +11224,8 @@ Rows_log_event::write_row(rpl_group_info *rgi,
|
|||||||
TABLE *table= m_table; // pointer to event's table
|
TABLE *table= m_table; // pointer to event's table
|
||||||
int error;
|
int error;
|
||||||
int UNINIT_VAR(keynum);
|
int UNINIT_VAR(keynum);
|
||||||
|
const bool invoke_triggers=
|
||||||
|
slave_run_triggers_for_rbr && !master_had_triggers && table->triggers;
|
||||||
auto_afree_ptr<char> key(NULL);
|
auto_afree_ptr<char> key(NULL);
|
||||||
|
|
||||||
prepare_record(table, m_width,
|
prepare_record(table, m_width,
|
||||||
@ -11131,13 +11235,17 @@ Rows_log_event::write_row(rpl_group_info *rgi,
|
|||||||
if ((error= unpack_current_row(rgi)))
|
if ((error= unpack_current_row(rgi)))
|
||||||
DBUG_RETURN(error);
|
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
|
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);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -11147,6 +11255,12 @@ Rows_log_event::write_row(rpl_group_info *rgi,
|
|||||||
DBUG_PRINT_BITSET("debug", "read_set = %s", table->read_set);
|
DBUG_PRINT_BITSET("debug", "read_set = %s", table->read_set);
|
||||||
#endif
|
#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,
|
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
|
we try to change it using ha_update_row() if possible. Otherwise we delete
|
||||||
@ -11273,38 +11387,61 @@ Rows_log_event::write_row(rpl_group_info *rgi,
|
|||||||
!table->file->referenced_by_foreign_key())
|
!table->file->referenced_by_foreign_key())
|
||||||
{
|
{
|
||||||
DBUG_PRINT("info",("Updating row using ha_update_row()"));
|
DBUG_PRINT("info",("Updating row using ha_update_row()"));
|
||||||
error=table->file->ha_update_row(table->record[1],
|
if (invoke_triggers &&
|
||||||
table->record[0]);
|
process_triggers(TRG_EVENT_UPDATE, TRG_ACTION_BEFORE, FALSE))
|
||||||
switch (error) {
|
error= HA_ERR_GENERIC; // in case if error is not set yet
|
||||||
|
else
|
||||||
case HA_ERR_RECORD_IS_THE_SAME:
|
{
|
||||||
DBUG_PRINT("info",("ignoring HA_ERR_RECORD_IS_THE_SAME error from"
|
error= table->file->ha_update_row(table->record[1],
|
||||||
" ha_update_row()"));
|
table->record[0]);
|
||||||
error= 0;
|
switch (error) {
|
||||||
|
|
||||||
case 0:
|
case HA_ERR_RECORD_IS_THE_SAME:
|
||||||
break;
|
DBUG_PRINT("info",("ignoring HA_ERR_RECORD_IS_THE_SAME error from"
|
||||||
|
" ha_update_row()"));
|
||||||
default:
|
error= 0;
|
||||||
DBUG_PRINT("info",("ha_update_row() returns error %d",error));
|
|
||||||
table->file->print_error(error, MYF(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);
|
DBUG_RETURN(error);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
DBUG_PRINT("info",("Deleting offending row and trying to write new one again"));
|
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));
|
if ((error= table->file->ha_delete_row(table->record[1])))
|
||||||
table->file->print_error(error, MYF(0));
|
{
|
||||||
DBUG_RETURN(error);
|
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. */
|
/* 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);
|
DBUG_RETURN(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -11336,6 +11473,16 @@ void Write_rows_log_event::print(FILE *file, PRINT_EVENT_INFO* print_event_info)
|
|||||||
}
|
}
|
||||||
#endif
|
#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
|
Delete_rows_log_event member functions
|
||||||
**************************************************************************/
|
**************************************************************************/
|
||||||
@ -11960,6 +12107,8 @@ Delete_rows_log_event::do_before_row_operations(const Slave_reporting_capability
|
|||||||
*/
|
*/
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
if (slave_run_triggers_for_rbr && !master_had_triggers)
|
||||||
|
m_table->prepare_triggers_for_delete_stmt_or_event();
|
||||||
|
|
||||||
return find_key();
|
return find_key();
|
||||||
}
|
}
|
||||||
@ -11980,6 +12129,8 @@ Delete_rows_log_event::do_after_row_operations(const Slave_reporting_capability
|
|||||||
int Delete_rows_log_event::do_exec_row(rpl_group_info *rgi)
|
int Delete_rows_log_event::do_exec_row(rpl_group_info *rgi)
|
||||||
{
|
{
|
||||||
int error;
|
int error;
|
||||||
|
const bool invoke_triggers=
|
||||||
|
slave_run_triggers_for_rbr && !master_had_triggers && m_table->triggers;
|
||||||
DBUG_ASSERT(m_table != NULL);
|
DBUG_ASSERT(m_table != NULL);
|
||||||
|
|
||||||
if (!(error= find_row(rgi)))
|
if (!(error= find_row(rgi)))
|
||||||
@ -11987,7 +12138,14 @@ int Delete_rows_log_event::do_exec_row(rpl_group_info *rgi)
|
|||||||
/*
|
/*
|
||||||
Delete the record found, located in record[0]
|
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();
|
m_table->file->ha_index_or_rnd_end();
|
||||||
}
|
}
|
||||||
return error;
|
return error;
|
||||||
@ -12004,6 +12162,13 @@ void Delete_rows_log_event::print(FILE *file,
|
|||||||
#endif
|
#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
|
Update_rows_log_event member functions
|
||||||
**************************************************************************/
|
**************************************************************************/
|
||||||
@ -12086,6 +12251,9 @@ Update_rows_log_event::do_before_row_operations(const Slave_reporting_capability
|
|||||||
if ((err= find_key()))
|
if ((err= find_key()))
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
|
if (slave_run_triggers_for_rbr && !master_had_triggers)
|
||||||
|
m_table->prepare_triggers_for_update_stmt_or_event();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -12105,6 +12273,8 @@ Update_rows_log_event::do_after_row_operations(const Slave_reporting_capability
|
|||||||
int
|
int
|
||||||
Update_rows_log_event::do_exec_row(rpl_group_info *rgi)
|
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;
|
||||||
DBUG_ASSERT(m_table != NULL);
|
DBUG_ASSERT(m_table != NULL);
|
||||||
|
|
||||||
int error= find_row(rgi);
|
int error= find_row(rgi);
|
||||||
@ -12151,10 +12321,21 @@ Update_rows_log_event::do_exec_row(rpl_group_info *rgi)
|
|||||||
DBUG_DUMP("new values", m_table->record[0], m_table->s->reclength);
|
DBUG_DUMP("new values", m_table->record[0], m_table->s->reclength);
|
||||||
#endif
|
#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]);
|
error= m_table->file->ha_update_row(m_table->record[1], m_table->record[0]);
|
||||||
if (error == HA_ERR_RECORD_IS_THE_SAME)
|
if (error == HA_ERR_RECORD_IS_THE_SAME)
|
||||||
error= 0;
|
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:
|
err:
|
||||||
m_table->file->ha_index_or_rnd_end();
|
m_table->file->ha_index_or_rnd_end();
|
||||||
return error;
|
return error;
|
||||||
@ -12170,6 +12351,12 @@ void Update_rows_log_event::print(FILE *file,
|
|||||||
}
|
}
|
||||||
#endif
|
#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,
|
Incident_log_event::Incident_log_event(const char *buf, uint event_len,
|
||||||
const Format_description_log_event *descr_event)
|
const Format_description_log_event *descr_event)
|
||||||
|
@ -4049,7 +4049,9 @@ public:
|
|||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
TM_NO_FLAGS = 0U,
|
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; }
|
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; }
|
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:
|
protected:
|
||||||
/*
|
/*
|
||||||
The constructors are protected since you're supposed to inherit
|
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 */
|
uchar *m_extra_row_data; /* Pointer to extra row data if any */
|
||||||
/* If non null, first byte is length */
|
/* If non null, first byte is length */
|
||||||
|
|
||||||
|
|
||||||
/* helper functions */
|
/* helper functions */
|
||||||
|
|
||||||
#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
|
#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
|
||||||
@ -4315,6 +4322,7 @@ protected:
|
|||||||
uchar *m_key; /* Buffer to keep key value during searches */
|
uchar *m_key; /* Buffer to keep key value during searches */
|
||||||
KEY *m_key_info; /* Pointer to KEY info for m_key_nr */
|
KEY *m_key_info; /* Pointer to KEY info for m_key_nr */
|
||||||
uint m_key_nr; /* Key number */
|
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_key(); // Find a best key to use in find_row()
|
||||||
int find_row(rpl_group_info *);
|
int find_row(rpl_group_info *);
|
||||||
@ -4329,6 +4337,9 @@ protected:
|
|||||||
return ::unpack_row(rgi, m_table, m_width, m_curr_row, &m_cols,
|
return ::unpack_row(rgi, m_table, m_width, m_curr_row, &m_cols,
|
||||||
&m_curr_row_end, &m_master_reclength, m_rows_end);
|
&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
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -4433,6 +4444,10 @@ public:
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
|
||||||
|
uint8 get_trg_event_map();
|
||||||
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual Log_event_type get_general_type_code() { return (Log_event_type)TYPE_CODE; }
|
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;
|
return Rows_log_event::is_valid() && m_cols_ai.bitmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
|
||||||
|
uint8 get_trg_event_map();
|
||||||
|
#endif
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual Log_event_type get_general_type_code() { return (Log_event_type)TYPE_CODE; }
|
virtual Log_event_type get_general_type_code() { return (Log_event_type)TYPE_CODE; }
|
||||||
|
|
||||||
@ -4571,7 +4590,11 @@ public:
|
|||||||
cols, fields, before_record);
|
cols, fields, before_record);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
|
||||||
|
uint8 get_trg_event_map();
|
||||||
|
#endif
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual Log_event_type get_general_type_code() { return (Log_event_type)TYPE_CODE; }
|
virtual Log_event_type get_general_type_code() { return (Log_event_type)TYPE_CODE; }
|
||||||
|
|
||||||
|
@ -479,6 +479,7 @@ ulong open_files_limit, max_binlog_size;
|
|||||||
ulong slave_trans_retries;
|
ulong slave_trans_retries;
|
||||||
uint slave_net_timeout;
|
uint slave_net_timeout;
|
||||||
ulong slave_exec_mode_options;
|
ulong slave_exec_mode_options;
|
||||||
|
ulong slave_run_triggers_for_rbr= 0;
|
||||||
ulong slave_ddl_exec_mode_options= SLAVE_EXEC_MODE_IDEMPOTENT;
|
ulong slave_ddl_exec_mode_options= SLAVE_EXEC_MODE_IDEMPOTENT;
|
||||||
ulonglong slave_type_conversions_options;
|
ulonglong slave_type_conversions_options;
|
||||||
ulong thread_cache_size=0;
|
ulong thread_cache_size=0;
|
||||||
|
@ -98,6 +98,7 @@ extern my_bool opt_safe_show_db, opt_local_infile, opt_myisam_use_mmap;
|
|||||||
extern my_bool opt_slave_compressed_protocol, use_temp_pool;
|
extern my_bool opt_slave_compressed_protocol, use_temp_pool;
|
||||||
extern ulong slave_exec_mode_options, slave_ddl_exec_mode_options;
|
extern ulong slave_exec_mode_options, slave_ddl_exec_mode_options;
|
||||||
extern ulong slave_retried_transactions;
|
extern ulong slave_retried_transactions;
|
||||||
|
extern ulong slave_run_triggers_for_rbr;
|
||||||
extern ulonglong slave_type_conversions_options;
|
extern ulonglong slave_type_conversions_options;
|
||||||
extern my_bool read_only, opt_readonly;
|
extern my_bool read_only, opt_readonly;
|
||||||
extern my_bool lower_case_file_system;
|
extern my_bool lower_case_file_system;
|
||||||
|
@ -238,6 +238,7 @@ struct RPL_TABLE_LIST
|
|||||||
bool m_tabledef_valid;
|
bool m_tabledef_valid;
|
||||||
table_def m_tabledef;
|
table_def m_tabledef;
|
||||||
TABLE *m_conv_table;
|
TABLE *m_conv_table;
|
||||||
|
bool master_had_triggers;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -80,6 +80,9 @@ enum enum_delay_key_write { DELAY_KEY_WRITE_NONE, DELAY_KEY_WRITE_ON,
|
|||||||
enum enum_slave_exec_mode { SLAVE_EXEC_MODE_STRICT,
|
enum enum_slave_exec_mode { SLAVE_EXEC_MODE_STRICT,
|
||||||
SLAVE_EXEC_MODE_IDEMPOTENT,
|
SLAVE_EXEC_MODE_IDEMPOTENT,
|
||||||
SLAVE_EXEC_MODE_LAST_BIT };
|
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,
|
enum enum_slave_type_conversions { SLAVE_TYPE_CONVERSIONS_ALL_LOSSY,
|
||||||
SLAVE_TYPE_CONVERSIONS_ALL_NON_LOSSY};
|
SLAVE_TYPE_CONVERSIONS_ALL_NON_LOSSY};
|
||||||
enum enum_mark_columns
|
enum enum_mark_columns
|
||||||
|
@ -521,16 +521,8 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
|
|||||||
init_ftfuncs(thd, select_lex, 1);
|
init_ftfuncs(thd, select_lex, 1);
|
||||||
THD_STAGE_INFO(thd, stage_updating);
|
THD_STAGE_INFO(thd, stage_updating);
|
||||||
|
|
||||||
if (table->triggers &&
|
if (table->prepare_triggers_for_delete_stmt_or_event())
|
||||||
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);
|
|
||||||
will_batch= FALSE;
|
will_batch= FALSE;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -938,17 +930,7 @@ multi_delete::initialize_tables(JOIN *join)
|
|||||||
transactional_tables= 1;
|
transactional_tables= 1;
|
||||||
else
|
else
|
||||||
normal_tables= 1;
|
normal_tables= 1;
|
||||||
if (tbl->triggers &&
|
tbl->prepare_triggers_for_delete_stmt_or_event();
|
||||||
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_for_position();
|
tbl->prepare_for_position();
|
||||||
tbl->mark_columns_needed_for_delete();
|
tbl->mark_columns_needed_for_delete();
|
||||||
}
|
}
|
||||||
|
@ -373,48 +373,6 @@ static int check_update_fields(THD *thd, TABLE_LIST *insert_table_list,
|
|||||||
return 0;
|
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
|
Upgrade table-level lock of INSERT statement to TL_WRITE if
|
||||||
a more concurrent lock is infeasible for some reason. This is
|
a more concurrent lock is infeasible for some reason. This is
|
||||||
@ -902,7 +860,8 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
|
|||||||
|
|
||||||
thd->abort_on_warning= !ignore && thd->is_strict_mode();
|
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) ||
|
if (table_list->prepare_where(thd, 0, TRUE) ||
|
||||||
@ -3537,7 +3496,10 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
|
|||||||
table_list->prepare_check_option(thd));
|
table_list->prepare_check_option(thd));
|
||||||
|
|
||||||
if (!res)
|
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);
|
DBUG_RETURN(res);
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,6 @@ void upgrade_lock_type_for_insert(THD *thd, thr_lock_type *lock_type,
|
|||||||
bool is_multi_insert);
|
bool is_multi_insert);
|
||||||
int check_that_all_fields_are_given_values(THD *thd, TABLE *entry,
|
int check_that_all_fields_are_given_values(THD *thd, TABLE *entry,
|
||||||
TABLE_LIST *table_list);
|
TABLE_LIST *table_list);
|
||||||
void prepare_triggers_for_insert_stmt(TABLE *table);
|
|
||||||
int write_record(THD *thd, TABLE *table, COPY_INFO *info);
|
int write_record(THD *thd, TABLE *table, COPY_INFO *info);
|
||||||
void kill_delayed_threads(void);
|
void kill_delayed_threads(void);
|
||||||
|
|
||||||
|
@ -28,7 +28,6 @@
|
|||||||
#include <my_dir.h>
|
#include <my_dir.h>
|
||||||
#include "sql_view.h" // check_key_in_view
|
#include "sql_view.h" // check_key_in_view
|
||||||
#include "sql_insert.h" // check_that_all_fields_are_given_values,
|
#include "sql_insert.h" // check_that_all_fields_are_given_values,
|
||||||
// prepare_triggers_for_insert_stmt,
|
|
||||||
// write_record
|
// write_record
|
||||||
#include "sql_acl.h" // INSERT_ACL, UPDATE_ACL
|
#include "sql_acl.h" // INSERT_ACL, UPDATE_ACL
|
||||||
#include "log_event.h" // Delete_file_log_event,
|
#include "log_event.h" // Delete_file_log_event,
|
||||||
@ -298,7 +297,8 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
|
|||||||
DBUG_RETURN(TRUE);
|
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;
|
uint tot_length=0;
|
||||||
bool use_blobs= 0, use_vars= 0;
|
bool use_blobs= 0, use_vars= 0;
|
||||||
|
@ -703,16 +703,8 @@ int mysql_update(THD *thd,
|
|||||||
|
|
||||||
transactional_table= table->file->has_transactions();
|
transactional_table= table->file->has_transactions();
|
||||||
thd->abort_on_warning= !ignore && thd->is_strict_mode();
|
thd->abort_on_warning= !ignore && thd->is_strict_mode();
|
||||||
if (table->triggers &&
|
if (table->prepare_triggers_for_update_stmt_or_event())
|
||||||
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);
|
|
||||||
will_batch= FALSE;
|
will_batch= FALSE;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -1610,17 +1602,7 @@ int multi_update::prepare(List<Item> ¬_used_values,
|
|||||||
table->no_keyread=1;
|
table->no_keyread=1;
|
||||||
table->covering_keys.clear_all();
|
table->covering_keys.clear_all();
|
||||||
table->pos_in_table_list= tl;
|
table->pos_in_table_list= tl;
|
||||||
if (table->triggers &&
|
table->prepare_triggers_for_update_stmt_or_event();
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2737,6 +2737,21 @@ static Sys_var_enum Slave_ddl_exec_mode(
|
|||||||
GLOBAL_VAR(slave_ddl_exec_mode_options), CMD_LINE(REQUIRED_ARG),
|
GLOBAL_VAR(slave_ddl_exec_mode_options), CMD_LINE(REQUIRED_ARG),
|
||||||
slave_exec_mode_names, DEFAULT(SLAVE_EXEC_MODE_IDEMPOTENT));
|
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 const char *slave_type_conversions_name[]= {"ALL_LOSSY", "ALL_NON_LOSSY", 0};
|
||||||
static Sys_var_set Slave_type_conversions(
|
static Sys_var_set Slave_type_conversions(
|
||||||
"slave_type_conversions",
|
"slave_type_conversions",
|
||||||
|
79
sql/table.cc
79
sql/table.cc
@ -3994,6 +3994,10 @@ void TABLE::init(THD *thd, TABLE_LIST *tl)
|
|||||||
created= TRUE;
|
created= TRUE;
|
||||||
cond_selectivity= 1.0;
|
cond_selectivity= 1.0;
|
||||||
cond_selectivity_sampling_explain= NULL;
|
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. */
|
/* Catch wrong handling of the auto_increment_field_not_null. */
|
||||||
DBUG_ASSERT(!auto_increment_field_not_null);
|
DBUG_ASSERT(!auto_increment_field_not_null);
|
||||||
@ -6656,6 +6660,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
|
@brief Reset const_table flag
|
||||||
|
|
||||||
|
@ -1234,6 +1234,10 @@ public:
|
|||||||
bool get_fields_in_item_tree; /* Signal to fix_field */
|
bool get_fields_in_item_tree; /* Signal to fix_field */
|
||||||
bool m_needs_reopen;
|
bool m_needs_reopen;
|
||||||
bool created; /* For tmp tables. TRUE <=> tmp table was actually created.*/
|
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 */
|
REGINFO reginfo; /* field connections */
|
||||||
MEM_ROOT mem_root;
|
MEM_ROOT mem_root;
|
||||||
@ -1362,6 +1366,10 @@ public:
|
|||||||
ulong actual_key_flags(KEY *keyinfo);
|
ulong actual_key_flags(KEY *keyinfo);
|
||||||
int update_default_fields();
|
int update_default_fields();
|
||||||
inline ha_rows stat_records() { return used_stat_records; }
|
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();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user