mirror of
https://github.com/MariaDB/server.git
synced 2025-08-08 11:22:35 +03:00
When opening and locking tables, if triggers will be invoked in a
separate database, thd->set_db() is invoked, thus freeeing the memory
and headers which thd->db had previously pointed to. In row based
replication, the event execution logic initializes thd->db to point
to the database which the event targets, which is owned by the
corresponding table share (introduced in d9898c9
for MDEV-7409).
The problem then, is that during the table opening and locking
process for a row event, memory which belongs to the table share
would be freed, which is not valid.
This patch replaces the thd->reset_db() calls to thd->set_db(),
which copies-by-value, rather than by reference. Then when the
memory is freed, our copy of memory is freed, rather than memory
which belongs to a table share.
Notes:
1. The call to change thd->db now happens on a higher-level, in
Rows_log_event::do_apply_event() rather than ::do_exec_row(), in the
call stack. This is because do_exec_row() is called within a loop,
and each invocation would redundantly set and unset the db to the
same value.
2. thd->set_db() is only used if triggers are to be invoked, as
there is no vulnerability in the non-trigger case, and copying
memory would be an unnecessary inefficiency.
Reviewed By:
============
Andrei Elkin <andrei.elkin@mariadb.com>
99 lines
2.0 KiB
Plaintext
99 lines
2.0 KiB
Plaintext
#
|
|
# This test ensures that a table share's database name is not freed when
|
|
# using row based replication with triggers that open different databases
|
|
#
|
|
#
|
|
# References:
|
|
# MDEV-29894: Calling a function from a different database in a slave side
|
|
# trigger crashes
|
|
#
|
|
--source include/master-slave.inc
|
|
--source include/have_binlog_format_row.inc
|
|
|
|
--connection slave
|
|
--let $old_slave_run_triggers= `SELECT @@global.slave_run_triggers_for_rbr`
|
|
set global slave_run_triggers_for_rbr=1;
|
|
|
|
--connection master
|
|
CREATE TABLE t1 (a int);
|
|
--sync_slave_with_master
|
|
|
|
--connection slave
|
|
CREATE DATABASE db2;
|
|
CREATE FUNCTION db2.get_value(a INT) RETURNS int(2) RETURN 0;
|
|
|
|
--echo #
|
|
--echo # Test Insert_rows_log_event
|
|
|
|
--connection slave
|
|
DELIMITER //;
|
|
CREATE TRIGGER tr_ins BEFORE INSERT ON t1 FOR EACH ROW BEGIN
|
|
DECLARE a INT;
|
|
SET a = db2.get_value(1);
|
|
END//
|
|
DELIMITER ;//
|
|
|
|
--connection master
|
|
INSERT INTO t1 VALUES (1);
|
|
--sync_slave_with_master
|
|
|
|
--connection slave
|
|
DROP TRIGGER tr_ins;
|
|
|
|
|
|
--echo #
|
|
--echo # Test Update_rows_log_event
|
|
--connection master
|
|
--let $row_val=5
|
|
--eval INSERT INTO t1 VALUES ($row_val)
|
|
--sync_slave_with_master
|
|
|
|
--connection slave
|
|
DELIMITER //;
|
|
CREATE TRIGGER tr_upd BEFORE UPDATE ON t1 FOR EACH ROW BEGIN
|
|
DECLARE a INT;
|
|
SET a = db2.get_value(1);
|
|
END//
|
|
DELIMITER ;//
|
|
|
|
--connection master
|
|
--eval UPDATE t1 SET a=a+1 WHERE a=$row_val
|
|
--sync_slave_with_master
|
|
|
|
--connection slave
|
|
DROP TRIGGER tr_upd;
|
|
|
|
|
|
--echo #
|
|
--echo # Test Delete_rows_log_event
|
|
--connection master
|
|
--let $row_val=7
|
|
--eval INSERT INTO t1 VALUES ($row_val)
|
|
--sync_slave_with_master
|
|
|
|
--connection slave
|
|
DELIMITER //;
|
|
CREATE TRIGGER tr_del BEFORE DELETE ON t1 FOR EACH ROW BEGIN
|
|
DECLARE a INT;
|
|
SET a = db2.get_value(1);
|
|
END//
|
|
DELIMITER ;//
|
|
|
|
--connection master
|
|
--eval DELETE FROM t1 WHERE a=$row_val
|
|
--sync_slave_with_master
|
|
|
|
--connection slave
|
|
DROP TRIGGER tr_del;
|
|
|
|
|
|
--echo #
|
|
--echo # Cleanup
|
|
--connection slave
|
|
--eval SET GLOBAL slave_run_triggers_for_rbr=$old_slave_run_triggers
|
|
DROP DATABASE db2;
|
|
--connection master
|
|
DROP TABLE t1;
|
|
|
|
--source include/rpl_end.inc
|