#
# 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
-- source include/have_innodb.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;

--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;
--disable_cursor_protocol
select variable_value into @threads_cached from information_schema.global_status where variable_name='threads_cached';
--enable_cursor_protocol
set debug_sync='now SIGNAL go3';
if (`select @@thread_handling != 'pool-of-threads'`) {
  # cannot check that a thread was added to thread pool on windows, but the test works there w/o the wait
  let wait_condition= select variable_value>@threads_cached from information_schema.global_status where variable_name='threads_cached';
  source include/wait_condition.inc;
}
drop table t1;
set debug_sync='reset';


--echo #
--echo # MDEV-33285 - Assertion `m_table' failed in ha_perfschema::rnd_end on
--echo #              CHECKSUM TABLE
--echo #
let $id= `select connection_id()`;
CREATE TABLE t1 (a INT);
INSERT INTO t1 VALUES(1);
SET debug_sync='mysql_checksum_table_after_calculate_checksum SIGNAL parked WAIT_FOR go';
send CHECKSUM TABLE t1;

connect con1, localhost, root;
connection con1;
SET debug_sync='now WAIT_FOR parked';
replace_result $id id;
eval KILL QUERY $id;
SET debug_sync='now SIGNAL go';
connection default;
error ER_QUERY_INTERRUPTED;
reap;

SET debug_sync='RESET';
SET debug_sync='mysql_checksum_table_before_calculate_checksum SIGNAL parked WAIT_FOR go';
send CHECKSUM TABLE t1;
connection con1;
SET debug_sync='now WAIT_FOR parked';
replace_result $id id;
eval KILL QUERY $id;
SET debug_sync='now SIGNAL go';

connection default;
error ER_QUERY_INTERRUPTED;
reap;
DROP TABLE t1;
disconnect con1;
SET debug_sync='RESET';
