MWL#182: SHOW EXPLAIN: Merge 5.3->5.5

This commit is contained in:
Sergey Petrunya 2012-05-16 19:20:00 +04:00
commit dfbd777fd8
25 changed files with 1629 additions and 129 deletions

View File

@ -1137,6 +1137,7 @@ plugin/handler_socket/perl-Net-HandlerSocket/Makefile.PL
libmysqld/libmysqld_exports_file.cc
libmysqld/gcalc_slicescan.cc
libmysqld/gcalc_tools.cc
libmysqld/my_apc.cc
sql/share/errmsg.sys
sql/share/mysql
install_manifest.txt

View File

@ -82,6 +82,8 @@ static my_bool non_blocking_api_enabled= 0;
#define QUERY_SEND_FLAG 1
#define QUERY_REAP_FLAG 2
#define QUERY_PRINT_ORIGINAL_FLAG 4
#ifndef HAVE_SETENV
static int setenv(const char *name, const char *value, int overwrite);
#endif
@ -339,7 +341,8 @@ enum enum_commands {
Q_ERROR,
Q_SEND, Q_REAP,
Q_DIRTY_CLOSE, Q_REPLACE, Q_REPLACE_COLUMN,
Q_PING, Q_EVAL,
Q_PING, Q_EVAL,
Q_EVALP,
Q_EVAL_RESULT,
Q_ENABLE_QUERY_LOG, Q_DISABLE_QUERY_LOG,
Q_ENABLE_RESULT_LOG, Q_DISABLE_RESULT_LOG,
@ -405,6 +408,7 @@ const char *command_names[]=
"replace_column",
"ping",
"eval",
"evalp",
"eval_result",
/* Enable/disable that the _query_ is logged to result file */
"enable_query_log",
@ -8116,7 +8120,8 @@ void run_query(struct st_connection *cn, struct st_command *command, int flags)
/*
Evaluate query if this is an eval command
*/
if (command->type == Q_EVAL || command->type == Q_SEND_EVAL)
if (command->type == Q_EVAL || command->type == Q_SEND_EVAL ||
command->type == Q_EVALP)
{
init_dynamic_string(&eval_query, "", command->query_len+256, 1024);
do_eval(&eval_query, command->query, command->end, FALSE);
@ -8148,10 +8153,20 @@ void run_query(struct st_connection *cn, struct st_command *command, int flags)
*/
if (!disable_query_log && (flags & QUERY_SEND_FLAG))
{
replace_dynstr_append_mem(ds, query, query_len);
char *print_query= query;
int print_len= query_len;
if (flags & QUERY_PRINT_ORIGINAL_FLAG)
{
print_query= command->query;
print_len= command->end - command->query;
}
replace_dynstr_append_mem(ds, print_query, print_len);
dynstr_append_mem(ds, delimiter, delimiter_length);
dynstr_append_mem(ds, "\n", 1);
}
/* We're done with this flag */
flags &= ~QUERY_PRINT_ORIGINAL_FLAG;
/*
Write the command to the result file before we execute the query
@ -9007,6 +9022,7 @@ int main(int argc, char **argv)
case Q_EVAL_RESULT:
die("'eval_result' command is deprecated");
case Q_EVAL:
case Q_EVALP:
case Q_QUERY_VERTICAL:
case Q_QUERY_HORIZONTAL:
if (command->query == command->query_buf)
@ -9034,6 +9050,9 @@ int main(int argc, char **argv)
flags= QUERY_REAP_FLAG;
}
if (command->type == Q_EVALP)
flags |= QUERY_PRINT_ORIGINAL_FLAG;
/* Check for special property for this query */
display_result_vertically|= (command->type == Q_QUERY_VERTICAL);

View File

@ -95,6 +95,7 @@ SET(SQL_EMBEDDED_SOURCES emb_qcache.cc libmysqld.c lib_sql.cc
../sql/create_options.cc ../sql/rpl_utility.cc
../sql/rpl_reporting.cc
../sql/sql_expression_cache.cc
../sql/my_apc.cc ../sql/my_apc.h
${GEN_SOURCES}
${MYSYS_LIBWRAP_SOURCE}
)

View File

@ -313,7 +313,7 @@ alter table t1 add index i3(key3);
update t1 set key2=key1,key3=key1;
explain select * from t1 where (key3 > 30 and key3<35) or (key2 >32 and key2 < 40);
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 index_merge i2,i3 i3,i2 4,4 NULL 9 Using sort_union(i3,i2); Using where
1 SIMPLE t1 index_merge i2,i3 i3,i2 4,4 NULL 8 Using sort_union(i3,i2); Using where
select * from t1 where (key3 > 30 and key3<35) or (key2 >32 and key2 < 40);
key1 key2 key3
31 31 31

View File

@ -0,0 +1,137 @@
drop table if exists t0, t1;
create table t0 (a int);
insert into t0 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
create table t1 (a int);
insert into t1 select A.a + 10*B.a + 100*C.a from t0 A, t0 B, t0 C;
alter table t1 add b int, add c int, add filler char(32);
update t1 set b=a, c=a, filler='fooo';
alter table t1 add key(a), add key(b);
show explain for 2*1000*1000*1000;
ERROR HY000: Unknown thread id: 2000000000
show explain for (select max(a) from t0);
ERROR 42000: This version of MariaDB doesn't yet support 'Usage of subqueries or stored function calls as part of this statement'
show explain for $thr2;
ERROR HY000: Error when executing command SHOW EXPLAIN: Target is not running EXPLAINable command
show explain for $thr1;
ERROR HY000: Error when executing command SHOW EXPLAIN: Target is not running EXPLAINable command
set @show_explain_probe_select_id=1;
set debug_dbug='d,show_explain_probe_join_exec_start';
select count(*) from t1 where a < 100000;
show explain for $thr2;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 index a a 5 NULL 1000 Using where; Using index
count(*)
1000
select max(c) from t1 where a < 10;
show explain for $thr2;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 range a a 5 NULL 10 Using index condition
max(c)
9
# We can catch EXPLAIN, too.
set @show_expl_tmp= @@optimizer_switch;
set optimizer_switch='index_condition_pushdown=on,mrr=on,mrr_sort_keys=on';
explain select max(c) from t1 where a < 10;
show explain for $thr2;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 range a a 5 NULL 10 Using index condition; Rowid-ordered scan
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 range a a 5 NULL 10 Using index condition; Rowid-ordered scan
set optimizer_switch= @show_expl_tmp;
# UNION, first branch
set @show_explain_probe_select_id=1;
set debug_dbug='d,show_explain_probe_join_exec_start';
explain select a from t0 A union select a+1 from t0 B;
show explain for $thr2;
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY A ALL NULL NULL NULL NULL 10
2 UNION B ALL NULL NULL NULL NULL 10
NULL UNION RESULT <union1,2> ALL NULL NULL NULL NULL NULL
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY A ALL NULL NULL NULL NULL 10
2 UNION B ALL NULL NULL NULL NULL 10
NULL UNION RESULT <union1,2> ALL NULL NULL NULL NULL NULL
# UNION, second branch
set @show_explain_probe_select_id=1;
set debug_dbug='d,show_explain_probe_join_exec_start';
explain select a from t0 A union select a+1 from t0 B;
show explain for $thr2;
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY A ALL NULL NULL NULL NULL 10
2 UNION B ALL NULL NULL NULL NULL 10
NULL UNION RESULT <union1,2> ALL NULL NULL NULL NULL NULL
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY A ALL NULL NULL NULL NULL 10
2 UNION B ALL NULL NULL NULL NULL 10
NULL UNION RESULT <union1,2> ALL NULL NULL NULL NULL NULL
# Uncorrelated subquery, select
set @show_explain_probe_select_id=1;
set debug_dbug='d,show_explain_probe_join_exec_start';
select a, (select max(a) from t0 B) from t0 A where a<1;
show explain for $thr2;
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY A ALL NULL NULL NULL NULL 10 Using where
2 SUBQUERY B ALL NULL NULL NULL NULL 10
a (select max(a) from t0 B)
0 9
# Uncorrelated subquery, explain
set @show_explain_probe_select_id=1;
set debug_dbug='d,show_explain_probe_join_exec_start';
explain select a, (select max(a) from t0 B) from t0 A where a<1;
show explain for $thr2;
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY A ALL NULL NULL NULL NULL 10 Using where
2 SUBQUERY B ALL NULL NULL NULL NULL 10
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY A ALL NULL NULL NULL NULL 10 Using where
2 SUBQUERY B ALL NULL NULL NULL NULL 10
# correlated subquery, select
set @show_explain_probe_select_id=1;
set debug_dbug='d,show_explain_probe_join_exec_start';
select a, (select max(a) from t0 b where b.a+a.a<10) from t0 a where a<1;
show explain for $thr2;
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY a ALL NULL NULL NULL NULL 10 Using where
2 DEPENDENT SUBQUERY b ALL NULL NULL NULL NULL 10 Using where
a (select max(a) from t0 b where b.a+a.a<10)
0 9
# correlated subquery, explain
set @show_explain_probe_select_id=1;
set debug_dbug='d,show_explain_probe_join_exec_start';
select a, (select max(a) from t0 b where b.a+a.a<10) from t0 a where a<1;
show explain for $thr2;
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY a ALL NULL NULL NULL NULL 10 Using where
2 DEPENDENT SUBQUERY b ALL NULL NULL NULL NULL 10 Using where
a (select max(a) from t0 b where b.a+a.a<10)
0 9
# correlated subquery, select, while inside the subquery
set @show_explain_probe_select_id=2;
set debug_dbug='d,show_explain_probe_join_exec_start';
select a, (select max(a) from t0 b where b.a+a.a<10) from t0 a where a<1;
show explain for $thr2;
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY a ALL NULL NULL NULL NULL 10 Using where
2 DEPENDENT SUBQUERY b ALL NULL NULL NULL NULL 10 Using where
a (select max(a) from t0 b where b.a+a.a<10)
0 9
# correlated subquery, explain, while inside the subquery
set @show_explain_probe_select_id=2;
set debug_dbug='d,show_explain_probe_join_exec_start';
select a, (select max(a) from t0 b where b.a+a.a<10) from t0 a where a<1;
show explain for $thr2;
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY a ALL NULL NULL NULL NULL 10 Using where
2 DEPENDENT SUBQUERY b ALL NULL NULL NULL NULL 10 Using where
a (select max(a) from t0 b where b.a+a.a<10)
0 9
# correlated subquery, explain, while inside the subquery
set @show_explain_probe_select_id=1;
set debug_dbug='d,show_explain_probe_join_exec_end';
select a, (select max(a) from t0 b where b.a+a.a<10) from t0 a where a<1;
show explain for $thr2;
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY NULL NULL NULL NULL NULL NULL NULL Query plan already deleted
a (select max(a) from t0 b where b.a+a.a<10)
0 9
drop table t0,t1;

View File

@ -0,0 +1,211 @@
#
# Tests for SHOW EXPLAIN FOR functionality
#
--source include/have_debug.inc
--disable_warnings
drop table if exists t0, t1;
--enable_warnings
#
# Testcases in this file do not work with embedded server. The reason for this
# is that we use the following commands for synchronization:
#
# set @show_explain_probe_select_id=1;
# set debug_dbug='d,show_explain_probe_join_exec_start';
# send select count(*) from t1 where a < 100000;
#
# When ran with mysqltest_embedded, this translates into:
#
# Thread1> DBUG_PUSH("d,show_explain_probe_join_exec_start");
# Thread1> create another thread for doing "send ... reap"
# Thread2> mysql_parse("select count(*) from t1 where a < 100000");
#
# That is, "select count(*) ..." is ran in a thread for which DBUG_PUSH(...)
# has not been called. As a result, show_explain_probe_join_exec_start does not fire, and
# "select count(*) ..." does not wait till its SHOW EXPLAIN command, and the
# test fails.
#
-- source include/not_embedded.inc
create table t0 (a int);
insert into t0 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
create table t1 (a int);
insert into t1 select A.a + 10*B.a + 100*C.a from t0 A, t0 B, t0 C;
alter table t1 add b int, add c int, add filler char(32);
update t1 set b=a, c=a, filler='fooo';
alter table t1 add key(a), add key(b);
#
# Try killing a non-existent thread
#
--error ER_NO_SUCH_THREAD
show explain for 2*1000*1000*1000;
--error ER_NOT_SUPPORTED_YET
show explain for (select max(a) from t0);
#
# Setup two threads and their ids
#
let $thr1=`select connection_id()`;
connect (con1, localhost, root,,);
connection con1;
let $thr2=`select connection_id()`;
connection default;
# SHOW EXPLAIN FOR <idle thread>
--error ER_ERROR_WHEN_EXECUTING_COMMAND
evalp show explain for $thr2;
# SHOW EXPLAIN FOR <ourselves>
--error ER_ERROR_WHEN_EXECUTING_COMMAND
evalp show explain for $thr1;
let $wait_condition= select State='show_explain_trap' from information_schema.processlist where id=$thr2;
#
# Test SHOW EXPLAIN for simple queries
#
connection con1;
set @show_explain_probe_select_id=1;
set debug_dbug='d,show_explain_probe_join_exec_start';
send select count(*) from t1 where a < 100000;
connection default;
--source include/wait_condition.inc
evalp show explain for $thr2;
connection con1;
reap;
send select max(c) from t1 where a < 10;
connection default;
--source include/wait_condition.inc
evalp show explain for $thr2;
connection con1;
reap;
--echo # We can catch EXPLAIN, too.
set @show_expl_tmp= @@optimizer_switch;
set optimizer_switch='index_condition_pushdown=on,mrr=on,mrr_sort_keys=on';
send explain select max(c) from t1 where a < 10;
connection default;
--source include/wait_condition.inc
evalp show explain for $thr2;
connection con1;
reap;
set optimizer_switch= @show_expl_tmp;
--echo # UNION, first branch
set @show_explain_probe_select_id=1;
set debug_dbug='d,show_explain_probe_join_exec_start';
send explain select a from t0 A union select a+1 from t0 B;
connection default;
--source include/wait_condition.inc
evalp show explain for $thr2;
connection con1;
reap;
--echo # UNION, second branch
set @show_explain_probe_select_id=1;
set debug_dbug='d,show_explain_probe_join_exec_start';
send explain select a from t0 A union select a+1 from t0 B;
connection default;
--source include/wait_condition.inc
evalp show explain for $thr2;
connection con1;
reap;
--echo # Uncorrelated subquery, select
set @show_explain_probe_select_id=1;
set debug_dbug='d,show_explain_probe_join_exec_start';
send select a, (select max(a) from t0 B) from t0 A where a<1;
connection default;
--source include/wait_condition.inc
evalp show explain for $thr2;
connection con1;
reap;
--echo # Uncorrelated subquery, explain
set @show_explain_probe_select_id=1;
set debug_dbug='d,show_explain_probe_join_exec_start';
send explain select a, (select max(a) from t0 B) from t0 A where a<1;
connection default;
--source include/wait_condition.inc
evalp show explain for $thr2;
connection con1;
reap;
--echo # correlated subquery, select
set @show_explain_probe_select_id=1;
set debug_dbug='d,show_explain_probe_join_exec_start';
send select a, (select max(a) from t0 b where b.a+a.a<10) from t0 a where a<1;
connection default;
--source include/wait_condition.inc
evalp show explain for $thr2;
connection con1;
reap;
--echo # correlated subquery, explain
set @show_explain_probe_select_id=1;
set debug_dbug='d,show_explain_probe_join_exec_start';
send select a, (select max(a) from t0 b where b.a+a.a<10) from t0 a where a<1;
connection default;
--source include/wait_condition.inc
evalp show explain for $thr2;
connection con1;
reap;
--echo # correlated subquery, select, while inside the subquery
set @show_explain_probe_select_id=2; # <---
set debug_dbug='d,show_explain_probe_join_exec_start';
send select a, (select max(a) from t0 b where b.a+a.a<10) from t0 a where a<1;
connection default;
--source include/wait_condition.inc
evalp show explain for $thr2;
connection con1;
reap;
--echo # correlated subquery, explain, while inside the subquery
set @show_explain_probe_select_id=2;
set debug_dbug='d,show_explain_probe_join_exec_start';
send select a, (select max(a) from t0 b where b.a+a.a<10) from t0 a where a<1;
connection default;
--source include/wait_condition.inc
evalp show explain for $thr2;
connection con1;
reap;
--echo # correlated subquery, explain, while inside the subquery
set @show_explain_probe_select_id=1;
set debug_dbug='d,show_explain_probe_join_exec_end';
send select a, (select max(a) from t0 b where b.a+a.a<10) from t0 a where a<1;
connection default;
--source include/wait_condition.inc
evalp show explain for $thr2;
connection con1;
reap;
# TODO: explain in the parent subuqery when the un-correlated child has been
# run (and have done irreversible cleanups)
# TODO: hit JOIN::optimize for non-select commands: UPDATE/DELETE, SET.
## TODO: Test this: multiple SHOW EXPLAIN calls in course of running of one select
##
## TODO: Test this: have several SHOW EXPLAIN requests be queued up for a
## thread and served together.
## TODO: SHOW EXPLAIN while the primary query is running EXPLAIN EXTENDED/PARTITIONS
##
drop table t0,t1;

View File

@ -85,6 +85,7 @@ SET (SQL_SOURCE
gcalc_slicescan.cc gcalc_tools.cc
threadpool_common.cc
../sql-common/mysql_async.c
my_apc.cc my_apc.h
${GEN_SOURCES}
${MYSYS_LIBWRAP_SOURCE}
)

View File

@ -4298,7 +4298,7 @@ longlong Item_func_sleep::val_int()
#define extra_size sizeof(double)
static user_var_entry *get_variable(HASH *hash, LEX_STRING &name,
user_var_entry *get_variable(HASH *hash, LEX_STRING &name,
bool create_if_not_exists)
{
user_var_entry *entry;

366
sql/my_apc.cc Normal file
View File

@ -0,0 +1,366 @@
/*
TODO: MP AB Copyright
*/
#ifdef MY_APC_STANDALONE
#include <my_global.h>
#include <my_pthread.h>
#include <my_sys.h>
#else
#include "sql_priv.h"
#include "sql_class.h"
#endif
//#include "my_apc.h"
/*
Standalone testing:
g++ -c -DMY_APC_STANDALONE -g -I.. -I../include -o my_apc.o my_apc.cc
g++ -L../mysys -L../dbug -L../strings my_apc.o -lmysys -ldbug -lmystrings -lpthread -lrt
*/
void Apc_target::init()
{
// todo: should use my_pthread_... functions instead?
DBUG_ASSERT(!enabled);
(void)pthread_mutex_init(&LOCK_apc_queue, MY_MUTEX_INIT_SLOW);
#ifndef DBUG_OFF
n_calls_processed= 0;
#endif
}
void Apc_target::destroy()
{
DBUG_ASSERT(!enabled);
pthread_mutex_destroy(&LOCK_apc_queue);
}
void Apc_target::enable()
{
pthread_mutex_lock(&LOCK_apc_queue);
enabled++;
pthread_mutex_unlock(&LOCK_apc_queue);
}
void Apc_target::disable()
{
bool process= FALSE;
pthread_mutex_lock(&LOCK_apc_queue);
if (!(--enabled))
process= TRUE;
pthread_mutex_unlock(&LOCK_apc_queue);
if (process)
process_apc_requests();
}
void Apc_target::enqueue_request(Call_request *qe)
{
//call_queue_size++;
if (apc_calls)
{
Call_request *after= apc_calls->prev;
qe->next= apc_calls;
apc_calls->prev= qe;
qe->prev= after;
after->next= qe;
}
else
{
apc_calls= qe;
qe->next= qe->prev= qe;
}
}
void Apc_target::dequeue_request(Call_request *qe)
{
//call_queue_size--;
if (apc_calls == qe)
{
if ((apc_calls= apc_calls->next) == qe)
{
//DBUG_ASSERT(!call_queue_size);
apc_calls= NULL;
}
}
qe->prev->next= qe->next;
qe->next->prev= qe->prev;
}
/*
Make an apc call in another thread. The caller is responsible so
that we're not calling to ourselves.
psergey-todo: Should waits here be KILLable? (it seems one needs
to use thd->enter_cond() calls to be killable)
*/
bool Apc_target::make_apc_call(apc_func_t func, void *func_arg,
int timeout_sec, bool *timed_out)
{
bool res= TRUE;
*timed_out= FALSE;
pthread_mutex_lock(&LOCK_apc_queue);
if (enabled)
{
/* Create and post the request */
Call_request apc_request;
apc_request.func= func;
apc_request.func_arg= func_arg;
apc_request.done= FALSE;
(void)pthread_cond_init(&apc_request.COND_request, NULL);
(void)pthread_mutex_init(&apc_request.LOCK_request, MY_MUTEX_INIT_SLOW);
pthread_mutex_lock(&apc_request.LOCK_request);
enqueue_request(&apc_request);
apc_request.what="enqueued by make_apc_call";
pthread_mutex_unlock(&LOCK_apc_queue);
struct timespec abstime;
const int timeout= timeout_sec;
set_timespec(abstime, timeout);
int wait_res= 0;
/* todo: how about processing other errors here? */
while (!apc_request.done && (wait_res != ETIMEDOUT))
{
wait_res= pthread_cond_timedwait(&apc_request.COND_request,
&apc_request.LOCK_request, &abstime);
}
if (!apc_request.done)
{
/* We timed out */
apc_request.done= TRUE;
*timed_out= TRUE;
pthread_mutex_unlock(&apc_request.LOCK_request);
pthread_mutex_lock(&LOCK_apc_queue);
dequeue_request(&apc_request);
pthread_mutex_unlock(&LOCK_apc_queue);
res= TRUE;
}
else
{
/* Request was successfully executed and dequeued by the target thread */
pthread_mutex_unlock(&apc_request.LOCK_request);
res= FALSE;
}
/* Destroy all APC request data */
pthread_mutex_destroy(&apc_request.LOCK_request);
pthread_cond_destroy(&apc_request.COND_request);
}
else
{
pthread_mutex_unlock(&LOCK_apc_queue);
}
return res;
}
/*
Process all APC requests
*/
void Apc_target::process_apc_requests()
{
while (1)
{
Call_request *request;
pthread_mutex_lock(&LOCK_apc_queue);
if (!(request= get_first_in_queue()))
{
pthread_mutex_unlock(&LOCK_apc_queue);
break;
}
request->what="seen by process_apc_requests";
pthread_mutex_lock(&request->LOCK_request);
if (request->done)
{
/*
We can get here when
- the requestor thread has been waiting for this request
- the wait has timed out
- it has set request->done=TRUE
- it has released LOCK_request, because its next action
will be to remove the request from the queue, however,
it could not attempt to lock the queue while holding the lock on
request, because that would deadlock with this function
(we here first lock the queue and then lock the request)
*/
pthread_mutex_unlock(&request->LOCK_request);
pthread_mutex_unlock(&LOCK_apc_queue);
fprintf(stderr, "Whoa rare event #1!\n");
continue;
}
/*
Remove the request from the queue (we're holding its lock so we can be
sure that request owner won't try to remove it)
*/
request->what="dequeued by process_apc_requests";
dequeue_request(request);
request->done= TRUE;
pthread_mutex_unlock(&LOCK_apc_queue);
request->func(request->func_arg);
request->what="func called by process_apc_requests";
#ifndef DBUG_OFF
n_calls_processed++;
#endif
pthread_cond_signal(&request->COND_request);
pthread_mutex_unlock(&request->LOCK_request);
}
}
/*****************************************************************************
* Testing
*****************************************************************************/
#ifdef MY_APC_STANDALONE
volatile bool started= FALSE;
volatile bool service_should_exit= FALSE;
volatile bool requestors_should_exit=FALSE;
volatile int apcs_served= 0;
volatile int apcs_missed=0;
volatile int apcs_timed_out=0;
Apc_target apc_target;
int int_rand(int size)
{
return round (((double)rand() / RAND_MAX) * size);
}
/* An APC-serving thread */
void *test_apc_service_thread(void *ptr)
{
my_thread_init();
apc_target.init();
apc_target.enable();
started= TRUE;
fprintf(stderr, "# test_apc_service_thread started\n");
while (!service_should_exit)
{
//apc_target.disable();
usleep(10000);
//apc_target.enable();
for (int i = 0; i < 10 && !service_should_exit; i++)
{
apc_target.process_apc_requests();
usleep(int_rand(30));
}
}
apc_target.disable();
apc_target.destroy();
my_thread_end();
pthread_exit(0);
}
class Apc_order
{
public:
int value; // The value
int *where_to; // Where to write it
Apc_order(int a, int *b) : value(a), where_to(b) {}
};
void test_apc_func(void *arg)
{
Apc_order *order=(Apc_order*)arg;
usleep(int_rand(1000));
*(order->where_to) = order->value;
__sync_fetch_and_add(&apcs_served, 1);
}
void *test_apc_requestor_thread(void *ptr)
{
my_thread_init();
fprintf(stderr, "# test_apc_requestor_thread started\n");
while (!requestors_should_exit)
{
int dst_value= 0;
int src_value= int_rand(4*1000*100);
/* Create APC to do dst_value= src_value */
Apc_order apc_order(src_value, &dst_value);
bool timed_out;
bool res= apc_target.make_apc_call(test_apc_func, (void*)&apc_order, 60, &timed_out);
if (res)
{
if (timed_out)
__sync_fetch_and_add(&apcs_timed_out, 1);
else
__sync_fetch_and_add(&apcs_missed, 1);
if (dst_value != 0)
fprintf(stderr, "APC was done even though return value says it wasnt!\n");
}
else
{
if (dst_value != src_value)
fprintf(stderr, "APC was not done even though return value says it was!\n");
}
//usleep(300);
}
fprintf(stderr, "# test_apc_requestor_thread exiting\n");
my_thread_end();
}
const int N_THREADS=23;
int main(int args, char **argv)
{
pthread_t service_thr;
pthread_t request_thr[N_THREADS];
int i, j;
my_thread_global_init();
pthread_create(&service_thr, NULL, test_apc_service_thread, (void*)NULL);
while (!started)
usleep(1000);
for (i = 0; i < N_THREADS; i++)
pthread_create(&request_thr[i], NULL, test_apc_requestor_thread, (void*)NULL);
for (i = 0; i < 15; i++)
{
usleep(500*1000);
fprintf(stderr, "# %d APCs served %d missed\n", apcs_served, apcs_missed);
}
fprintf(stderr, "# Shutting down requestors\n");
requestors_should_exit= TRUE;
for (i = 0; i < N_THREADS; i++)
pthread_join(request_thr[i], NULL);
fprintf(stderr, "# Shutting down service\n");
service_should_exit= TRUE;
pthread_join(service_thr, NULL);
fprintf(stderr, "# Done.\n");
my_thread_end();
my_thread_global_end();
return 0;
}
#endif // MY_APC_STANDALONE

102
sql/my_apc.h Normal file
View File

@ -0,0 +1,102 @@
/*
TODO: MP AB Copyright
*/
/*
Design
- Mutex-guarded request queue (it belongs to the target), which can be enabled/
disabled (when empty).
- After the request has been put into queue, the requestor waits for request
to be satisfied. The worker satisifes the request and signals the
requestor.
*/
/*
Target for asynchronous calls.
*/
class Apc_target
{
public:
Apc_target() : enabled(0), apc_calls(NULL) /*, call_queue_size(0)*/ {}
~Apc_target() { DBUG_ASSERT(!enabled && !apc_calls);}
/*
Initialize the target. This must be called before anything else. Right
after initialization, the target is disabled.
*/
void init();
/*
Destroy the target. The target must be disabled when this call is made.
*/
void destroy();
/*
Enter into state where this target will be serving APC requests
*/
void enable();
/*
Leave the state where we could serve APC requests (will serve all already
enqueued requests)
*/
void disable();
/*
This should be called periodically to serve observation requests.
*/
void process_apc_requests();
typedef void (*apc_func_t)(void *arg);
/*
Make an APC call: schedule it for execution and wait until the target
thread has executed it. This function must not be called from a thread
that's different from the target thread.
@retval FALSE - Ok, the call has been made
@retval TRUE - Call wasnt made (either the target is in disabled state or
timeout occured)
*/
bool make_apc_call(apc_func_t func, void *func_arg,
int timeout_sec, bool *timed_out);
#ifndef DBUG_OFF
int n_calls_processed;
//int call_queue_size;
#endif
private:
class Call_request;
int enabled;
Call_request *apc_calls;
pthread_mutex_t LOCK_apc_queue;
class Call_request
{
public:
apc_func_t func;
void *func_arg;
bool done;
pthread_mutex_t LOCK_request;
pthread_cond_t COND_request;
Call_request *next;
Call_request *prev;
const char *what;
};
void enqueue_request(Call_request *qe);
void dequeue_request(Call_request *qe);
Call_request *get_first_in_queue()
{
return apc_calls;
}
};
///////////////////////////////////////////////////////////////////////

View File

@ -3336,6 +3336,7 @@ SHOW_VAR com_status_vars[]= {
{"show_engine_status", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_ENGINE_STATUS]), SHOW_LONG_STATUS},
{"show_events", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_EVENTS]), SHOW_LONG_STATUS},
{"show_errors", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_ERRORS]), SHOW_LONG_STATUS},
{"show_explain", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_EXPLAIN]), SHOW_LONG_STATUS},
{"show_fields", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_FIELDS]), SHOW_LONG_STATUS},
#ifndef DBUG_OFF
{"show_function_code", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_FUNC_CODE]), SHOW_LONG_STATUS},

View File

@ -1656,7 +1656,8 @@ static bool convert_subq_to_sj(JOIN *parent_join, Item_in_subselect *subq_pred)
while ((ifm= li++))
parent_lex->ftfunc_list->push_front(ifm);
}
parent_lex->have_merged_subqueries= TRUE;
DBUG_RETURN(FALSE);
}
@ -1767,6 +1768,8 @@ static bool convert_subq_to_jtbm(JOIN *parent_join,
create_subquery_temptable_name(tbl_alias, hash_sj_engine->materialize_join->
select_lex->select_number);
jtbm->alias= tbl_alias;
parent_lex->have_merged_subqueries= TRUE;
#if 0
/* Inject sj_on_expr into the parent's WHERE or ON */
if (emb_tbl_nest)

View File

@ -35,6 +35,7 @@ class Protocol
protected:
THD *thd;
String *packet;
/* Used by net_store_data() for charset conversions */
String *convert;
uint field_pos;
#ifndef DBUG_OFF
@ -49,6 +50,10 @@ protected:
MYSQL_FIELD *next_mysql_field;
MEM_ROOT *alloc;
#endif
/*
The following two are low-level functions that are invoked from
higher-level store_xxx() funcs. The data is stored into this->packet.
*/
bool net_store_data(const uchar *from, size_t length,
CHARSET_INFO *fromcs, CHARSET_INFO *tocs);
bool store_string_aux(const char *from, size_t length,
@ -73,6 +78,20 @@ public:
virtual bool send_result_set_metadata(List<Item> *list, uint flags);
bool send_result_set_row(List<Item> *row_items);
void get_packet(const char **start, size_t *length)
{
*start= packet->ptr();
*length= packet->length();
}
void set_packet(const char *start, size_t len)
{
packet->length(0);
packet->append(start, len);
#ifndef DBUG_OFF
field_pos= field_count - 1;
#endif
}
bool store(I_List<i_string> *str_list);
bool store(const char *from, CHARSET_INFO *cs);
String *storage_packet() { return packet; }

View File

@ -218,6 +218,7 @@ sp_get_flags_for_command(LEX *lex)
case SQLCOM_SHOW_CREATE_TRIGGER:
case SQLCOM_SHOW_DATABASES:
case SQLCOM_SHOW_ERRORS:
case SQLCOM_SHOW_EXPLAIN:
case SQLCOM_SHOW_FIELDS:
case SQLCOM_SHOW_FUNC_CODE:
case SQLCOM_SHOW_GRANTS:

View File

@ -1196,6 +1196,7 @@ void THD::init(void)
/* Initialize the Debug Sync Facility. See debug_sync.cc. */
debug_sync_init_thread(this);
#endif /* defined(ENABLED_DEBUG_SYNC) */
apc_target.init();
}
@ -1360,7 +1361,8 @@ void THD::cleanup(void)
mysql_mutex_unlock(&LOCK_user_locks);
ull= NULL;
}
apc_target.destroy();
cleanup_done=1;
DBUG_VOID_RETURN;
}
@ -2006,6 +2008,15 @@ CHANGED_TABLE_LIST* THD::changed_table_dup(const char *key, long key_length)
int THD::send_explain_fields(select_result *result)
{
List<Item> field_list;
make_explain_field_list(field_list);
return (result->send_result_set_metadata(field_list,
Protocol::SEND_NUM_ROWS |
Protocol::SEND_EOF));
}
void THD::make_explain_field_list(List<Item> &field_list)
{
Item *item;
CHARSET_INFO *cs= system_charset_info;
field_list.push_back(item= new Item_return_int("id",3, MYSQL_TYPE_LONGLONG));
@ -2044,10 +2055,9 @@ int THD::send_explain_fields(select_result *result)
}
item->maybe_null= 1;
field_list.push_back(new Item_empty_string("Extra", 255, cs));
return (result->send_result_set_metadata(field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF));
}
#ifdef SIGNAL_WITH_VIO_CLOSE
void THD::close_active_vio()
{
@ -2151,6 +2161,21 @@ void THD::rollback_item_tree_changes()
}
/*
Check if the thread has been killed, and also process "APC requests"
@retval true The thread is killed, execution should be interrupted
@retval false Not killed, continue execution
*/
bool THD::check_killed()
{
if (killed)
return TRUE;
apc_target.process_apc_requests();
return FALSE;
}
/*****************************************************************************
** Functions to provide a interface to select results
*****************************************************************************/
@ -2279,6 +2304,68 @@ int select_send::send_data(List<Item> &items)
DBUG_RETURN(0);
}
//////////////////////////////////////////////////////////////////////////////
int select_result_explain_buffer::send_data(List<Item> &items)
{
List_iterator_fast<Item> li(items);
char buff[MAX_FIELD_WIDTH];
String buffer(buff, sizeof(buff), &my_charset_bin);
DBUG_ENTER("select_send::send_data");
protocol->prepare_for_resend();
Item *item;
while ((item=li++))
{
if (item->send(protocol, &buffer))
{
protocol->free(); // Free used buffer
my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0));
break;
}
/*
Reset buffer to its original state, as it may have been altered in
Item::send().
*/
buffer.set(buff, sizeof(buff), &my_charset_bin);
}
//TODO: do we need the following:
if (thd->is_error())
{
protocol->remove_last_row();
DBUG_RETURN(1);
}
/* psergey-TODO: instead of protocol->write(), steal the packet here */
const char *packet_data;
size_t len;
protocol->get_packet(&packet_data, &len);
String *s= new (thd->mem_root) String;
s->append(packet_data, len);
data_rows.push_back(s);
protocol->remove_last_row(); // <-- this does nothing. Do we need it?
// prepare_for_resend() will wipe out the packet
DBUG_RETURN(0);
}
void select_result_explain_buffer::flush_data()
{
List_iterator<String> it(data_rows);
String *str;
while ((str= it++))
{
/* TODO: write out the lines. */
protocol->set_packet(str->ptr(), str->length());
protocol->write();
delete str;
}
data_rows.empty();
}
//////////////////////////////////////////////////////////////////////////////
bool select_send::send_eof()
{
/*
@ -3143,6 +3230,10 @@ void THD::end_statement()
}
/*
Start using arena specified by @set. Current arena data will be saved to
*backup.
*/
void THD::set_n_backup_active_arena(Query_arena *set, Query_arena *backup)
{
DBUG_ENTER("THD::set_n_backup_active_arena");
@ -3157,6 +3248,12 @@ void THD::set_n_backup_active_arena(Query_arena *set, Query_arena *backup)
}
/*
Stop using the temporary arena, and start again using the arena that is
specified in *backup.
The temporary arena is returned back into *set.
*/
void THD::restore_active_arena(Query_arena *set, Query_arena *backup)
{
DBUG_ENTER("THD::restore_active_arena");
@ -3169,6 +3266,30 @@ void THD::restore_active_arena(Query_arena *set, Query_arena *backup)
DBUG_VOID_RETURN;
}
/*
Produce EXPLAIN data.
This function is APC-scheduled to be run in the context of the thread that
we're producing EXPLAIN for.
*/
void Show_explain_request::get_explain_data(void *arg)
{
Show_explain_request *req= (Show_explain_request*)arg;
//TODO: change mem_root to point to request_thd->mem_root.
// Actually, change the ARENA, because we're going to allocate items!
Query_arena backup_arena;
req->target_thd->set_n_backup_active_arena((Query_arena*)req->request_thd,
&backup_arena);
req->target_thd->lex->unit.print_explain(req->explain_buf);
req->target_thd->restore_active_arena((Query_arena*)req->request_thd,
&backup_arena);
}
Statement::~Statement()
{
}

View File

@ -43,7 +43,7 @@
#include "violite.h" /* vio_is_connected */
#include "thr_lock.h" /* thr_lock_type, THR_LOCK_DATA,
THR_LOCK_INFO */
#include "my_apc.h"
class Reprepare_observer;
class Relay_log_info;
@ -1520,6 +1520,25 @@ private:
extern "C" void my_message_sql(uint error, const char *str, myf MyFlags);
class select_result_explain_buffer;
class Show_explain_request
{
public:
THD *target_thd;
THD *request_thd;
select_result_explain_buffer *explain_buf;
static void get_explain_data(void *arg);
};
class THD;
void mysqld_show_explain(THD *thd, ulong thread_id);
#ifndef DBUG_OFF
void dbug_serve_apcs(THD *thd, int n_calls);
#endif
/**
@class THD
For each client connection we create a separate thread with THD serving as
@ -2185,6 +2204,8 @@ public:
*/
killed_state volatile killed;
bool check_killed();
/* scramble - random string sent to client on handshake */
char scramble[SCRAMBLE_LENGTH+1];
@ -2383,10 +2404,20 @@ public:
void close_active_vio();
#endif
void awake(killed_state state_to_set);
/** Disconnect the associated communication endpoint. */
void disconnect();
/*
This is what allows this thread to serve as a target for others to
schedule Async Procedure Calls on.
It's possible to schedule arbitrary C function call but currently this
facility is used only by SHOW EXPLAIN code (See Show_explain_request)
*/
Apc_target apc_target;
#ifndef MYSQL_CLIENT
enum enum_binlog_query_type {
/* The query can be logged in row format or in statement format. */
@ -2580,7 +2611,7 @@ public:
void add_changed_table(const char *key, long key_length);
CHANGED_TABLE_LIST * changed_table_dup(const char *key, long key_length);
int send_explain_fields(select_result *result);
void make_explain_field_list(List<Item> &field_list);
/**
Clear the current error, if any.
We do not clear is_fatal_error or is_fatal_sub_stmt_error since we
@ -3190,10 +3221,42 @@ public:
class JOIN;
class select_result :public Sql_alloc {
/* Pure interface for sending tabular data */
class select_result_sink: public Sql_alloc
{
public:
/*
send_data returns 0 on ok, 1 on error and -1 if data was ignored, for
example for a duplicate row entry written to a temp table.
*/
virtual int send_data(List<Item> &items)=0;
virtual ~select_result_sink() {};
};
/*
Interface for sending tabular data, together with some other stuff:
- Primary purpose seems to be seding typed tabular data:
= the DDL is sent with send_fields()
= the rows are sent with send_data()
Besides that,
- there seems to be an assumption that the sent data is a result of
SELECT_LEX_UNIT *unit,
- nest_level is used by SQL parser
*/
class select_result :public select_result_sink
{
protected:
THD *thd;
/*
All descendant classes have their send_data() skip the first
unit->offset_limit_cnt rows sent. Select_materialize
also uses unit->get_unit_column_types().
*/
SELECT_LEX_UNIT *unit;
/* Something used only by the parser: */
public:
select_result();
virtual ~select_result() {};
@ -3211,11 +3274,6 @@ public:
virtual uint field_count(List<Item> &fields) const
{ return fields.elements; }
virtual bool send_result_set_metadata(List<Item> &list, uint flags)=0;
/*
send_data returns 0 on ok, 1 on error and -1 if data was ignored, for
example for a duplicate row entry written to a temp table.
*/
virtual int send_data(List<Item> &items)=0;
virtual bool initialize_tables (JOIN *join=0) { return 0; }
virtual void send_error(uint errcode,const char *err);
virtual bool send_eof()=0;
@ -3242,6 +3300,35 @@ public:
};
/*
A select result sink that collects the sent data and then can flush it to
network when requested.
This class is targeted at collecting EXPLAIN output:
- Unoptimized data storage (can't handle big datasets)
- Unlike select_result class, we don't assume that the sent data is an
output of a SELECT_LEX_UNIT (and so we dont apply "LIMIT x,y" from the
unit)
*/
class select_result_explain_buffer : public select_result_sink
{
public:
THD *thd;
Protocol *protocol;
select_result_explain_buffer(){};
/* The following is called in the child thread: */
int send_data(List<Item> &items);
/* this will be called in the parent thread: */
void flush_data();
List<String> data_rows;
};
/*
Base class for select_result descendands which intercept and
transform result set rows. As the rows are not sent to the client,
@ -3812,6 +3899,8 @@ class user_var_entry
DTCollation collation;
};
user_var_entry *get_variable(HASH *hash, LEX_STRING &name,
bool create_if_not_exists);
/*
Unique -- class for unique (removing of duplicates).

View File

@ -1872,6 +1872,7 @@ void st_select_lex::init_query()
nest_level= 0;
link_next= 0;
is_prep_leaf_list_saved= FALSE;
have_merged_subqueries= FALSE;
bzero((char*) expr_cache_may_be_used, sizeof(expr_cache_may_be_used));
m_non_agg_field_used= false;
m_agg_func_used= false;
@ -3440,7 +3441,7 @@ bool st_select_lex::optimize_unflattened_subqueries()
if (options & SELECT_DESCRIBE)
{
/* Optimize the subquery in the context of EXPLAIN. */
sl->set_explain_type();
sl->set_explain_type(FALSE);
sl->options|= SELECT_DESCRIBE;
inner_join->select_options|= SELECT_DESCRIBE;
}
@ -3832,7 +3833,7 @@ void SELECT_LEX::update_used_tables()
Set the EXPLAIN type for this subquery.
*/
void st_select_lex::set_explain_type()
void st_select_lex::set_explain_type(bool on_the_fly)
{
bool is_primary= FALSE;
if (next_select())
@ -3854,6 +3855,9 @@ void st_select_lex::set_explain_type()
}
}
if (on_the_fly && !is_primary && have_merged_subqueries)
is_primary= TRUE;
SELECT_LEX *first= master_unit()->first_select();
/* drop UNCACHEABLE_EXPLAIN, because it is for internal usage only */
uint8 is_uncacheable= (uncacheable & ~UNCACHEABLE_EXPLAIN);
@ -3906,10 +3910,15 @@ void st_select_lex::set_explain_type()
else
{
type= is_uncacheable ? "UNCACHEABLE UNION": "UNION";
if (this == master_unit()->fake_select_lex)
type= "UNION RESULT";
}
}
}
options|= SELECT_DESCRIBE;
if (!on_the_fly)
options|= SELECT_DESCRIBE;
}
@ -4056,6 +4065,83 @@ bool st_select_lex::is_merged_child_of(st_select_lex *ancestor)
}
int print_explain_message_line(select_result_sink *result,
SELECT_LEX *select_lex,
bool on_the_fly,
uint8 options,
const char *message);
int st_select_lex::print_explain(select_result_sink *output)
{
int res;
if (join && join->have_query_plan == JOIN::QEP_AVAILABLE)
{
res= join->print_explain(output, TRUE,
FALSE, // need_tmp_table,
FALSE, // bool need_order,
FALSE, // bool distinct,
NULL); //const char *message
if (res)
goto err;
for (SELECT_LEX_UNIT *unit= join->select_lex->first_inner_unit();
unit;
unit= unit->next_unit())
{
/*
Display subqueries only if they are not parts of eliminated WHERE/ON
clauses.
*/
if (!(unit->item && unit->item->eliminated))
{
unit->print_explain(output);
}
}
}
else
{
const char *msg;
if (!join)
DBUG_ASSERT(0); // psergey: TODO: can this happen or not?
if (join->have_query_plan == JOIN::QEP_NOT_PRESENT_YET)
msg= "Not yet optimized";
else
{
DBUG_ASSERT(join->have_query_plan == JOIN::QEP_DELETED);
msg= "Query plan already deleted";
}
res= print_explain_message_line(output, this, TRUE /* on_the_fly */,
0, msg);
}
err:
return 0;
}
int st_select_lex_unit::print_explain(select_result_sink *output)
{
int res= 0;
SELECT_LEX *first= first_select();
for (SELECT_LEX *sl= first; sl; sl= sl->next_select())
{
if ((res= sl->print_explain(output)))
break;
}
/*
Note: it could be that fake_select_lex->join == NULL still at this point
*/
if (fake_select_lex && !fake_select_lex->join)
{
res= print_fake_select_lex_join(output, TRUE /* on the fly */,
fake_select_lex, 0 /* flags */);
}
return res;
}
/**
A routine used by the parser to decide whether we are specifying a full
partitioning or if only partitions to add or to split.

View File

@ -193,6 +193,7 @@ enum enum_sql_command {
SQLCOM_SHOW_RELAYLOG_EVENTS,
SQLCOM_SHOW_USER_STATS, SQLCOM_SHOW_TABLE_STATS, SQLCOM_SHOW_INDEX_STATS,
SQLCOM_SHOW_CLIENT_STATS,
SQLCOM_SHOW_EXPLAIN,
/*
When a command is added here, be sure it's also added in mysqld.cc
@ -355,6 +356,8 @@ typedef uchar index_clause_map;
#define INDEX_HINT_MASK_ALL (INDEX_HINT_MASK_JOIN | INDEX_HINT_MASK_GROUP | \
INDEX_HINT_MASK_ORDER)
class select_result_sink;
/* Single element of an USE/FORCE/IGNORE INDEX list specified as a SQL hint */
class Index_hint : public Sql_alloc
{
@ -715,6 +718,7 @@ public:
friend int subselect_union_engine::exec();
List<Item> *get_unit_column_types();
int print_explain(select_result_sink *output);
};
typedef class st_select_lex_unit SELECT_LEX_UNIT;
@ -775,6 +779,12 @@ public:
those converted to jtbm nests. The list is emptied when conversion is done.
*/
List<Item_in_subselect> sj_subselects;
/*
Needed to correctly generate 'PRIMARY' or 'SIMPLE' for select_type column
of EXPLAIN
*/
bool have_merged_subqueries;
List<TABLE_LIST> leaf_tables;
List<TABLE_LIST> leaf_tables_exec;
@ -1004,7 +1014,7 @@ public:
*/
bool optimize_unflattened_subqueries();
/* Set the EXPLAIN type for this subquery. */
void set_explain_type();
void set_explain_type(bool on_the_fly);
bool handle_derived(LEX *lex, uint phases);
void append_table_to_list(TABLE_LIST *TABLE_LIST::*link, TABLE_LIST *table);
bool get_free_table_map(table_map *map, uint *tablenr);
@ -1027,8 +1037,9 @@ public:
bool save_leaf_tables(THD *thd);
bool save_prep_leaf_tables(THD *thd);
bool is_merged_child_of(st_select_lex *ancestor);
bool is_merged_child_of(st_select_lex *ancestor);
int print_explain(select_result_sink *output);
/*
For MODE_ONLY_FULL_GROUP_BY we need to maintain two flags:
- Non-aggregated fields are used in this select.

View File

@ -335,6 +335,7 @@ void init_update_queries(void)
sql_command_flags[SQLCOM_SHOW_ENGINE_STATUS]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_ENGINE_MUTEX]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_ENGINE_LOGS]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_EXPLAIN]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_PROCESSLIST]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_GRANTS]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_CREATE_DB]= CF_STATUS_COMMAND;
@ -3127,6 +3128,32 @@ end_with_restore_list:
thd->security_ctx->priv_user),
lex->verbose);
break;
case SQLCOM_SHOW_EXPLAIN:
{
/* Same security as SHOW PROCESSLIST (TODO check this) */
if (!thd->security_ctx->priv_user[0] &&
check_global_access(thd,PROCESS_ACL))
break;
Item *it= (Item *)lex->value_list.head();
if (lex->table_or_sp_used())
{
my_error(ER_NOT_SUPPORTED_YET, MYF(0), "Usage of subqueries or stored "
"function calls as part of this statement");
break;
}
if ((!it->fixed && it->fix_fields(lex->thd, &it)) || it->check_cols(1))
{
my_message(ER_SET_CONSTANTS_ONLY, ER(ER_SET_CONSTANTS_ONLY),
MYF(0));
goto error;
}
mysqld_show_explain(thd, (ulong)it->val_int());
break;
}
case SQLCOM_SHOW_AUTHORS:
res= mysqld_show_authors(thd);
break;

View File

@ -2134,6 +2134,7 @@ static bool check_prepared_statement(Prepared_statement *stmt)
Note that we don't need to have cases in this list if they are
marked with CF_STATUS_COMMAND in sql_command_flags
*/
case SQLCOM_SHOW_EXPLAIN:
case SQLCOM_DROP_TABLE:
case SQLCOM_RENAME_TABLE:
case SQLCOM_ALTER_TABLE:

View File

@ -331,6 +331,7 @@ enum enum_yes_no_unknown
External variables
*/
/* sql_yacc.cc */
#ifndef DBUG_OFF
extern void turn_parser_debug_on();

View File

@ -271,6 +271,53 @@ Item_equal *find_item_equal(COND_EQUAL *cond_equal, Field *field,
JOIN_TAB *first_depth_first_tab(JOIN* join);
JOIN_TAB *next_depth_first_tab(JOIN* join, JOIN_TAB* tab);
#ifndef DBUG_OFF
// psergey:
void dbug_serve_apcs(THD *thd, int n_calls)
{
// TODO how do we signal that we're SHOW-EXPLAIN-READY?
const char *save_proc_info= thd->proc_info;
thd_proc_info(thd, "show_explain_trap");
int n_apcs= thd->apc_target.n_calls_processed + n_calls;
while (thd->apc_target.n_calls_processed < n_apcs)
{
my_sleep(300);
if (thd->check_killed())
break;
}
thd_proc_info(thd, save_proc_info);
}
/*
Debugging: check if @name=value, comparing as integer
Intended usage:
DBUG_EXECUTE_IF("show_explain_probe_2",
if (dbug_user_var_equals_int(thd, "select_id", select_id))
dbug_serve_apcs(thd, 1);
);
*/
bool dbug_user_var_equals_int(THD *thd, const char *name, int value)
{
user_var_entry *var;
LEX_STRING varname= {(char*)name, strlen(name)};
if ((var= get_variable(&thd->user_vars, varname, FALSE)))
{
bool null_value;
longlong var_value= var->val_int(&null_value);
if (!null_value && var_value == value)
return TRUE;
}
return FALSE;
}
#endif
/**
This handles SELECT with and without UNION.
*/
@ -927,6 +974,13 @@ err:
}
int JOIN::optimize()
{
int res= optimize_inner();
if (!res)
have_query_plan= QEP_AVAILABLE;
return res;
}
/**
global select optimisation.
@ -940,7 +994,7 @@ err:
*/
int
JOIN::optimize()
JOIN::optimize_inner()
{
ulonglong select_opts_for_readinfo;
uint no_jbuf_after;
@ -2131,6 +2185,32 @@ JOIN::save_join_tab()
}
void JOIN::exec()
{
/*
Enable SHOW EXPLAIN only if we're in the top-level query.
*/
thd->apc_target.enable();
DBUG_EXECUTE_IF("show_explain_probe_join_exec_start",
if (dbug_user_var_equals_int(thd,
"show_explain_probe_select_id",
select_lex->select_number))
dbug_serve_apcs(thd, 1);
);
exec_inner();
DBUG_EXECUTE_IF("show_explain_probe_join_exec_end",
if (dbug_user_var_equals_int(thd,
"show_explain_probe_select_id",
select_lex->select_number))
dbug_serve_apcs(thd, 1);
);
thd->apc_target.disable();
}
/**
Exec select.
@ -2142,13 +2222,13 @@ JOIN::save_join_tab()
@todo
When can we have here thd->net.report_error not zero?
*/
void
JOIN::exec()
void JOIN::exec_inner()
{
List<Item> *columns_list= &fields_list;
int tmp_error;
DBUG_ENTER("JOIN::exec");
thd_proc_info(thd, "executing");
error= 0;
if (procedure)
@ -2445,9 +2525,17 @@ JOIN::exec()
DBUG_PRINT("info",("Creating group table"));
/* Free first data from old join */
fprintf(stderr,"Q: %s\n", thd->query());
//DBUG_ASSERT(0);
/*
psergey-todo: this is the place of pre-mature JOIN::free call.
*/
curr_join->join_free();
//psergey-todo: SHOW EXPLAIN probe here
if (curr_join->make_simple_join(this, curr_tmp_table))
DBUG_VOID_RETURN;
//psergey-todo: SHOW EXPLAIN probe here
calc_group_buffer(curr_join, group_list);
count_field_types(select_lex, &curr_join->tmp_table_param,
curr_join->tmp_all_fields1,
@ -2569,7 +2657,9 @@ JOIN::exec()
if (curr_tmp_table->distinct)
curr_join->select_distinct=0; /* Each row is unique */
//psergey-todo: SHOW EXPLAIN probe here
curr_join->join_free(); /* Free quick selects */
//psergey-todo: SHOW EXPLAIN probe here
if (curr_join->select_distinct && ! curr_join->group_list)
{
thd_proc_info(thd, "Removing duplicates");
@ -3671,7 +3761,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
goto error;
/* Generate an execution plan from the found optimal join order. */
DBUG_RETURN(join->thd->killed || get_best_combination(join));
DBUG_RETURN(join->thd->check_killed() || get_best_combination(join));
error:
/*
@ -6720,7 +6810,7 @@ best_extension_by_limited_search(JOIN *join,
DBUG_ENTER("best_extension_by_limited_search");
THD *thd= join->thd;
if (thd->killed) // Abort
if (thd->check_killed()) // Abort
DBUG_RETURN(TRUE);
DBUG_EXECUTE("opt", print_plan(join, idx, read_time, record_count, idx,
@ -6877,7 +6967,7 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count,
{
DBUG_ENTER("find_best");
THD *thd= join->thd;
if (thd->killed)
if (thd->check_killed())
DBUG_RETURN(TRUE);
if (!rest_tables)
{
@ -10455,7 +10545,8 @@ void JOIN::cleanup(bool full)
{
DBUG_ENTER("JOIN::cleanup");
DBUG_PRINT("enter", ("full %u", (uint) full));
have_query_plan= QEP_DELETED;
if (table)
{
JOIN_TAB *tab;
@ -15358,7 +15449,7 @@ create_internal_tmp_table_from_heap2(THD *thd, TABLE *table,
DBUG_EXECUTE_IF("raise_error", write_err= HA_ERR_FOUND_DUPP_KEY ;);
if (write_err)
goto err;
if (thd->killed)
if (thd->check_killed())
{
thd->send_kill_message();
goto err_killed;
@ -15617,6 +15708,15 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure)
else
{
DBUG_ASSERT(join->table_count);
THD *thd= join->thd;
DBUG_EXECUTE_IF("show_explain_probe_do_select",
if (dbug_user_var_equals_int(thd,
"show_explain_probe_select_id",
join->select_lex->select_number))
dbug_serve_apcs(thd, 1);
);
if (join->outer_ref_cond && !join->outer_ref_cond->val_int())
error= NESTED_LOOP_NO_MORE_ROWS;
else
@ -15747,7 +15847,7 @@ sub_select_cache(JOIN *join, JOIN_TAB *join_tab, bool end_of_records)
rc= sub_select(join, join_tab, end_of_records);
DBUG_RETURN(rc);
}
if (join->thd->killed)
if (join->thd->check_killed())
{
/* The user has aborted the execution of the query */
join->thd->send_kill_message();
@ -16034,7 +16134,7 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab,
DBUG_RETURN(NESTED_LOOP_ERROR);
if (error < 0)
DBUG_RETURN(NESTED_LOOP_NO_MORE_ROWS);
if (join->thd->killed) // Aborted by user
if (join->thd->check_killed()) // Aborted by user
{
join->thd->send_kill_message();
DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
@ -17238,7 +17338,7 @@ end_write(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
}
}
end:
if (join->thd->killed)
if (join->thd->check_killed())
{
join->thd->send_kill_message();
DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
@ -17321,7 +17421,7 @@ end_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
}
join->send_records++;
end:
if (join->thd->killed)
if (join->thd->check_killed())
{
join->thd->send_kill_message();
DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
@ -17371,7 +17471,7 @@ end_unique_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */
}
}
if (join->thd->killed)
if (join->thd->check_killed())
{
join->thd->send_kill_message();
DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
@ -17449,7 +17549,7 @@ end_write_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
if (join->procedure)
join->procedure->add();
end:
if (join->thd->killed)
if (join->thd->check_killed())
{
join->thd->send_kill_message();
DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
@ -18912,7 +19012,7 @@ static int remove_dup_with_compare(THD *thd, TABLE *table, Field **first_field,
error= file->ha_rnd_next(record);
for (;;)
{
if (thd->killed)
if (thd->check_killed())
{
thd->send_kill_message();
error=0;
@ -19044,7 +19144,7 @@ static int remove_dup_with_hash_index(THD *thd, TABLE *table,
for (;;)
{
uchar *org_key_pos;
if (thd->killed)
if (thd->check_killed())
{
thd->send_kill_message();
error=0;
@ -21063,29 +21163,154 @@ void JOIN::clear()
}
}
/*
Print an EXPLAIN line with all NULLs and given message in the 'Extra' column
*/
int print_explain_message_line(select_result_sink *result,
SELECT_LEX *select_lex,
bool on_the_fly,
uint8 options,
const char *message)
{
const CHARSET_INFO *cs= system_charset_info;
Item *item_null= new Item_null();
List<Item> item_list;
if (on_the_fly)
select_lex->set_explain_type(on_the_fly);
item_list.push_back(new Item_int((int32)
select_lex->select_number));
item_list.push_back(new Item_string(select_lex->type,
strlen(select_lex->type), cs));
for (uint i=0 ; i < 7; i++)
item_list.push_back(item_null);
if (options & DESCRIBE_PARTITIONS)
item_list.push_back(item_null);
if (options & DESCRIBE_EXTENDED)
item_list.push_back(item_null);
item_list.push_back(new Item_string(message,strlen(message),cs));
if (result->send_data(item_list))
return 1;
return 0;
}
int print_fake_select_lex_join(select_result_sink *result, bool on_the_fly,
SELECT_LEX *select_lex, uint8 select_options)
{
const CHARSET_INFO *cs= system_charset_info;
Item *item_null= new Item_null();
List<Item> item_list;
if (on_the_fly)
select_lex->set_explain_type(on_the_fly); //psergey
/*
here we assume that the query will return at least two rows, so we
show "filesort" in EXPLAIN. Of course, sometimes we'll be wrong
and no filesort will be actually done, but executing all selects in
the UNION to provide precise EXPLAIN information will hardly be
appreciated :)
*/
char table_name_buffer[SAFE_NAME_LEN];
item_list.empty();
/* id */
item_list.push_back(new Item_null);
/* select_type */
item_list.push_back(new Item_string(select_lex->type,
strlen(select_lex->type),
cs));
/* table */
{
SELECT_LEX *sl= select_lex->master_unit()->first_select();
uint len= 6, lastop= 0;
memcpy(table_name_buffer, STRING_WITH_LEN("<union"));
for (; sl && len + lastop + 5 < NAME_LEN; sl= sl->next_select())
{
len+= lastop;
lastop= my_snprintf(table_name_buffer + len, NAME_LEN - len,
"%u,", sl->select_number);
}
if (sl || len + lastop >= NAME_LEN)
{
memcpy(table_name_buffer + len, STRING_WITH_LEN("...>") + 1);
len+= 4;
}
else
{
len+= lastop;
table_name_buffer[len - 1]= '>'; // change ',' to '>'
}
item_list.push_back(new Item_string(table_name_buffer, len, cs));
}
/* partitions */
if (/*join->thd->lex->describe*/ select_options & DESCRIBE_PARTITIONS)
item_list.push_back(item_null);
/* type */
item_list.push_back(new Item_string(join_type_str[JT_ALL],
strlen(join_type_str[JT_ALL]),
cs));
/* possible_keys */
item_list.push_back(item_null);
/* key*/
item_list.push_back(item_null);
/* key_len */
item_list.push_back(item_null);
/* ref */
item_list.push_back(item_null);
/* in_rows */
if (select_options & DESCRIBE_EXTENDED)
item_list.push_back(item_null);
/* rows */
item_list.push_back(item_null);
/* extra */
if (select_lex->master_unit()->global_parameters->order_list.first)
item_list.push_back(new Item_string("Using filesort",
14, cs));
else
item_list.push_back(new Item_string("", 0, cs));
if (result->send_data(item_list))
return 1;
return 0;
}
/**
EXPLAIN handling.
Send a description about what how the select will be done to stdout.
Produce lines explaining execution of *this* select (not including children
selects)
@param on_the_fly TRUE <=> we're being executed on-the-fly, so don't make
modifications to any select's data structures
*/
static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
bool distinct,const char *message)
int JOIN::print_explain(select_result_sink *result, bool on_the_fly,
bool need_tmp_table, bool need_order,
bool distinct, const char *message)
{
List<Item> field_list;
List<Item> item_list;
JOIN *join= this; /* Legacy: this code used to be a non-member function */
THD *thd=join->thd;
select_result *result=join->result;
Item *item_null= new Item_null();
CHARSET_INFO *cs= system_charset_info;
int quick_type;
DBUG_ENTER("select_describe");
int error= 0;
DBUG_ENTER("JOIN::print_explain");
DBUG_PRINT("info", ("Select 0x%lx, type %s, message %s",
(ulong)join->select_lex, join->select_lex->type,
message ? message : "NULL"));
DBUG_ASSERT(this->have_query_plan == QEP_AVAILABLE);
/* Don't log this into the slow query log */
thd->server_status&= ~(SERVER_QUERY_NO_INDEX_USED | SERVER_QUERY_NO_GOOD_INDEX_USED);
join->unit->offset_limit_cnt= 0;
if (!on_the_fly)
{
thd->server_status&= ~(SERVER_QUERY_NO_INDEX_USED | SERVER_QUERY_NO_GOOD_INDEX_USED);
join->unit->offset_limit_cnt= 0;
}
/*
NOTE: the number/types of items pushed into item_list must be in sync with
@ -21093,7 +21318,9 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
*/
if (message)
{
item_list.push_back(new Item_int((int32)
// join->thd->lex->describe <- as options
#if 0
item_list.push_bace(new Item_int((int32)
join->select_lex->select_number));
item_list.push_back(new Item_string(join->select_lex->type,
strlen(join->select_lex->type), cs));
@ -21106,82 +21333,29 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
item_list.push_back(new Item_string(message,strlen(message),cs));
if (result->send_data(item_list))
join->error= 1;
error= 1;
#endif
//psergey-todo: is passing join->thd->lex->describe correct for SHOW EXPLAIN?
if (print_explain_message_line(result, join->select_lex, on_the_fly,
join->thd->lex->describe, message))
error= 1;
}
else if (join->select_lex == join->unit->fake_select_lex)
{
/*
here we assume that the query will return at least two rows, so we
show "filesort" in EXPLAIN. Of course, sometimes we'll be wrong
and no filesort will be actually done, but executing all selects in
the UNION to provide precise EXPLAIN information will hardly be
appreciated :)
*/
char table_name_buffer[SAFE_NAME_LEN];
item_list.empty();
/* id */
item_list.push_back(new Item_null);
/* select_type */
item_list.push_back(new Item_string(join->select_lex->type,
strlen(join->select_lex->type),
cs));
/* table */
{
SELECT_LEX *sl= join->unit->first_select();
uint len= 6, lastop= 0;
memcpy(table_name_buffer, STRING_WITH_LEN("<union"));
for (; sl && len + lastop + 5 < NAME_LEN; sl= sl->next_select())
{
len+= lastop;
lastop= my_snprintf(table_name_buffer + len, NAME_LEN - len,
"%u,", sl->select_number);
}
if (sl || len + lastop >= NAME_LEN)
{
memcpy(table_name_buffer + len, STRING_WITH_LEN("...>") + 1);
len+= 4;
}
else
{
len+= lastop;
table_name_buffer[len - 1]= '>'; // change ',' to '>'
}
item_list.push_back(new Item_string(table_name_buffer, len, cs));
}
/* partitions */
if (join->thd->lex->describe & DESCRIBE_PARTITIONS)
item_list.push_back(item_null);
/* type */
item_list.push_back(new Item_string(join_type_str[JT_ALL],
strlen(join_type_str[JT_ALL]),
cs));
/* possible_keys */
item_list.push_back(item_null);
/* key*/
item_list.push_back(item_null);
/* key_len */
item_list.push_back(item_null);
/* ref */
item_list.push_back(item_null);
/* in_rows */
if (join->thd->lex->describe & DESCRIBE_EXTENDED)
item_list.push_back(item_null);
/* rows */
item_list.push_back(item_null);
/* extra */
if (join->unit->global_parameters->order_list.first)
item_list.push_back(new Item_string("Using filesort",
14, cs));
else
item_list.push_back(new Item_string("", 0, cs));
if (result->send_data(item_list))
join->error= 1;
//psergey-todo: is passing join->thd->lex->describe correct for SHOW EXPLAIN?
if (print_fake_select_lex_join(result, on_the_fly,
join->select_lex,
join->thd->lex->describe))
error= 1;
}
else if (!join->select_lex->master_unit()->derived ||
join->select_lex->master_unit()->derived->is_materialized_derived())
{
table_map used_tables=0;
//if (!join->select_lex->type)
if (on_the_fly)
join->select_lex->set_explain_type(on_the_fly);
bool printing_materialize_nest= FALSE;
uint select_id= join->select_lex->select_number;
@ -21235,6 +21409,7 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
join->select_lex->type;
item_list.push_back(new Item_string(stype, strlen(stype), cs));
enum join_type tab_type= tab->type;
if ((tab->type == JT_ALL || tab->type == JT_HASH) &&
tab->select && tab->select->quick)
{
@ -21243,9 +21418,9 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
(quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_INTERSECT) ||
(quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT) ||
(quick_type == QUICK_SELECT_I::QS_TYPE_ROR_UNION))
tab->type= tab->type == JT_ALL ? JT_INDEX_MERGE : JT_HASH_INDEX_MERGE;
tab_type= tab->type == JT_ALL ? JT_INDEX_MERGE : JT_HASH_INDEX_MERGE;
else
tab->type= tab->type == JT_ALL ? JT_RANGE : JT_HASH_RANGE;
tab_type= tab->type == JT_ALL ? JT_RANGE : JT_HASH_RANGE;
}
/* table */
@ -21294,8 +21469,8 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
#endif
}
/* "type" column */
item_list.push_back(new Item_string(join_type_str[tab->type],
strlen(join_type_str[tab->type]),
item_list.push_back(new Item_string(join_type_str[tab_type],
strlen(join_type_str[tab_type]),
cs));
/* Build "possible_keys" value and add it to item_list */
if (!tab->keys.is_clear_all())
@ -21319,7 +21494,7 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
item_list.push_back(item_null);
/* Build "key", "key_len", and "ref" values and add them to item_list */
if (tab->type == JT_NEXT)
if (tab_type == JT_NEXT)
{
key_info= table->key_info+tab->index;
key_len= key_info->key_length;
@ -21348,12 +21523,12 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
}
}
}
if (is_hj && tab->type != JT_HASH)
if (is_hj && tab_type != JT_HASH)
{
tmp2.append(':');
tmp3.append(':');
}
if (tab->type == JT_HASH_NEXT)
if (tab_type == JT_HASH_NEXT)
{
register uint length;
key_info= table->key_info+tab->index;
@ -21380,7 +21555,7 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
item_list.push_back(new Item_string(tmp3.ptr(),tmp3.length(),cs));
else
item_list.push_back(item_null);
if (key_info && tab->type != JT_NEXT)
if (key_info && tab_type != JT_NEXT)
item_list.push_back(new Item_string(tmp4.ptr(),tmp4.length(),cs));
else
item_list.push_back(item_null);
@ -21433,7 +21608,7 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
ha_rows examined_rows;
if (tab->select && tab->select->quick)
examined_rows= tab->select->quick->records;
else if (tab->type == JT_NEXT || tab->type == JT_ALL || is_hj)
else if (tab_type == JT_NEXT || tab_type == JT_ALL || is_hj)
{
if (tab->limit)
examined_rows= tab->limit;
@ -21472,7 +21647,7 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
/* Build "Extra" field and add it to item_list. */
key_read=table->key_read;
if ((tab->type == JT_NEXT || tab->type == JT_CONST) &&
if ((tab_type == JT_NEXT || tab_type == JT_CONST) &&
table->covering_keys.is_set(tab->index))
key_read=1;
if (quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT &&
@ -21687,9 +21862,27 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
// For next iteration
used_tables|=table->map;
if (result->send_data(item_list))
join->error= 1;
error= 1;
}
}
DBUG_RETURN(error);
}
/*
See st_select_lex::print_explain() for the SHOW EXPLAIN counterpart
*/
static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
bool distinct,const char *message)
{
THD *thd=join->thd;
select_result *result=join->result;
DBUG_ENTER("select_describe");
join->error= join->print_explain(result, FALSE, /* Not on-the-fly */
need_tmp_table, need_order, distinct,
message);
for (SELECT_LEX_UNIT *unit= join->select_lex->first_inner_unit();
unit;
unit= unit->next_unit())
@ -21732,7 +21925,7 @@ bool mysql_explain_union(THD *thd, SELECT_LEX_UNIT *unit, select_result *result)
for (SELECT_LEX *sl= first; sl; sl= sl->next_select())
{
sl->set_explain_type();
sl->set_explain_type(FALSE); //psergey-todo: maybe remove this from here?
sl->options|= SELECT_DESCRIBE;
}

View File

@ -1161,8 +1161,10 @@ public:
const char *zero_result_cause; ///< not 0 if exec must return zero result
bool union_part; ///< this subselect is part of union
bool optimized; ///< flag to avoid double optimization in EXPLAIN
bool optimized; ///< flag to avoid double optimization in EXPLAIN
bool initialized; ///< flag to avoid double init_execution calls
enum { QEP_NOT_PRESENT_YET, QEP_AVAILABLE, QEP_DELETED} have_query_plan;
/*
Additional WHERE and HAVING predicates to be considered for IN=>EXISTS
@ -1245,6 +1247,7 @@ public:
ref_pointer_array_size= 0;
zero_result_cause= 0;
optimized= 0;
have_query_plan= QEP_NOT_PRESENT_YET;
initialized= 0;
cond_equal= 0;
having_equal= 0;
@ -1273,9 +1276,11 @@ public:
SELECT_LEX_UNIT *unit);
bool prepare_stage2();
int optimize();
int optimize_inner();
int reinit();
int init_execution();
void exec();
void exec_inner();
int destroy();
void restore_tmp();
bool alloc_func_list();
@ -1387,6 +1392,10 @@ public:
{
return (unit->item && unit->item->is_in_predicate());
}
int print_explain(select_result_sink *result, bool on_the_fly,
bool need_tmp_table, bool need_order,
bool distinct,const char *message);
private:
/**
TRUE if the query contains an aggregate function but has no GROUP
@ -1726,6 +1735,9 @@ inline bool optimizer_flag(THD *thd, uint flag)
return (thd->variables.optimizer_switch & flag);
}
int print_fake_select_lex_join(select_result_sink *result, bool on_the_fly,
SELECT_LEX *select_lex, uint8 select_options);
uint get_index_for_order(ORDER *order, TABLE *table, SQL_SELECT *select,
ha_rows limit, bool *need_sort, bool *reverse);
ORDER *simple_remove_const(ORDER *order, COND *where);

View File

@ -1998,6 +1998,97 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose)
DBUG_VOID_RETURN;
}
/*
SHOW EXPLAIN FOR command handler
@param thd Current thread's thd
@param thread_id Thread whose explain we need
@notes
- Attempt to do "SHOW EXPLAIN FOR <myself>" will properly produce "target not
running EXPLAINable command".
- todo: check how all this can/will work when using thread pools
*/
void mysqld_show_explain(THD *thd, ulong thread_id)
{
THD *tmp;
Protocol *protocol= thd->protocol;
List<Item> field_list;
DBUG_ENTER("mysqld_show_explain");
thd->make_explain_field_list(field_list);
if (protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS |
Protocol::SEND_EOF))
DBUG_VOID_RETURN;
/*
Find the thread we need EXPLAIN for. Thread search code was copied from
kill_one_thread()
*/
mysql_mutex_lock(&LOCK_thread_count); // For unlink from list
I_List_iterator<THD> it(threads);
while ((tmp=it++))
{
if (tmp->command == COM_DAEMON)
continue;
if (tmp->thread_id == thread_id)
{
mysql_mutex_lock(&tmp->LOCK_thd_data); // Lock from delete
break;
}
}
mysql_mutex_unlock(&LOCK_thread_count);
if (tmp)
{
bool bres;
/*
Ok we've found the thread of interest and it won't go away because
we're holding its LOCK_thd data.
Post it an EXPLAIN request.
todo: where to get timeout from?
*/
bool timed_out;
int timeout_sec= 30;
Show_explain_request explain_req;
select_result_explain_buffer *explain_buf;
explain_buf= new select_result_explain_buffer;
explain_buf->thd=thd;
explain_buf->protocol= thd->protocol;
explain_req.explain_buf= explain_buf;
explain_req.target_thd= tmp;
explain_req.request_thd= thd;
bres= tmp->apc_target.make_apc_call(Show_explain_request::get_explain_data,
(void*)&explain_req,
timeout_sec, &timed_out);
if (bres)
{
/* TODO not enabled or time out */
my_error(ER_ERROR_WHEN_EXECUTING_COMMAND, MYF(0),
"SHOW EXPLAIN",
"Target is not running EXPLAINable command");
}
mysql_mutex_unlock(&tmp->LOCK_thd_data);
if (!bres)
{
explain_buf->flush_data();
my_eof(thd);
}
}
else
{
my_error(ER_NO_SUCH_THREAD, MYF(0), thread_id);
}
DBUG_VOID_RETURN;
}
int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond)
{
TABLE *table= tables->table;

View File

@ -11614,6 +11614,12 @@ show_param:
Lex->spname= $3;
Lex->sql_command = SQLCOM_SHOW_CREATE_EVENT;
}
| describe_command FOR_SYM expr
{
Lex->sql_command= SQLCOM_SHOW_EXPLAIN;
Lex->value_list.empty();
Lex->value_list.push_front($3);
}
;
show_engine_param: