summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarko Mäkelä <marko.makela@mariadb.com>2021-01-11 10:35:06 +0200
committerMarko Mäkelä <marko.makela@mariadb.com>2021-01-11 10:35:06 +0200
commitfd5e103aa4b97e080dd497b93992e5f32ef83fdf (patch)
tree4688325520abb9c71d1b41c62aa2265d32821db7
parenta131b976b205af4023a5d43569fadf44dceade70 (diff)
parent5a1a7141870459cf66d8e441b443187f87a10d5d (diff)
downloadmariadb-git-fd5e103aa4b97e080dd497b93992e5f32ef83fdf.tar.gz
Merge 10.3 into 10.4
-rw-r--r--cmake/install_macros.cmake2
-rw-r--r--mysql-test/suite/galera/t/galera_rsu_error.test3
-rw-r--r--mysql-test/suite/galera/t/galera_toi_ddl_sequential.test3
-rw-r--r--mysql-test/suite/galera/t/galera_toi_lock_shared.test3
-rw-r--r--mysql-test/suite/galera/t/galera_truncate.test3
-rw-r--r--mysql-test/suite/galera/t/galera_truncate_temporary.test3
-rw-r--r--mysql-test/suite/innodb/r/innodb-virtual-columns2.result25
-rw-r--r--mysql-test/suite/innodb/t/innodb-virtual-columns2.test20
-rw-r--r--mysql-test/suite/plugins/r/server_audit.result5
-rw-r--r--mysql-test/suite/plugins/t/server_audit.test1
-rw-r--r--mysql-test/suite/rpl/r/rpl_row_vcol_crash.result380
-rw-r--r--mysql-test/suite/rpl/t/rpl_row_vcol_crash.test425
-rw-r--r--mysql-test/suite/rpl/t/rpl_semisync_ali_issues.test15
-rw-r--r--mysql-test/suite/versioning/r/partition.result36
-rw-r--r--mysql-test/suite/versioning/r/replace.result13
-rw-r--r--mysql-test/suite/versioning/t/partition.test45
-rw-r--r--mysql-test/suite/versioning/t/replace.test18
-rw-r--r--plugin/server_audit/server_audit.c22
-rw-r--r--sql/log_event.cc42
-rw-r--r--sql/sql_base.cc122
-rw-r--r--sql/sql_class.cc73
-rw-r--r--sql/sql_class.h2
-rw-r--r--sql/sql_insert.cc2
-rw-r--r--sql/sql_prepare.cc7
-rw-r--r--sql/sql_update.cc3
-rw-r--r--sql/table.h6
-rw-r--r--storage/innobase/handler/ha_innodb.cc30
-rw-r--r--storage/innobase/handler/handler0alter.cc90
-rw-r--r--storage/innobase/include/que0que.h3
-rw-r--r--storage/innobase/lock/lock0lock.cc1
-rw-r--r--storage/innobase/que/que0que.cc3
-rw-r--r--storage/innobase/row/row0mysql.cc2
-rw-r--r--storage/innobase/trx/trx0trx.cc10
33 files changed, 1239 insertions, 179 deletions
diff --git a/cmake/install_macros.cmake b/cmake/install_macros.cmake
index 2318e9d5dea..dd1729bec42 100644
--- a/cmake/install_macros.cmake
+++ b/cmake/install_macros.cmake
@@ -181,7 +181,7 @@ IF(WIN32)
MARK_AS_ADVANCED(SIGNCODE)
IF(SIGNCODE)
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)")
FIND_PROGRAM(SIGNTOOL_EXECUTABLE signtool
PATHS "$ENV{ProgramFiles}/Microsoft SDKs/Windows/v7.0A/bin"
diff --git a/mysql-test/suite/galera/t/galera_rsu_error.test b/mysql-test/suite/galera/t/galera_rsu_error.test
index cad8154ac76..6de7607b6ec 100644
--- a/mysql-test/suite/galera/t/galera_rsu_error.test
+++ b/mysql-test/suite/galera/t/galera_rsu_error.test
@@ -9,6 +9,9 @@ CREATE TABLE t1 (f1 INTEGER) Engine=InnoDB;
INSERT INTO t1 VALUES (1), (1);
--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";
--error ER_DUP_ENTRY
ALTER TABLE t1 ADD PRIMARY KEY (f1);
diff --git a/mysql-test/suite/galera/t/galera_toi_ddl_sequential.test b/mysql-test/suite/galera/t/galera_toi_ddl_sequential.test
index 51eae7005df..89a1af845c9 100644
--- a/mysql-test/suite/galera/t/galera_toi_ddl_sequential.test
+++ b/mysql-test/suite/galera/t/galera_toi_ddl_sequential.test
@@ -9,6 +9,9 @@ CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
INSERT INTO t1 VALUES (1);
--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;
INSERT INTO t1 VALUES (2, 3);
diff --git a/mysql-test/suite/galera/t/galera_toi_lock_shared.test b/mysql-test/suite/galera/t/galera_toi_lock_shared.test
index 6857a0e08ca..6b7feec6031 100644
--- a/mysql-test/suite/galera/t/galera_toi_lock_shared.test
+++ b/mysql-test/suite/galera/t/galera_toi_lock_shared.test
@@ -10,6 +10,9 @@ CREATE TABLE t1 (id INT PRIMARY KEY) ENGINE=InnoDB;
INSERT INTO t1 VALUES (1);
--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;
--connection node_1
diff --git a/mysql-test/suite/galera/t/galera_truncate.test b/mysql-test/suite/galera/t/galera_truncate.test
index 79f9bad1f1b..3c3ee56a23f 100644
--- a/mysql-test/suite/galera/t/galera_truncate.test
+++ b/mysql-test/suite/galera/t/galera_truncate.test
@@ -14,6 +14,9 @@ CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) Engine=InnoDB;
INSERT INTO t1 VALUES (1);
--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;
SELECT COUNT(*) = 0 FROM t1;
diff --git a/mysql-test/suite/galera/t/galera_truncate_temporary.test b/mysql-test/suite/galera/t/galera_truncate_temporary.test
index 3ad94eb9930..ea20911bd5d 100644
--- a/mysql-test/suite/galera/t/galera_truncate_temporary.test
+++ b/mysql-test/suite/galera/t/galera_truncate_temporary.test
@@ -67,6 +67,9 @@ CREATE TEMPORARY TABLE t1 (f1 INTEGER) ENGINE=InnoDB;
INSERT INTO t1 VALUES (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;
SELECT COUNT(*) = 0 FROM t1;
diff --git a/mysql-test/suite/innodb/r/innodb-virtual-columns2.result b/mysql-test/suite/innodb/r/innodb-virtual-columns2.result
index 3574ba72849..99a1c610bd3 100644
--- a/mysql-test/suite/innodb/r/innodb-virtual-columns2.result
+++ b/mysql-test/suite/innodb/r/innodb-virtual-columns2.result
@@ -62,3 +62,28 @@ INSERT INTO t1 (i) VALUES (1),(2);
SELECT * FROM t1 WHERE y BETWEEN 2012 AND 2016 FOR UPDATE;
y i b vi
DROP TABLE t1;
+#
+# MDEV-23632 ALTER TABLE...ADD KEY creates corrupted index on virtual column
+#
+CREATE TABLE t1(a INT PRIMARY KEY, b INT, g INT GENERATED ALWAYS AS(b)VIRTUAL) ENGINE=InnoDB;
+INSERT INTO t1 VALUES (1,1,default);
+ALTER TABLE t1 ADD COLUMN c INT;
+ALTER TABLE t1 ADD KEY(g);
+CHECK TABLE t1;
+Table Op Msg_type Msg_text
+test.t1 check status OK
+SELECT g FROM t1 FORCE INDEX (g);
+g
+1
+DROP TABLE t1;
+CREATE TABLE t1(a INT, b INT, g INT GENERATED ALWAYS AS(b)VIRTUAL) ENGINE=InnoDB;
+INSERT INTO t1 VALUES (1,1,default);
+ALTER TABLE t1 ADD COLUMN c INT PRIMARY KEY;
+ALTER TABLE t1 ADD KEY(g);
+CHECK TABLE t1;
+Table Op Msg_type Msg_text
+test.t1 check status OK
+SELECT g FROM t1 FORCE INDEX (g);
+g
+1
+DROP TABLE t1;
diff --git a/mysql-test/suite/innodb/t/innodb-virtual-columns2.test b/mysql-test/suite/innodb/t/innodb-virtual-columns2.test
index 474a6354576..13ecffcc896 100644
--- a/mysql-test/suite/innodb/t/innodb-virtual-columns2.test
+++ b/mysql-test/suite/innodb/t/innodb-virtual-columns2.test
@@ -52,3 +52,23 @@ SELECT * FROM t1 WHERE y BETWEEN 2012 AND 2016 FOR UPDATE;
INSERT INTO t1 (i) VALUES (1),(2);
SELECT * FROM t1 WHERE y BETWEEN 2012 AND 2016 FOR UPDATE;
DROP TABLE t1;
+
+--echo #
+--echo # MDEV-23632 ALTER TABLE...ADD KEY creates corrupted index on virtual column
+--echo #
+
+CREATE TABLE t1(a INT PRIMARY KEY, b INT, g INT GENERATED ALWAYS AS(b)VIRTUAL) ENGINE=InnoDB;
+INSERT INTO t1 VALUES (1,1,default);
+ALTER TABLE t1 ADD COLUMN c INT;
+ALTER TABLE t1 ADD KEY(g);
+CHECK TABLE t1;
+SELECT g FROM t1 FORCE INDEX (g);
+DROP TABLE t1;
+
+CREATE TABLE t1(a INT, b INT, g INT GENERATED ALWAYS AS(b)VIRTUAL) ENGINE=InnoDB;
+INSERT INTO t1 VALUES (1,1,default);
+ALTER TABLE t1 ADD COLUMN c INT PRIMARY KEY; # Triggers `new_clustered`
+ALTER TABLE t1 ADD KEY(g);
+CHECK TABLE t1;
+SELECT g FROM t1 FORCE INDEX (g);
+DROP TABLE t1;
diff --git a/mysql-test/suite/plugins/r/server_audit.result b/mysql-test/suite/plugins/r/server_audit.result
index 6ded4f687a6..2c06b70c509 100644
--- a/mysql-test/suite/plugins/r/server_audit.result
+++ b/mysql-test/suite/plugins/r/server_audit.result
@@ -227,6 +227,7 @@ set global server_audit_logging= on;
disconnect cn1;
drop user user1@localhost;
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_dest IDENTIFIED BY 'plug_dest_passwd';
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_size 1000000
server_audit_file_rotations 9
-server_audit_incl_users root
+server_audit_incl_users root, plug_dest
server_audit_logging ON
server_audit_mode 1
server_audit_output_type file
@@ -431,6 +432,7 @@ 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,global_priv,
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,db,
TIME,HOSTNAME,root,localhost,ID,ID,WRITE,mysql,tables_priv,
TIME,HOSTNAME,root,localhost,ID,ID,WRITE,mysql,columns_priv,
@@ -454,6 +456,7 @@ TIME,HOSTNAME,root,localhost,ID,ID,WRITE,mysql,global_priv,
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,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,root,localhost,ID,ID,WRITE,mysql,db,
TIME,HOSTNAME,root,localhost,ID,ID,WRITE,mysql,tables_priv,
diff --git a/mysql-test/suite/plugins/t/server_audit.test b/mysql-test/suite/plugins/t/server_audit.test
index 787541f7ca0..0d65c451d08 100644
--- a/mysql-test/suite/plugins/t/server_audit.test
+++ b/mysql-test/suite/plugins/t/server_audit.test
@@ -173,6 +173,7 @@ source include/wait_until_count_sessions.inc;
drop user user1@localhost;
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_dest IDENTIFIED BY 'plug_dest_passwd';
diff --git a/mysql-test/suite/rpl/r/rpl_row_vcol_crash.result b/mysql-test/suite/rpl/r/rpl_row_vcol_crash.result
new file mode 100644
index 00000000000..f76d8935fa8
--- /dev/null
+++ b/mysql-test/suite/rpl/r/rpl_row_vcol_crash.result
@@ -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
diff --git a/mysql-test/suite/rpl/t/rpl_row_vcol_crash.test b/mysql-test/suite/rpl/t/rpl_row_vcol_crash.test
new file mode 100644
index 00000000000..84ee14977f3
--- /dev/null
+++ b/mysql-test/suite/rpl/t/rpl_row_vcol_crash.test
@@ -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
diff --git a/mysql-test/suite/rpl/t/rpl_semisync_ali_issues.test b/mysql-test/suite/rpl/t/rpl_semisync_ali_issues.test
index 52cd9e31753..f67c6e2ac0a 100644
--- a/mysql-test/suite/rpl/t/rpl_semisync_ali_issues.test
+++ b/mysql-test/suite/rpl/t/rpl_semisync_ali_issues.test
@@ -31,8 +31,12 @@ echo [ enable semi-sync on slave ];
stop slave;
set global rpl_semi_sync_slave_enabled = 1;
start slave;
+let $status_var= rpl_semi_sync_slave_status;
+let $status_var_value= ON;
+source include/wait_for_status_var.inc;
show status like 'rpl_semi_sync_slave%';
+
connection master;
CREATE TABLE t1(a INT) ENGINE=InnoDB;
sync_slave_with_master;
@@ -190,6 +194,12 @@ connection con1;
INSERT INTO t1 values (2);
sync_slave_with_master;
connection con1;
+let $status_var= Rpl_semi_sync_master_clients;
+let $status_var_value= 1;
+source include/wait_for_status_var.inc;
+let $status_var= Rpl_semi_sync_master_status;
+let $status_var_value= ON;
+source include/wait_for_status_var.inc;
show status like 'Rpl_semi_sync_master_clients';
show status like 'Rpl_semi_sync_master_status';
@@ -259,7 +269,12 @@ START SLAVE IO_THREAD;
--echo #########################################################
connection con1;
SET GLOBAL rpl_semi_sync_master_enabled = 0;
+
+let $status_var= Rpl_semi_sync_master_clients;
+let $status_var_value= 1;
+source include/wait_for_status_var.inc;
show status like 'Rpl_semi_sync_master_clients';
+
INSERT INTO t1 VALUES (1);
SET GLOBAL rpl_semi_sync_master_enabled = 1;
INSERT INTO t1 VALUES (2);
diff --git a/mysql-test/suite/versioning/r/partition.result b/mysql-test/suite/versioning/r/partition.result
index ca6d026df30..9eeec045ef7 100644
--- a/mysql-test/suite/versioning/r/partition.result
+++ b/mysql-test/suite/versioning/r/partition.result
@@ -538,6 +538,7 @@ set timestamp=1523466002.799571;
insert into t1 values (11),(12);
set timestamp=1523466004.169435;
delete from t1 where pk in (11, 12);
+set timestamp= default;
#
# MDEV-18136 Server crashes in Item_func_dyncol_create::prepare_arguments
#
@@ -690,6 +691,41 @@ create table t1 (a int) with system versioning partition by system_time
(partition p1 history, partition pn current);
alter table t1 add partition (partition p2);
ERROR HY000: Wrong partitioning type, expected type: `SYSTEM_TIME`
+# MDEV-17891 Assertion failures in select_insert::abort_result_set and
+# mysql_load upon attempt to replace into a full table
+set @@max_heap_table_size= 1024*1024;
+create or replace table t1 (
+pk integer auto_increment,
+primary key (pk),
+f varchar(45000)
+) with system versioning engine=memory
+partition by system_time interval 1 year (partition p1 history,
+partition pn current);
+# fill the table until full
+insert into t1 () values (),(),(),(),(),(),(),(),(),(),(),(),(),(),(),();
+insert into t1 (f) select f from t1;
+ERROR HY000: The table 't1' is full
+# leave space for exactly one record in current partition
+delete from t1 where pk = 1;
+# copy all data into history partition
+replace into t1 select * from t1;
+replace into t1 select * from t1;
+ERROR HY000: The table 't1' is full
+create or replace table t1 (
+pk integer auto_increment,
+primary key (pk),
+f varchar(45000)
+) with system versioning engine=memory
+partition by system_time interval 1 year (partition p1 history,
+partition pn current);
+insert into t1 () values (),(),(),(),(),(),(),(),(),(),(),(),(),(),(),();
+select * into outfile 'load.data' from t1;
+load data infile 'load.data' replace into table t1;
+load data infile 'load.data' replace into table t1;
+ERROR HY000: The table 't1' is full
+load data infile 'load.data' replace into table t1;
+ERROR HY000: The table 't1' is full
+set @@max_heap_table_size= 1048576;
drop table t1;
#
# MDEV-22178 Assertion `info->alias.str' failed in partition_info::check_partition_info instead of ER_VERS_WRONG_PARTS
diff --git a/mysql-test/suite/versioning/r/replace.result b/mysql-test/suite/versioning/r/replace.result
index bda61f118b0..57a992cce49 100644
--- a/mysql-test/suite/versioning/r/replace.result
+++ b/mysql-test/suite/versioning/r/replace.result
@@ -48,3 +48,16 @@ INSERT INTO t1 () VALUES (),(),(),(),(),();
UPDATE IGNORE t1 SET f = 1;
REPLACE t1 SELECT * FROM t1;
DROP TABLE t1;
+# MDEV-22540 ER_DUP_ENTRY upon REPLACE or Assertion failed
+set timestamp=1589245268.41934;
+create table t1 (a int primary key) with system versioning;
+insert into t1 values (1),(2);
+connect con1,localhost,root,,test;
+set timestamp=1589245268.52093;
+replace into t1 values (1),(2);
+connection default;
+replace into t1 values (1),(2);
+connection con1;
+replace into t1 values (1),(2);
+ERROR 23000: Duplicate entry '1' for key 'PRIMARY'
+drop table t1;
diff --git a/mysql-test/suite/versioning/t/partition.test b/mysql-test/suite/versioning/t/partition.test
index f9163b09517..f50e6c07b64 100644
--- a/mysql-test/suite/versioning/t/partition.test
+++ b/mysql-test/suite/versioning/t/partition.test
@@ -473,6 +473,7 @@ set timestamp=1523466002.799571;
insert into t1 values (11),(12);
set timestamp=1523466004.169435;
delete from t1 where pk in (11, 12);
+set timestamp= default;
--echo #
--echo # MDEV-18136 Server crashes in Item_func_dyncol_create::prepare_arguments
@@ -640,7 +641,51 @@ create table t1 (a int) with system versioning partition by system_time
--error ER_PARTITION_WRONG_TYPE
alter table t1 add partition (partition p2);
+--echo # MDEV-17891 Assertion failures in select_insert::abort_result_set and
+--echo # mysql_load upon attempt to replace into a full table
+
+--let $max_heap_table_size_orig= `select @@max_heap_table_size;`
+set @@max_heap_table_size= 1024*1024;
+create or replace table t1 (
+ pk integer auto_increment,
+ primary key (pk),
+ f varchar(45000)
+) with system versioning engine=memory
+ partition by system_time interval 1 year (partition p1 history,
+ partition pn current);
+
+--echo # fill the table until full
+insert into t1 () values (),(),(),(),(),(),(),(),(),(),(),(),(),(),(),();
+--error ER_RECORD_FILE_FULL
+insert into t1 (f) select f from t1;
+--echo # leave space for exactly one record in current partition
+delete from t1 where pk = 1;
+--echo # copy all data into history partition
+replace into t1 select * from t1;
+--error ER_RECORD_FILE_FULL
+replace into t1 select * from t1;
+
+create or replace table t1 (
+ pk integer auto_increment,
+ primary key (pk),
+ f varchar(45000)
+) with system versioning engine=memory
+ partition by system_time interval 1 year (partition p1 history,
+ partition pn current);
+
+insert into t1 () values (),(),(),(),(),(),(),(),(),(),(),(),(),(),(),();
+
+select * into outfile 'load.data' from t1;
+load data infile 'load.data' replace into table t1;
+--error ER_RECORD_FILE_FULL
+load data infile 'load.data' replace into table t1;
+--error ER_RECORD_FILE_FULL
+load data infile 'load.data' replace into table t1;
+
# Cleanup
+--let $datadir= `select @@datadir`
+--remove_file $datadir/test/load.data
+eval set @@max_heap_table_size= $max_heap_table_size_orig;
drop table t1;
--echo #
diff --git a/mysql-test/suite/versioning/t/replace.test b/mysql-test/suite/versioning/t/replace.test
index 392c0ffcf35..83489f4a4b9 100644
--- a/mysql-test/suite/versioning/t/replace.test
+++ b/mysql-test/suite/versioning/t/replace.test
@@ -59,4 +59,22 @@ UPDATE IGNORE t1 SET f = 1;
REPLACE t1 SELECT * FROM t1;
DROP TABLE t1;
+--echo # MDEV-22540 ER_DUP_ENTRY upon REPLACE or Assertion failed
+set timestamp=1589245268.41934;
+create table t1 (a int primary key) with system versioning;
+insert into t1 values (1),(2);
+
+--connect (con1,localhost,root,,test)
+set timestamp=1589245268.52093;
+replace into t1 values (1),(2);
+
+--connection default
+replace into t1 values (1),(2);
+
+--connection con1
+--error ER_DUP_ENTRY
+replace into t1 values (1),(2);
+
+drop table t1;
+
--source suite/versioning/common_finish.inc
diff --git a/plugin/server_audit/server_audit.c b/plugin/server_audit/server_audit.c
index d3f75dae927..0bfe1232a0c 100644
--- a/plugin/server_audit/server_audit.c
+++ b/plugin/server_audit/server_audit.c
@@ -1581,22 +1581,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;
if (!name)
return 0;
- len= strlen(name);
if (take_lock)
flogger_mutex_lock(&lock_operations);
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)
- 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
result= 1;
@@ -2137,7 +2142,9 @@ void auditing(MYSQL_THD thd, unsigned int event_class, const void *ev)
}
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 *) ev;
@@ -2157,7 +2164,8 @@ void auditing(MYSQL_THD thd, unsigned int event_class, const void *ev)
{
const struct mysql_event_table *event =
(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)
{
diff --git a/sql/log_event.cc b/sql/log_event.cc
index 6871eeda79e..e3079c5f9b9 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -11267,7 +11267,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
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())
(*lex->first_not_own_table()->prev_global)= NULL;
@@ -11282,6 +11282,8 @@ int Rows_log_event::do_apply_event(rpl_group_info *rgi)
TABLE* table;
DBUG_ENTER("Rows_log_event::do_apply_event(Relay_log_info*)");
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
contain any data. In that case, we just remove all tables in the
@@ -11372,27 +11374,29 @@ int Rows_log_event::do_apply_event(rpl_group_info *rgi)
DBUG_ASSERT(!debug_sync_set_action(thd, STRING_WITH_LEN(action)));
};);
- 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.
+ /*
+ 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;
+ 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)
+ for (TABLE_LIST *tables= rgi->tables_to_lock; tables;
+ tables= tables->next_global)
+ {
+ if (slave_run_triggers_for_rbr)
{
tables->trg_event_map= new_trg_event_map;
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)))
{
@@ -11749,8 +11753,7 @@ int Rows_log_event::do_apply_event(rpl_group_info *rgi)
}
/* 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 (WSREP(thd) && wsrep_thd_is_applying(thd))
@@ -11769,8 +11772,7 @@ int Rows_log_event::do_apply_event(rpl_group_info *rgi)
DBUG_RETURN(error);
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);
DBUG_RETURN(error);
}
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 8bb601160d6..31d478032d8 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -4693,7 +4693,72 @@ add_internal_tables(THD *thd, Query_tables_list *prelocking_ctx,
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
@@ -4740,53 +4805,20 @@ handle_table(THD *thd, Query_tables_list *prelocking_ctx,
if (table->file->referenced_by_foreign_key())
{
- List <FOREIGN_KEY_INFO> fk_list;
- List_iterator<FOREIGN_KEY_INFO> fk_list_it(fk_list);
- FOREIGN_KEY_INFO *fk;
- Query_arena *arena, backup;
-
- 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);
+ if (prepare_fk_prelocking_list(thd, prelocking_ctx, table_list,
+ need_prelocking,
+ table_list->trg_event_map))
+ return TRUE;
}
}
+ 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) */
DBUG_PRINT("info", ("table: %p name: %s db: %s flags: %u",
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index ceb8dc1ade8..9d1588a7ce1 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -1,6 +1,6 @@
/*
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
it under the terms of the GNU General Public License as published by
@@ -1512,16 +1512,29 @@ void THD::reset_db(const LEX_CSTRING *new_db)
/* Do operations that may take a long time */
-void THD::cleanup(void)
+void THD::cleanup(bool have_mutex)
{
DBUG_ENTER("THD::cleanup");
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 WITH_WSREP
if (wsrep_cs().state() != wsrep::client_state::s_none)
{
- wsrep_cs().cleanup();
+ if (have_mutex)
+ {
+ mysql_mutex_assert_owner(static_cast<mysql_mutex_t*>
+ (m_wsrep_mutex.native()));
+ // Below wsrep-lib function will not acquire any mutexes
+ wsrep::unique_lock<wsrep::mutex> lock(m_wsrep_mutex, std::adopt_lock);
+ wsrep_cs().cleanup(lock);
+ lock.release();
+ }
+ else
+ wsrep_cs().cleanup();
}
wsrep_client_thread= false;
#endif /* WITH_WSREP */
@@ -1598,16 +1611,38 @@ void THD::cleanup(void)
void THD::free_connection()
{
DBUG_ASSERT(free_connection_done == 0);
- my_free((char*) db.str);
+ /* Make sure threads are not available via server_threads. */
+ assert_not_linked();
+
+ /*
+ 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= nullptr;
+#endif /* WITH_WSREP */
+ my_free(const_cast<char*>(db.str));
db= null_clex_str;
#ifndef EMBEDDED_LIBRARY
if (net.vio)
vio_delete(net.vio);
- net.vio= 0;
+ net.vio= nullptr;
net_end(&net);
#endif
- if (!cleanup_done)
- cleanup();
+ if (!cleanup_done)
+ cleanup(true); // We have locked THD::LOCK_thd_kill
ha_close_connection(this);
plugin_thdvar_cleanup(this);
mysql_audit_free_thd(this);
@@ -1618,6 +1653,8 @@ void THD::free_connection()
#if defined(ENABLED_PROFILING)
profiling.restart(); // Reset profiling
#endif
+ mysql_mutex_unlock(&LOCK_thd_kill);
+ mysql_mutex_unlock(&LOCK_thd_data);
}
/*
@@ -1671,24 +1708,10 @@ THD::~THD()
if (!status_in_global)
add_status_to_global();
- /*
- Other threads may have a lock on 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 delete
- */
- if (WSREP_NNULL(this)) mysql_mutex_lock(&LOCK_thd_data);
- mysql_mutex_lock(&LOCK_thd_kill);
- mysql_mutex_unlock(&LOCK_thd_kill);
- if (WSREP_NNULL(this)) mysql_mutex_unlock(&LOCK_thd_data);
-
if (!free_connection_done)
free_connection();
#ifdef WITH_WSREP
- if (wsrep_rgi != NULL) {
- delete wsrep_rgi;
- wsrep_rgi = NULL;
- }
mysql_cond_destroy(&COND_wsrep_thd);
#endif
mdl_context.destroy();
@@ -3142,12 +3165,12 @@ static File create_file(THD *thd, char *path, sql_exchange *exchange,
}
/* Create the file world readable */
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;
#ifdef HAVE_FCHMOD
- (void) fchmod(file, 0666); // Because of umask()
+ (void) fchmod(file, 0644); // Because of umask()
#else
- (void) chmod(path, 0666);
+ (void) chmod(path, 0644);
#endif
if (init_io_cache(cache, file, 0L, WRITE_CACHE, 0L, 1, MYF(MY_WME)))
{
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 936437fac17..a5857a6feb0 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -3281,7 +3281,7 @@ public:
void update_all_stats();
void update_stats(void);
void change_user(void);
- void cleanup(void);
+ void cleanup(bool have_mutex=false);
void cleanup_after_query();
void free_connection();
void reset_for_reuse();
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index 8c0e5f0c0ee..c728203f07a 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -1981,6 +1981,8 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
if (likely(!error))
{
info->deleted++;
+ if (!table->file->has_transactions())
+ thd->transaction.stmt.modified_non_trans_table= TRUE;
if (table->versioned(VERS_TIMESTAMP))
{
store_record(table, record[2]);
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index ee504028074..0312bc9a758 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -1,5 +1,5 @@
/* 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
it under the terms of the GNU General Public License as published by
@@ -118,6 +118,8 @@ When one supplies long data for a placeholder:
#include <mysql.h>
#else
#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
#include "lock.h" // MYSQL_OPEN_FORCE_SHARED_MDL
#include "sql_handler.h"
@@ -127,9 +129,6 @@ When one supplies long data for a placeholder:
#include "wsrep_trans_observer.h"
#endif /* WITH_WSREP */
-/* 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.
*/
diff --git a/sql/sql_update.cc b/sql/sql_update.cc
index eac8d91b3f5..e8a9bd771f7 100644
--- a/sql/sql_update.cc
+++ b/sql/sql_update.cc
@@ -2485,6 +2485,7 @@ int multi_update::send_data(List<Item> &not_used_values)
{
TABLE_LIST *cur_table;
DBUG_ENTER("multi_update::send_data");
+ int error= 0;
for (cur_table= update_tables; cur_table; cur_table= cur_table->next_local)
{
@@ -2531,7 +2532,6 @@ int multi_update::send_data(List<Item> &not_used_values)
found++;
if (!can_compare_record || compare_record(table))
{
- int error;
if ((error= cur_table->view_check_option(thd, ignore)) !=
VIEW_CHECK_OK)
@@ -2614,7 +2614,6 @@ error:
}
else
{
- int error;
TABLE *tmp_table= tmp_tables[offset];
if (copy_funcs(tmp_table_param[offset].items_to_copy, thd))
DBUG_RETURN(1);
diff --git a/sql/table.h b/sql/table.h
index 6073e35fa85..4c9efdf3529 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -2577,8 +2577,12 @@ struct TABLE_LIST
Indicates what triggers we need to pre-load for this TABLE_LIST
when opening an associated TABLE. This is filled after
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. */
bool optimized_away;
diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc
index d5f58827e3b..d4c15c0b254 100644
--- a/storage/innobase/handler/ha_innodb.cc
+++ b/storage/innobase/handler/ha_innodb.cc
@@ -4,7 +4,7 @@ Copyright (c) 2000, 2020, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2008, 2009 Google Inc.
Copyright (c) 2009, Percona Inc.
Copyright (c) 2012, Facebook Inc.
-Copyright (c) 2013, 2020, MariaDB Corporation.
+Copyright (c) 2013, 2021, MariaDB Corporation.
Portions of this file contain modifications contributed and copyrighted by
Google, Inc. Those modifications are gratefully acknowledged and are described
@@ -5066,7 +5066,7 @@ rollback_and_free:
DBUG_RETURN(0);
}
-UNIV_INTERN void lock_cancel_waiting_and_release(lock_t* lock);
+void lock_cancel_waiting_and_release(lock_t *lock);
/** Cancel any pending lock request associated with the current THD.
@sa THD::awake() @sa ha_kill_query() */
@@ -5076,6 +5076,7 @@ static void innobase_kill_query(handlerton*, THD *thd, enum thd_kill_levels)
if (trx_t* trx= thd_to_trx(thd))
{
+ ut_ad(trx->mysql_thd == thd);
#ifdef WITH_WSREP
if (trx->is_wsrep() && wsrep_thd_is_aborting(thd))
/* if victim has been signaled by BF thread and/or aborting is already
@@ -5084,28 +5085,13 @@ static void innobase_kill_query(handlerton*, THD *thd, enum thd_kill_levels)
DBUG_VOID_RETURN;
#endif /* WITH_WSREP */
lock_mutex_enter();
- mutex_enter(&trx_sys.mutex);
- trx_mutex_enter(trx);
- /* It is possible that innobase_close_connection() is concurrently
- 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)
+ if (lock_t *lock= trx->lock.wait_lock)
+ {
+ trx_mutex_enter(trx);
lock_cancel_waiting_and_release(lock);
+ trx_mutex_exit(trx);
+ }
lock_mutex_exit();
- trx_mutex_exit(trx);
}
DBUG_VOID_RETURN;
diff --git a/storage/innobase/handler/handler0alter.cc b/storage/innobase/handler/handler0alter.cc
index d473f9dec8f..2734bd5a77b 100644
--- a/storage/innobase/handler/handler0alter.cc
+++ b/storage/innobase/handler/handler0alter.cc
@@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 2005, 2019, Oracle and/or its affiliates. All Rights Reserved.
-Copyright (c) 2013, 2020, MariaDB Corporation.
+Copyright (c) 2013, 2021, 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
@@ -10150,6 +10150,44 @@ innobase_page_compression_try(
DBUG_RETURN(false);
}
+static
+void
+dict_stats_try_drop_table(THD *thd, const table_name_t &name,
+ const LEX_CSTRING &table_name)
+{
+ char errstr[1024];
+ if (dict_stats_drop_table(name.m_name, errstr, sizeof(errstr)) != DB_SUCCESS)
+ {
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_ALTER_INFO,
+ "Deleting persistent statistics"
+ " for table '%s' in InnoDB failed: %s",
+ table_name.str,
+ errstr);
+ }
+}
+
+/** Evict the table from cache and reopen it. Drop outdated statistics.
+ @param thd mariadb THD entity
+ @param table innodb table
+ @param maria_table_name user-friendly table name for errors
+ @return newly opened table */
+static
+dict_table_t*
+innobase_reload_table(THD *thd, dict_table_t *table,
+ const LEX_CSTRING &table_name)
+{
+ char *tb_name= strdup(table->name.m_name);
+ dict_table_close(table, true, false);
+ dict_sys.remove(table);
+ table= dict_table_open_on_name(tb_name, TRUE, TRUE,
+ DICT_ERR_IGNORE_FK_NOKEY);
+
+ /* Drop outdated table stats. */
+ dict_stats_try_drop_table(thd, table->name, table_name);
+ free(tb_name);
+ return table;
+}
+
/** Commit the changes made during prepare_inplace_alter_table()
and inplace_alter_table() inside the data dictionary tables,
when not rebuilding the table.
@@ -11358,44 +11396,25 @@ foreign_fail:
Currently dict_load_column_low() is the only place where
num_base for virtual columns is assigned to nonzero. */
if (ctx0->num_to_drop_vcol || ctx0->num_to_add_vcol
+ || (ctx0->new_table->n_v_cols && !new_clustered
+ && (ha_alter_info->alter_info->drop_list.elements
+ || ha_alter_info->alter_info->create_list.elements))
|| (ctx0->is_instant()
&& m_prebuilt->table->n_v_cols
&& ha_alter_info->handler_flags & ALTER_STORED_COLUMN_ORDER)) {
- /* FIXME: this workaround does not seem to work with
- partitioned tables */
DBUG_ASSERT(ctx0->old_table->get_ref_count() == 1);
-
trx_commit_for_mysql(m_prebuilt->trx);
- char tb_name[NAME_LEN * 2 + 1 + 1];
- strcpy(tb_name, m_prebuilt->table->name.m_name);
- dict_table_close(m_prebuilt->table, true, false);
if (ctx0->is_instant()) {
for (unsigned i = ctx0->old_n_v_cols; i--; ) {
ctx0->old_v_cols[i].~dict_v_col_t();
}
const_cast<unsigned&>(ctx0->old_n_v_cols) = 0;
}
- dict_sys.remove(m_prebuilt->table);
- m_prebuilt->table = dict_table_open_on_name(
- tb_name, TRUE, TRUE, DICT_ERR_IGNORE_FK_NOKEY);
- /* Drop outdated table stats. */
- char errstr[1024];
- if (dict_stats_drop_table(
- m_prebuilt->table->name.m_name,
- errstr, sizeof(errstr))
- != DB_SUCCESS) {
- push_warning_printf(
- m_user_thd,
- Sql_condition::WARN_LEVEL_WARN,
- ER_ALTER_INFO,
- "Deleting persistent statistics"
- " for table '%s' in"
- " InnoDB failed: %s",
- table->s->table_name.str,
- errstr);
- }
+ m_prebuilt->table = innobase_reload_table(m_user_thd,
+ m_prebuilt->table,
+ table->s->table_name);
row_mysql_unlock_data_dictionary(trx);
trx->free();
@@ -11455,25 +11474,12 @@ foreign_fail:
old copy of the table (which was renamed to
ctx->tmp_name). */
- char errstr[1024];
-
DBUG_ASSERT(0 == strcmp(ctx->old_table->name.m_name,
ctx->tmp_name));
- if (dict_stats_drop_table(
- ctx->new_table->name.m_name,
- errstr, sizeof(errstr))
- != DB_SUCCESS) {
- push_warning_printf(
- m_user_thd,
- Sql_condition::WARN_LEVEL_WARN,
- ER_ALTER_INFO,
- "Deleting persistent statistics"
- " for rebuilt table '%s' in"
- " InnoDB failed: %s",
- table->s->table_name.str,
- errstr);
- }
+ dict_stats_try_drop_table(m_user_thd,
+ ctx->new_table->name,
+ table->s->table_name);
DBUG_EXECUTE_IF("ib_ddl_crash_before_commit",
DBUG_SUICIDE(););
diff --git a/storage/innobase/include/que0que.h b/storage/innobase/include/que0que.h
index c8e1f92e670..f018f73527d 100644
--- a/storage/innobase/include/que0que.h
+++ b/storage/innobase/include/que0que.h
@@ -1,7 +1,7 @@
/*****************************************************************************
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
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 */
enum que_thr_state_t {
QUE_THR_RUNNING,
- QUE_THR_PROCEDURE_WAIT,
/** 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
statements, this means the thread has done its task */
diff --git a/storage/innobase/lock/lock0lock.cc b/storage/innobase/lock/lock0lock.cc
index b6f4067c8c0..abee7cde995 100644
--- a/storage/innobase/lock/lock0lock.cc
+++ b/storage/innobase/lock/lock0lock.cc
@@ -6150,6 +6150,7 @@ lock_cancel_waiting_and_release(
ut_ad(lock_mutex_own());
ut_ad(trx_mutex_own(lock->trx));
+ ut_ad(lock->trx->state == TRX_STATE_ACTIVE);
lock->trx->lock.cancel = true;
diff --git a/storage/innobase/que/que0que.cc b/storage/innobase/que/que0que.cc
index 3f4810dcc0e..e98d50ea0fc 100644
--- a/storage/innobase/que/que0que.cc
+++ b/storage/innobase/que/que0que.cc
@@ -1,7 +1,7 @@
/*****************************************************************************
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
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_LOCK_WAIT:
- case QUE_THR_PROCEDURE_WAIT:
ut_error;
}
}
diff --git a/storage/innobase/row/row0mysql.cc b/storage/innobase/row/row0mysql.cc
index 5721bc7a64a..38248d8f2b7 100644
--- a/storage/innobase/row/row0mysql.cc
+++ b/storage/innobase/row/row0mysql.cc
@@ -4342,6 +4342,8 @@ row_rename_table_for_mysql(
"END;\n"
, FALSE, trx);
+ ut_ad(err != DB_DUPLICATE_KEY);
+
/* SYS_TABLESPACES and SYS_DATAFILES need to be updated if
the table is in a single-table tablespace. */
if (err != DB_SUCCESS || !dict_table_is_file_per_table(table)) {
diff --git a/storage/innobase/trx/trx0trx.cc b/storage/innobase/trx/trx0trx.cc
index 01cb842cc0b..8170170c193 100644
--- a/storage/innobase/trx/trx0trx.cc
+++ b/storage/innobase/trx/trx0trx.cc
@@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved.
-Copyright (c) 2015, 2020, MariaDB Corporation.
+Copyright (c) 2015, 2021, 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
@@ -414,9 +414,11 @@ void trx_t::free()
/* do not poison mutex */
MEM_NOACCESS(&id, sizeof id);
MEM_NOACCESS(&no, sizeof no);
- /* state is accessed by innobase_kill_connection() */
+ MEM_NOACCESS(&state, sizeof state);
MEM_NOACCESS(&is_recovered, sizeof is_recovered);
- /* wsrep is accessed by innobase_kill_connection() */
+#ifdef WITH_WSREP
+ MEM_NOACCESS(&wsrep, sizeof wsrep);
+#endif
MEM_NOACCESS(&read_view, sizeof read_view);
MEM_NOACCESS(&trx_list, sizeof trx_list);
MEM_NOACCESS(&lock, sizeof lock);
@@ -437,7 +439,7 @@ void trx_t::free()
MEM_NOACCESS(&start_time_micro, sizeof start_time_micro);
MEM_NOACCESS(&commit_lsn, sizeof commit_lsn);
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_offset, sizeof mysql_log_offset);
MEM_NOACCESS(&n_mysql_tables_in_use, sizeof n_mysql_tables_in_use);