Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions include/mysql/plugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -687,6 +687,7 @@ int thd_in_lock_tables(const MYSQL_THD thd);
int thd_tablespace_op(const MYSQL_THD thd);
long long thd_test_options(const MYSQL_THD thd, long long test_options);
int thd_sql_command(const MYSQL_THD thd);

struct DDL_options_st;
struct DDL_options_st *thd_ddl_options(const MYSQL_THD thd);
void thd_storage_lock_wait(MYSQL_THD thd, long long value);
Expand Down
47 changes: 47 additions & 0 deletions include/mysql/service_thd_binlog.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/* Copyright (c) 2026, MariaDB Corporation.

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */

#pragma once

#ifdef __cplusplus
extern "C" {
#endif

struct TABLE;
class Event_log;
class binlog_cache_data;

int thd_is_current_stmt_binlog_format_row(const MYSQL_THD thd);

int thd_rpl_use_binlog_events_for_fk_cascade(const MYSQL_THD thd);

void thd_binlog_mark_fk_cascade_events(MYSQL_THD thd);

int thd_binlog_update_row(MYSQL_THD thd, struct TABLE *table,
class Event_log *bin_log,
class binlog_cache_data *cache_data,
int is_trans, unsigned long row_image,
const unsigned char *before_record,
const unsigned char *after_record);

int thd_binlog_delete_row(MYSQL_THD thd, struct TABLE *table,
class Event_log *bin_log,
class binlog_cache_data *cache_data,
int is_trans, unsigned long row_image,
const unsigned char *before_record);

#ifdef __cplusplus
}
#endif
2 changes: 2 additions & 0 deletions include/mysql/service_wsrep.h
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ extern struct wsrep_service_st {
#define wsrep_report_bf_lock_wait(T,I) wsrep_service->wsrep_report_bf_lock_wait(T,I)
#define wsrep_thd_set_PA_unsafe(T) wsrep_service->wsrep_thd_set_PA_unsafe_func(T)
#define wsrep_get_domain_id(T) wsrep_service->wsrep_get_domain_id_func(T)
#define wsrep_emulate_binlog(T) wsrep_service->wsrep_emulate_binlog_func(T)
#else

#define MYSQL_SERVICE_WSREP_STATIC_INCLUDED
Expand Down Expand Up @@ -256,5 +257,6 @@ extern "C" void wsrep_report_bf_lock_wait(const THD *thd,
/* declare parallel applying unsafety for the THD */
extern "C" void wsrep_thd_set_PA_unsafe(MYSQL_THD thd);
extern "C" uint32 wsrep_get_domain_id();
extern "C" my_bool wsrep_emulate_binlog(const MYSQL_THD thd);
#endif
#endif /* MYSQL_SERVICE_WSREP_INCLUDED */
4 changes: 4 additions & 0 deletions mysql-test/main/mysqld--help.result
Original file line number Diff line number Diff line change
Expand Up @@ -1274,6 +1274,9 @@ The following specify which files/extra groups are read (specified before remain
play when stop slave is executed.
--rpl-semi-sync-slave-trace-level=#
The tracing level for semi-sync replication.
--rpl-use-binlog-events-for-fk-cascade
If enabled, the master will write row events for foreign
key cascade operations.
--safe-mode Skip some optimize stages (for testing). Deprecated.
--safe-user-create Don't allow new user creation by the user who has no
write privileges to the mysql.user table.
Expand Down Expand Up @@ -1952,6 +1955,7 @@ rpl-semi-sync-slave-delay-master FALSE
rpl-semi-sync-slave-enabled FALSE
rpl-semi-sync-slave-kill-conn-timeout 5
rpl-semi-sync-slave-trace-level 32
rpl-use-binlog-events-for-fk-cascade FALSE
safe-user-create FALSE
secure-auth TRUE
secure-file-priv (No default value)
Expand Down
85 changes: 85 additions & 0 deletions mysql-test/suite/rpl/r/rpl_fk_cascade_binlog_row.result
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
include/master-slave.inc
[connection master]
SET @old_binlog_format := @@session.binlog_format;
SET @old_default_storage_engine := @@session.default_storage_engine;
SET @old_rpl_use_binlog_events_for_fk_cascade := @@session.rpl_use_binlog_events_for_fk_cascade;
SET SESSION default_storage_engine=InnoDB;
SET SESSION binlog_format=ROW;
SET SESSION rpl_use_binlog_events_for_fk_cascade=0;
CREATE TABLE p (
id INT PRIMARY KEY,
v INT
) ENGINE=InnoDB;
CREATE TABLE c (
id INT PRIMARY KEY,
pid INT,
v INT,
KEY(pid),
CONSTRAINT fk FOREIGN KEY (pid) REFERENCES p(id)
ON DELETE CASCADE
ON UPDATE CASCADE
) ENGINE=InnoDB;
INSERT INTO p VALUES (1, 10);
INSERT INTO c VALUES (100, 1, 20);
UPDATE p SET id=2 WHERE id=1;
connection slave;
connection slave;
SELECT pid FROM c ORDER BY id;
pid
2
connection master;
DELETE FROM p WHERE id=2;
connection slave;
connection slave;
SELECT COUNT(*) FROM p;
COUNT(*)
0
SELECT COUNT(*) FROM c;
COUNT(*)
0
connection master;
FLUSH BINARY LOGS;
NOT FOUND /### UPDATE `test`.`c`/ in fk_cascade_binlog_row_off.sql
NOT FOUND /### DELETE FROM `test`.`c`/ in fk_cascade_binlog_row_off.sql
DROP TABLE c, p;
SET SESSION rpl_use_binlog_events_for_fk_cascade=1;
CREATE TABLE p (
id INT PRIMARY KEY,
v INT
) ENGINE=InnoDB;
CREATE TABLE c (
id INT PRIMARY KEY,
pid INT,
v INT,
KEY(pid),
CONSTRAINT fk FOREIGN KEY (pid) REFERENCES p(id)
ON DELETE CASCADE
ON UPDATE CASCADE
) ENGINE=InnoDB;
INSERT INTO p VALUES (1, 10);
INSERT INTO c VALUES (100, 1, 20);
UPDATE p SET id=2 WHERE id=1;
connection slave;
connection slave;
SELECT pid FROM c ORDER BY id;
pid
2
connection master;
DELETE FROM p WHERE id=2;
connection slave;
connection slave;
SELECT COUNT(*) FROM p;
COUNT(*)
0
SELECT COUNT(*) FROM c;
COUNT(*)
0
connection master;
FLUSH BINARY LOGS;
FOUND 1 /### UPDATE `test`.`c`/ in fk_cascade_binlog_row_on.sql
FOUND 1 /### DELETE FROM `test`.`c`/ in fk_cascade_binlog_row_on.sql
DROP TABLE c, p;
SET SESSION default_storage_engine=@old_default_storage_engine;
SET SESSION binlog_format=@old_binlog_format;
SET SESSION rpl_use_binlog_events_for_fk_cascade=@old_rpl_use_binlog_events_for_fk_cascade;
include/rpl_end.inc
61 changes: 61 additions & 0 deletions mysql-test/suite/rpl/r/rpl_fk_set_null_binlog_row.result
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
include/master-slave.inc
[connection master]
SET @old_binlog_format := @@session.binlog_format;
SET @old_default_storage_engine := @@session.default_storage_engine;
SET @old_rpl_use_binlog_events_for_fk_cascade := @@session.rpl_use_binlog_events_for_fk_cascade;
SET SESSION default_storage_engine=InnoDB;
SET SESSION binlog_format=ROW;
SET SESSION rpl_use_binlog_events_for_fk_cascade=0;
CREATE TABLE p (
id INT PRIMARY KEY,
v INT
) ENGINE=InnoDB;
CREATE TABLE c (
id INT PRIMARY KEY,
pid INT NULL,
v INT,
KEY(pid),
CONSTRAINT fk FOREIGN KEY (pid) REFERENCES p(id)
ON DELETE SET NULL
ON UPDATE SET NULL
) ENGINE=InnoDB;
INSERT INTO p VALUES (1, 10);
INSERT INTO c VALUES (100, 1, 20);
UPDATE p SET id=2 WHERE id=1;
connection slave;
SELECT pid FROM c ORDER BY id;
pid
NULL
connection master;
FLUSH BINARY LOGS;
NOT FOUND /### UPDATE `test`.`c`/ in fk_set_null_binlog_row_off.sql
DROP TABLE c, p;
SET SESSION rpl_use_binlog_events_for_fk_cascade=1;
CREATE TABLE p (
id INT PRIMARY KEY,
v INT
) ENGINE=InnoDB;
CREATE TABLE c (
id INT PRIMARY KEY,
pid INT NULL,
v INT,
KEY(pid),
CONSTRAINT fk FOREIGN KEY (pid) REFERENCES p(id)
ON DELETE SET NULL
ON UPDATE SET NULL
) ENGINE=InnoDB;
INSERT INTO p VALUES (1, 10);
INSERT INTO c VALUES (100, 1, 20);
UPDATE p SET id=2 WHERE id=1;
connection slave;
SELECT pid FROM c ORDER BY id;
pid
NULL
connection master;
FLUSH BINARY LOGS;
FOUND 1 /### UPDATE `test`.`c`/ in fk_set_null_binlog_row_on.sql
DROP TABLE c, p;
SET SESSION default_storage_engine=@old_default_storage_engine;
SET SESSION binlog_format=@old_binlog_format;
SET SESSION rpl_use_binlog_events_for_fk_cascade=@old_rpl_use_binlog_events_for_fk_cascade;
include/rpl_end.inc
133 changes: 133 additions & 0 deletions mysql-test/suite/rpl/t/rpl_fk_cascade_binlog_row.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
--source include/have_innodb.inc
--source include/have_log_bin.inc
--source include/have_binlog_format_row.inc

--source include/master-slave.inc

SET @old_binlog_format := @@session.binlog_format;
SET @old_default_storage_engine := @@session.default_storage_engine;
SET @old_rpl_use_binlog_events_for_fk_cascade := @@session.rpl_use_binlog_events_for_fk_cascade;

SET SESSION default_storage_engine=InnoDB;
SET SESSION binlog_format=ROW;

# Phase A: feature OFF (no explicit FK cascade row events in binlog)
SET SESSION rpl_use_binlog_events_for_fk_cascade=0;

CREATE TABLE p (
id INT PRIMARY KEY,
v INT
) ENGINE=InnoDB;

CREATE TABLE c (
id INT PRIMARY KEY,
pid INT,
v INT,
KEY(pid),
CONSTRAINT fk FOREIGN KEY (pid) REFERENCES p(id)
ON DELETE CASCADE
ON UPDATE CASCADE
) ENGINE=InnoDB;

INSERT INTO p VALUES (1, 10);
INSERT INTO c VALUES (100, 1, 20);

UPDATE p SET id=2 WHERE id=1;

sync_slave_with_master;
connection slave;
SELECT pid FROM c ORDER BY id;

connection master;
DELETE FROM p WHERE id=2;

sync_slave_with_master;
connection slave;
SELECT COUNT(*) FROM p;
SELECT COUNT(*) FROM c;

connection master;

--let $binlog = query_get_value(SHOW MASTER STATUS, File, 1)
--let $datadir = `SELECT @@datadir`

FLUSH BINARY LOGS;

--exec $MYSQL_BINLOG --verbose --verbose --base64-output=DECODE-ROWS $datadir/$binlog > $MYSQLTEST_VARDIR/tmp/fk_cascade_binlog_row_off.sql

--let SEARCH_PATTERN= ### UPDATE `test`.`c`
--let SEARCH_FILE= $MYSQLTEST_VARDIR/tmp/fk_cascade_binlog_row_off.sql
--let SEARCH_ABORT= FOUND
--source include/search_pattern_in_file.inc

--let SEARCH_PATTERN= ### DELETE FROM `test`.`c`
--let SEARCH_FILE= $MYSQLTEST_VARDIR/tmp/fk_cascade_binlog_row_off.sql
--let SEARCH_ABORT= FOUND
--source include/search_pattern_in_file.inc

DROP TABLE c, p;

# Phase B: feature ON (explicit FK cascade row events in binlog)
SET SESSION rpl_use_binlog_events_for_fk_cascade=1;

CREATE TABLE p (
id INT PRIMARY KEY,
v INT
) ENGINE=InnoDB;

CREATE TABLE c (
id INT PRIMARY KEY,
pid INT,
v INT,
KEY(pid),
CONSTRAINT fk FOREIGN KEY (pid) REFERENCES p(id)
ON DELETE CASCADE
ON UPDATE CASCADE
) ENGINE=InnoDB;

INSERT INTO p VALUES (1, 10);
INSERT INTO c VALUES (100, 1, 20);

UPDATE p SET id=2 WHERE id=1;

sync_slave_with_master;
connection slave;
SELECT pid FROM c ORDER BY id;

connection master;
DELETE FROM p WHERE id=2;

sync_slave_with_master;
connection slave;
SELECT COUNT(*) FROM p;
SELECT COUNT(*) FROM c;

connection master;

--let $binlog = query_get_value(SHOW MASTER STATUS, File, 1)
--let $datadir = `SELECT @@datadir`

FLUSH BINARY LOGS;

--exec $MYSQL_BINLOG --verbose --verbose --base64-output=DECODE-ROWS $datadir/$binlog > $MYSQLTEST_VARDIR/tmp/fk_cascade_binlog_row_on.sql

--let SEARCH_PATTERN= ### UPDATE `test`.`c`
--let SEARCH_FILE= $MYSQLTEST_VARDIR/tmp/fk_cascade_binlog_row_on.sql
--let SEARCH_ABORT= NOT FOUND|FOUND 0|FOUND [2-9]
--source include/search_pattern_in_file.inc

--let SEARCH_PATTERN= ### DELETE FROM `test`.`c`
--let SEARCH_FILE= $MYSQLTEST_VARDIR/tmp/fk_cascade_binlog_row_on.sql
--let SEARCH_ABORT= NOT FOUND|FOUND 0|FOUND [2-9]
--source include/search_pattern_in_file.inc

DROP TABLE c, p;

SET SESSION default_storage_engine=@old_default_storage_engine;
SET SESSION binlog_format=@old_binlog_format;
SET SESSION rpl_use_binlog_events_for_fk_cascade=@old_rpl_use_binlog_events_for_fk_cascade;

--remove_file $MYSQLTEST_VARDIR/tmp/fk_cascade_binlog_row_off.sql
--remove_file $MYSQLTEST_VARDIR/tmp/fk_cascade_binlog_row_on.sql

--source include/rpl_end.inc
Loading
Loading