Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
M
mariadb
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
mariadb
Commits
af3180ab
Commit
af3180ab
authored
Mar 19, 2014
by
unknown
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
MDEV-9095: Executing triggers on slave in row-based replication
parent
f1ca1f37
Changes
20
Hide whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
956 additions
and
130 deletions
+956
-130
mysql-test/include/have_rbr_triggers.inc
mysql-test/include/have_rbr_triggers.inc
+5
-0
mysql-test/r/mysqld--help.result
mysql-test/r/mysqld--help.result
+1
-0
mysql-test/suite/rpl/r/rpl_row_triggers.result
mysql-test/suite/rpl/r/rpl_row_triggers.result
+240
-0
mysql-test/suite/rpl/r/rpl_row_triggers_sbr.result
mysql-test/suite/rpl/r/rpl_row_triggers_sbr.result
+14
-0
mysql-test/suite/rpl/t/rpl_row_triggers.test
mysql-test/suite/rpl/t/rpl_row_triggers.test
+280
-0
mysql-test/suite/rpl/t/rpl_row_triggers_sbr.test
mysql-test/suite/rpl/t/rpl_row_triggers_sbr.test
+43
-0
sql/log_event.cc
sql/log_event.cc
+228
-41
sql/log_event.h
sql/log_event.h
+25
-2
sql/mysqld.cc
sql/mysqld.cc
+1
-0
sql/mysqld.h
sql/mysqld.h
+1
-0
sql/rpl_utility.h
sql/rpl_utility.h
+1
-0
sql/sql_class.h
sql/sql_class.h
+3
-0
sql/sql_delete.cc
sql/sql_delete.cc
+2
-20
sql/sql_insert.cc
sql/sql_insert.cc
+6
-44
sql/sql_insert.h
sql/sql_insert.h
+0
-1
sql/sql_load.cc
sql/sql_load.cc
+2
-2
sql/sql_update.cc
sql/sql_update.cc
+2
-20
sql/sys_vars.cc
sql/sys_vars.cc
+15
-0
sql/table.cc
sql/table.cc
+79
-0
sql/table.h
sql/table.h
+8
-0
No files found.
mysql-test/include/have_rbr_triggers.inc
0 → 100644
View file @
af3180ab
if
(
`select count(*) = 0 from information_schema.session_variables where variable_name = 'slave_run_triggers_for_rbr'`
)
{
skip
RBR
triggers
are
not
available
;
}
mysql-test/r/mysqld--help.result
View file @
af3180ab
...
...
@@ -1301,6 +1301,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
...
...
mysql-test/suite/rpl/r/rpl_row_triggers.result
0 → 100644
View file @
af3180ab
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
mysql-test/suite/rpl/r/rpl_row_triggers_sbr.result
0 → 100644
View file @
af3180ab
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
mysql-test/suite/rpl/t/rpl_row_triggers.test
0 → 100644
View file @
af3180ab
--
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
mysql-test/suite/rpl/t/rpl_row_triggers_sbr.test
0 → 100644
View file @
af3180ab
--
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
sql/log_event.cc
View file @
af3180ab
...
...
@@ -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
)
#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
{
/*
...
...
@@ -9218,7 +9219,8 @@ Rows_log_event::Rows_log_event(const char *buf, uint event_len,
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*,...)"
);
...
...
@@ -9473,10 +9475,28 @@ int Rows_log_event::do_add_row_data(uchar *row_data, size_t length)
}
#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
;
/*
...
...
@@ -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 */
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
();
...
...
@@ -9573,8 +9615,9 @@ int Rows_log_event::do_apply_event(rpl_group_info *rgi)
"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
;
}
/*
...
...
@@ -9618,8 +9661,9 @@ int Rows_log_event::do_apply_event(rpl_group_info *rgi)
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"
,
...
...
@@ -9645,21 +9689,35 @@ int Rows_log_event::do_apply_event(rpl_group_info *rgi)
*/
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
...
...
@@ -9823,9 +9881,13 @@ int Rows_log_event::do_apply_event(rpl_group_info *rgi)
*/
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
,
...
...
@@ -9833,6 +9895,12 @@ int Rows_log_event::do_apply_event(rpl_group_info *rgi)
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
...
...
@@ -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_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
...
...
@@ -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
));
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
))))
{
...
...
@@ -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
())
m_null_bits
[(
i
/
8
)]
+=
1
<<
(
i
%
8
);
DBUG_VOID_RETURN
;
}
#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
->
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
)
{
...
...
@@ -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
);
my_b_printf
(
&
print_event_info
->
head_cache
,
"
\t
Table_map: %`s.%`s mapped to number %lu
\n
"
,
m_dbnam
,
m_tblnam
,
(
ulong
)
m_table_id
);
"
\t
Table_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
);
}
}
...
...
@@ -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
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
);
/*
...
...
@@ -10995,6 +11076,8 @@ Write_rows_log_event::do_before_row_operations(const Slave_reporting_capability
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
;
...
...
@@ -11040,6 +11123,25 @@ Write_rows_log_event::do_after_row_operations(const Slave_reporting_capability *
#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.
*/
...
...
@@ -11122,6 +11224,8 @@ Rows_log_event::write_row(rpl_group_info *rgi,
TABLE
*
table
=
m_table
;
// pointer to event's table
int
error
;
int
UNINIT_VAR
(
keynum
);
const
bool
invoke_triggers
=
slave_run_triggers_for_rbr
&&
!
master_had_triggers
&&
table
->
triggers
;
auto_afree_ptr
<
char
>
key
(
NULL
);
prepare_record
(
table
,
m_width
,
...
...
@@ -11131,13 +11235,17 @@ Rows_log_event::write_row(rpl_group_info *rgi,
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
);
}
...
...
@@ -11147,6 +11255,12 @@ Rows_log_event::write_row(rpl_group_info *rgi,
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
...
...
@@ -11273,38 +11387,61 @@ Rows_log_event::write_row(rpl_group_info *rgi,
!
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
);
}
...
...
@@ -11336,6 +11473,16 @@ void Write_rows_log_event::print(FILE *file, PRINT_EVENT_INFO* print_event_info)
}
#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
**************************************************************************/
...
...
@@ -11960,6 +12107,8 @@ Delete_rows_log_event::do_before_row_operations(const Slave_reporting_capability
*/
return
0
;
}
if
(
slave_run_triggers_for_rbr
&&
!
master_had_triggers
)
m_table
->
prepare_triggers_for_delete_stmt_or_event
();
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
error
;
const
bool
invoke_triggers
=
slave_run_triggers_for_rbr
&&
!
master_had_triggers
&&
m_table
->
triggers
;
DBUG_ASSERT
(
m_table
!=
NULL
);
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]
*/
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
;
...
...
@@ -12004,6 +12162,13 @@ void Delete_rows_log_event::print(FILE *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
**************************************************************************/
...
...
@@ -12086,6 +12251,9 @@ Update_rows_log_event::do_before_row_operations(const Slave_reporting_capability
if
((
err
=
find_key
()))
return
err
;
if
(
slave_run_triggers_for_rbr
&&
!
master_had_triggers
)
m_table
->
prepare_triggers_for_update_stmt_or_event
();
return
0
;
}
...
...
@@ -12105,6 +12273,8 @@ Update_rows_log_event::do_after_row_operations(const Slave_reporting_capability
int
Update_rows_log_event
::
do_exec_row
(
rpl_group_info
*
rgi
)
{
const
bool
invoke_triggers
=
slave_run_triggers_for_rbr
&&
!
master_had_triggers
&&
m_table
->
triggers
;
DBUG_ASSERT
(
m_table
!=
NULL
);
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
);
#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
;
...
...
@@ -12170,6 +12351,12 @@ void Update_rows_log_event::print(FILE *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
)
...
...
sql/log_event.h
View file @
af3180ab
...
...
@@ -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
;
}
...
...
sql/mysqld.cc
View file @
af3180ab
...
...
@@ -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
;
...
...
sql/mysqld.h
View file @
af3180ab
...
...
@@ -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
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
;
...
...
sql/rpl_utility.h
View file @
af3180ab
...
...
@@ -238,6 +238,7 @@ struct RPL_TABLE_LIST
bool
m_tabledef_valid
;
table_def
m_tabledef
;
TABLE
*
m_conv_table
;
bool
master_had_triggers
;
};
...
...
sql/sql_class.h
View file @
af3180ab
...
...
@@ -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
,
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
...
...
sql/sql_delete.cc
View file @
af3180ab
...
...
@@ -521,16 +521,8 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
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 *join)
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
();
}
...
...
sql/sql_insert.cc
View file @
af3180ab
...
...
@@ -373,48 +373,6 @@ static int check_update_fields(THD *thd, TABLE_LIST *insert_table_list,
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 *table_list,
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> &values, SELECT_LEX_UNIT *u)
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
);
}
...
...
sql/sql_insert.h
View file @
af3180ab
...
...
@@ -38,7 +38,6 @@ void upgrade_lock_type_for_insert(THD *thd, thr_lock_type *lock_type,
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
);
...
...
sql/sql_load.cc
View file @
af3180ab
...
...
@@ -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,TABLE_LIST *table_list,
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
;
...
...
sql/sql_update.cc
View file @
af3180ab
...
...
@@ -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> ¬_used_values,
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
();
}
}
...
...
sql/sys_vars.cc
View file @
af3180ab
...
...
@@ -2737,6 +2737,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"
,
...
...
sql/table.cc
View file @
af3180ab
...
...
@@ -3994,6 +3994,10 @@ void TABLE::init(THD *thd, TABLE_LIST *tl)
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
);
...
...
@@ -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
...
...
sql/table.h
View file @
af3180ab
...
...
@@ -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
();
};
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment