Merge 10.2 into 10.3 (except MDEV-17556)

The fix of MDEV-17556 (commit e25623e78a3efde05e30070dc7362f8dc0d8c459
and commit 61a362c9493df63dc588fcb71409537ae56ab9c8) has been
omitted due to conflicts and will have to be applied separately later.
This commit is contained in:
Marko Mäkelä 2021-01-11 09:41:54 +02:00
commit 5a1a714187
22 changed files with 997 additions and 119 deletions

View File

@ -182,7 +182,7 @@ IF(WIN32)
MARK_AS_ADVANCED(SIGNCODE) MARK_AS_ADVANCED(SIGNCODE)
IF(SIGNCODE) IF(SIGNCODE)
SET(SIGNTOOL_PARAMETERS SET(SIGNTOOL_PARAMETERS
/a /t http://timestamp.verisign.com/scripts/timstamp.dll /a /t http://timestamp.globalsign.com/scripts/timstamp.dll
CACHE STRING "parameters for signtool (list)") CACHE STRING "parameters for signtool (list)")
FIND_PROGRAM(SIGNTOOL_EXECUTABLE signtool FIND_PROGRAM(SIGNTOOL_EXECUTABLE signtool
PATHS "$ENV{ProgramFiles}/Microsoft SDKs/Windows/v7.0A/bin" PATHS "$ENV{ProgramFiles}/Microsoft SDKs/Windows/v7.0A/bin"

View File

@ -9,6 +9,9 @@ CREATE TABLE t1 (f1 INTEGER) Engine=InnoDB;
INSERT INTO t1 VALUES (1), (1); INSERT INTO t1 VALUES (1), (1);
--connection node_2 --connection node_2
--let $wait_condition = SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.INNODB_SYS_TABLES WHERE NAME LIKE 'test/t1';
--source include/wait_condition.inc
SET SESSION wsrep_OSU_method = "RSU"; SET SESSION wsrep_OSU_method = "RSU";
--error ER_DUP_ENTRY --error ER_DUP_ENTRY
ALTER TABLE t1 ADD PRIMARY KEY (f1); ALTER TABLE t1 ADD PRIMARY KEY (f1);

View File

@ -9,6 +9,9 @@ CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
INSERT INTO t1 VALUES (1); INSERT INTO t1 VALUES (1);
--connection node_2 --connection node_2
--let $wait_condition = SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.INNODB_SYS_TABLES WHERE NAME LIKE 'test/t1';
--source include/wait_condition.inc
ALTER TABLE t1 ADD COLUMN f2 INTEGER; ALTER TABLE t1 ADD COLUMN f2 INTEGER;
INSERT INTO t1 VALUES (2, 3); INSERT INTO t1 VALUES (2, 3);

View File

@ -10,6 +10,9 @@ CREATE TABLE t1 (id INT PRIMARY KEY) ENGINE=InnoDB;
INSERT INTO t1 VALUES (1); INSERT INTO t1 VALUES (1);
--connection node_2 --connection node_2
--let $wait_condition = SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.INNODB_SYS_TABLES WHERE NAME LIKE 'test/t1';
--source include/wait_condition.inc
ALTER TABLE t1 ADD COLUMN f2 INTEGER, LOCK=SHARED; ALTER TABLE t1 ADD COLUMN f2 INTEGER, LOCK=SHARED;
--connection node_1 --connection node_1

View File

@ -14,6 +14,9 @@ CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) Engine=InnoDB;
INSERT INTO t1 VALUES (1); INSERT INTO t1 VALUES (1);
--connection node_2 --connection node_2
--let $wait_condition = SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.INNODB_SYS_TABLES WHERE NAME LIKE 'test/t1';
--source include/wait_condition.inc
TRUNCATE TABLE t1; TRUNCATE TABLE t1;
SELECT COUNT(*) = 0 FROM t1; SELECT COUNT(*) = 0 FROM t1;

View File

@ -67,6 +67,9 @@ CREATE TEMPORARY TABLE t1 (f1 INTEGER) ENGINE=InnoDB;
INSERT INTO t1 VALUES (2); INSERT INTO t1 VALUES (2);
--connection node_2 --connection node_2
--let $wait_condition = SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.INNODB_SYS_TABLES WHERE NAME LIKE 'test/t1';
--source include/wait_condition.inc
TRUNCATE TABLE t1; TRUNCATE TABLE t1;
SELECT COUNT(*) = 0 FROM t1; SELECT COUNT(*) = 0 FROM t1;

View File

@ -227,6 +227,7 @@ set global server_audit_logging= on;
disconnect cn1; disconnect cn1;
drop user user1@localhost; drop user user1@localhost;
set global server_audit_events=''; set global server_audit_events='';
set global server_audit_incl_users='root, plug_dest';
CREATE USER plug IDENTIFIED WITH 'test_plugin_server' AS 'plug_dest'; CREATE USER plug IDENTIFIED WITH 'test_plugin_server' AS 'plug_dest';
CREATE USER plug_dest IDENTIFIED BY 'plug_dest_passwd'; CREATE USER plug_dest IDENTIFIED BY 'plug_dest_passwd';
connect(localhost,plug,plug_dest,test,MYSQL_PORT,MYSQL_SOCK); connect(localhost,plug,plug_dest,test,MYSQL_PORT,MYSQL_SOCK);
@ -277,7 +278,7 @@ server_audit_file_path
server_audit_file_rotate_now OFF server_audit_file_rotate_now OFF
server_audit_file_rotate_size 1000000 server_audit_file_rotate_size 1000000
server_audit_file_rotations 9 server_audit_file_rotations 9
server_audit_incl_users root server_audit_incl_users root, plug_dest
server_audit_logging ON server_audit_logging ON
server_audit_mode 1 server_audit_mode 1
server_audit_output_type file server_audit_output_type file
@ -419,6 +420,7 @@ TIME,HOSTNAME,root,localhost,ID,ID,WRITE,mysql,procs_priv,
TIME,HOSTNAME,root,localhost,ID,ID,WRITE,mysql,proxies_priv, TIME,HOSTNAME,root,localhost,ID,ID,WRITE,mysql,proxies_priv,
TIME,HOSTNAME,root,localhost,ID,ID,WRITE,mysql,roles_mapping, TIME,HOSTNAME,root,localhost,ID,ID,WRITE,mysql,roles_mapping,
TIME,HOSTNAME,root,localhost,ID,ID,QUERY,sa_db,'set global server_audit_events=\'\'',0 TIME,HOSTNAME,root,localhost,ID,ID,QUERY,sa_db,'set global server_audit_events=\'\'',0
TIME,HOSTNAME,root,localhost,ID,ID,QUERY,sa_db,'set global server_audit_incl_users=\'root, plug_dest\'',0
TIME,HOSTNAME,root,localhost,ID,ID,WRITE,mysql,user, TIME,HOSTNAME,root,localhost,ID,ID,WRITE,mysql,user,
TIME,HOSTNAME,root,localhost,ID,ID,WRITE,mysql,db, TIME,HOSTNAME,root,localhost,ID,ID,WRITE,mysql,db,
TIME,HOSTNAME,root,localhost,ID,ID,WRITE,mysql,tables_priv, TIME,HOSTNAME,root,localhost,ID,ID,WRITE,mysql,tables_priv,
@ -442,6 +444,7 @@ TIME,HOSTNAME,root,localhost,ID,ID,WRITE,mysql,proxies_priv,
TIME,HOSTNAME,root,localhost,ID,ID,QUERY,sa_db,'GRANT PROXY ON plug_dest TO plug',0 TIME,HOSTNAME,root,localhost,ID,ID,QUERY,sa_db,'GRANT PROXY ON plug_dest TO plug',0
TIME,HOSTNAME,plug,localhost,ID,0,CONNECT,test,,0 TIME,HOSTNAME,plug,localhost,ID,0,CONNECT,test,,0
TIME,HOSTNAME,plug,localhost,ID,0,PROXY_CONNECT,test,`plug_dest`@`%`,0 TIME,HOSTNAME,plug,localhost,ID,0,PROXY_CONNECT,test,`plug_dest`@`%`,0
TIME,HOSTNAME,plug,localhost,ID,ID,QUERY,test,'select USER(),CURRENT_USER()',0
TIME,HOSTNAME,plug,localhost,ID,0,DISCONNECT,test,,0 TIME,HOSTNAME,plug,localhost,ID,0,DISCONNECT,test,,0
TIME,HOSTNAME,root,localhost,ID,ID,WRITE,mysql,user, TIME,HOSTNAME,root,localhost,ID,ID,WRITE,mysql,user,
TIME,HOSTNAME,root,localhost,ID,ID,WRITE,mysql,db, TIME,HOSTNAME,root,localhost,ID,ID,WRITE,mysql,db,

View File

@ -173,6 +173,7 @@ source include/wait_until_count_sessions.inc;
drop user user1@localhost; drop user user1@localhost;
set global server_audit_events=''; set global server_audit_events='';
set global server_audit_incl_users='root, plug_dest';
CREATE USER plug IDENTIFIED WITH 'test_plugin_server' AS 'plug_dest'; CREATE USER plug IDENTIFIED WITH 'test_plugin_server' AS 'plug_dest';
CREATE USER plug_dest IDENTIFIED BY 'plug_dest_passwd'; CREATE USER plug_dest IDENTIFIED BY 'plug_dest_passwd';

View File

@ -0,0 +1,380 @@
include/master-slave.inc
[connection master]
#
# Test case 1: KEY on a virtual column with ON DELETE CASCADE
#
CREATE TABLE t1 (id INT NOT NULL PRIMARY KEY) ENGINE=InnoDB;
INSERT INTO t1 VALUES (1),(2),(3);
CREATE TABLE t2 (id INT NOT NULL PRIMARY KEY,
t1_id INT NOT NULL,
v_col INT AS (t1_id+1) VIRTUAL, KEY (v_col), KEY (t1_id),
CONSTRAINT a FOREIGN KEY (t1_id) REFERENCES t1 (id) ON DELETE CASCADE
) ENGINE=InnoDB;
INSERT INTO t2 VALUES (90,1,NULL);
INSERT INTO t2 VALUES (91,2,default);
DELETE FROM t1 WHERE id=1;
connection slave;
#
# Verify data consistency on slave
#
include/diff_tables.inc [master:test.t1, slave:test.t1]
include/diff_tables.inc [master:test.t2, slave:test.t2]
connection master;
DROP TABLE t2,t1;
connection slave;
#
# Test case 2: Verify "ON DELETE CASCADE" for parent->child->child scenario
# Parent table: users
# Child tables: matchmaking_groups, matchmaking_group_users
# Parent table: matchmaking_groups
# Child tables: matchmaking_group_users, matchmaking_group_maps
#
# Deleting a row from parent table should be reflected in
# child tables.
# matchmaking_groups->matchmaking_group_users->matchmaking_group_maps
# users->matchmaking_group_users->matchmaking_group_maps
#
connection master;
CREATE TABLE users (id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(32) NOT NULL DEFAULT ''
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE matchmaking_groups (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
host_user_id INT UNSIGNED NOT NULL UNIQUE,
v_col INT AS (host_user_id+1) VIRTUAL, KEY (v_col),
CONSTRAINT FOREIGN KEY (host_user_id) REFERENCES users (id)
ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE matchmaking_group_users (
matchmaking_group_id BIGINT UNSIGNED NOT NULL,
user_id INT UNSIGNED NOT NULL,
v_col1 int as (user_id+1) virtual, KEY (v_col1),
PRIMARY KEY (matchmaking_group_id,user_id),
UNIQUE KEY user_id (user_id),
CONSTRAINT FOREIGN KEY (matchmaking_group_id)
REFERENCES matchmaking_groups (id) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT FOREIGN KEY (user_id)
REFERENCES users (id) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE matchmaking_group_maps (
matchmaking_group_id BIGINT UNSIGNED NOT NULL,
map_id TINYINT UNSIGNED NOT NULL,
v_col2 INT AS (map_id+1) VIRTUAL, KEY (v_col2),
PRIMARY KEY (matchmaking_group_id,map_id),
CONSTRAINT FOREIGN KEY (matchmaking_group_id)
REFERENCES matchmaking_groups (id) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
connection slave;
connection master;
INSERT INTO users VALUES (NULL,'foo'),(NULL,'bar');
INSERT INTO matchmaking_groups VALUES (10,1,default),(11,2,default);
INSERT INTO matchmaking_group_users VALUES (10,1,default),(11,2,default);
INSERT INTO matchmaking_group_maps VALUES (10,55,default),(11,66,default);
DELETE FROM matchmaking_groups WHERE id = 10;
connection slave;
#
# No rows should be returned as ON DELETE CASCASE should have removed
# corresponding rows from child tables. There should not any mismatch
# of 'id' field between parent->child.
#
SELECT * FROM matchmaking_group_users WHERE matchmaking_group_id NOT IN (SELECT id FROM matchmaking_groups);
matchmaking_group_id user_id v_col1
SELECT * FROM matchmaking_group_maps WHERE matchmaking_group_id NOT IN (SELECT id FROM matchmaking_groups);
matchmaking_group_id map_id v_col2
#
# Rows with id=11 should be present
#
SELECT * FROM matchmaking_group_users;
matchmaking_group_id user_id v_col1
11 2 3
SELECT * FROM matchmaking_group_maps;
matchmaking_group_id map_id v_col2
11 66 67
connection master;
DELETE FROM users WHERE id = 2;
connection slave;
#
# No rows should be present in both the child tables
#
SELECT * FROM matchmaking_group_users;
matchmaking_group_id user_id v_col1
SELECT * FROM matchmaking_group_maps;
matchmaking_group_id map_id v_col2
connection master;
DROP TABLE matchmaking_group_maps, matchmaking_group_users, matchmaking_groups, users;
connection slave;
#
# Test case 3: KEY on a virtual column with ON UPDATE CASCADE
#
connection master;
CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY, b INT NOT NULL) ENGINE=InnoDB;
INSERT INTO t1 VALUES (1, 80);
CREATE TABLE t2 (a INT KEY, b INT,
v_col int as (b+1) virtual, KEY (v_col),
CONSTRAINT b FOREIGN KEY (b) REFERENCES t1(a) ON UPDATE CASCADE
) ENGINE=InnoDB;
INSERT INTO t2 VALUES (51, 1, default);
connection slave;
connection master;
UPDATE t1 SET a = 50 WHERE a = 1;
#
# Master: Verify that ON UPDATE CASCADE works fine
# old_row: (51, 1, 2) ON UPDATE New_row: (51, 50, 51)
#
SELECT * FROM t2 WHERE b=50;
a b v_col
51 50 51
connection slave;
#
# Slave: Verify that ON UPDATE CASCADE works fine
# old_row: (51, 1, 2) ON UPDATE New_row: (51, 50, 51)
#
SELECT * FROM t2 WHERE b=50;
a b v_col
51 50 51
connection master;
DROP TABLE t2, t1;
connection slave;
#
# Test case 4: Define triggers on master, their results should be
# replicated as part of row events and they should be
# applied on slave with the default
# slave_run_triggers_for_rbr=NO
#
connection master;
CREATE TABLE t1 (id INT NOT NULL PRIMARY KEY) ENGINE=InnoDB;
CREATE TABLE t2 (count INT NOT NULL) ENGINE=InnoDB;
CREATE TRIGGER trg AFTER INSERT ON t1 FOR EACH ROW INSERT INTO t2 VALUES (1);
INSERT INTO t1 VALUES (2),(3);
connection slave;
SHOW GLOBAL VARIABLES LIKE 'slave_run_triggers_for_rbr';
Variable_name Value
slave_run_triggers_for_rbr NO
#
# As two rows are inserted in table 't1', two rows should get inserted
# into table 't2' as part of trigger.
#
include/assert.inc [Table t2 should have two rows.]
connection master;
DROP TABLE t1,t2;
connection slave;
#
# Test case 5: Define triggers + Foreign Keys on master, their results
# should be replicated as part of row events and master
# and slave should be in sync.
#
connection master;
CREATE TABLE t1 (id INT NOT NULL PRIMARY KEY) ENGINE=InnoDB;
CREATE TABLE t2 (t1_id INT NOT NULL,
v_col INT AS (t1_id+1) VIRTUAL, KEY (v_col), KEY (t1_id),
CONSTRAINT a FOREIGN KEY (t1_id) REFERENCES t1 (id) ON DELETE CASCADE
) ENGINE=InnoDB;
CREATE TABLE t3 (count INT NOT NULL) ENGINE=InnoDB;
CREATE TRIGGER trg AFTER INSERT ON t1 FOR EACH ROW INSERT INTO t3 VALUES (1);
INSERT INTO t1 VALUES (2),(3);
INSERT INTO t2 VALUES (2, default), (3, default);
connection slave;
#
# As two rows are inserted in table 't1', two rows should get inserted
# into table 't3' as part of trigger.
#
include/assert.inc [Table t3 should have two rows.]
#
# Verify ON DELETE CASCASE correctness
#
connection master;
DELETE FROM t1 WHERE id=2;
connection slave;
connection master;
include/diff_tables.inc [master:test.t1, slave:test.t1]
include/diff_tables.inc [master:test.t2, slave:test.t2]
include/diff_tables.inc [master:test.t3, slave:test.t3]
DROP TABLE t3,t2,t1;
connection slave;
#
# Test case 6: Triggers are present only on slave and
# 'slave_run_triggers_for_rbr=NO'
#
connection slave;
SET @save_slave_run_triggers_for_rbr= @@GLOBAL.slave_run_triggers_for_rbr;
SET GLOBAL slave_run_triggers_for_rbr= NO;;
SHOW GLOBAL VARIABLES LIKE '%slave_run_triggers_for_rbr%';
Variable_name Value
slave_run_triggers_for_rbr NO
connection master;
CREATE TABLE t1 (id INT NOT NULL PRIMARY KEY) ENGINE=InnoDB;
CREATE TABLE t2 (t1_id INT NOT NULL,
v_col INT AS (t1_id+1) VIRTUAL, KEY (v_col),
KEY (t1_id), CONSTRAINT a FOREIGN KEY (t1_id) REFERENCES t1 (id) ON DELETE CASCADE
) ENGINE=InnoDB;
CREATE TABLE t3 (count INT NOT NULL) ENGINE=InnoDB;
connection slave;
CREATE TRIGGER trg AFTER INSERT ON t2 FOR EACH ROW INSERT INTO t3 VALUES (1);
connection master;
INSERT INTO t1 VALUES (2),(3);
INSERT INTO t2 VALUES (2, default), (3, default);
connection slave;
#
# Count must be 0
#
include/assert.inc [Table t3 should have zero rows.]
connection master;
DELETE FROM t1 WHERE id=2;
connection slave;
SET GLOBAL slave_run_triggers_for_rbr= @save_slave_run_triggers_for_rbr;
#
# Verify t1, t2 are consistent on slave.
#
include/diff_tables.inc [master:test.t1, slave:test.t1]
include/diff_tables.inc [master:test.t2, slave:test.t2]
connection master;
DROP TABLE t3,t2,t1;
connection slave;
#
# Test case 7: Triggers are present only on slave and
# 'slave_run_triggers_for_rbr=YES'
#
connection slave;
SET @save_slave_run_triggers_for_rbr= @@GLOBAL.slave_run_triggers_for_rbr;
SET GLOBAL slave_run_triggers_for_rbr= YES;;
SHOW GLOBAL VARIABLES LIKE '%slave_run_triggers_for_rbr%';
Variable_name Value
slave_run_triggers_for_rbr YES
connection master;
CREATE TABLE t1 (id INT NOT NULL PRIMARY KEY) ENGINE=InnoDB;
CREATE TABLE t2 (t1_id INT NOT NULL,
v_col INT AS (t1_id+1) VIRTUAL, KEY (v_col),
KEY (t1_id), CONSTRAINT a FOREIGN KEY (t1_id) REFERENCES t1 (id) ON DELETE CASCADE
) ENGINE=InnoDB;
CREATE TABLE t3 (count INT NOT NULL) ENGINE=InnoDB;
connection slave;
CREATE TRIGGER trg AFTER INSERT ON t2 FOR EACH ROW INSERT INTO t3 VALUES (1);
connection master;
INSERT INTO t1 VALUES (2),(3);
INSERT INTO t2 VALUES (2, default), (3, default);
connection slave;
#
# Count must be 2
#
include/assert.inc [Table t3 should have two rows.]
connection master;
DELETE FROM t1 WHERE id=2;
connection slave;
SET GLOBAL slave_run_triggers_for_rbr= @save_slave_run_triggers_for_rbr;
#
# Verify t1, t2 are consistent on slave.
#
include/diff_tables.inc [master:test.t1, slave:test.t1]
include/diff_tables.inc [master:test.t2, slave:test.t2]
connection master;
DROP TABLE t3,t2,t1;
connection slave;
#
# Test case 8: Triggers and Foreign Keys are present only on slave and
# 'slave_run_triggers_for_rbr=NO'
#
connection slave;
SET @save_slave_run_triggers_for_rbr= @@GLOBAL.slave_run_triggers_for_rbr;
SET GLOBAL slave_run_triggers_for_rbr= NO;;
SHOW GLOBAL VARIABLES LIKE '%slave_run_triggers_for_rbr%';
Variable_name Value
slave_run_triggers_for_rbr NO
connection master;
CREATE TABLE t1 (id INT NOT NULL PRIMARY KEY) ENGINE=InnoDB;
SET sql_log_bin=0;
CREATE TABLE t2 (t1_id INT NOT NULL,v_col INT AS (t1_id+1) VIRTUAL) ENGINE=INNODB;
SET sql_log_bin=1;
CREATE TABLE t3 (count INT NOT NULL) ENGINE=InnoDB;
connection slave;
CREATE TABLE t2 (t1_id INT NOT NULL,
v_col INT AS (t1_id+1) VIRTUAL, KEY (v_col), KEY (t1_id),
CONSTRAINT a FOREIGN KEY (t1_id) REFERENCES t1 (id) ON DELETE CASCADE
) ENGINE=InnoDB;
CREATE TRIGGER trg AFTER INSERT ON t2 FOR EACH ROW INSERT INTO t3 VALUES (1);
connection master;
INSERT INTO t1 VALUES (2),(3);
INSERT INTO t2 VALUES (2, default), (3, default);
connection slave;
#
# Count must be 0
#
include/assert.inc [Table t3 should have zero rows.]
connection master;
DELETE FROM t1 WHERE id=2;
# t1: Should have one row
SELECT * FROM t1;
id
3
# t2: Should have two rows
SELECT * FROM t2;
t1_id v_col
2 3
3 4
connection slave;
# t1: Should have one row
SELECT * FROM t1;
id
3
# t2: Should have one row on slave due to ON DELETE CASCASE
SELECT * FROM t2;
t1_id v_col
3 4
SET GLOBAL slave_run_triggers_for_rbr= @save_slave_run_triggers_for_rbr;
connection master;
DROP TABLE t3,t2,t1;
connection slave;
#
# Test case 9: Triggers are Foreign Keys are present only on slave and
# 'slave_run_triggers_for_rbr=YES'
#
connection slave;
SET @save_slave_run_triggers_for_rbr= @@GLOBAL.slave_run_triggers_for_rbr;
SET GLOBAL slave_run_triggers_for_rbr= YES;;
SHOW GLOBAL VARIABLES LIKE '%slave_run_triggers_for_rbr%';
Variable_name Value
slave_run_triggers_for_rbr YES
connection master;
CREATE TABLE t1 (id INT NOT NULL PRIMARY KEY) ENGINE=InnoDB;
SET sql_log_bin=0;
CREATE TABLE t2 (t1_id INT NOT NULL,v_col INT AS (t1_id+1) VIRTUAL) ENGINE=INNODB;
SET sql_log_bin=1;
CREATE TABLE t3 (count INT NOT NULL) ENGINE=InnoDB;
connection slave;
CREATE TABLE t2 (t1_id INT NOT NULL,
v_col INT AS (t1_id+1) VIRTUAL, KEY (v_col), KEY (t1_id),
CONSTRAINT a FOREIGN KEY (t1_id) REFERENCES t1 (id) ON DELETE CASCADE
) ENGINE=InnoDB;
CREATE TRIGGER trg AFTER INSERT ON t2 FOR EACH ROW INSERT INTO t3 VALUES (1);
connection master;
INSERT INTO t1 VALUES (2),(3);
INSERT INTO t2 VALUES (2, default), (3, default);
connection slave;
#
# Count must be 2
#
include/assert.inc [Table t3 should have two rows.]
connection master;
DELETE FROM t1 WHERE id=2;
# t1: Should have one row
SELECT * FROM t1;
id
3
# t2: Should have two rows
SELECT * FROM t2;
t1_id v_col
2 3
3 4
connection slave;
# t1: Should have one row
SELECT * FROM t1;
id
3
# t2: Should have one row on slave due to ON DELETE CASCASE
SELECT * FROM t2;
t1_id v_col
3 4
SET GLOBAL slave_run_triggers_for_rbr= @save_slave_run_triggers_for_rbr;
connection master;
DROP TABLE t3,t2,t1;
connection slave;
include/rpl_end.inc

View File

@ -0,0 +1,425 @@
# ==== Purpose ====
#
# Test verifies that, slave doesn't report any assert on UPDATE or DELETE of
# row which tries to update the virtual columns with associated KEYs.
#
# Test scenarios are listed below.
# 1) KEY on a virtual column with ON DELETE CASCADE
# 2) Verify "ON DELETE CASCADE" for parent->child->child scenario
# 3) KEY on a virtual column with ON UPDATE CASCADE
# 4) Define triggers on master, their results should be replicated
# as part of row events and they should be applied on slave with
# the default slave_run_triggers_for_rbr=NO
# 5) Define triggers + Foreign Keys on master, their results should be
# replicated as part of row events and master and slave should be in sync.
# 6) Triggers are present only on slave and 'slave_run_triggers_for_rbr=NO'
# 7) Triggers are present only on slave and 'slave_run_triggers_for_rbr=YES'
# 8) Triggers and Foreign Keys are present only on slave and
# 'slave_run_triggers_for_rbr=NO'
# 9) Triggers are Foreign Keys are present only on slave and
# 'slave_run_triggers_for_rbr=YES'
#
# ==== References ====
#
# MDEV-23033: All slaves crash once in ~24 hours and loop restart with signal 11
#
--source include/have_binlog_format_row.inc
--source include/have_innodb.inc
--source include/master-slave.inc
--echo #
--echo # Test case 1: KEY on a virtual column with ON DELETE CASCADE
--echo #
CREATE TABLE t1 (id INT NOT NULL PRIMARY KEY) ENGINE=InnoDB;
INSERT INTO t1 VALUES (1),(2),(3);
CREATE TABLE t2 (id INT NOT NULL PRIMARY KEY,
t1_id INT NOT NULL,
v_col INT AS (t1_id+1) VIRTUAL, KEY (v_col), KEY (t1_id),
CONSTRAINT a FOREIGN KEY (t1_id) REFERENCES t1 (id) ON DELETE CASCADE
) ENGINE=InnoDB;
INSERT INTO t2 VALUES (90,1,NULL);
INSERT INTO t2 VALUES (91,2,default);
# Following query results in an assert on slave
DELETE FROM t1 WHERE id=1;
--sync_slave_with_master
--echo #
--echo # Verify data consistency on slave
--echo #
--let $diff_tables= master:test.t1, slave:test.t1
--source include/diff_tables.inc
--let $diff_tables= master:test.t2, slave:test.t2
--source include/diff_tables.inc
--connection master
DROP TABLE t2,t1;
--sync_slave_with_master
--echo #
--echo # Test case 2: Verify "ON DELETE CASCADE" for parent->child->child scenario
--echo # Parent table: users
--echo # Child tables: matchmaking_groups, matchmaking_group_users
--echo # Parent table: matchmaking_groups
--echo # Child tables: matchmaking_group_users, matchmaking_group_maps
--echo #
--echo # Deleting a row from parent table should be reflected in
--echo # child tables.
--echo # matchmaking_groups->matchmaking_group_users->matchmaking_group_maps
--echo # users->matchmaking_group_users->matchmaking_group_maps
--echo #
--connection master
CREATE TABLE users (id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(32) NOT NULL DEFAULT ''
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE matchmaking_groups (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
host_user_id INT UNSIGNED NOT NULL UNIQUE,
v_col INT AS (host_user_id+1) VIRTUAL, KEY (v_col),
CONSTRAINT FOREIGN KEY (host_user_id) REFERENCES users (id)
ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE matchmaking_group_users (
matchmaking_group_id BIGINT UNSIGNED NOT NULL,
user_id INT UNSIGNED NOT NULL,
v_col1 int as (user_id+1) virtual, KEY (v_col1),
PRIMARY KEY (matchmaking_group_id,user_id),
UNIQUE KEY user_id (user_id),
CONSTRAINT FOREIGN KEY (matchmaking_group_id)
REFERENCES matchmaking_groups (id) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT FOREIGN KEY (user_id)
REFERENCES users (id) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE matchmaking_group_maps (
matchmaking_group_id BIGINT UNSIGNED NOT NULL,
map_id TINYINT UNSIGNED NOT NULL,
v_col2 INT AS (map_id+1) VIRTUAL, KEY (v_col2),
PRIMARY KEY (matchmaking_group_id,map_id),
CONSTRAINT FOREIGN KEY (matchmaking_group_id)
REFERENCES matchmaking_groups (id) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--sync_slave_with_master
--connection master
INSERT INTO users VALUES (NULL,'foo'),(NULL,'bar');
INSERT INTO matchmaking_groups VALUES (10,1,default),(11,2,default);
INSERT INTO matchmaking_group_users VALUES (10,1,default),(11,2,default);
INSERT INTO matchmaking_group_maps VALUES (10,55,default),(11,66,default);
DELETE FROM matchmaking_groups WHERE id = 10;
--sync_slave_with_master
--echo #
--echo # No rows should be returned as ON DELETE CASCASE should have removed
--echo # corresponding rows from child tables. There should not any mismatch
--echo # of 'id' field between parent->child.
--echo #
SELECT * FROM matchmaking_group_users WHERE matchmaking_group_id NOT IN (SELECT id FROM matchmaking_groups);
SELECT * FROM matchmaking_group_maps WHERE matchmaking_group_id NOT IN (SELECT id FROM matchmaking_groups);
--echo #
--echo # Rows with id=11 should be present
--echo #
SELECT * FROM matchmaking_group_users;
SELECT * FROM matchmaking_group_maps;
--connection master
DELETE FROM users WHERE id = 2;
--sync_slave_with_master
--echo #
--echo # No rows should be present in both the child tables
--echo #
SELECT * FROM matchmaking_group_users;
SELECT * FROM matchmaking_group_maps;
--connection master
DROP TABLE matchmaking_group_maps, matchmaking_group_users, matchmaking_groups, users;
--sync_slave_with_master
--echo #
--echo # Test case 3: KEY on a virtual column with ON UPDATE CASCADE
--echo #
--connection master
CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY, b INT NOT NULL) ENGINE=InnoDB;
INSERT INTO t1 VALUES (1, 80);
CREATE TABLE t2 (a INT KEY, b INT,
v_col int as (b+1) virtual, KEY (v_col),
CONSTRAINT b FOREIGN KEY (b) REFERENCES t1(a) ON UPDATE CASCADE
) ENGINE=InnoDB;
INSERT INTO t2 VALUES (51, 1, default);
--sync_slave_with_master
--connection master
UPDATE t1 SET a = 50 WHERE a = 1;
--echo #
--echo # Master: Verify that ON UPDATE CASCADE works fine
--echo # old_row: (51, 1, 2) ON UPDATE New_row: (51, 50, 51)
--echo #
SELECT * FROM t2 WHERE b=50;
--sync_slave_with_master
--echo #
--echo # Slave: Verify that ON UPDATE CASCADE works fine
--echo # old_row: (51, 1, 2) ON UPDATE New_row: (51, 50, 51)
--echo #
SELECT * FROM t2 WHERE b=50;
--connection master
DROP TABLE t2, t1;
--sync_slave_with_master
--echo #
--echo # Test case 4: Define triggers on master, their results should be
--echo # replicated as part of row events and they should be
--echo # applied on slave with the default
--echo # slave_run_triggers_for_rbr=NO
--echo #
# In row-based replication, the binary log contains row changes. It will have
# both the changes made by the statement itself, and the changes made by the
# triggers that were invoked by the statement. Slave server(s) do not need to
# run triggers for row changes they are applying. Hence verify that this
# property remains the same and data should be available as if trigger was
# executed. Please note by default slave_run_triggers_for_rbr=NO.
--connection master
CREATE TABLE t1 (id INT NOT NULL PRIMARY KEY) ENGINE=InnoDB;
CREATE TABLE t2 (count INT NOT NULL) ENGINE=InnoDB;
CREATE TRIGGER trg AFTER INSERT ON t1 FOR EACH ROW INSERT INTO t2 VALUES (1);
INSERT INTO t1 VALUES (2),(3);
--sync_slave_with_master
SHOW GLOBAL VARIABLES LIKE 'slave_run_triggers_for_rbr';
--echo #
--echo # As two rows are inserted in table 't1', two rows should get inserted
--echo # into table 't2' as part of trigger.
--echo #
--let $assert_cond= COUNT(*) = 2 FROM t2
--let $assert_text= Table t2 should have two rows.
--source include/assert.inc
--connection master
DROP TABLE t1,t2;
--sync_slave_with_master
--echo #
--echo # Test case 5: Define triggers + Foreign Keys on master, their results
--echo # should be replicated as part of row events and master
--echo # and slave should be in sync.
--echo #
--connection master
CREATE TABLE t1 (id INT NOT NULL PRIMARY KEY) ENGINE=InnoDB;
CREATE TABLE t2 (t1_id INT NOT NULL,
v_col INT AS (t1_id+1) VIRTUAL, KEY (v_col), KEY (t1_id),
CONSTRAINT a FOREIGN KEY (t1_id) REFERENCES t1 (id) ON DELETE CASCADE
) ENGINE=InnoDB;
CREATE TABLE t3 (count INT NOT NULL) ENGINE=InnoDB;
CREATE TRIGGER trg AFTER INSERT ON t1 FOR EACH ROW INSERT INTO t3 VALUES (1);
INSERT INTO t1 VALUES (2),(3);
INSERT INTO t2 VALUES (2, default), (3, default);
--sync_slave_with_master
--echo #
--echo # As two rows are inserted in table 't1', two rows should get inserted
--echo # into table 't3' as part of trigger.
--echo #
--let $assert_cond= COUNT(*) = 2 FROM t3
--let $assert_text= Table t3 should have two rows.
--source include/assert.inc
--echo #
--echo # Verify ON DELETE CASCASE correctness
--echo #
--connection master
DELETE FROM t1 WHERE id=2;
--sync_slave_with_master
--connection master
--let $diff_tables= master:test.t1, slave:test.t1
--source include/diff_tables.inc
--let $diff_tables= master:test.t2, slave:test.t2
--source include/diff_tables.inc
--let $diff_tables= master:test.t3, slave:test.t3
--source include/diff_tables.inc
DROP TABLE t3,t2,t1;
--sync_slave_with_master
#
# Test case: Triggers only on slave
#
--write_file $MYSQLTEST_VARDIR/tmp/trig_on_slave.inc PROCEDURE
if ($slave_run_triggers_for_rbr == '') {
--die !!!ERROR IN TEST: you must set $slave_run_triggers_for_rbr
}
--connection slave
SET @save_slave_run_triggers_for_rbr= @@GLOBAL.slave_run_triggers_for_rbr;
--eval SET GLOBAL slave_run_triggers_for_rbr= $slave_run_triggers_for_rbr;
SHOW GLOBAL VARIABLES LIKE '%slave_run_triggers_for_rbr%';
--connection master
CREATE TABLE t1 (id INT NOT NULL PRIMARY KEY) ENGINE=InnoDB;
CREATE TABLE t2 (t1_id INT NOT NULL,
v_col INT AS (t1_id+1) VIRTUAL, KEY (v_col),
KEY (t1_id), CONSTRAINT a FOREIGN KEY (t1_id) REFERENCES t1 (id) ON DELETE CASCADE
) ENGINE=InnoDB;
CREATE TABLE t3 (count INT NOT NULL) ENGINE=InnoDB;
--sync_slave_with_master
CREATE TRIGGER trg AFTER INSERT ON t2 FOR EACH ROW INSERT INTO t3 VALUES (1);
--connection master
INSERT INTO t1 VALUES (2),(3);
INSERT INTO t2 VALUES (2, default), (3, default);
--sync_slave_with_master
if ($slave_run_triggers_for_rbr == 'NO') {
--echo #
--echo # Count must be 0
--echo #
--let $assert_cond= COUNT(*) = 0 FROM t3
--let $assert_text= Table t3 should have zero rows.
--source include/assert.inc
}
if ($slave_run_triggers_for_rbr == 'YES') {
--echo #
--echo # Count must be 2
--echo #
--let $assert_cond= COUNT(*) = 2 FROM t3
--let $assert_text= Table t3 should have two rows.
--source include/assert.inc
}
--connection master
DELETE FROM t1 WHERE id=2;
--sync_slave_with_master
SET GLOBAL slave_run_triggers_for_rbr= @save_slave_run_triggers_for_rbr;
--echo #
--echo # Verify t1, t2 are consistent on slave.
--echo #
--let $diff_tables= master:test.t1, slave:test.t1
--source include/diff_tables.inc
--let $diff_tables= master:test.t2, slave:test.t2
--source include/diff_tables.inc
--connection master
DROP TABLE t3,t2,t1;
--sync_slave_with_master
#END OF
PROCEDURE
--echo #
--echo # Test case 6: Triggers are present only on slave and
--echo # 'slave_run_triggers_for_rbr=NO'
--echo #
--let $slave_run_triggers_for_rbr=NO
--source $MYSQLTEST_VARDIR/tmp/trig_on_slave.inc
--echo #
--echo # Test case 7: Triggers are present only on slave and
--echo # 'slave_run_triggers_for_rbr=YES'
--echo #
--let $slave_run_triggers_for_rbr=YES
--source $MYSQLTEST_VARDIR/tmp/trig_on_slave.inc
--remove_file $MYSQLTEST_VARDIR/tmp/trig_on_slave.inc
#
# Test case: Trigger and Foreign Key are present only on slave
#
--write_file $MYSQLTEST_VARDIR/tmp/trig_fk_on_slave.inc PROCEDURE
if ($slave_run_triggers_for_rbr == '') {
--die !!!ERROR IN TEST: you must set $slave_run_triggers_for_rbr
}
--connection slave
SET @save_slave_run_triggers_for_rbr= @@GLOBAL.slave_run_triggers_for_rbr;
--eval SET GLOBAL slave_run_triggers_for_rbr= $slave_run_triggers_for_rbr;
SHOW GLOBAL VARIABLES LIKE '%slave_run_triggers_for_rbr%';
--connection master
CREATE TABLE t1 (id INT NOT NULL PRIMARY KEY) ENGINE=InnoDB;
SET sql_log_bin=0;
CREATE TABLE t2 (t1_id INT NOT NULL,v_col INT AS (t1_id+1) VIRTUAL) ENGINE=INNODB;
SET sql_log_bin=1;
CREATE TABLE t3 (count INT NOT NULL) ENGINE=InnoDB;
--sync_slave_with_master
# Have foreign key and trigger on slave.
CREATE TABLE t2 (t1_id INT NOT NULL,
v_col INT AS (t1_id+1) VIRTUAL, KEY (v_col), KEY (t1_id),
CONSTRAINT a FOREIGN KEY (t1_id) REFERENCES t1 (id) ON DELETE CASCADE
) ENGINE=InnoDB;
CREATE TRIGGER trg AFTER INSERT ON t2 FOR EACH ROW INSERT INTO t3 VALUES (1);
--connection master
INSERT INTO t1 VALUES (2),(3);
INSERT INTO t2 VALUES (2, default), (3, default);
--sync_slave_with_master
if ($slave_run_triggers_for_rbr == 'NO') {
--echo #
--echo # Count must be 0
--echo #
--let $assert_cond= COUNT(*) = 0 FROM t3
--let $assert_text= Table t3 should have zero rows.
--source include/assert.inc
}
if ($slave_run_triggers_for_rbr == 'YES') {
--echo #
--echo # Count must be 2
--echo #
--let $assert_cond= COUNT(*) = 2 FROM t3
--let $assert_text= Table t3 should have two rows.
--source include/assert.inc
}
--connection master
DELETE FROM t1 WHERE id=2;
--echo # t1: Should have one row
SELECT * FROM t1;
--echo # t2: Should have two rows
SELECT * FROM t2;
--sync_slave_with_master
--echo # t1: Should have one row
SELECT * FROM t1;
--echo # t2: Should have one row on slave due to ON DELETE CASCASE
SELECT * FROM t2;
SET GLOBAL slave_run_triggers_for_rbr= @save_slave_run_triggers_for_rbr;
--connection master
DROP TABLE t3,t2,t1;
--sync_slave_with_master
#END OF
PROCEDURE
--echo #
--echo # Test case 8: Triggers and Foreign Keys are present only on slave and
--echo # 'slave_run_triggers_for_rbr=NO'
--echo #
--let $slave_run_triggers_for_rbr=NO
--source $MYSQLTEST_VARDIR/tmp/trig_fk_on_slave.inc
--echo #
--echo # Test case 9: Triggers are Foreign Keys are present only on slave and
--echo # 'slave_run_triggers_for_rbr=YES'
--echo #
--let $slave_run_triggers_for_rbr=YES
--source $MYSQLTEST_VARDIR/tmp/trig_fk_on_slave.inc
--remove_file $MYSQLTEST_VARDIR/tmp/trig_fk_on_slave.inc
--source include/rpl_end.inc

View File

@ -1580,22 +1580,27 @@ no_password:
static int do_log_user(const char *name, int take_lock) static int do_log_user(const char *name, int len,
const char *proxy, int proxy_len, int take_lock)
{ {
size_t len;
int result; int result;
if (!name) if (!name)
return 0; return 0;
len= strlen(name);
if (take_lock) if (take_lock)
flogger_mutex_lock(&lock_operations); flogger_mutex_lock(&lock_operations);
if (incl_user_coll.n_users) if (incl_user_coll.n_users)
result= coll_search(&incl_user_coll, name, len) != 0; {
result= coll_search(&incl_user_coll, name, len) != 0 ||
(proxy && coll_search(&incl_user_coll, proxy, proxy_len) != 0);
}
else if (excl_user_coll.n_users) else if (excl_user_coll.n_users)
result= coll_search(&excl_user_coll, name, len) == 0; {
result= coll_search(&excl_user_coll, name, len) == 0 &&
(proxy && coll_search(&excl_user_coll, proxy, proxy_len) == 0);
}
else else
result= 1; result= 1;
@ -2136,7 +2141,9 @@ void auditing(MYSQL_THD thd, unsigned int event_class, const void *ev)
} }
if (event_class == MYSQL_AUDIT_GENERAL_CLASS && FILTER(EVENT_QUERY) && if (event_class == MYSQL_AUDIT_GENERAL_CLASS && FILTER(EVENT_QUERY) &&
cn && (cn->log_always || do_log_user(cn->user, 1))) cn && (cn->log_always || do_log_user(cn->user, cn->user_length,
cn->proxy, cn->proxy_length,
1)))
{ {
const struct mysql_event_general *event = const struct mysql_event_general *event =
(const struct mysql_event_general *) ev; (const struct mysql_event_general *) ev;
@ -2156,7 +2163,8 @@ void auditing(MYSQL_THD thd, unsigned int event_class, const void *ev)
{ {
const struct mysql_event_table *event = const struct mysql_event_table *event =
(const struct mysql_event_table *) ev; (const struct mysql_event_table *) ev;
if (do_log_user(event->user, 1)) if (do_log_user(event->user, (int) SAFE_STRLEN(event->user),
cn->proxy, cn->proxy_length, 1))
{ {
switch (event->event_subclass) switch (event->event_subclass)
{ {

View File

@ -11232,7 +11232,7 @@ int Rows_log_event::do_add_row_data(uchar *row_data, size_t length)
There was the same problem with MERGE MYISAM tables and so here we try to There was the same problem with MERGE MYISAM tables and so here we try to
go the same way. go the same way.
*/ */
static void restore_empty_query_table_list(LEX *lex) inline void restore_empty_query_table_list(LEX *lex)
{ {
if (lex->first_not_own_table()) if (lex->first_not_own_table())
(*lex->first_not_own_table()->prev_global)= NULL; (*lex->first_not_own_table()->prev_global)= NULL;
@ -11247,6 +11247,8 @@ int Rows_log_event::do_apply_event(rpl_group_info *rgi)
TABLE* table; 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;
LEX *lex= thd->lex;
uint8 new_trg_event_map= get_trg_event_map();
/* /*
If m_table_id == ~0ULL, then we have a dummy event that does not If m_table_id == ~0ULL, then we have a dummy event that does not
contain any data. In that case, we just remove all tables in the contain any data. In that case, we just remove all tables in the
@ -11337,27 +11339,29 @@ int Rows_log_event::do_apply_event(rpl_group_info *rgi)
DBUG_ASSERT(!debug_sync_set_action(thd, STRING_WITH_LEN(action))); DBUG_ASSERT(!debug_sync_set_action(thd, STRING_WITH_LEN(action)));
};); };);
if (slave_run_triggers_for_rbr) /*
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)
{ {
LEX *lex= thd->lex; if (slave_run_triggers_for_rbr)
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; tables->trg_event_map= new_trg_event_map;
lex->query_tables_last= &tables->next_global; lex->query_tables_last= &tables->next_global;
} }
else if (!WSREP_ON)
{
tables->slave_fk_event_map= new_trg_event_map;
lex->query_tables_last= &tables->next_global;
}
} }
if (unlikely(open_and_lock_tables(thd, rgi->tables_to_lock, FALSE, 0))) if (unlikely(open_and_lock_tables(thd, rgi->tables_to_lock, FALSE, 0)))
{ {
@ -11707,8 +11711,7 @@ int Rows_log_event::do_apply_event(rpl_group_info *rgi)
} }
/* remove trigger's tables */ /* remove trigger's tables */
if (slave_run_triggers_for_rbr) restore_empty_query_table_list(thd->lex);
restore_empty_query_table_list(thd->lex);
#if defined(WITH_WSREP) && defined(HAVE_QUERY_CACHE) #if defined(WITH_WSREP) && defined(HAVE_QUERY_CACHE)
if (WSREP(thd) && thd->wsrep_exec_mode == REPL_RECV) if (WSREP(thd) && thd->wsrep_exec_mode == REPL_RECV)
@ -11727,8 +11730,7 @@ int Rows_log_event::do_apply_event(rpl_group_info *rgi)
DBUG_RETURN(error); DBUG_RETURN(error);
err: err:
if (slave_run_triggers_for_rbr) restore_empty_query_table_list(thd->lex);
restore_empty_query_table_list(thd->lex);
rgi->slave_close_thread_tables(thd); rgi->slave_close_thread_tables(thd);
DBUG_RETURN(error); DBUG_RETURN(error);
} }

View File

@ -4508,7 +4508,72 @@ add_internal_tables(THD *thd, Query_tables_list *prelocking_ctx,
DBUG_RETURN(FALSE); DBUG_RETURN(FALSE);
} }
/**
Extend the table_list to include foreign tables for prelocking.
@param[in] thd Thread context.
@param[in] prelocking_ctx Prelocking context of the statement.
@param[in] table_list Table list element for table.
@param[in] sp Routine body.
@param[out] need_prelocking Set to TRUE if method detects that prelocking
required, not changed otherwise.
@retval FALSE Success.
@retval TRUE Failure (OOM).
*/
inline bool
prepare_fk_prelocking_list(THD *thd, Query_tables_list *prelocking_ctx,
TABLE_LIST *table_list, bool *need_prelocking,
uint8 op)
{
DBUG_ENTER("prepare_fk_prelocking_list");
List <FOREIGN_KEY_INFO> fk_list;
List_iterator<FOREIGN_KEY_INFO> fk_list_it(fk_list);
FOREIGN_KEY_INFO *fk;
Query_arena *arena, backup;
TABLE *table= table_list->table;
arena= thd->activate_stmt_arena_if_needed(&backup);
table->file->get_parent_foreign_key_list(thd, &fk_list);
if (unlikely(thd->is_error()))
{
if (arena)
thd->restore_active_arena(arena, &backup);
return TRUE;
}
*need_prelocking= TRUE;
while ((fk= fk_list_it++))
{
// FK_OPTION_RESTRICT and FK_OPTION_NO_ACTION only need read access
thr_lock_type lock_type;
if ((op & (1 << TRG_EVENT_DELETE) && fk_modifies_child(fk->delete_method))
|| (op & (1 << TRG_EVENT_UPDATE) && fk_modifies_child(fk->update_method)))
lock_type= TL_WRITE_ALLOW_WRITE;
else
lock_type= TL_READ;
if (table_already_fk_prelocked(prelocking_ctx->query_tables,
fk->foreign_db, fk->foreign_table,
lock_type))
continue;
TABLE_LIST *tl= (TABLE_LIST *) thd->alloc(sizeof(TABLE_LIST));
tl->init_one_table_for_prelocking(fk->foreign_db,
fk->foreign_table,
NULL, lock_type,
TABLE_LIST::PRELOCK_FK,
table_list->belong_to_view, op,
&prelocking_ctx->query_tables_last,
table_list->for_insert_data);
}
if (arena)
thd->restore_active_arena(arena, &backup);
DBUG_RETURN(FALSE);
}
/** /**
Defines how prelocking algorithm for DML statements should handle table list Defines how prelocking algorithm for DML statements should handle table list
@ -4555,53 +4620,20 @@ handle_table(THD *thd, Query_tables_list *prelocking_ctx,
if (table->file->referenced_by_foreign_key()) if (table->file->referenced_by_foreign_key())
{ {
List <FOREIGN_KEY_INFO> fk_list; if (prepare_fk_prelocking_list(thd, prelocking_ctx, table_list,
List_iterator<FOREIGN_KEY_INFO> fk_list_it(fk_list); need_prelocking,
FOREIGN_KEY_INFO *fk; table_list->trg_event_map))
Query_arena *arena, backup; return TRUE;
arena= thd->activate_stmt_arena_if_needed(&backup);
table->file->get_parent_foreign_key_list(thd, &fk_list);
if (unlikely(thd->is_error()))
{
if (arena)
thd->restore_active_arena(arena, &backup);
DBUG_RETURN(TRUE);
}
*need_prelocking= TRUE;
while ((fk= fk_list_it++))
{
// FK_OPTION_RESTRICT and FK_OPTION_NO_ACTION only need read access
uint8 op= table_list->trg_event_map;
thr_lock_type lock_type;
if ((op & (1 << TRG_EVENT_DELETE) && fk_modifies_child(fk->delete_method))
|| (op & (1 << TRG_EVENT_UPDATE) && fk_modifies_child(fk->update_method)))
lock_type= TL_WRITE_ALLOW_WRITE;
else
lock_type= TL_READ;
if (table_already_fk_prelocked(prelocking_ctx->query_tables,
fk->foreign_db, fk->foreign_table,
lock_type))
continue;
TABLE_LIST *tl= (TABLE_LIST *) thd->alloc(sizeof(TABLE_LIST));
tl->init_one_table_for_prelocking(fk->foreign_db,
fk->foreign_table,
NULL, lock_type,
TABLE_LIST::PRELOCK_FK,
table_list->belong_to_view, op,
&prelocking_ctx->query_tables_last,
table_list->for_insert_data);
}
if (arena)
thd->restore_active_arena(arena, &backup);
} }
} }
else if (table_list->slave_fk_event_map &&
table->file->referenced_by_foreign_key())
{
if (prepare_fk_prelocking_list(thd, prelocking_ctx, table_list,
need_prelocking,
table_list->slave_fk_event_map))
return TRUE;
}
/* Open any tables used by DEFAULT (like sequence tables) */ /* Open any tables used by DEFAULT (like sequence tables) */
DBUG_PRINT("info", ("table: %p name: %s db: %s flags: %u", DBUG_PRINT("info", ("table: %p name: %s db: %s flags: %u",

View File

@ -1,6 +1,6 @@
/* /*
Copyright (c) 2000, 2015, Oracle and/or its affiliates. Copyright (c) 2000, 2015, Oracle and/or its affiliates.
Copyright (c) 2008, 2020, MariaDB Corporation. Copyright (c) 2008, 2021, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -1458,12 +1458,15 @@ void THD::reset_db(const LEX_CSTRING *new_db)
/* Do operations that may take a long time */ /* Do operations that may take a long time */
void THD::cleanup(void) void THD::cleanup(bool have_mutex)
{ {
DBUG_ENTER("THD::cleanup"); DBUG_ENTER("THD::cleanup");
DBUG_ASSERT(cleanup_done == 0); DBUG_ASSERT(cleanup_done == 0);
set_killed(KILL_CONNECTION); if (have_mutex)
set_killed_no_mutex(KILL_CONNECTION,0,0);
else
set_killed(KILL_CONNECTION);
#ifdef ENABLE_WHEN_BINLOG_WILL_BE_ABLE_TO_PREPARE #ifdef ENABLE_WHEN_BINLOG_WILL_BE_ABLE_TO_PREPARE
if (transaction.xid_state.xa_state == XA_PREPARED) if (transaction.xid_state.xa_state == XA_PREPARED)
{ {
@ -1541,7 +1544,29 @@ void THD::cleanup(void)
void THD::free_connection() void THD::free_connection()
{ {
DBUG_ASSERT(free_connection_done == 0); DBUG_ASSERT(free_connection_done == 0);
my_free((char*) db.str); /* Check that we have already called thd->unlink() */
DBUG_ASSERT(prev == 0 && next == 0);
/*
Other threads may have a lock on THD::LOCK_thd_data or
THD::LOCK_thd_kill to ensure that this THD is not deleted
while they access it. The following mutex_lock ensures
that no one else is using this THD and it's now safe to
continue.
For example consider KILL-statement execution on
sql_parse.cc kill_one_thread() that will use
THD::LOCK_thd_data to protect victim thread during
THD::awake().
*/
mysql_mutex_lock(&LOCK_thd_data);
mysql_mutex_lock(&LOCK_thd_kill);
#ifdef WITH_WSREP
delete wsrep_rgi;
wsrep_rgi= 0;
#endif /* WITH_WSREP */
my_free(const_cast<char*>(db.str));
db= null_clex_str; db= null_clex_str;
#ifndef EMBEDDED_LIBRARY #ifndef EMBEDDED_LIBRARY
if (net.vio) if (net.vio)
@ -1549,8 +1574,8 @@ void THD::free_connection()
net.vio= 0; net.vio= 0;
net_end(&net); net_end(&net);
#endif #endif
if (!cleanup_done) if (!cleanup_done)
cleanup(); cleanup(true); // We have locked THD::LOCK_thd_kill
ha_close_connection(this); ha_close_connection(this);
plugin_thdvar_cleanup(this); plugin_thdvar_cleanup(this);
mysql_audit_free_thd(this); mysql_audit_free_thd(this);
@ -1561,6 +1586,8 @@ void THD::free_connection()
#if defined(ENABLED_PROFILING) #if defined(ENABLED_PROFILING)
profiling.restart(); // Reset profiling profiling.restart(); // Reset profiling
#endif #endif
mysql_mutex_unlock(&LOCK_thd_kill);
mysql_mutex_unlock(&LOCK_thd_data);
} }
/* /*
@ -1620,9 +1647,6 @@ THD::~THD()
mysql_mutex_lock(&LOCK_thd_kill); mysql_mutex_lock(&LOCK_thd_kill);
mysql_mutex_unlock(&LOCK_thd_kill); mysql_mutex_unlock(&LOCK_thd_kill);
#ifdef WITH_WSREP
delete wsrep_rgi;
#endif
if (!free_connection_done) if (!free_connection_done)
free_connection(); free_connection();
@ -3070,12 +3094,12 @@ static File create_file(THD *thd, char *path, sql_exchange *exchange,
} }
/* Create the file world readable */ /* Create the file world readable */
if ((file= mysql_file_create(key_select_to_file, if ((file= mysql_file_create(key_select_to_file,
path, 0666, O_WRONLY|O_EXCL, MYF(MY_WME))) < 0) path, 0644, O_WRONLY|O_EXCL, MYF(MY_WME))) < 0)
return file; return file;
#ifdef HAVE_FCHMOD #ifdef HAVE_FCHMOD
(void) fchmod(file, 0666); // Because of umask() (void) fchmod(file, 0644); // Because of umask()
#else #else
(void) chmod(path, 0666); (void) chmod(path, 0644);
#endif #endif
if (init_io_cache(cache, file, 0L, WRITE_CACHE, 0L, 1, MYF(MY_WME))) if (init_io_cache(cache, file, 0L, WRITE_CACHE, 0L, 1, MYF(MY_WME)))
{ {

View File

@ -3291,7 +3291,7 @@ public:
void update_all_stats(); void update_all_stats();
void update_stats(void); void update_stats(void);
void change_user(void); void change_user(void);
void cleanup(void); void cleanup(bool have_mutex=false);
void cleanup_after_query(); void cleanup_after_query();
void free_connection(); void free_connection();
void reset_for_reuse(); void reset_for_reuse();

View File

@ -1,5 +1,5 @@
/* Copyright (c) 2002, 2015, Oracle and/or its affiliates. /* Copyright (c) 2002, 2015, Oracle and/or its affiliates.
Copyright (c) 2008, 2019, MariaDB Copyright (c) 2008, 2021, MariaDB
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -117,15 +117,14 @@ When one supplies long data for a placeholder:
#include <mysql.h> #include <mysql.h>
#else #else
#include <mysql_com.h> #include <mysql_com.h>
/* Constants defining bits in parameter type flags. Flags are read from high byte of short value */
static const uint PARAMETER_FLAG_UNSIGNED= 128U << 8;
#endif #endif
#include "lock.h" // MYSQL_OPEN_FORCE_SHARED_MDL #include "lock.h" // MYSQL_OPEN_FORCE_SHARED_MDL
#include "sql_handler.h" #include "sql_handler.h"
#include "transaction.h" // trans_rollback_implicit #include "transaction.h" // trans_rollback_implicit
#include "wsrep_mysqld.h" #include "wsrep_mysqld.h"
/* Constants defining bits in parameter type flags. Flags are read from high byte of short value */
static const uint PARAMETER_FLAG_UNSIGNED = 128U << 8;
/** /**
A result class used to send cursor rows using the binary protocol. A result class used to send cursor rows using the binary protocol.
*/ */

View File

@ -2484,8 +2484,12 @@ struct TABLE_LIST
Indicates what triggers we need to pre-load for this TABLE_LIST Indicates what triggers we need to pre-load for this TABLE_LIST
when opening an associated TABLE. This is filled after when opening an associated TABLE. This is filled after
the parsed tree is created. the parsed tree is created.
slave_fk_event_map is filled on the slave side with bitmaps value
representing row-based event operation to help find and prelock
possible FK constrain-related child tables.
*/ */
uint8 trg_event_map; uint8 trg_event_map, slave_fk_event_map;
/* TRUE <=> this table is a const one and was optimized away. */ /* TRUE <=> this table is a const one and was optimized away. */
bool optimized_away; bool optimized_away;

View File

@ -5082,29 +5082,15 @@ static void innobase_kill_query(handlerton*, THD* thd, enum thd_kill_levels)
if (trx_t* trx= thd_to_trx(thd)) if (trx_t* trx= thd_to_trx(thd))
{ {
ut_ad(trx->mysql_thd == thd);
lock_mutex_enter(); lock_mutex_enter();
mutex_enter(&trx_sys.mutex); if (lock_t *lock= trx->lock.wait_lock)
trx_mutex_enter(trx); {
/* It is possible that innobase_close_connection() is concurrently trx_mutex_enter(trx);
being executed on our victim. Even if the trx object is later
reused for another client connection or a background transaction,
its trx->mysql_thd will differ from our thd.
trx_t::state changes are protected by trx_t::mutex, and
trx_sys.trx_list is protected by trx_sys.mutex, in
both trx_create() and trx_t::free().
At this point, trx may have been reallocated for another client
connection, or for a background operation. In that case, either
trx_t::state or trx_t::mysql_thd should not match our expectations. */
bool cancel= trx->mysql_thd == thd && trx->state == TRX_STATE_ACTIVE &&
!trx->lock.was_chosen_as_deadlock_victim;
mutex_exit(&trx_sys.mutex);
if (!cancel);
else if (lock_t *lock= trx->lock.wait_lock)
lock_cancel_waiting_and_release(lock); lock_cancel_waiting_and_release(lock);
trx_mutex_exit(trx);
}
lock_mutex_exit(); lock_mutex_exit();
trx_mutex_exit(trx);
} }
DBUG_VOID_RETURN; DBUG_VOID_RETURN;

View File

@ -1,7 +1,7 @@
/***************************************************************************** /*****************************************************************************
Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2017, 2020, MariaDB Corporation. Copyright (c) 2017, 2021, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under 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 the terms of the GNU General Public License as published by the Free Software
@ -303,7 +303,6 @@ que_fork_scheduler_round_robin(
/** Query thread states */ /** Query thread states */
enum que_thr_state_t { enum que_thr_state_t {
QUE_THR_RUNNING, QUE_THR_RUNNING,
QUE_THR_PROCEDURE_WAIT,
/** in selects this means that the thread is at the end of its /** in selects this means that the thread is at the end of its
result set (or start, in case of a scroll cursor); in other result set (or start, in case of a scroll cursor); in other
statements, this means the thread has done its task */ statements, this means the thread has done its task */

View File

@ -6153,6 +6153,7 @@ lock_cancel_waiting_and_release(
ut_ad(lock_mutex_own()); ut_ad(lock_mutex_own());
ut_ad(trx_mutex_own(lock->trx)); ut_ad(trx_mutex_own(lock->trx));
ut_ad(lock->trx->state == TRX_STATE_ACTIVE);
lock->trx->lock.cancel = true; lock->trx->lock.cancel = true;

View File

@ -1,7 +1,7 @@
/***************************************************************************** /*****************************************************************************
Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2017, 2018, 2020 MariaDB Corporation. Copyright (c) 2017, 2021, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under 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 the terms of the GNU General Public License as published by the Free Software
@ -349,7 +349,6 @@ que_fork_start_command(
case QUE_THR_RUNNING: case QUE_THR_RUNNING:
case QUE_THR_LOCK_WAIT: case QUE_THR_LOCK_WAIT:
case QUE_THR_PROCEDURE_WAIT:
ut_error; ut_error;
} }
} }

View File

@ -410,7 +410,7 @@ void trx_t::free()
/* do not poison mutex */ /* do not poison mutex */
MEM_NOACCESS(&id, sizeof id); MEM_NOACCESS(&id, sizeof id);
MEM_NOACCESS(&no, sizeof no); MEM_NOACCESS(&no, sizeof no);
/* state is accessed by innobase_kill_connection() */ MEM_NOACCESS(&state, sizeof state);
MEM_NOACCESS(&is_recovered, sizeof is_recovered); MEM_NOACCESS(&is_recovered, sizeof is_recovered);
#ifdef WITH_WSREP #ifdef WITH_WSREP
MEM_NOACCESS(&wsrep, sizeof wsrep); MEM_NOACCESS(&wsrep, sizeof wsrep);
@ -435,7 +435,7 @@ void trx_t::free()
MEM_NOACCESS(&start_time_micro, sizeof start_time_micro); MEM_NOACCESS(&start_time_micro, sizeof start_time_micro);
MEM_NOACCESS(&commit_lsn, sizeof commit_lsn); MEM_NOACCESS(&commit_lsn, sizeof commit_lsn);
MEM_NOACCESS(&table_id, sizeof table_id); MEM_NOACCESS(&table_id, sizeof table_id);
/* mysql_thd is accessed by innobase_kill_connection() */ MEM_NOACCESS(&mysql_thd, sizeof mysql_thd);
MEM_NOACCESS(&mysql_log_file_name, sizeof mysql_log_file_name); MEM_NOACCESS(&mysql_log_file_name, sizeof mysql_log_file_name);
MEM_NOACCESS(&mysql_log_offset, sizeof mysql_log_offset); MEM_NOACCESS(&mysql_log_offset, sizeof mysql_log_offset);
MEM_NOACCESS(&n_mysql_tables_in_use, sizeof n_mysql_tables_in_use); MEM_NOACCESS(&n_mysql_tables_in_use, sizeof n_mysql_tables_in_use);