Commit 3e90dc1f authored by Sergey Petrunya's avatar Sergey Petrunya

MWL#182: Explain running statements

- Make SHOW EXPLAIN command be KILLable with KILL QUERY.
parent b97678f0
......@@ -775,4 +775,44 @@ a
set debug_dbug='';
revoke all privileges on test.* from test2@localhost;
drop user test2@localhost;
#
# Test that it is possible to KILL a SHOW EXPLAIN command that's waiting
# on its target thread
#
create table t1 (pk int primary key, data char(64)) engine=innodb;
insert into t1 select A.a + 10 * B.a + 100 * C.a, 'data1' from t0 A, t0 B, t0 C;
# Lock two threads
set autocommit=0;
select * from t1 where pk between 10 and 20 for update;
pk data
10 data1
11 data1
12 data1
13 data1
14 data1
15 data1
16 data1
17 data1
18 data1
19 data1
20 data1
set autocommit=0;
select * from t1 where pk between 10 and 20 for update;
show explain for 3;
kill query $thr_default;
ERROR 70100: Query execution was interrupted
rollback;
pk data
10 data1
11 data1
12 data1
13 data1
14 data1
15 data1
16 data1
17 data1
18 data1
19 data1
20 data1
drop table t1;
drop table t0;
......@@ -2,6 +2,7 @@
# Tests for SHOW EXPLAIN FOR functionality
#
--source include/have_debug.inc
--source include/have_innodb.inc
--disable_warnings
drop table if exists t0, t1, t2, t3, t4;
......@@ -788,11 +789,57 @@ connection con1;
reap;
set debug_dbug='';
revoke all privileges on test.* from test2@localhost;
drop user test2@localhost;
disconnect con2;
--echo #
--echo # Test that it is possible to KILL a SHOW EXPLAIN command that's waiting
--echo # on its target thread
--echo #
connect (con2, localhost, root,,);
connect (con3, localhost, root,,);
connection con2;
create table t1 (pk int primary key, data char(64)) engine=innodb;
insert into t1 select A.a + 10 * B.a + 100 * C.a, 'data1' from t0 A, t0 B, t0 C;
--echo # Lock two threads
set autocommit=0;
select * from t1 where pk between 10 and 20 for update;
connection con1;
set autocommit=0;
# This will freeze
send
select * from t1 where pk between 10 and 20 for update;
# run SHOW EXPLAIN on a frozen thread
connection default;
let $wait_condition= select State='Sending data' from information_schema.processlist where id=$thr2;
let $thr_default=`select connection_id()`;
--source include/wait_condition.inc
send_eval show explain for $thr2;
# kill the SHOW EXPLAIN command
connection con3;
let $wait_condition= select State='show_explain' from information_schema.processlist where id=$thr_default;
--source include/wait_condition.inc
evalp kill query $thr_default;
connection default;
--error ER_QUERY_INTERRUPTED
reap;
connection con2;
rollback;
connection con1;
reap;
drop table t1;
disconnect con3;
disconnect con2;
## TODO: Test this: have several SHOW EXPLAIN requests be queued up for a
## thread and served together.
......
......@@ -145,8 +145,8 @@ void Apc_target::dequeue_request(Call_request *qe)
to use thd->enter_cond() calls to be killable)
*/
bool Apc_target::make_apc_call(Apc_call *call, int timeout_sec,
bool *timed_out)
bool Apc_target::make_apc_call(THD *caller_thd, Apc_call *call,
int timeout_sec, bool *timed_out)
{
bool res= TRUE;
*timed_out= FALSE;
......@@ -166,6 +166,9 @@ bool Apc_target::make_apc_call(Apc_call *call, int timeout_sec,
set_timespec(abstime, timeout);
int wait_res= 0;
const char *old_msg;
old_msg= caller_thd->enter_cond(&apc_request.COND_request,
LOCK_thd_data_ptr, "show_explain");
/* todo: how about processing other errors here? */
while (!apc_request.processed && (wait_res != ETIMEDOUT))
{
......@@ -173,13 +176,18 @@ bool Apc_target::make_apc_call(Apc_call *call, int timeout_sec,
wait_res= mysql_cond_timedwait(&apc_request.COND_request,
LOCK_thd_data_ptr, &abstime);
// &apc_request.LOCK_request, &abstime);
if (caller_thd->killed)
{
break;
}
}
if (!apc_request.processed)
{
/*
The wait has timed out. Remove the request from the queue (ok to do
because we own LOCK_thd_data_ptr.
The wait has timed out, or this thread was KILLed.
Remove the request from the queue (ok to do because we own
LOCK_thd_data_ptr)
*/
apc_request.processed= TRUE;
dequeue_request(&apc_request);
......@@ -191,7 +199,10 @@ bool Apc_target::make_apc_call(Apc_call *call, int timeout_sec,
/* Request was successfully executed and dequeued by the target thread */
res= FALSE;
}
mysql_mutex_unlock(LOCK_thd_data_ptr);
/*
exit_cond() will call mysql_mutex_unlock(LOCK_thd_data_ptr) for us:
*/
caller_thd->exit_cond(old_msg);
/* Destroy all APC request data */
mysql_cond_destroy(&apc_request.COND_request);
......
......@@ -33,6 +33,8 @@
requestor.
*/
class THD;
/*
Target for asynchronous procedure calls (APCs).
- A target is running in some particular thread,
......@@ -62,7 +64,7 @@ public:
};
/* Make a call in the target thread (see function definition for details) */
bool make_apc_call(Apc_call *call, int timeout_sec, bool *timed_out);
bool make_apc_call(THD *caller_thd, Apc_call *call, int timeout_sec, bool *timed_out);
#ifndef DBUG_OFF
int n_calls_processed; /* Number of calls served by this target */
......
......@@ -2084,11 +2084,15 @@ void mysqld_show_explain(THD *thd, const char *calling_user, ulong thread_id)
explain_req.failed_to_produce= FALSE;
/* Ok, we have a lock on target->LOCK_thd_data, can call: */
bres= tmp->apc_target.make_apc_call(&explain_req, timeout_sec, &timed_out);
bres= tmp->apc_target.make_apc_call(thd, &explain_req, timeout_sec, &timed_out);
if (bres || explain_req.failed_to_produce)
{
/* TODO not enabled or time out */
if (thd->killed)
{
thd->send_kill_message();
}
else
if (timed_out)
{
my_error(ER_ERROR_WHEN_EXECUTING_COMMAND, MYF(0),
......
......@@ -24,6 +24,29 @@
#include <tap.h>
/*
A fake THD with enter_cond/exit_cond and some other members.
*/
class THD
{
mysql_mutex_t* thd_mutex;
public:
bool killed;
THD() : killed(FALSE) {}
inline const char* enter_cond(mysql_cond_t *cond, mysql_mutex_t* mutex,
const char* msg)
{
mysql_mutex_assert_owner(mutex);
thd_mutex= mutex;
return NULL;
}
inline void exit_cond(const char* old_msg)
{
mysql_mutex_unlock(thd_mutex);
}
};
#include "../sql/my_apc.h"
#define MY_APC_STANDALONE 1
......@@ -115,6 +138,8 @@ void *test_apc_requestor_thread(void *ptr)
{
my_thread_init();
fprintf(stderr, "# test_apc_requestor_thread started\n");
THD my_thd;
while (!requestors_should_exit)
{
int dst_value= 0;
......@@ -124,7 +149,7 @@ void *test_apc_requestor_thread(void *ptr)
bool timed_out;
mysql_mutex_lock(&target_mutex);
bool res= apc_target.make_apc_call(&apc_order, 60, &timed_out);
bool res= apc_target.make_apc_call(&my_thd, &apc_order, 60, &timed_out);
if (res)
{
if (timed_out)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment