From 620d520d680fc8849a518d0fe5172bcedb632564 Mon Sep 17 00:00:00 2001 From: Daniel Black Date: Thu, 29 Sep 2022 00:20:23 +1000 Subject: [PATCH 1/6] MDEV-29614 mariadb-upgrade calls mysql and mysql-check (#2279) rather than mariadb/mariadb-check --- client/mysql_upgrade.c | 6 +++--- mysql-test/main/mysql_upgrade.result | 6 +++--- mysql-test/main/mysql_upgrade.test | 12 ++++++------ 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/client/mysql_upgrade.c b/client/mysql_upgrade.c index 4e7ddec312f90..ad54b06101ebf 100644 --- a/client/mysql_upgrade.c +++ b/client/mysql_upgrade.c @@ -935,7 +935,7 @@ static int run_mysqlcheck_upgrade(my_bool mysql_db_only) return 0; } verbose("Phase %d/%d: Checking and upgrading %s", ++phase, phases_total, what); - print_conn_args("mysqlcheck"); + print_conn_args("mariadb-check"); retch= run_tool(mysqlcheck_path, NULL, /* Send output from mysqlcheck directly to screen */ defaults_file, @@ -1451,7 +1451,7 @@ int main(int argc, char **argv) } /* Find mysql */ - find_tool(mysql_path, IF_WIN("mysql.exe", "mysql"), self_name); + find_tool(mysql_path, IF_WIN("mariadb.exe", "mariadb"), self_name); open_mysql_upgrade_file(); @@ -1459,7 +1459,7 @@ int main(int argc, char **argv) exit(upgrade_already_done(0) == 0); /* Find mysqlcheck */ - find_tool(mysqlcheck_path, IF_WIN("mysqlcheck.exe", "mysqlcheck"), self_name); + find_tool(mysqlcheck_path, IF_WIN("mariadb-check.exe", "mariadb-check"), self_name); if (opt_systables_only && !opt_silent) printf("The --upgrade-system-tables option was used, user tables won't be touched.\n"); diff --git a/mysql-test/main/mysql_upgrade.result b/mysql-test/main/mysql_upgrade.result index f1872c17802af..e2155b522a30e 100644 --- a/mysql-test/main/mysql_upgrade.result +++ b/mysql-test/main/mysql_upgrade.result @@ -778,18 +778,18 @@ FLUSH PRIVILEGES; # This installation of MariaDB is already upgraded to MariaDB . There is no need to run mysql_upgrade again for MariaDB . -Looking for 'mysql' as: mysql +Looking for 'mariadb' as: mariadb This installation of MariaDB is already upgraded to MariaDB . There is no need to run mysql_upgrade again for MariaDB . # # MDEV-27279: mariadb_upgrade check-if-upgrade absence is do it # -Looking for 'mysql' as: mysql +Looking for 'mariadb' as: mariadb Empty or non existent ...mysql_upgrade_info. Assuming mysql_upgrade has to be run! # # MDEV-27279: mariadb_upgrade check-if-upgrade with minor version change # -Looking for 'mysql' as: mysql +Looking for 'mariadb' as: mariadb This installation of MariaDB is already upgraded to MariaDB . There is no need to run mysql_upgrade again for MariaDB . This installation of MariaDB is already upgraded to MariaDB . diff --git a/mysql-test/main/mysql_upgrade.test b/mysql-test/main/mysql_upgrade.test index 017a7cc742fce..19d490d73bd09 100644 --- a/mysql-test/main/mysql_upgrade.test +++ b/mysql-test/main/mysql_upgrade.test @@ -55,7 +55,7 @@ DROP USER mysqltest1@'%'; --echo Run mysql_upgrade with a non existing server socket --replace_result $MYSQLTEST_VARDIR var ---replace_regex /.*mysqlcheck.*: Got/mysqlcheck: Got/ /\([0-9|-]*\)/(errno)/ +--replace_regex /.*mariadb-check.*: Got/mariadb-check: Got/ /\([0-9|-]*\)/(errno)/ --error 1 # NC: Added --skip-version-check, as the version check would fail when # mysql_upgrade tries to get the server version. @@ -139,7 +139,7 @@ let $MYSQLD_DATADIR= `select @@datadir`; --echo Run mysql_upgrade with unauthorized access --error 1 --exec $MYSQL_UPGRADE --skip-verbose --user=root --password=wrong_password 2>&1 ---replace_regex /.*mysqlcheck.*: Got/mysqlcheck: Got/ /\([0-9|-]*\)/(errno)/ +--replace_regex /.*mariadb-check.*: Got/mariadb-check: Got/ /\([0-9|-]*\)/(errno)/ --error 1 --exec $MYSQL_UPGRADE --skip-verbose --skip-version-check --user=root --password=wrong_password 2>&1 @@ -290,7 +290,7 @@ FLUSH PRIVILEGES; --replace_regex /\d\d\.\d*\.\d*[^ .\n]*/MariaDB / --error 1 --exec $MYSQL_UPGRADE --check-if-upgrade-is-needed ---replace_regex /\d\d\.\d*\.\d*[^ .\n]*/MariaDB / /'mysql.* as:[^\n]*/'mysql' as: mysql/ +--replace_regex /\d\d\.\d*\.\d*[^ .\n]*/MariaDB / /'mariadb.* as:[^\n]*/'mariadb' as: mariadb/ --error 1 --exec $MYSQL_UPGRADE --check-if-upgrade-is-needed --verbose @@ -302,7 +302,7 @@ FLUSH PRIVILEGES; --replace_regex /[^ ]*mysql_upgrade_info/...mysql_upgrade_info/ --exec $MYSQL_UPGRADE --check-if-upgrade-is-needed ---replace_regex /'mysql.* as:[^\n]*/'mysql' as: mysql/ /open .* Assuming/open XXX. Assuming/ /[^ ]*mysql_upgrade_info/...mysql_upgrade_info/ +--replace_regex /'mariadb.* as:[^\n]*/'mariadb' as: mariadb/ /open .* Assuming/open XXX. Assuming/ /[^ ]*mysql_upgrade_info/...mysql_upgrade_info/ --exec $MYSQL_UPGRADE --check-if-upgrade-is-needed --verbose --echo # @@ -324,7 +324,7 @@ EOF --error 1 --exec $MYSQL_UPGRADE --check-if-upgrade-is-needed --silent ---replace_regex /\d\d\.\d*\.\d*[^ .\n]*/MariaDB / /'mysql.* as:[^\n]*/'mysql' as: mysql/ +--replace_regex /\d\d\.\d*\.\d*[^ .\n]*/MariaDB / /'mariadb.* as:[^\n]*/'mariadb' as: mariadb/ --error 1 --exec $MYSQL_UPGRADE --check-if-upgrade-is-needed --verbose --replace_regex /\d\d\.\d*\.\d*[^ .\n]*/MariaDB / @@ -351,7 +351,7 @@ EOF --exec $MYSQL_UPGRADE --check-if-upgrade-is-needed --silent --replace_regex /\d\d\.\d*\.\d*[^ .\n]*/MariaDB / --exec $MYSQL_UPGRADE --check-if-upgrade-is-needed ---replace_regex /\d\d\.\d*\.\d*[^ .\n]*/MariaDB / /'mysql.* as:[^\n]*/'mysql' as: mysql/ +--replace_regex /\d\d\.\d*\.\d*[^ .\n]*/MariaDB / /'mariadb.* as:[^\n]*/'mysql' as: mysql/ --exec $MYSQL_UPGRADE --check-if-upgrade-is-needed --verbose --remove_file $MYSQLD_DATADIR/mysql_upgrade_info drop table mysql.global_priv; From d7d3ad698abde16978e094b825e17a6ecd8604e8 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Wed, 28 Sep 2022 13:00:38 +0200 Subject: [PATCH 2/6] debug_sync: ignore "sort" kills and disconnects only "hard" kills will now interrupt debug_sync waits. this is needed to have debug_sync points that work during disconnect --- sql/debug_sync.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/debug_sync.cc b/sql/debug_sync.cc index 09aa069512730..b5de53be0000e 100644 --- a/sql/debug_sync.cc +++ b/sql/debug_sync.cc @@ -1467,7 +1467,7 @@ static void debug_sync_execute(THD *thd, st_debug_sync_action *action) the required dynamic memory allocated. */ while (stringcmp(&debug_sync_global.ds_signal, &action->wait_for) && - !thd->killed && opt_debug_sync_timeout) + !(thd->killed & KILL_HARD_BIT) && opt_debug_sync_timeout) { error= mysql_cond_timedwait(&debug_sync_global.ds_cond, &debug_sync_global.ds_mutex, From 74ac683a7e822f3287e75527d7d3a7ff1256c46c Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Wed, 28 Sep 2022 14:43:30 +0200 Subject: [PATCH 3/6] cleanup: kill test split it into debug and non-debug tests --- mysql-test/main/kill.result | 227 +---------------------- mysql-test/main/kill.test | 296 +---------------------------- mysql-test/main/kill_debug.result | 223 ++++++++++++++++++++++ mysql-test/main/kill_debug.test | 298 ++++++++++++++++++++++++++++++ 4 files changed, 529 insertions(+), 515 deletions(-) create mode 100644 mysql-test/main/kill_debug.result create mode 100644 mysql-test/main/kill_debug.test diff --git a/mysql-test/main/kill.result b/mysql-test/main/kill.result index 5d38bd900596a..797052710e86c 100644 --- a/mysql-test/main/kill.result +++ b/mysql-test/main/kill.result @@ -1,225 +1,7 @@ set local sql_mode=""; set global sql_mode=""; -SET DEBUG_SYNC = 'RESET'; -DROP TABLE IF EXISTS t1, t2, t3; -DROP FUNCTION IF EXISTS MY_KILL; -CREATE FUNCTION MY_KILL(tid INT) RETURNS INT -BEGIN -DECLARE CONTINUE HANDLER FOR SQLEXCEPTION BEGIN END; -KILL tid; -RETURN (SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.PROCESSLIST WHERE ID = tid); -END| -connect con1, localhost, root,,; -connect con2, localhost, root,,; -connection con1; -connection con2; -connection con1; -SET DEBUG_SYNC= 'thread_end SIGNAL con1_end'; -SET DEBUG_SYNC= 'before_do_command_net_read SIGNAL con1_read'; -connection con2; -SET DEBUG_SYNC='now WAIT_FOR con1_read'; -SET DEBUG_SYNC= 'now WAIT_FOR con1_end'; -SET DEBUG_SYNC = 'RESET'; -connection con1; -SELECT 1; -Got one of the listed errors -SELECT 1; -1 -1 -SELECT @id != CONNECTION_ID(); -@id != CONNECTION_ID() -1 -connection con2; -SELECT 4; -4 -4 -connection default; -KILL (SELECT COUNT(*) FROM mysql.user); -ERROR 42000: KILL does not support subqueries or stored functions -connection con1; -connection con2; -connection con1; -SET DEBUG_SYNC= 'thread_end SIGNAL con1_end'; -SET DEBUG_SYNC= 'before_do_command_net_read SIGNAL con1_read WAIT_FOR kill'; -connection con2; -SET DEBUG_SYNC= 'now WAIT_FOR con1_read'; -SET DEBUG_SYNC= 'now WAIT_FOR con1_end'; -SET DEBUG_SYNC = 'RESET'; -connection con1; -SELECT 1; -Got one of the listed errors -SELECT 1; -1 -1 -SELECT @id != CONNECTION_ID(); -@id != CONNECTION_ID() -1 -connection con2; -SELECT 4; -4 -4 -connection default; -CREATE TABLE t1 (id INT PRIMARY KEY AUTO_INCREMENT); -CREATE TABLE t2 (id INT UNSIGNED NOT NULL); -INSERT INTO t1 VALUES -(0),(0),(0),(0),(0),(0),(0),(0), (0),(0),(0),(0),(0),(0),(0),(0), -(0),(0),(0),(0),(0),(0),(0),(0), (0),(0),(0),(0),(0),(0),(0),(0), -(0),(0),(0),(0),(0),(0),(0),(0), (0),(0),(0),(0),(0),(0),(0),(0), -(0),(0),(0),(0),(0),(0),(0),(0), (0),(0),(0),(0),(0),(0),(0),(0); -INSERT t1 SELECT 0 FROM t1 AS a1, t1 AS a2 LIMIT 4032; -INSERT INTO t2 SELECT id FROM t1; -connection con1; -connection con2; -connection con1; -SET DEBUG_SYNC= 'thread_end SIGNAL con1_end'; -SET DEBUG_SYNC= 'before_acos_function SIGNAL in_sync'; -SELECT id FROM t1 WHERE id IN -(SELECT DISTINCT a.id FROM t2 a, t2 b, t2 c, t2 d -GROUP BY ACOS(1/a.id), b.id, c.id, d.id -HAVING a.id BETWEEN 10 AND 20); -connection con2; -SET DEBUG_SYNC= 'now WAIT_FOR in_sync'; -KILL @id; -SET DEBUG_SYNC= 'now WAIT_FOR con1_end'; -connection con1; -Got one of the listed errors -SELECT 1; -1 -1 -connection default; -SET DEBUG_SYNC = 'RESET'; -DROP TABLE t1, t2; -connection con1; -connection con2; -connection con1; -SET DEBUG_SYNC= 'before_acos_function SIGNAL in_sync WAIT_FOR kill'; -SELECT ACOS(0); -connection con2; -SET DEBUG_SYNC= 'now WAIT_FOR in_sync'; -KILL QUERY @id; -connection con1; -ACOS(0) -1.5707963267948966 -SELECT 1; -1 -1 -SELECT @id = CONNECTION_ID(); -@id = CONNECTION_ID() -1 -connection default; -SET DEBUG_SYNC = 'RESET'; -CREATE TABLE t1 (f1 INT); -CREATE FUNCTION bug27563() RETURNS INT(11) -DETERMINISTIC -BEGIN -DECLARE CONTINUE HANDLER FOR SQLSTATE '70100' SET @a:= 'killed'; -DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET @a:= 'exception'; -SET DEBUG_SYNC= 'now SIGNAL in_sync WAIT_FOR kill'; -RETURN 1; -END| -connection con1; -connection con2; -connection con1; -INSERT INTO t1 VALUES (bug27563()); -connection con2; -SET DEBUG_SYNC= 'now WAIT_FOR in_sync'; -KILL QUERY @id; -connection con1; -ERROR 70100: Query execution was interrupted -SELECT * FROM t1; -f1 -connection default; -SET DEBUG_SYNC = 'RESET'; -INSERT INTO t1 VALUES(0); -connection con1; -UPDATE t1 SET f1= bug27563(); -connection con2; -SET DEBUG_SYNC= 'now WAIT_FOR in_sync'; -KILL QUERY @id; -connection con1; -ERROR 70100: Query execution was interrupted -SELECT * FROM t1; -f1 -0 -connection default; -SET DEBUG_SYNC = 'RESET'; -INSERT INTO t1 VALUES(1); -connection con1; -DELETE FROM t1 WHERE bug27563() IS NULL; -connection con2; -SET DEBUG_SYNC= 'now WAIT_FOR in_sync'; -KILL QUERY @id; -connection con1; -ERROR 70100: Query execution was interrupted -SELECT * FROM t1; -f1 -0 -1 -connection default; -SET DEBUG_SYNC = 'RESET'; -connection con1; -SELECT * FROM t1 WHERE f1= bug27563(); -connection con2; -SET DEBUG_SYNC= 'now WAIT_FOR in_sync'; -KILL QUERY @id; -connection con1; -ERROR 70100: Query execution was interrupted -SELECT * FROM t1; -f1 -0 -1 -connection default; -SET DEBUG_SYNC = 'RESET'; -DROP FUNCTION bug27563; -CREATE TABLE t2 (f2 INT); -CREATE TRIGGER trg27563 BEFORE INSERT ON t1 FOR EACH ROW -BEGIN -DECLARE CONTINUE HANDLER FOR SQLSTATE '70100' SET @a:= 'killed'; -DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET @a:= 'exception'; -INSERT INTO t2 VALUES(0); -SET DEBUG_SYNC= 'now SIGNAL in_sync WAIT_FOR kill'; -INSERT INTO t2 VALUES(1); -END| -connection con1; -INSERT INTO t1 VALUES(2),(3); -connection con2; -SET DEBUG_SYNC= 'now WAIT_FOR in_sync'; -KILL QUERY @id; -connection con1; -ERROR 70100: Query execution was interrupted -SELECT * FROM t1; -f1 -0 -1 -SELECT * FROM t2; -f2 -0 -connection default; -SET DEBUG_SYNC = 'RESET'; -DROP TABLE t1, t2; -# -# Bug#19723: kill of active connection yields different error code -# depending on platform. -# -connection con1; -SET DEBUG_SYNC= 'thread_end SIGNAL con1_end'; -KILL @id; -ERROR 70100: Connection was killed -connection con2; -SET DEBUG_SYNC= 'now WAIT_FOR con1_end'; -connection con1; -# ER_SERVER_SHUTDOWN, CR_SERVER_GONE_ERROR, CR_SERVER_LOST, -# depending on the timing of close of the connection socket -SELECT 1; -Got one of the listed errors -SELECT 1; -1 -1 -SELECT @id != CONNECTION_ID(); -@id != CONNECTION_ID() -1 -connection default; -SET DEBUG_SYNC = 'RESET'; +connect con1, localhost, root; +connect con2, localhost, root; # # Additional test for WL#3726 "DDL locking for all metadata objects" # Check that DDL and DML statements waiting for metadata locks can @@ -227,7 +9,6 @@ SET DEBUG_SYNC = 'RESET'; # can be tricky to write test case for some of them (e.g. REPAIR or # ALTER and other statements under LOCK TABLES). # -drop tables if exists t1, t2, t3; create table t1 (i int primary key); connect blocker, localhost, root, , ; connect dml, localhost, root, , ; @@ -380,9 +161,9 @@ connection default; ERROR 70100: Query execution was interrupted disconnect con5; DROP USER u1@localhost; -SET DEBUG_SYNC = 'RESET'; -DROP FUNCTION MY_KILL; set global sql_mode=default; +disconnect con1; +disconnect con2; # # MDEV-17998 # Deadlock and eventual Assertion `!table->pos_in_locked_tables' failed diff --git a/mysql-test/main/kill.test b/mysql-test/main/kill.test index 7dc5715882557..e40918ee6b2a8 100644 --- a/mysql-test/main/kill.test +++ b/mysql-test/main/kill.test @@ -8,294 +8,11 @@ # -- source include/not_embedded.inc --- source include/have_debug_sync.inc set local sql_mode=""; set global sql_mode=""; ---disable_warnings -SET DEBUG_SYNC = 'RESET'; -DROP TABLE IF EXISTS t1, t2, t3; -DROP FUNCTION IF EXISTS MY_KILL; ---enable_warnings - -delimiter |; -# Helper function used to repeatedly kill a session. -CREATE FUNCTION MY_KILL(tid INT) RETURNS INT -BEGIN - DECLARE CONTINUE HANDLER FOR SQLEXCEPTION BEGIN END; - KILL tid; - RETURN (SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.PROCESSLIST WHERE ID = tid); -END| -delimiter ;| - -connect (con1, localhost, root,,); -connect (con2, localhost, root,,); - -# Save id of con1 -connection con1; ---disable_reconnect -let $ID= `SELECT @id := CONNECTION_ID()`; -connection con2; -let $ignore= `SELECT @id := $ID`; -connection con1; -# Signal when this connection is terminating. -SET DEBUG_SYNC= 'thread_end SIGNAL con1_end'; -# See if we can kill read(). -# Run into read() immediately after hitting 'before_do_command_net_read'. -SET DEBUG_SYNC= 'before_do_command_net_read SIGNAL con1_read'; - -# Kill con1 -connection con2; -SET DEBUG_SYNC='now WAIT_FOR con1_read'; -# At this point we have no way to figure out, when con1 is blocked in -# reading from the socket. Sending KILL to early would not terminate -# con1. So we repeat KILL until con1 terminates. -let $wait_condition= SELECT MY_KILL(@id); ---source include/wait_condition.inc -# If KILL missed the read(), sync point wait will time out. -SET DEBUG_SYNC= 'now WAIT_FOR con1_end'; -SET DEBUG_SYNC = 'RESET'; - -connection con1; ---error 1053,2006,2013,5014 -SELECT 1; - ---enable_reconnect -# this should work, and we should have a new connection_id() -SELECT 1; -let $ignore= `SELECT @id := $ID`; -SELECT @id != CONNECTION_ID(); - -#make sure the server is still alive -connection con2; -SELECT 4; -connection default; - ---error ER_SUBQUERIES_NOT_SUPPORTED -KILL (SELECT COUNT(*) FROM mysql.user); - -connection con1; -let $ID= `SELECT @id := CONNECTION_ID()`; -connection con2; -let $ignore= `SELECT @id := $ID`; -connection con1; -disable_reconnect; -# Signal when this connection is terminating. -SET DEBUG_SYNC= 'thread_end SIGNAL con1_end'; -# See if we can kill the sync point itself. -# Wait in 'before_do_command_net_read' until killed. -# It doesn't wait for a signal 'kill' but for to be killed. -# The signal name doesn't matter here. -SET DEBUG_SYNC= 'before_do_command_net_read SIGNAL con1_read WAIT_FOR kill'; -connection con2; -SET DEBUG_SYNC= 'now WAIT_FOR con1_read'; -# Repeat KILL until con1 terminates. -let $wait_condition= SELECT MY_KILL(@id); ---source include/wait_condition.inc -SET DEBUG_SYNC= 'now WAIT_FOR con1_end'; -SET DEBUG_SYNC = 'RESET'; - -connection con1; ---error 1053,2006,2013,5014 -SELECT 1; -enable_reconnect; -SELECT 1; -let $ignore= `SELECT @id := $ID`; -SELECT @id != CONNECTION_ID(); -connection con2; -SELECT 4; -connection default; - -# -# BUG#14851: killing long running subquery processed via a temporary table. -# - -CREATE TABLE t1 (id INT PRIMARY KEY AUTO_INCREMENT); -CREATE TABLE t2 (id INT UNSIGNED NOT NULL); - -INSERT INTO t1 VALUES -(0),(0),(0),(0),(0),(0),(0),(0), (0),(0),(0),(0),(0),(0),(0),(0), -(0),(0),(0),(0),(0),(0),(0),(0), (0),(0),(0),(0),(0),(0),(0),(0), -(0),(0),(0),(0),(0),(0),(0),(0), (0),(0),(0),(0),(0),(0),(0),(0), -(0),(0),(0),(0),(0),(0),(0),(0), (0),(0),(0),(0),(0),(0),(0),(0); -INSERT t1 SELECT 0 FROM t1 AS a1, t1 AS a2 LIMIT 4032; - -INSERT INTO t2 SELECT id FROM t1; - -connection con1; -let $ID= `SELECT @id := CONNECTION_ID()`; -connection con2; -let $ignore= `SELECT @id := $ID`; - -connection con1; -SET DEBUG_SYNC= 'thread_end SIGNAL con1_end'; -SET DEBUG_SYNC= 'before_acos_function SIGNAL in_sync'; -# This is a very long running query. If this test start failing, -# it may be necessary to change to an even longer query. -send SELECT id FROM t1 WHERE id IN - (SELECT DISTINCT a.id FROM t2 a, t2 b, t2 c, t2 d - GROUP BY ACOS(1/a.id), b.id, c.id, d.id - HAVING a.id BETWEEN 10 AND 20); - -connection con2; -SET DEBUG_SYNC= 'now WAIT_FOR in_sync'; -KILL @id; -SET DEBUG_SYNC= 'now WAIT_FOR con1_end'; - -connection con1; ---error 1317,1053,2006,2013,5014 -reap; -SELECT 1; - -connection default; -SET DEBUG_SYNC = 'RESET'; -DROP TABLE t1, t2; - -# -# Test of blocking of sending ERROR after OK or EOF -# -connection con1; -let $ID= `SELECT @id := CONNECTION_ID()`; -connection con2; -let $ignore= `SELECT @id := $ID`; -connection con1; -SET DEBUG_SYNC= 'before_acos_function SIGNAL in_sync WAIT_FOR kill'; -send SELECT ACOS(0); -connection con2; -SET DEBUG_SYNC= 'now WAIT_FOR in_sync'; -KILL QUERY @id; -connection con1; -reap; -SELECT 1; -SELECT @id = CONNECTION_ID(); -connection default; -SET DEBUG_SYNC = 'RESET'; - -# -# Bug#27563: Stored functions and triggers wasn't throwing an error when killed. -# -CREATE TABLE t1 (f1 INT); -delimiter |; -CREATE FUNCTION bug27563() RETURNS INT(11) -DETERMINISTIC -BEGIN - DECLARE CONTINUE HANDLER FOR SQLSTATE '70100' SET @a:= 'killed'; - DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET @a:= 'exception'; - SET DEBUG_SYNC= 'now SIGNAL in_sync WAIT_FOR kill'; - RETURN 1; -END| -delimiter ;| -# Test stored functions -# Test INSERT -connection con1; -let $ID= `SELECT @id := CONNECTION_ID()`; -connection con2; -let $ignore= `SELECT @id := $ID`; -connection con1; -send INSERT INTO t1 VALUES (bug27563()); -connection con2; -SET DEBUG_SYNC= 'now WAIT_FOR in_sync'; -KILL QUERY @id; -connection con1; ---error 1317 -reap; -SELECT * FROM t1; -connection default; -SET DEBUG_SYNC = 'RESET'; - -# Test UPDATE -INSERT INTO t1 VALUES(0); -connection con1; -send UPDATE t1 SET f1= bug27563(); -connection con2; -SET DEBUG_SYNC= 'now WAIT_FOR in_sync'; -KILL QUERY @id; -connection con1; ---error 1317 -reap; -SELECT * FROM t1; -connection default; -SET DEBUG_SYNC = 'RESET'; - -# Test DELETE -INSERT INTO t1 VALUES(1); -connection con1; -send DELETE FROM t1 WHERE bug27563() IS NULL; -connection con2; -SET DEBUG_SYNC= 'now WAIT_FOR in_sync'; -KILL QUERY @id; -connection con1; ---error 1317 -reap; -SELECT * FROM t1; -connection default; -SET DEBUG_SYNC = 'RESET'; - -# Test SELECT -connection con1; -send SELECT * FROM t1 WHERE f1= bug27563(); -connection con2; -SET DEBUG_SYNC= 'now WAIT_FOR in_sync'; -KILL QUERY @id; -connection con1; ---error 1317 -reap; -SELECT * FROM t1; -connection default; -SET DEBUG_SYNC = 'RESET'; -DROP FUNCTION bug27563; - -# Test TRIGGERS -CREATE TABLE t2 (f2 INT); -delimiter |; -CREATE TRIGGER trg27563 BEFORE INSERT ON t1 FOR EACH ROW -BEGIN - DECLARE CONTINUE HANDLER FOR SQLSTATE '70100' SET @a:= 'killed'; - DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET @a:= 'exception'; - INSERT INTO t2 VALUES(0); - SET DEBUG_SYNC= 'now SIGNAL in_sync WAIT_FOR kill'; - INSERT INTO t2 VALUES(1); -END| -delimiter ;| -connection con1; -send INSERT INTO t1 VALUES(2),(3); -connection con2; -SET DEBUG_SYNC= 'now WAIT_FOR in_sync'; -KILL QUERY @id; -connection con1; ---error 1317 -reap; -SELECT * FROM t1; -SELECT * FROM t2; -connection default; -SET DEBUG_SYNC = 'RESET'; -DROP TABLE t1, t2; - ---echo # ---echo # Bug#19723: kill of active connection yields different error code ---echo # depending on platform. ---echo # - ---connection con1 -let $ID= `SELECT @id := CONNECTION_ID()`; -SET DEBUG_SYNC= 'thread_end SIGNAL con1_end'; ---disable_reconnect ---error ER_CONNECTION_KILLED -KILL @id; - -connection con2; -SET DEBUG_SYNC= 'now WAIT_FOR con1_end'; -connection con1; ---echo # ER_SERVER_SHUTDOWN, CR_SERVER_GONE_ERROR, CR_SERVER_LOST, ---echo # depending on the timing of close of the connection socket ---error 1053,2006,2013,5014 -SELECT 1; ---enable_reconnect -SELECT 1; -let $ignore= `SELECT @id := $ID`; -SELECT @id != CONNECTION_ID(); -connection default; -SET DEBUG_SYNC = 'RESET'; +connect con1, localhost, root; +connect con2, localhost, root; --echo # --echo # Additional test for WL#3726 "DDL locking for all metadata objects" @@ -304,9 +21,6 @@ SET DEBUG_SYNC = 'RESET'; --echo # can be tricky to write test case for some of them (e.g. REPAIR or --echo # ALTER and other statements under LOCK TABLES). --echo # ---disable_warnings -drop tables if exists t1, t2, t3; ---enable_warnings create table t1 (i int primary key); connect (blocker, localhost, root, , ); @@ -570,11 +284,9 @@ reap; disconnect con5; DROP USER u1@localhost; - -SET DEBUG_SYNC = 'RESET'; -DROP FUNCTION MY_KILL; - set global sql_mode=default; +disconnect con1; +disconnect con2; --echo # --echo # MDEV-17998 diff --git a/mysql-test/main/kill_debug.result b/mysql-test/main/kill_debug.result new file mode 100644 index 0000000000000..d0fadb5c7b5a3 --- /dev/null +++ b/mysql-test/main/kill_debug.result @@ -0,0 +1,223 @@ +set local sql_mode=""; +set global sql_mode=""; +CREATE FUNCTION MY_KILL(tid INT) RETURNS INT +BEGIN +DECLARE CONTINUE HANDLER FOR SQLEXCEPTION BEGIN END; +KILL tid; +RETURN (SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.PROCESSLIST WHERE ID = tid); +END| +connect con1, localhost, root; +connect con2, localhost, root; +connection con1; +connection con2; +connection con1; +SET DEBUG_SYNC= 'thread_end SIGNAL con1_end'; +SET DEBUG_SYNC= 'before_do_command_net_read SIGNAL con1_read'; +connection con2; +SET DEBUG_SYNC='now WAIT_FOR con1_read'; +SET DEBUG_SYNC= 'now WAIT_FOR con1_end'; +SET DEBUG_SYNC = 'RESET'; +connection con1; +SELECT 1; +Got one of the listed errors +SELECT 1; +1 +1 +SELECT @id != CONNECTION_ID(); +@id != CONNECTION_ID() +1 +connection con2; +SELECT 4; +4 +4 +connection default; +KILL (SELECT COUNT(*) FROM mysql.user); +ERROR 42000: KILL does not support subqueries or stored functions +connection con1; +connection con2; +connection con1; +SET DEBUG_SYNC= 'thread_end SIGNAL con1_end'; +SET DEBUG_SYNC= 'before_do_command_net_read SIGNAL con1_read WAIT_FOR kill'; +connection con2; +SET DEBUG_SYNC= 'now WAIT_FOR con1_read'; +SET DEBUG_SYNC= 'now WAIT_FOR con1_end'; +SET DEBUG_SYNC = 'RESET'; +connection con1; +SELECT 1; +Got one of the listed errors +SELECT 1; +1 +1 +SELECT @id != CONNECTION_ID(); +@id != CONNECTION_ID() +1 +connection con2; +SELECT 4; +4 +4 +connection default; +CREATE TABLE t1 (id INT PRIMARY KEY AUTO_INCREMENT); +CREATE TABLE t2 (id INT UNSIGNED NOT NULL); +INSERT INTO t1 VALUES +(0),(0),(0),(0),(0),(0),(0),(0), (0),(0),(0),(0),(0),(0),(0),(0), +(0),(0),(0),(0),(0),(0),(0),(0), (0),(0),(0),(0),(0),(0),(0),(0), +(0),(0),(0),(0),(0),(0),(0),(0), (0),(0),(0),(0),(0),(0),(0),(0), +(0),(0),(0),(0),(0),(0),(0),(0), (0),(0),(0),(0),(0),(0),(0),(0); +INSERT t1 SELECT 0 FROM t1 AS a1, t1 AS a2 LIMIT 4032; +INSERT INTO t2 SELECT id FROM t1; +connection con1; +connection con2; +connection con1; +SET DEBUG_SYNC= 'thread_end SIGNAL con1_end'; +SET DEBUG_SYNC= 'before_acos_function SIGNAL in_sync'; +SELECT id FROM t1 WHERE id IN +(SELECT DISTINCT a.id FROM t2 a, t2 b, t2 c, t2 d +GROUP BY ACOS(1/a.id), b.id, c.id, d.id +HAVING a.id BETWEEN 10 AND 20); +connection con2; +SET DEBUG_SYNC= 'now WAIT_FOR in_sync'; +KILL @id; +SET DEBUG_SYNC= 'now WAIT_FOR con1_end'; +connection con1; +Got one of the listed errors +SELECT 1; +1 +1 +connection default; +SET DEBUG_SYNC = 'RESET'; +DROP TABLE t1, t2; +connection con1; +connection con2; +connection con1; +SET DEBUG_SYNC= 'before_acos_function SIGNAL in_sync WAIT_FOR kill'; +SELECT ACOS(0); +connection con2; +SET DEBUG_SYNC= 'now WAIT_FOR in_sync'; +KILL QUERY @id; +connection con1; +ACOS(0) +1.5707963267948966 +SELECT 1; +1 +1 +SELECT @id = CONNECTION_ID(); +@id = CONNECTION_ID() +1 +connection default; +SET DEBUG_SYNC = 'RESET'; +CREATE TABLE t1 (f1 INT); +CREATE FUNCTION bug27563() RETURNS INT(11) +DETERMINISTIC +BEGIN +DECLARE CONTINUE HANDLER FOR SQLSTATE '70100' SET @a:= 'killed'; +DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET @a:= 'exception'; +SET DEBUG_SYNC= 'now SIGNAL in_sync WAIT_FOR kill'; +RETURN 1; +END| +connection con1; +connection con2; +connection con1; +INSERT INTO t1 VALUES (bug27563()); +connection con2; +SET DEBUG_SYNC= 'now WAIT_FOR in_sync'; +KILL QUERY @id; +connection con1; +ERROR 70100: Query execution was interrupted +SELECT * FROM t1; +f1 +connection default; +SET DEBUG_SYNC = 'RESET'; +INSERT INTO t1 VALUES(0); +connection con1; +UPDATE t1 SET f1= bug27563(); +connection con2; +SET DEBUG_SYNC= 'now WAIT_FOR in_sync'; +KILL QUERY @id; +connection con1; +ERROR 70100: Query execution was interrupted +SELECT * FROM t1; +f1 +0 +connection default; +SET DEBUG_SYNC = 'RESET'; +INSERT INTO t1 VALUES(1); +connection con1; +DELETE FROM t1 WHERE bug27563() IS NULL; +connection con2; +SET DEBUG_SYNC= 'now WAIT_FOR in_sync'; +KILL QUERY @id; +connection con1; +ERROR 70100: Query execution was interrupted +SELECT * FROM t1; +f1 +0 +1 +connection default; +SET DEBUG_SYNC = 'RESET'; +connection con1; +SELECT * FROM t1 WHERE f1= bug27563(); +connection con2; +SET DEBUG_SYNC= 'now WAIT_FOR in_sync'; +KILL QUERY @id; +connection con1; +ERROR 70100: Query execution was interrupted +SELECT * FROM t1; +f1 +0 +1 +connection default; +SET DEBUG_SYNC = 'RESET'; +DROP FUNCTION bug27563; +CREATE TABLE t2 (f2 INT); +CREATE TRIGGER trg27563 BEFORE INSERT ON t1 FOR EACH ROW +BEGIN +DECLARE CONTINUE HANDLER FOR SQLSTATE '70100' SET @a:= 'killed'; +DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET @a:= 'exception'; +INSERT INTO t2 VALUES(0); +SET DEBUG_SYNC= 'now SIGNAL in_sync WAIT_FOR kill'; +INSERT INTO t2 VALUES(1); +END| +connection con1; +INSERT INTO t1 VALUES(2),(3); +connection con2; +SET DEBUG_SYNC= 'now WAIT_FOR in_sync'; +KILL QUERY @id; +connection con1; +ERROR 70100: Query execution was interrupted +SELECT * FROM t1; +f1 +0 +1 +SELECT * FROM t2; +f2 +0 +connection default; +SET DEBUG_SYNC = 'RESET'; +DROP TABLE t1, t2; +# +# Bug#19723: kill of active connection yields different error code +# depending on platform. +# +connection con1; +SET DEBUG_SYNC= 'thread_end SIGNAL con1_end'; +KILL @id; +ERROR 70100: Connection was killed +connection con2; +SET DEBUG_SYNC= 'now WAIT_FOR con1_end'; +connection con1; +# ER_SERVER_SHUTDOWN, CR_SERVER_GONE_ERROR, CR_SERVER_LOST, +# depending on the timing of close of the connection socket +SELECT 1; +Got one of the listed errors +SELECT 1; +1 +1 +SELECT @id != CONNECTION_ID(); +@id != CONNECTION_ID() +1 +connection default; +SET DEBUG_SYNC = 'RESET'; +DROP FUNCTION MY_KILL; +set global sql_mode=default; +disconnect con1; +disconnect con2; diff --git a/mysql-test/main/kill_debug.test b/mysql-test/main/kill_debug.test new file mode 100644 index 0000000000000..ba6a0ced52884 --- /dev/null +++ b/mysql-test/main/kill_debug.test @@ -0,0 +1,298 @@ +# +# Test KILL and KILL QUERY statements. +# +# Killing a connection in an embedded server does not work like in a normal +# server, if it is waiting for a new statement. In an embedded server, the +# connection does not read() from a socket, but returns control to the +# application. 'mysqltest' does not handle the kill request. +# + +-- source include/not_embedded.inc +-- source include/have_debug_sync.inc +set local sql_mode=""; +set global sql_mode=""; + +delimiter |; +# Helper function used to repeatedly kill a session. +CREATE FUNCTION MY_KILL(tid INT) RETURNS INT +BEGIN + DECLARE CONTINUE HANDLER FOR SQLEXCEPTION BEGIN END; + KILL tid; + RETURN (SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.PROCESSLIST WHERE ID = tid); +END| +delimiter ;| + +connect con1, localhost, root; +connect con2, localhost, root; + +# Save id of con1 +connection con1; +--disable_reconnect +let $ID= `SELECT @id := CONNECTION_ID()`; +connection con2; +let $ignore= `SELECT @id := $ID`; +connection con1; +# Signal when this connection is terminating. +SET DEBUG_SYNC= 'thread_end SIGNAL con1_end'; +# See if we can kill read(). +# Run into read() immediately after hitting 'before_do_command_net_read'. +SET DEBUG_SYNC= 'before_do_command_net_read SIGNAL con1_read'; + +# Kill con1 +connection con2; +SET DEBUG_SYNC='now WAIT_FOR con1_read'; +# At this point we have no way to figure out, when con1 is blocked in +# reading from the socket. Sending KILL to early would not terminate +# con1. So we repeat KILL until con1 terminates. +let $wait_condition= SELECT MY_KILL(@id); +--source include/wait_condition.inc +# If KILL missed the read(), sync point wait will time out. +SET DEBUG_SYNC= 'now WAIT_FOR con1_end'; +SET DEBUG_SYNC = 'RESET'; + +connection con1; +--error 1053,2006,2013,5014 +SELECT 1; + +--enable_reconnect +# this should work, and we should have a new connection_id() +SELECT 1; +let $ignore= `SELECT @id := $ID`; +SELECT @id != CONNECTION_ID(); + +#make sure the server is still alive +connection con2; +SELECT 4; +connection default; + +--error ER_SUBQUERIES_NOT_SUPPORTED +KILL (SELECT COUNT(*) FROM mysql.user); + +connection con1; +let $ID= `SELECT @id := CONNECTION_ID()`; +connection con2; +let $ignore= `SELECT @id := $ID`; +connection con1; +disable_reconnect; +# Signal when this connection is terminating. +SET DEBUG_SYNC= 'thread_end SIGNAL con1_end'; +# See if we can kill the sync point itself. +# Wait in 'before_do_command_net_read' until killed. +# It doesn't wait for a signal 'kill' but for to be killed. +# The signal name doesn't matter here. +SET DEBUG_SYNC= 'before_do_command_net_read SIGNAL con1_read WAIT_FOR kill'; +connection con2; +SET DEBUG_SYNC= 'now WAIT_FOR con1_read'; +# Repeat KILL until con1 terminates. +let $wait_condition= SELECT MY_KILL(@id); +--source include/wait_condition.inc +SET DEBUG_SYNC= 'now WAIT_FOR con1_end'; +SET DEBUG_SYNC = 'RESET'; + +connection con1; +--error 1053,2006,2013,5014 +SELECT 1; +enable_reconnect; +SELECT 1; +let $ignore= `SELECT @id := $ID`; +SELECT @id != CONNECTION_ID(); +connection con2; +SELECT 4; +connection default; + +# +# BUG#14851: killing long running subquery processed via a temporary table. +# + +CREATE TABLE t1 (id INT PRIMARY KEY AUTO_INCREMENT); +CREATE TABLE t2 (id INT UNSIGNED NOT NULL); + +INSERT INTO t1 VALUES +(0),(0),(0),(0),(0),(0),(0),(0), (0),(0),(0),(0),(0),(0),(0),(0), +(0),(0),(0),(0),(0),(0),(0),(0), (0),(0),(0),(0),(0),(0),(0),(0), +(0),(0),(0),(0),(0),(0),(0),(0), (0),(0),(0),(0),(0),(0),(0),(0), +(0),(0),(0),(0),(0),(0),(0),(0), (0),(0),(0),(0),(0),(0),(0),(0); +INSERT t1 SELECT 0 FROM t1 AS a1, t1 AS a2 LIMIT 4032; + +INSERT INTO t2 SELECT id FROM t1; + +connection con1; +let $ID= `SELECT @id := CONNECTION_ID()`; +connection con2; +let $ignore= `SELECT @id := $ID`; + +connection con1; +SET DEBUG_SYNC= 'thread_end SIGNAL con1_end'; +SET DEBUG_SYNC= 'before_acos_function SIGNAL in_sync'; +# This is a very long running query. If this test start failing, +# it may be necessary to change to an even longer query. +send SELECT id FROM t1 WHERE id IN + (SELECT DISTINCT a.id FROM t2 a, t2 b, t2 c, t2 d + GROUP BY ACOS(1/a.id), b.id, c.id, d.id + HAVING a.id BETWEEN 10 AND 20); + +connection con2; +SET DEBUG_SYNC= 'now WAIT_FOR in_sync'; +KILL @id; +SET DEBUG_SYNC= 'now WAIT_FOR con1_end'; + +connection con1; +--error 1317,1053,2006,2013,5014 +reap; +SELECT 1; + +connection default; +SET DEBUG_SYNC = 'RESET'; +DROP TABLE t1, t2; + +# +# Test of blocking of sending ERROR after OK or EOF +# +connection con1; +let $ID= `SELECT @id := CONNECTION_ID()`; +connection con2; +let $ignore= `SELECT @id := $ID`; +connection con1; +SET DEBUG_SYNC= 'before_acos_function SIGNAL in_sync WAIT_FOR kill'; +send SELECT ACOS(0); +connection con2; +SET DEBUG_SYNC= 'now WAIT_FOR in_sync'; +KILL QUERY @id; +connection con1; +reap; +SELECT 1; +SELECT @id = CONNECTION_ID(); +connection default; +SET DEBUG_SYNC = 'RESET'; + +# +# Bug#27563: Stored functions and triggers wasn't throwing an error when killed. +# +CREATE TABLE t1 (f1 INT); +delimiter |; +CREATE FUNCTION bug27563() RETURNS INT(11) +DETERMINISTIC +BEGIN + DECLARE CONTINUE HANDLER FOR SQLSTATE '70100' SET @a:= 'killed'; + DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET @a:= 'exception'; + SET DEBUG_SYNC= 'now SIGNAL in_sync WAIT_FOR kill'; + RETURN 1; +END| +delimiter ;| +# Test stored functions +# Test INSERT +connection con1; +let $ID= `SELECT @id := CONNECTION_ID()`; +connection con2; +let $ignore= `SELECT @id := $ID`; +connection con1; +send INSERT INTO t1 VALUES (bug27563()); +connection con2; +SET DEBUG_SYNC= 'now WAIT_FOR in_sync'; +KILL QUERY @id; +connection con1; +--error 1317 +reap; +SELECT * FROM t1; +connection default; +SET DEBUG_SYNC = 'RESET'; + +# Test UPDATE +INSERT INTO t1 VALUES(0); +connection con1; +send UPDATE t1 SET f1= bug27563(); +connection con2; +SET DEBUG_SYNC= 'now WAIT_FOR in_sync'; +KILL QUERY @id; +connection con1; +--error 1317 +reap; +SELECT * FROM t1; +connection default; +SET DEBUG_SYNC = 'RESET'; + +# Test DELETE +INSERT INTO t1 VALUES(1); +connection con1; +send DELETE FROM t1 WHERE bug27563() IS NULL; +connection con2; +SET DEBUG_SYNC= 'now WAIT_FOR in_sync'; +KILL QUERY @id; +connection con1; +--error 1317 +reap; +SELECT * FROM t1; +connection default; +SET DEBUG_SYNC = 'RESET'; + +# Test SELECT +connection con1; +send SELECT * FROM t1 WHERE f1= bug27563(); +connection con2; +SET DEBUG_SYNC= 'now WAIT_FOR in_sync'; +KILL QUERY @id; +connection con1; +--error 1317 +reap; +SELECT * FROM t1; +connection default; +SET DEBUG_SYNC = 'RESET'; +DROP FUNCTION bug27563; + +# Test TRIGGERS +CREATE TABLE t2 (f2 INT); +delimiter |; +CREATE TRIGGER trg27563 BEFORE INSERT ON t1 FOR EACH ROW +BEGIN + DECLARE CONTINUE HANDLER FOR SQLSTATE '70100' SET @a:= 'killed'; + DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET @a:= 'exception'; + INSERT INTO t2 VALUES(0); + SET DEBUG_SYNC= 'now SIGNAL in_sync WAIT_FOR kill'; + INSERT INTO t2 VALUES(1); +END| +delimiter ;| +connection con1; +send INSERT INTO t1 VALUES(2),(3); +connection con2; +SET DEBUG_SYNC= 'now WAIT_FOR in_sync'; +KILL QUERY @id; +connection con1; +--error 1317 +reap; +SELECT * FROM t1; +SELECT * FROM t2; +connection default; +SET DEBUG_SYNC = 'RESET'; +DROP TABLE t1, t2; + +--echo # +--echo # Bug#19723: kill of active connection yields different error code +--echo # depending on platform. +--echo # + +--connection con1 +let $ID= `SELECT @id := CONNECTION_ID()`; +SET DEBUG_SYNC= 'thread_end SIGNAL con1_end'; +--disable_reconnect +--error ER_CONNECTION_KILLED +KILL @id; + +connection con2; +SET DEBUG_SYNC= 'now WAIT_FOR con1_end'; +connection con1; +--echo # ER_SERVER_SHUTDOWN, CR_SERVER_GONE_ERROR, CR_SERVER_LOST, +--echo # depending on the timing of close of the connection socket +--error 1053,2006,2013,5014 +SELECT 1; +--enable_reconnect +SELECT 1; +let $ignore= `SELECT @id := $ID`; +SELECT @id != CONNECTION_ID(); +connection default; + +SET DEBUG_SYNC = 'RESET'; +DROP FUNCTION MY_KILL; + +set global sql_mode=default; +disconnect con1; +disconnect con2; From de130323b4401ba9dfcb08ebd7f8d688cb317a80 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Wed, 28 Sep 2022 14:27:55 +0200 Subject: [PATCH 4/6] MDEV-29368 Assertion `trx->mysql_thd == thd' failed in innobase_kill_query from process_timers/timer_handler and use-after-poison in innobase_kill_query This is a 10.5 version of 9b750dcbd89e, fix for MDEV-23536 Race condition between KILL and transaction commit InnoDB needs to remove trx from thd before destroying it (trx), otherwise a concurrent KILL might get a pointer from thd to a destroyed trx. ha_close_connection() should allow engines to clear ha_data in hton->on close_connection(). To prevent the engine from being unloaded while hton->close_connection() is running, we remove the lock from ha_data and unlock the plugin manually. --- mysql-test/main/kill_debug.result | 16 ++++++++++++++++ mysql-test/main/kill_debug.test | 20 ++++++++++++++++++++ sql/handler.cc | 9 ++++----- sql/sql_class.cc | 2 ++ sql/sql_parse.cc | 2 +- storage/innobase/handler/ha_innodb.cc | 2 ++ 6 files changed, 45 insertions(+), 6 deletions(-) diff --git a/mysql-test/main/kill_debug.result b/mysql-test/main/kill_debug.result index d0fadb5c7b5a3..061e760238396 100644 --- a/mysql-test/main/kill_debug.result +++ b/mysql-test/main/kill_debug.result @@ -221,3 +221,19 @@ DROP FUNCTION MY_KILL; set global sql_mode=default; disconnect con1; disconnect con2; +# +# MDEV-29368 Assertion `trx->mysql_thd == thd' failed in innobase_kill_query from process_timers/timer_handler and use-after-poison in innobase_kill_query +# +connect foo,localhost,root; +create table t1 (a int) engine=innodb; +insert t1 values (1); +set debug_sync='THD_cleanup_after_set_killed SIGNAL go0 WAIT_FOR go1'; +set debug_sync='innobase_connection_closed SIGNAL go2 WAIT_FOR go3'; +disconnect foo; +connection default; +set debug_sync='now WAIT_FOR go0'; +set debug_sync='found_killee SIGNAL go1 WAIT_FOR go2'; +kill $id; +set debug_sync='now SIGNAL go3'; +drop table t1; +set debug_sync='reset'; diff --git a/mysql-test/main/kill_debug.test b/mysql-test/main/kill_debug.test index ba6a0ced52884..32a764004e3eb 100644 --- a/mysql-test/main/kill_debug.test +++ b/mysql-test/main/kill_debug.test @@ -9,6 +9,7 @@ -- source include/not_embedded.inc -- source include/have_debug_sync.inc +-- source include/have_innodb.inc set local sql_mode=""; set global sql_mode=""; @@ -296,3 +297,22 @@ DROP FUNCTION MY_KILL; set global sql_mode=default; disconnect con1; disconnect con2; + +--echo # +--echo # MDEV-29368 Assertion `trx->mysql_thd == thd' failed in innobase_kill_query from process_timers/timer_handler and use-after-poison in innobase_kill_query +--echo # +connect foo,localhost,root; +let $id=`select connection_id()`; +create table t1 (a int) engine=innodb; +insert t1 values (1); +set debug_sync='THD_cleanup_after_set_killed SIGNAL go0 WAIT_FOR go1'; +set debug_sync='innobase_connection_closed SIGNAL go2 WAIT_FOR go3'; +disconnect foo; + +connection default; +set debug_sync='now WAIT_FOR go0'; +set debug_sync='found_killee SIGNAL go1 WAIT_FOR go2'; +evalp kill $id; +set debug_sync='now SIGNAL go3'; +drop table t1; +set debug_sync='reset'; diff --git a/sql/handler.cc b/sql/handler.cc index 0d5609f753ac1..85600d02b63f5 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -894,15 +894,14 @@ void ha_close_connection(THD* thd) { for (auto i= 0; i < MAX_HA; i++) { - if (thd->ha_data[i].lock) + if (plugin_ref plugin= thd->ha_data[i].lock) { - handlerton *hton= plugin_hton(thd->ha_data[i].lock); + thd->ha_data[i].lock= NULL; + handlerton *hton= plugin_hton(plugin); if (hton->close_connection) hton->close_connection(hton, thd); - /* make sure SE didn't reset ha_data in close_connection() */ - DBUG_ASSERT(thd->ha_data[i].lock); - /* make sure ha_data is reset and ha_data_lock is released */ thd_set_ha_data(thd, hton, 0); + plugin_unlock(NULL, plugin); } DBUG_ASSERT(!thd->ha_data[i].ha_ptr); } diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 87526acbdbae2..87f5262f9cea5 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -1533,6 +1533,8 @@ void THD::cleanup(void) wsrep_client_thread= false; #endif /* WITH_WSREP */ + DEBUG_SYNC(this, "THD_cleanup_after_set_killed"); + mysql_ha_cleanup(this); locked_tables_list.unlock_locked_tables(this); diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index ec22b5396683c..643d671878fc3 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -9271,7 +9271,7 @@ kill_one_thread(THD *thd, longlong id, killed_state kill_signal, killed_type typ tmp= find_thread_by_id(id, type == KILL_TYPE_QUERY); if (!tmp) DBUG_RETURN(error); - + DEBUG_SYNC(thd, "found_killee"); if (tmp->get_command() != COM_DAEMON) { /* diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 25f2f4d635ffa..4a710a5c38440 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -4791,6 +4791,7 @@ static int innobase_close_connection(handlerton *hton, THD *thd) DBUG_ASSERT(hton == innodb_hton_ptr); if (auto trx= thd_to_trx(thd)) { + thd_set_ha_data(thd, innodb_hton_ptr, NULL); if (trx->state == TRX_STATE_PREPARED && trx->has_logged_persistent()) { trx_disconnect_prepared(trx); @@ -4798,6 +4799,7 @@ static int innobase_close_connection(handlerton *hton, THD *thd) } innobase_rollback_trx(trx); trx->free(); + DEBUG_SYNC(thd, "innobase_connection_closed"); } return 0; } From 6b685ea7b0776430d45b095cb4be3ef0739a3c04 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Wed, 28 Sep 2022 18:55:15 +0200 Subject: [PATCH 5/6] correctness assert thd_get_ha_data() can be used without a lock, but only from the current thd thread, when calling from anoher thread it *must* be protected by thd->LOCK_thd_data * fix group commit code to take thd->LOCK_thd_data * remove innobase_close_connection() from the innodb background thread, it's not needed after 87775402cd0c and was failing the assert with current_thd==0 --- include/my_pthread.h | 1 + sql/log.cc | 4 ++++ sql/sql_class.cc | 1 + storage/innobase/handler/ha_innodb.cc | 4 ---- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/include/my_pthread.h b/include/my_pthread.h index f5bfda49a28c3..1c83e77a41bb2 100644 --- a/include/my_pthread.h +++ b/include/my_pthread.h @@ -437,6 +437,7 @@ void safe_mutex_free_deadlock_data(safe_mutex_t *mp); #define my_cond_wait(A,B) safe_cond_wait((A), (B), __FILE__, __LINE__) #else +#define safe_mutex_is_owner(mp) (1) #define safe_mutex_assert_owner(mp) do {} while (0) #define safe_mutex_assert_not_owner(mp) do {} while (0) #define safe_mutex_setflags(mp, F) do {} while (0) diff --git a/sql/log.cc b/sql/log.cc index c679e8f5111fa..ec21282578718 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -8512,7 +8512,11 @@ MYSQL_BIN_LOG::trx_group_commit_leader(group_commit_entry *leader) ++num_commits; if (current->cache_mngr->using_xa && likely(!current->error) && DBUG_EVALUATE_IF("skip_commit_ordered", 0, 1)) + { + mysql_mutex_lock(¤t->thd->LOCK_thd_data); run_commit_ordered(current->thd, current->all); + mysql_mutex_unlock(¤t->thd->LOCK_thd_data); + } current->thd->wakeup_subsequent_commits(current->error); /* diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 87f5262f9cea5..cd80602a9f297 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -462,6 +462,7 @@ void thd_storage_lock_wait(THD *thd, long long value) extern "C" void *thd_get_ha_data(const THD *thd, const struct handlerton *hton) { + DBUG_ASSERT(thd == current_thd || mysql_mutex_is_owner(&thd->LOCK_thd_data)); return thd->ha_data[hton->slot].ha_ptr; } diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 4a710a5c38440..a3184abd2d34f 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -1484,10 +1484,6 @@ innobase_destroy_background_thd( /*============================*/ MYSQL_THD thd) { - /* need to close the connection explicitly, the server won't do it - if innodb is in the PLUGIN_IS_DYING state */ - innobase_close_connection(innodb_hton_ptr, thd); - thd_set_ha_data(thd, innodb_hton_ptr, NULL); destroy_background_thd(thd); } From e29fb956145cfa1f4f8c41cafcddea36a20b23aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Fri, 30 Sep 2022 08:25:00 +0300 Subject: [PATCH 6/6] Cleanup: Remove innobase_destroy_background_thd() We do not need a non-inline wrapper for the function destroy_background_thd(). --- storage/innobase/fts/fts0opt.cc | 2 +- storage/innobase/handler/ha_innodb.cc | 11 ----------- storage/innobase/include/ha_prototypes.h | 7 +++---- storage/innobase/srv/srv0srv.cc | 2 +- 4 files changed, 5 insertions(+), 17 deletions(-) diff --git a/storage/innobase/fts/fts0opt.cc b/storage/innobase/fts/fts0opt.cc index 348566ae9525a..9e3b4b3121d27 100644 --- a/storage/innobase/fts/fts0opt.cc +++ b/storage/innobase/fts/fts0opt.cc @@ -2965,7 +2965,7 @@ static void fts_optimize_callback(void *) ib_wqueue_free(fts_optimize_wq); fts_optimize_wq = NULL; - innobase_destroy_background_thd(fts_opt_thd); + destroy_background_thd(fts_opt_thd); ib::info() << "FTS optimize thread exiting."; os_event_set(fts_opt_shutdown_event); diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index a3184abd2d34f..e18d0d5968c47 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -127,7 +127,6 @@ void thd_clear_error(MYSQL_THD thd); TABLE *find_fk_open_table(THD *thd, const char *db, size_t db_len, const char *table, size_t table_len); MYSQL_THD create_background_thd(); -void destroy_background_thd(MYSQL_THD thd); void reset_thd(MYSQL_THD thd); TABLE *get_purge_table(THD *thd); TABLE *open_purge_table(THD *thd, const char *db, size_t dblen, @@ -1477,16 +1476,6 @@ innobase_create_background_thd(const char* name) } -/** Destroy a background purge thread THD. -@param[in] thd MYSQL_THD to destroy */ -void -innobase_destroy_background_thd( -/*============================*/ - MYSQL_THD thd) -{ - destroy_background_thd(thd); -} - /** Close opened tables, free memory, delete items for a MYSQL_THD. @param[in] thd MYSQL_THD to reset */ void diff --git a/storage/innobase/include/ha_prototypes.h b/storage/innobase/include/ha_prototypes.h index c1c41a8f77c56..04e1ec96b7352 100644 --- a/storage/innobase/include/ha_prototypes.h +++ b/storage/innobase/include/ha_prototypes.h @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 2006, 2016, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2017, 2021, MariaDB Corporation. +Copyright (c) 2017, 2022, 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 @@ -512,10 +512,9 @@ innodb_set_buf_pool_size(ulonglong buf_pool_size); MYSQL_THD innobase_create_background_thd(const char* name); -/** Destroy a background purge thread THD. +/** Destroy a THD object associated with a background task. @param[in] thd MYSQL_THD to destroy */ -void -innobase_destroy_background_thd(MYSQL_THD); +void destroy_background_thd(MYSQL_THD thd); /** Close opened tables, free memory, delete items for a MYSQL_THD. @param[in] thd MYSQL_THD to reset */ diff --git a/storage/innobase/srv/srv0srv.cc b/storage/innobase/srv/srv0srv.cc index a33d34c565434..f8e02f95c5e35 100644 --- a/storage/innobase/srv/srv0srv.cc +++ b/storage/innobase/srv/srv0srv.cc @@ -2003,7 +2003,7 @@ static void srv_shutdown_purge_tasks() std::unique_lock lk(purge_thd_mutex); while (!purge_thds.empty()) { - innobase_destroy_background_thd(purge_thds.front()); + destroy_background_thd(purge_thds.front()); purge_thds.pop_front(); } n_purge_thds= 0;