summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Petrunya <psergey@askmonty.org>2012-07-05 22:04:13 +0400
committerSergey Petrunya <psergey@askmonty.org>2012-07-05 22:04:13 +0400
commit3e90dc1f77dc3fa51d542bf82a336753310f7776 (patch)
treed0c14418aa1ab3d3af27a98cd9a27d603d753fac
parentb97678f066dd9dfb32409c61028080ac14efb1eb (diff)
downloadmariadb-git-3e90dc1f77dc3fa51d542bf82a336753310f7776.tar.gz
MWL#182: Explain running statements
- Make SHOW EXPLAIN command be KILLable with KILL QUERY.
-rw-r--r--mysql-test/r/show_explain.result40
-rw-r--r--mysql-test/t/show_explain.test51
-rw-r--r--sql/my_apc.cc21
-rw-r--r--sql/my_apc.h4
-rw-r--r--sql/sql_show.cc8
-rw-r--r--unittest/sql/my_apc-t.cc27
6 files changed, 140 insertions, 11 deletions
diff --git a/mysql-test/r/show_explain.result b/mysql-test/r/show_explain.result
index 7c22a2c6435..c5891f96e82 100644
--- a/mysql-test/r/show_explain.result
+++ b/mysql-test/r/show_explain.result
@@ -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;
diff --git a/mysql-test/t/show_explain.test b/mysql-test/t/show_explain.test
index 5cfc0a5e202..c8d52ad7bb5 100644
--- a/mysql-test/t/show_explain.test
+++ b/mysql-test/t/show_explain.test
@@ -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.
diff --git a/sql/my_apc.cc b/sql/my_apc.cc
index 1cc13f41566..dd15daf48a2 100644
--- a/sql/my_apc.cc
+++ b/sql/my_apc.cc
@@ -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);
diff --git a/sql/my_apc.h b/sql/my_apc.h
index 93b934c9df1..84819b9beea 100644
--- a/sql/my_apc.h
+++ b/sql/my_apc.h
@@ -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 */
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index d26c8f18340..6c407f0cec3 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -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),
diff --git a/unittest/sql/my_apc-t.cc b/unittest/sql/my_apc-t.cc
index fccfb6ecd13..3b837b4700e 100644
--- a/unittest/sql/my_apc-t.cc
+++ b/unittest/sql/my_apc-t.cc
@@ -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)