diff options
author | Sergey Petrunya <psergey@askmonty.org> | 2012-07-05 22:04:13 +0400 |
---|---|---|
committer | Sergey Petrunya <psergey@askmonty.org> | 2012-07-05 22:04:13 +0400 |
commit | 3e90dc1f77dc3fa51d542bf82a336753310f7776 (patch) | |
tree | d0c14418aa1ab3d3af27a98cd9a27d603d753fac | |
parent | b97678f066dd9dfb32409c61028080ac14efb1eb (diff) | |
download | mariadb-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.result | 40 | ||||
-rw-r--r-- | mysql-test/t/show_explain.test | 51 | ||||
-rw-r--r-- | sql/my_apc.cc | 21 | ||||
-rw-r--r-- | sql/my_apc.h | 4 | ||||
-rw-r--r-- | sql/sql_show.cc | 8 | ||||
-rw-r--r-- | unittest/sql/my_apc-t.cc | 27 |
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) |