From ca8aa3901c5596d8d393f0f0ded5ea7292ed01c8 Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Thu, 26 Apr 2012 06:40:36 +0530 Subject: [PATCH 01/12] MWL#182: Explain running statements - Code cleanup --- mysql-test/r/show_explain.result | 58 +++++++++++++++++++++++++- mysql-test/t/show_explain.test | 71 ++++++++++++++++++++++++++++++-- sql/my_apc.cc | 56 ++++++++++++++++++++----- sql/my_apc.h | 63 +++++++++++++++------------- sql/sql_class.cc | 3 +- sql/sql_class.h | 2 + sql/sql_lex.cc | 9 ++++ sql/sql_show.cc | 5 ++- 8 files changed, 220 insertions(+), 47 deletions(-) diff --git a/mysql-test/r/show_explain.result b/mysql-test/r/show_explain.result index 6742c6eacfe..07ccab1419e 100644 --- a/mysql-test/r/show_explain.result +++ b/mysql-test/r/show_explain.result @@ -1,4 +1,4 @@ -drop table if exists t0, t1; +drop table if exists t0, t1, t2; create table t0 (a int); insert into t0 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); create table t1 (a int); @@ -125,4 +125,60 @@ id select_type table type possible_keys key key_len ref rows Extra 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 +# Try to do SHOW EXPLAIN for a query that runs a SET command: +# I've found experimentally that select_id==2 here... +# +set @show_explain_probe_select_id=2; +set debug='d,show_explain_probe_1'; +set @foo= (select max(a) from t0 where sin(a) >0); +show explain for $thr2; +ERROR HY000: Error when executing command SHOW EXPLAIN: Target is not running EXPLAINable command +# +# Attempt SHOW EXPLAIN for an UPDATE +# +create table t2 as select a as a, a as dummy from t0 limit 2; +set @show_explain_probe_select_id=2; +set debug='d,show_explain_probe_1'; +update t2 set dummy=0 where (select max(a) from t0 where t2.a + t0.a <3) >3 ; +show explain for $thr2; +ERROR HY000: Error when executing command SHOW EXPLAIN: Target is not running EXPLAINable command +show explain for $thr2; +ERROR HY000: Error when executing command SHOW EXPLAIN: Target is not running EXPLAINable command +drop table t2; +# +# Attempt SHOW EXPLAIN for a DELETE +# +create table t2 as select a as a, a as dummy from t0 limit 2; +set @show_explain_probe_select_id=2; +set debug='d,show_explain_probe_1'; +delete from t2 where (select max(a) from t0 where t2.a + t0.a <3) >3 ; +show explain for $thr2; +ERROR HY000: Error when executing command SHOW EXPLAIN: Target is not running EXPLAINable command +show explain for $thr2; +ERROR HY000: Error when executing command SHOW EXPLAIN: Target is not running EXPLAINable command +drop table t2; +# +# Multiple SHOW EXPLAIN calls for one select +# +create table t2 as select a as a, a as dummy from t0 limit 3; +set @show_explain_probe_select_id=2; +set debug='d,show_explain_probe_1'; +select t2.a, ((select max(a) from t0 where t2.a + t0.a <3) >3) as SUBQ from t2; +show explain for $thr2; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t2 ALL NULL NULL NULL NULL 3 +2 DEPENDENT SUBQUERY t0 ALL NULL NULL NULL NULL 10 Using where +show explain for $thr2; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t2 ALL NULL NULL NULL NULL 3 +2 DEPENDENT SUBQUERY t0 ALL NULL NULL NULL NULL 10 Using where +show explain for $thr2; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t2 ALL NULL NULL NULL NULL 3 +2 DEPENDENT SUBQUERY t0 ALL NULL NULL NULL NULL 10 Using where +a SUBQ +0 0 +1 0 +2 0 +drop table t2; drop table t0,t1; diff --git a/mysql-test/t/show_explain.test b/mysql-test/t/show_explain.test index 5ed78be1ea4..717949d5cc5 100644 --- a/mysql-test/t/show_explain.test +++ b/mysql-test/t/show_explain.test @@ -4,7 +4,7 @@ --source include/have_debug.inc --disable_warnings -drop table if exists t0, t1; +drop table if exists t0, t1, t2; --enable_warnings # @@ -186,12 +186,75 @@ reap; # TODO: explain in the parent subuqery when the un-correlated child has been # run (and have done irreversible cleanups) +# ^^ Is this at all possible after 5.3? +# Maybe, for 5.3 try this: +# - run before/after the parent has invoked child's optimization +# - run after materialization -# TODO: hit JOIN::optimize for non-select commands: UPDATE/DELETE, SET. +--echo # Try to do SHOW EXPLAIN for a query that runs a SET command: +--echo # I've found experimentally that select_id==2 here... +--echo # +set @show_explain_probe_select_id=2; +set debug='d,show_explain_probe_1'; +send set @foo= (select max(a) from t0 where sin(a) >0); +connection default; +--source include/wait_condition.inc +--error ER_ERROR_WHEN_EXECUTING_COMMAND +evalp show explain for $thr2; +connection con1; +reap; + +--echo # +--echo # Attempt SHOW EXPLAIN for an UPDATE +--echo # +create table t2 as select a as a, a as dummy from t0 limit 2; +set @show_explain_probe_select_id=2; +set debug='d,show_explain_probe_1'; +send update t2 set dummy=0 where (select max(a) from t0 where t2.a + t0.a <3) >3 ; +connection default; +--source include/wait_condition.inc +--error ER_ERROR_WHEN_EXECUTING_COMMAND +evalp show explain for $thr2; +--error ER_ERROR_WHEN_EXECUTING_COMMAND +evalp show explain for $thr2; +connection con1; +reap; +drop table t2; + +--echo # +--echo # Attempt SHOW EXPLAIN for a DELETE +--echo # +create table t2 as select a as a, a as dummy from t0 limit 2; +set @show_explain_probe_select_id=2; +set debug='d,show_explain_probe_1'; +send delete from t2 where (select max(a) from t0 where t2.a + t0.a <3) >3 ; +connection default; +--source include/wait_condition.inc +--error ER_ERROR_WHEN_EXECUTING_COMMAND +evalp show explain for $thr2; +--error ER_ERROR_WHEN_EXECUTING_COMMAND +evalp show explain for $thr2; +connection con1; +reap; +drop table t2; -## TODO: Test this: multiple SHOW EXPLAIN calls in course of running of one select -## +--echo # +--echo # Multiple SHOW EXPLAIN calls for one select +--echo # +create table t2 as select a as a, a as dummy from t0 limit 3; +set @show_explain_probe_select_id=2; +set debug='d,show_explain_probe_1'; +send select t2.a, ((select max(a) from t0 where t2.a + t0.a <3) >3) as SUBQ from t2; +connection default; +--source include/wait_condition.inc +evalp show explain for $thr2; +evalp show explain for $thr2; +evalp show explain for $thr2; +connection con1; +reap; +drop table t2; + ## 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 91559483b1f..58290ece56b 100644 --- a/sql/my_apc.cc +++ b/sql/my_apc.cc @@ -24,6 +24,14 @@ */ +/* + Initialize the target. + + @note + Initialization must be done prior to enabling/disabling the target, or making + any call requests to it. + Initial state after initialization is 'disabled'. +*/ void Apc_target::init() { // todo: should use my_pthread_... functions instead? @@ -36,6 +44,9 @@ void Apc_target::init() } +/* + Destroy the target. The target must be disabled when this call is made. +*/ void Apc_target::destroy() { DBUG_ASSERT(!enabled); @@ -43,6 +54,9 @@ void Apc_target::destroy() } +/* + Enter ther state where the target is available for serving APC requests +*/ void Apc_target::enable() { pthread_mutex_lock(&LOCK_apc_queue); @@ -51,6 +65,13 @@ void Apc_target::enable() } +/* + Make the target unavailable for serving APC requests. + + @note + This call will serve all requests that were already enqueued +*/ + void Apc_target::disable() { bool process= FALSE; @@ -62,6 +83,9 @@ void Apc_target::disable() process_apc_requests(); } + +/* (internal) Put request into the request list */ + void Apc_target::enqueue_request(Call_request *qe) { //call_queue_size++; @@ -81,6 +105,13 @@ void Apc_target::enqueue_request(Call_request *qe) } } + +/* + (internal) Remove given request from the request queue. + + The request is not necessarily first in the queue. +*/ + void Apc_target::dequeue_request(Call_request *qe) { //call_queue_size--; @@ -99,8 +130,10 @@ void Apc_target::dequeue_request(Call_request *qe) /* - Make an apc call in another thread. The caller is responsible so - that we're not calling to ourselves. + Make an APC (Async Procedure Call) in another thread. + + The caller is responsible for making sure he's not calling an Apc_target + that is serviced by the same thread it is called from. psergey-todo: Should waits here be KILLable? (it seems one needs to use thd->enter_cond() calls to be killable) @@ -119,7 +152,7 @@ bool Apc_target::make_apc_call(apc_func_t func, void *func_arg, Call_request apc_request; apc_request.func= func; apc_request.func_arg= func_arg; - apc_request.done= FALSE; + apc_request.processed= 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); @@ -133,19 +166,19 @@ bool Apc_target::make_apc_call(apc_func_t func, void *func_arg, int wait_res= 0; /* todo: how about processing other errors here? */ - while (!apc_request.done && (wait_res != ETIMEDOUT)) + while (!apc_request.processed && (wait_res != ETIMEDOUT)) { wait_res= pthread_cond_timedwait(&apc_request.COND_request, &apc_request.LOCK_request, &abstime); } - if (!apc_request.done) + if (!apc_request.processed) { - /* We timed out */ - apc_request.done= TRUE; + /* The wait has timed out. Remove the request from the queue */ + apc_request.processed= TRUE; *timed_out= TRUE; pthread_mutex_unlock(&apc_request.LOCK_request); - + //psergey-todo: "Whoa rare event" refers to this part, right? put a comment. pthread_mutex_lock(&LOCK_apc_queue); dequeue_request(&apc_request); pthread_mutex_unlock(&LOCK_apc_queue); @@ -171,7 +204,8 @@ bool Apc_target::make_apc_call(apc_func_t func, void *func_arg, /* - Process all APC requests + Process all APC requests. + This should be called periodically by the APC target thread. */ void Apc_target::process_apc_requests() @@ -190,7 +224,7 @@ void Apc_target::process_apc_requests() request->what="seen by process_apc_requests"; pthread_mutex_lock(&request->LOCK_request); - if (request->done) + if (request->processed) { /* We can get here when @@ -214,7 +248,7 @@ void Apc_target::process_apc_requests() */ request->what="dequeued by process_apc_requests"; dequeue_request(request); - request->done= TRUE; + request->processed= TRUE; pthread_mutex_unlock(&LOCK_apc_queue); diff --git a/sql/my_apc.h b/sql/my_apc.h index 3906aa24408..0698703ad40 100644 --- a/sql/my_apc.h +++ b/sql/my_apc.h @@ -3,9 +3,18 @@ */ /* - Design - - Mutex-guarded request queue (it belongs to the target), which can be enabled/ - disabled (when empty). + Interface + ~~~~~~~~~ + ( + - This is an APC request queue + - We assume there is a particular owner thread which periodically calls + process_apc_requests() to serve the call requests. + - Other threads can post call requests, and block until they are exectued. + ) + + Implementation + ~~~~~~~~~~~~~~ + - The target has a mutex-guarded request queue. - After the request has been put into queue, the requestor waits for request to be satisfied. The worker satisifes the request and signals the @@ -21,31 +30,11 @@ 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); @@ -68,18 +57,32 @@ public: #endif private: class Call_request; + + /* + Non-zero value means we're enabled. It's an int, not bool, because one can + call enable() N times (and then needs to call disable() N times before the + target is really disabled) + */ int enabled; + /* + Circular, double-linked list of all enqueued call requests. + We use this structure, because we + - process requests sequentially + - a thread that has posted a request may time out (or be KILLed) and + cancel the request, which means we'll need to remove its request at + arbitrary point in time. + */ Call_request *apc_calls; - pthread_mutex_t LOCK_apc_queue; + pthread_mutex_t LOCK_apc_queue; class Call_request { public: - apc_func_t func; - void *func_arg; - bool done; + apc_func_t func; /* Function to call */ + void *func_arg; /* Argument to pass it */ + bool processed; pthread_mutex_t LOCK_request; pthread_cond_t COND_request; @@ -87,13 +90,15 @@ private: Call_request *next; Call_request *prev; - const char *what; + const char *what; /* State of the request */ }; void enqueue_request(Call_request *qe); void dequeue_request(Call_request *qe); + + /* return the first call request in queue, or NULL if there are none enqueued */ Call_request *get_first_in_queue() - { + { return apc_calls; } }; diff --git a/sql/sql_class.cc b/sql/sql_class.cc index d6f976cb505..545c9aff5e9 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -3033,7 +3033,8 @@ void Show_explain_request::get_explain_data(void *arg) 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); + if (req->target_thd->lex->unit.print_explain(req->explain_buf)) + req->failed_to_produce= TRUE; req->target_thd->restore_active_arena((Query_arena*)req->request_thd, &backup_arena); diff --git a/sql/sql_class.h b/sql/sql_class.h index 85f5d35f3d2..de7c8de6c26 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1467,6 +1467,8 @@ public: THD *target_thd; THD *request_thd; + bool failed_to_produce; + select_result_explain_buffer *explain_buf; static void get_explain_data(void *arg); diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 544246a1a4d..661ca2b4383 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -3792,6 +3792,15 @@ int st_select_lex_unit::print_explain(select_result_sink *output) { int res= 0; SELECT_LEX *first= first_select(); + + if (first && !first->next_select() && !first->join) + { + /* + If there is only one child, 'first', and it has join==NULL, emit "not in + EXPLAIN state" error. + */ + return 1; + } for (SELECT_LEX *sl= first; sl; sl= sl->next_select()) { diff --git a/sql/sql_show.cc b/sql/sql_show.cc index c30a8aad1eb..dd1b749e8bd 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -2112,16 +2112,19 @@ void mysqld_show_explain(THD *thd, ulong thread_id) explain_req.explain_buf= explain_buf; explain_req.target_thd= tmp; explain_req.request_thd= thd; + explain_req.failed_to_produce= FALSE; bres= tmp->apc_target.make_apc_call(Show_explain_request::get_explain_data, (void*)&explain_req, timeout_sec, &timed_out); - if (bres) + + if (bres || explain_req.failed_to_produce) { /* TODO not enabled or time out */ my_error(ER_ERROR_WHEN_EXECUTING_COMMAND, MYF(0), "SHOW EXPLAIN", "Target is not running EXPLAINable command"); + bres= TRUE; } pthread_mutex_unlock(&tmp->LOCK_thd_data); if (!bres) From 9b269ea11b49c186a12f1a877c1d7cfa26b8b5c1 Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Thu, 26 Apr 2012 07:17:34 +0530 Subject: [PATCH 02/12] MWL#182: Explain running statements - Correct thd->killed checks in join cache and filesort modules. --- sql/filesort.cc | 20 +++++++++++--------- sql/sql_join_cache.cc | 6 +++--- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/sql/filesort.cc b/sql/filesort.cc index 772698c6e1a..3b551796ae1 100644 --- a/sql/filesort.cc +++ b/sql/filesort.cc @@ -493,7 +493,7 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select, my_off_t record; TABLE *sort_form; THD *thd= current_thd; - volatile killed_state *killed= &thd->killed; + //volatile killed_state *killed= &thd->killed; handler *file; MY_BITMAP *save_read_set, *save_write_set, *save_vcol_set; uchar *next_sort_key= sort_keys_buf; @@ -588,7 +588,7 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select, break; } - if (*killed) + if (thd->check_killed()) { DBUG_PRINT("info",("Sort killed by user")); if (!indexfile && !quick_select) @@ -1229,18 +1229,20 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file, void *first_cmp_arg; element_count dupl_count= 0; uchar *src; - killed_state not_killable; + /* killed_state not_killable; */ uchar *unique_buff= param->unique_buff; - volatile killed_state *killed= ¤t_thd->killed; + /* volatile killed_state *killed= ¤t_thd->killed; */ + const bool killable= !param->not_killable; + THD* const thd=current_thd; DBUG_ENTER("merge_buffers"); - status_var_increment(current_thd->status_var.filesort_merge_passes); - current_thd->query_plan_fsort_passes++; - if (param->not_killable) + status_var_increment(thd->status_var.filesort_merge_passes); + thd->query_plan_fsort_passes++; + /*if (param->not_killable) { killed= ¬_killable; not_killable= NOT_KILLED; - } + }*/ error=0; rec_length= param->rec_length; @@ -1317,7 +1319,7 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file, while (queue.elements > 1) { - if (*killed) + if (killable && thd->check_killed()) { error= 1; goto err; /* purecov: inspected */ } diff --git a/sql/sql_join_cache.cc b/sql/sql_join_cache.cc index d49be2e446c..6492fb6cd61 100644 --- a/sql/sql_join_cache.cc +++ b/sql/sql_join_cache.cc @@ -2235,7 +2235,7 @@ enum_nested_loop_state JOIN_CACHE::join_matching_records(bool skip_last) while (!(error= join_tab_scan->next())) { - if (join->thd->killed) + if (join->thd->check_killed()) { /* The user has aborted the execution of the query */ join->thd->send_kill_message(); @@ -2505,7 +2505,7 @@ enum_nested_loop_state JOIN_CACHE::join_null_complements(bool skip_last) for ( ; cnt; cnt--) { - if (join->thd->killed) + if (join->thd->check_killed()) { /* The user has aborted the execution of the query */ join->thd->send_kill_message(); @@ -3355,7 +3355,7 @@ int JOIN_TAB_SCAN::next() update_virtual_fields(thd, table); while (!err && select && (skip_rc= select->skip_record(thd)) <= 0) { - if (thd->killed || skip_rc < 0) + if (thd->check_killed() || skip_rc < 0) return 1; /* Move to the next record if the last retrieved record does not From ff40705f89b977d966427710e1b32a0774a2f0b3 Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Thu, 26 Apr 2012 08:48:31 +0530 Subject: [PATCH 03/12] Make SHOW EXPLAIN FOR produce a warning with the original text of query that the EXPLAIN is for. --- mysql-test/r/show_explain.result | 28 ++++++++++++++++++++++++++++ sql/sql_class.cc | 16 +++++++++++----- sql/sql_class.h | 2 ++ sql/sql_show.cc | 5 +++++ 4 files changed, 46 insertions(+), 5 deletions(-) diff --git a/mysql-test/r/show_explain.result b/mysql-test/r/show_explain.result index 07ccab1419e..e451ef3108b 100644 --- a/mysql-test/r/show_explain.result +++ b/mysql-test/r/show_explain.result @@ -20,12 +20,16 @@ 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 +Warnings: +Note 1003 select count(*) from t1 where a < 100000 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 +Warnings: +Note 1003 select max(c) from t1 where a < 10 max(c) 9 # We can catch EXPLAIN, too. @@ -35,6 +39,8 @@ 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 +Warnings: +Note 1003 explain select max(c) from t1 where a < 10 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; @@ -47,6 +53,8 @@ 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 ALL NULL NULL NULL NULL NULL +Warnings: +Note 1003 explain select a from t0 A union select a+1 from t0 B 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 @@ -60,6 +68,8 @@ 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 ALL NULL NULL NULL NULL NULL +Warnings: +Note 1003 explain select a from t0 A union select a+1 from t0 B 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 @@ -72,6 +82,8 @@ 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 +Warnings: +Note 1003 select a, (select max(a) from t0 B) from t0 A where a<1 a (select max(a) from t0 B) 0 9 # Uncorrelated subquery, explain @@ -82,6 +94,8 @@ 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 +Warnings: +Note 1003 explain select a, (select max(a) from t0 B) from t0 A where a<1 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 @@ -93,6 +107,8 @@ 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 +Warnings: +Note 1003 select a, (select max(a) from t0 b where b.a+a.a<10) from t0 a where a<1 a (select max(a) from t0 b where b.a+a.a<10) 0 9 # correlated subquery, explain @@ -103,6 +119,8 @@ 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 +Warnings: +Note 1003 select a, (select max(a) from t0 b where b.a+a.a<10) from t0 a where a<1 a (select max(a) from t0 b where b.a+a.a<10) 0 9 # correlated subquery, select, while inside the subquery @@ -113,6 +131,8 @@ 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 +Warnings: +Note 1003 select a, (select max(a) from t0 b where b.a+a.a<10) from t0 a where a<1 a (select max(a) from t0 b where b.a+a.a<10) 0 9 # correlated subquery, explain, while inside the subquery @@ -123,6 +143,8 @@ 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 +Warnings: +Note 1003 select a, (select max(a) from t0 b where b.a+a.a<10) from t0 a where a<1 a (select max(a) from t0 b where b.a+a.a<10) 0 9 # Try to do SHOW EXPLAIN for a query that runs a SET command: @@ -168,14 +190,20 @@ show explain for $thr2; id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t2 ALL NULL NULL NULL NULL 3 2 DEPENDENT SUBQUERY t0 ALL NULL NULL NULL NULL 10 Using where +Warnings: +Note 1003 select t2.a, ((select max(a) from t0 where t2.a + t0.a <3) >3) as SUBQ from t2 show explain for $thr2; id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t2 ALL NULL NULL NULL NULL 3 2 DEPENDENT SUBQUERY t0 ALL NULL NULL NULL NULL 10 Using where +Warnings: +Note 1003 select t2.a, ((select max(a) from t0 where t2.a + t0.a <3) >3) as SUBQ from t2 show explain for $thr2; id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t2 ALL NULL NULL NULL NULL 3 2 DEPENDENT SUBQUERY t0 ALL NULL NULL NULL NULL 10 Using where +Warnings: +Note 1003 select t2.a, ((select max(a) from t0 where t2.a + t0.a <3) >3) as SUBQ from t2 a SUBQ 0 0 1 0 diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 545c9aff5e9..4e9a0507bba 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -3030,14 +3030,20 @@ void Show_explain_request::get_explain_data(void *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); + THD *target_thd= req->target_thd; - if (req->target_thd->lex->unit.print_explain(req->explain_buf)) + target_thd->set_n_backup_active_arena((Query_arena*)req->request_thd, + &backup_arena); + + req->query_str.copy(target_thd->query(), + target_thd->query_length(), + &my_charset_bin); + + if (target_thd->lex->unit.print_explain(req->explain_buf)) req->failed_to_produce= TRUE; - req->target_thd->restore_active_arena((Query_arena*)req->request_thd, - &backup_arena); + target_thd->restore_active_arena((Query_arena*)req->request_thd, + &backup_arena); } diff --git a/sql/sql_class.h b/sql/sql_class.h index de7c8de6c26..223a42b1d02 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1471,6 +1471,8 @@ public: select_result_explain_buffer *explain_buf; + String query_str; + static void get_explain_data(void *arg); }; diff --git a/sql/sql_show.cc b/sql/sql_show.cc index dd1b749e8bd..a0c39ff6c8c 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -2126,6 +2126,11 @@ void mysqld_show_explain(THD *thd, ulong thread_id) "Target is not running EXPLAINable command"); bres= TRUE; } + else + { + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + ER_YES, explain_req.query_str.c_ptr_safe()); + } pthread_mutex_unlock(&tmp->LOCK_thd_data); if (!bres) { From cdc9a1172d7b75b16d92a6179478c2689ac04bae Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Thu, 10 May 2012 01:45:38 +0530 Subject: [PATCH 04/12] MWL#182: Explain running statements: Make SHOW EXPLAIN work for queries that do "Using temporary" and/or "Using filesort" - Patch#1: Don't lose "Using temporary/filesort" in the SHOW EXPLAIN output. --- mysql-test/r/show_explain.result | 75 ++++++++++++++++++++++++++++++++ mysql-test/t/show_explain.test | 46 ++++++++++++++++++++ sql/sql_lex.cc | 6 +-- 3 files changed, 124 insertions(+), 3 deletions(-) diff --git a/mysql-test/r/show_explain.result b/mysql-test/r/show_explain.result index e451ef3108b..74def21585f 100644 --- a/mysql-test/r/show_explain.result +++ b/mysql-test/r/show_explain.result @@ -209,4 +209,79 @@ a SUBQ 1 0 2 0 drop table t2; +# +# SHOW EXPLAIN for SELECT ... ORDER BY with "Using filesort" +# +explain select * from t0 order by a; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t0 ALL NULL NULL NULL NULL 10 Using filesort +set debug='d,show_explain_probe_1'; +set @show_explain_probe_select_id=1; +select * from t0 order by a; +show explain for $thr2; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t0 ALL NULL NULL NULL NULL 10 Using filesort +Warnings: +Note 1003 select * from t0 order by a +a +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +# +# SHOW EXPLAIN for SELECT ... with "Using temporary" +# +explain select distinct a from t0; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t0 ALL NULL NULL NULL NULL 10 Using temporary +set debug='d,show_explain_probe_1'; +set @show_explain_probe_select_id=1; +select distinct a from t0; +show explain for $thr2; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t0 ALL NULL NULL NULL NULL 10 Using temporary +Warnings: +Note 1003 select distinct a from t0 +a +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +# +# SHOW EXPLAIN for SELECT ... with "Using temporary; Using filesort" +# +explain select distinct a from t0; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t0 ALL NULL NULL NULL NULL 10 Using temporary +set debug='d,show_explain_probe_1'; +set @show_explain_probe_select_id=1; +select distinct a from t0; +show explain for $thr2; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t0 ALL NULL NULL NULL NULL 10 Using temporary +Warnings: +Note 1003 select distinct a from t0 +a +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 drop table t0,t1; diff --git a/mysql-test/t/show_explain.test b/mysql-test/t/show_explain.test index 717949d5cc5..4fc6aaad932 100644 --- a/mysql-test/t/show_explain.test +++ b/mysql-test/t/show_explain.test @@ -255,6 +255,52 @@ connection con1; reap; drop table t2; +--echo # +--echo # SHOW EXPLAIN for SELECT ... ORDER BY with "Using filesort" +--echo # +explain select * from t0 order by a; + +set debug='d,show_explain_probe_1'; +set @show_explain_probe_select_id=1; +send select * from t0 order by a; +connection default; +--source include/wait_condition.inc +evalp show explain for $thr2; +connection con1; +reap; + +--echo # +--echo # SHOW EXPLAIN for SELECT ... with "Using temporary" +--echo # +connection default; +explain select distinct a from t0; +connection con1; + +set debug='d,show_explain_probe_1'; +set @show_explain_probe_select_id=1; +send select distinct a from t0; +connection default; +--source include/wait_condition.inc +evalp show explain for $thr2; +connection con1; +reap; + +--echo # +--echo # SHOW EXPLAIN for SELECT ... with "Using temporary; Using filesort" +--echo # +connection default; +explain select distinct a from t0; +connection con1; + +set debug='d,show_explain_probe_1'; +set @show_explain_probe_select_id=1; +send select distinct a from t0; +connection default; +--source include/wait_condition.inc +evalp show explain for $thr2; +connection con1; +reap; + ## TODO: Test this: have several SHOW EXPLAIN requests be queued up for a ## thread and served together. diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 661ca2b4383..a47ba9b5024 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -3752,9 +3752,9 @@ int st_select_lex::print_explain(select_result_sink *output) if (join && join->optimized == 2) { res= join->print_explain(output, TRUE, - FALSE, // need_tmp_table, - FALSE, // bool need_order, - FALSE, // bool distinct, + join->need_tmp, // need_tmp_table + (join->order != 0 && !join->skip_sort_order), // bool need_order + join->select_distinct, // bool distinct NULL); //const char *message if (res) goto err; From 58b9164f0468768eff64841f4f066840b9ff2206 Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Thu, 10 May 2012 13:43:48 +0400 Subject: [PATCH 05/12] MDEV-238: SHOW EXPLAIN: Server crashes in JOIN::print_explain with FROM subquery and GROUP BY - Support SHOW EXPLAIN for selects that have "Using temporary; Using filesort". --- mysql-test/r/show_explain.result | 25 ++++++++++++++++++ mysql-test/t/show_explain.test | 22 ++++++++++++++++ sql/filesort.cc | 6 +++++ sql/sql_select.cc | 44 ++++++++++++++++++++++++-------- sql/sql_select.h | 14 ++++++++++ sql/table.h | 7 ++++- 6 files changed, 106 insertions(+), 12 deletions(-) diff --git a/mysql-test/r/show_explain.result b/mysql-test/r/show_explain.result index 74def21585f..db1d57b7004 100644 --- a/mysql-test/r/show_explain.result +++ b/mysql-test/r/show_explain.result @@ -284,4 +284,29 @@ a 7 8 9 +set debug=''; +# +# MDEV-238: SHOW EXPLAIN: Server crashes in JOIN::print_explain with FROM subquery and GROUP BY +# +CREATE TABLE t2 ( a INT ); +INSERT INTO t2 VALUES (1),(2),(1),(4),(2); +explain SELECT alias.a FROM t2, ( SELECT * FROM t2 ) AS alias GROUP BY alias.a; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t2 ALL NULL NULL NULL NULL 5 Using temporary; Using filesort +1 SIMPLE t2 ALL NULL NULL NULL NULL 5 Using join buffer (flat, BNL join) +set debug='d,show_explain_in_find_all_keys'; +SELECT alias.a FROM t2, ( SELECT * FROM t2 ) AS alias GROUP BY alias.a; +# NOTE: current code will not show "Using join buffer": +show explain for $thr2; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t2 ALL NULL NULL NULL NULL 5 Using temporary; Using filesort +1 SIMPLE t2 ALL NULL NULL NULL NULL 5 +Warnings: +Note 1003 SELECT alias.a FROM t2, ( SELECT * FROM t2 ) AS alias GROUP BY alias.a +a +1 +2 +4 +set debug=''; +DROP TABLE t2; drop table t0,t1; diff --git a/mysql-test/t/show_explain.test b/mysql-test/t/show_explain.test index 4fc6aaad932..8c9d5f4c1e8 100644 --- a/mysql-test/t/show_explain.test +++ b/mysql-test/t/show_explain.test @@ -300,8 +300,30 @@ connection default; evalp show explain for $thr2; connection con1; reap; +set debug=''; + +--echo # +--echo # MDEV-238: SHOW EXPLAIN: Server crashes in JOIN::print_explain with FROM subquery and GROUP BY +--echo # +CREATE TABLE t2 ( a INT ); +INSERT INTO t2 VALUES (1),(2),(1),(4),(2); +explain SELECT alias.a FROM t2, ( SELECT * FROM t2 ) AS alias GROUP BY alias.a; + +set debug='d,show_explain_in_find_all_keys'; +send SELECT alias.a FROM t2, ( SELECT * FROM t2 ) AS alias GROUP BY alias.a; + +connection default; +--source include/wait_condition.inc +--echo # NOTE: current code will not show "Using join buffer": +evalp show explain for $thr2; +connection con1; +reap; +set debug=''; + ## TODO: Test this: have several SHOW EXPLAIN requests be queued up for a ## thread and served together. +DROP TABLE t2; + drop table t0,t1; diff --git a/sql/filesort.cc b/sql/filesort.cc index 3b551796ae1..3e6588636fa 100644 --- a/sql/filesort.cc +++ b/sql/filesort.cc @@ -515,6 +515,12 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select, if (indexfile || flag) ref_pos= &file->ref[0]; next_pos=ref_pos; + + DBUG_EXECUTE_IF("show_explain_in_find_all_keys", + dbug_serve_apcs(thd, 1); + ); + + if (! indexfile && ! quick_select) { next_pos=(uchar*) 0; /* Find records in sequence */ diff --git a/sql/sql_select.cc b/sql/sql_select.cc index d739c6789c3..6a842ead2a4 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -7132,28 +7132,36 @@ prev_record_reads(POSITION *positions, uint idx, table_map found_ref) return found; } +enum enum_exec_or_opt {WALK_OPTIMIZATION_TABS , WALK_EXECUTION_TABS}; /* Enumerate join tabs in breadth-first fashion, including const tables. */ -JOIN_TAB *first_breadth_first_tab(JOIN *join) +JOIN_TAB *first_breadth_first_tab(JOIN *join, enum enum_exec_or_opt tabs_kind) { - return join->join_tab; /* There's always one (i.e. first) table */ + /* There's always one (i.e. first) table */ + return (tabs_kind == WALK_EXECUTION_TABS)? join->join_tab: + join->table_access_tabs; } -JOIN_TAB *next_breadth_first_tab(JOIN *join, JOIN_TAB *tab) +JOIN_TAB *next_breadth_first_tab(JOIN *join, enum enum_exec_or_opt tabs_kind, + JOIN_TAB *tab) { + JOIN_TAB* const first_top_tab= first_breadth_first_tab(join, tabs_kind); + const uint n_top_tabs_count= (tabs_kind == WALK_EXECUTION_TABS)? + join->top_join_tab_count: + join->top_table_access_tabs_count; if (!tab->bush_root_tab) { /* We're at top level. Get the next top-level tab */ tab++; - if (tab < join->join_tab + join->top_join_tab_count) + if (tab < first_top_tab + n_top_tabs_count) return tab; /* No more top-level tabs. Switch to enumerating SJM nest children */ - tab= join->join_tab; + tab= first_top_tab; } else { @@ -7177,7 +7185,7 @@ JOIN_TAB *next_breadth_first_tab(JOIN *join, JOIN_TAB *tab) Ok, "tab" points to a top-level table, and we need to find the next SJM nest and enter it. */ - for (; tab < join->join_tab + join->top_join_tab_count; tab++) + for (; tab < first_top_tab + n_top_tabs_count; tab++) { if (tab->bush_children) return tab->bush_children->start; @@ -7201,7 +7209,7 @@ JOIN_TAB *first_top_level_tab(JOIN *join, enum enum_with_const_tables with_const JOIN_TAB *next_top_level_tab(JOIN *join, JOIN_TAB *tab) { - tab= next_breadth_first_tab(join, tab); + tab= next_breadth_first_tab(join, WALK_EXECUTION_TABS, tab); if (tab && tab->bush_root_tab) tab= NULL; return tab; @@ -7501,6 +7509,13 @@ get_best_combination(JOIN *join) join->top_join_tab_count= join->join_tab_ranges.head()->end - join->join_tab_ranges.head()->start; + /* + Save pointers to select join tabs for SHOW EXPLAIN + */ + join->table_access_tabs= join->join_tab; + join->top_table_access_tabs_count= join->top_join_tab_count; + + update_depend_map(join); DBUG_RETURN(0); } @@ -7922,6 +7937,7 @@ JOIN::make_simple_join(JOIN *parent, TABLE *temp_table) !(parent->join_tab_reexec= (JOIN_TAB*) thd->alloc(sizeof(JOIN_TAB)))) DBUG_RETURN(TRUE); /* purecov: inspected */ + // psergey-todo: here, save the pointer for original join_tabs. join_tab= parent->join_tab_reexec; table= &parent->table_reexec[0]; parent->table_reexec[0]= temp_table; table_count= top_join_tab_count= 1; @@ -10077,7 +10093,12 @@ void JOIN_TAB::cleanup() if (cache) { cache->free(); - cache= 0; + cache= 0; // psergey: this is why we don't see "Using join cache" in SHOW EXPLAIN + // when it is run for "Using temporary+filesort" queries while they + // are at reading-from-tmp-table phase. + // + // TODO ask igor if this can be just moved to later phase + // (JOIN_CACHE objects themselves are not big, arent they) } limit= 0; if (table) @@ -21204,9 +21225,10 @@ int JOIN::print_explain(select_result_sink *result, bool on_the_fly, bool printing_materialize_nest= FALSE; uint select_id= join->select_lex->select_number; + JOIN_TAB* const first_top_tab= first_breadth_first_tab(join, WALK_OPTIMIZATION_TABS); - for (JOIN_TAB *tab= first_breadth_first_tab(join); tab; - tab= next_breadth_first_tab(join, tab)) + for (JOIN_TAB *tab= first_breadth_first_tab(join, WALK_OPTIMIZATION_TABS); tab; + tab= next_breadth_first_tab(join, WALK_OPTIMIZATION_TABS, tab)) { if (tab->bush_root_tab) { @@ -21643,7 +21665,7 @@ int JOIN::print_explain(select_result_sink *result, bool on_the_fly, extra.append(STRING_WITH_LEN("; End temporary")); else if (tab->do_firstmatch) { - if (tab->do_firstmatch == join->join_tab - 1) + if (tab->do_firstmatch == /*join->join_tab*/ first_top_tab - 1) extra.append(STRING_WITH_LEN("; FirstMatch")); else { diff --git a/sql/sql_select.h b/sql/sql_select.h index 779702e4b1c..c60419f1ffd 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -896,6 +896,20 @@ protected: public: JOIN_TAB *join_tab, **best_ref; + + /* + For "Using temporary+Using filesort" queries, JOIN::join_tab can point to + either: + 1. array of join tabs describing how to run the select, or + 2. array of single join tab describing read from the temporary table. + + SHOW EXPLAIN code needs to read/show #1. This is why two next members are + there for saving it. + */ + JOIN_TAB *table_access_tabs; + uint top_table_access_tabs_count; + + JOIN_TAB **map2table; ///< mapping between table indexes and JOIN_TABs JOIN_TAB *join_tab_save; ///< saved join_tab for subquery reexecution diff --git a/sql/table.h b/sql/table.h index da2109809d4..9b6d807b844 100644 --- a/sql/table.h +++ b/sql/table.h @@ -850,7 +850,12 @@ struct st_table { See TABLE_LIST::process_index_hints(). */ bool force_index_group; - bool distinct,const_table,no_rows, used_for_duplicate_elimination; + /* + TRUE<=> this table was created with create_tmp_table(... distinct=TRUE..) + call + */ + bool distinct; + bool const_table,no_rows, used_for_duplicate_elimination; /** If set, the optimizer has found that row retrieval should access index From 5116bed06c1a1641d54d8de8bc8770ad65ab165d Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Thu, 10 May 2012 13:44:57 +0400 Subject: [PATCH 06/12] bzr ignore libmysqld/my_apc.cc --- .bzrignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.bzrignore b/.bzrignore index a956e0c6ab4..e4fe3e8a9f2 100644 --- a/.bzrignore +++ b/.bzrignore @@ -1982,3 +1982,4 @@ plugin/handler_socket/perl-Net-HandlerSocket/HandlerSocket.bs plugin/handler_socket/perl-Net-HandlerSocket/Makefile.PL libmysqld/gcalc_slicescan.cc libmysqld/gcalc_tools.cc +libmysqld/my_apc.cc From 6fae4447f0873c159d94d1a3d8deafbd224d8100 Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Thu, 10 May 2012 15:13:57 +0400 Subject: [PATCH 07/12] # MDEV-239: Assertion `field_types == 0 ... ' failed in Protocol_text::store... - Make all functions that produce parts of EXPLAIN output take explain_flags as parameter, instead of looking into thd->lex->describe --- mysql-test/r/show_explain.result | 28 ++++++++++++++++++++++++++ mysql-test/t/show_explain.test | 27 +++++++++++++++++++++++-- sql/sql_class.cc | 2 +- sql/sql_lex.cc | 16 ++++++++------- sql/sql_lex.h | 4 ++-- sql/sql_select.cc | 34 +++++++++++++++++++++----------- sql/sql_select.h | 3 ++- 7 files changed, 89 insertions(+), 25 deletions(-) diff --git a/mysql-test/r/show_explain.result b/mysql-test/r/show_explain.result index db1d57b7004..4f0e89975ff 100644 --- a/mysql-test/r/show_explain.result +++ b/mysql-test/r/show_explain.result @@ -309,4 +309,32 @@ a 4 set debug=''; DROP TABLE t2; +# +# MDEV-239: Assertion `field_types == 0 ... ' failed in Protocol_text::store(double, uint32, String*) with +# SHOW EXPLAIN over EXPLAIN EXTENDED +# +CREATE TABLE t2 (a INT); +INSERT INTO t2 VALUES (1),(2),(1),(4),(2); +EXPLAIN EXTENDED SELECT alias.a FROM t2, ( SELECT * FROM t2 ) AS alias GROUP BY alias.a ; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t2 ALL NULL NULL NULL NULL 5 100.00 Using temporary; Using filesort +1 SIMPLE t2 ALL NULL NULL NULL NULL 5 100.00 Using join buffer (flat, BNL join) +Warnings: +Note 1003 select `test`.`t2`.`a` AS `a` from `test`.`t2` join `test`.`t2` group by `test`.`t2`.`a` +set @show_explain_probe_select_id=1; +set debug='d,show_explain_probe_join_exec_end'; +EXPLAIN EXTENDED SELECT alias.a FROM t2, ( SELECT * FROM t2 ) AS alias GROUP BY alias.a ; +show explain for $thr2; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t2 ALL NULL NULL NULL NULL 5 Using temporary; Using filesort +1 SIMPLE t2 ALL NULL NULL NULL NULL 5 Using join buffer (flat, BNL join) +Warnings: +Note 1003 EXPLAIN EXTENDED SELECT alias.a FROM t2, ( SELECT * FROM t2 ) AS alias GROUP BY alias.a +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t2 ALL NULL NULL NULL NULL 5 100.00 Using temporary; Using filesort +1 SIMPLE t2 ALL NULL NULL NULL NULL 5 100.00 Using join buffer (flat, BNL join) +Warnings: +Note 1003 select `test`.`t2`.`a` AS `a` from `test`.`t2` join `test`.`t2` group by `test`.`t2`.`a` +set debug=''; +DROP TABLE t2; drop table t0,t1; diff --git a/mysql-test/t/show_explain.test b/mysql-test/t/show_explain.test index 8c9d5f4c1e8..02850efedd8 100644 --- a/mysql-test/t/show_explain.test +++ b/mysql-test/t/show_explain.test @@ -319,11 +319,34 @@ evalp show explain for $thr2; connection con1; reap; set debug=''; +DROP TABLE t2; + + +--echo # +--echo # MDEV-239: Assertion `field_types == 0 ... ' failed in Protocol_text::store(double, uint32, String*) with +--echo # SHOW EXPLAIN over EXPLAIN EXTENDED +--echo # + + +CREATE TABLE t2 (a INT); +INSERT INTO t2 VALUES (1),(2),(1),(4),(2); + +EXPLAIN EXTENDED SELECT alias.a FROM t2, ( SELECT * FROM t2 ) AS alias GROUP BY alias.a ; + +set @show_explain_probe_select_id=1; +set debug='d,show_explain_probe_join_exec_end'; +send EXPLAIN EXTENDED SELECT alias.a FROM t2, ( SELECT * FROM t2 ) AS alias GROUP BY alias.a ; + +connection default; +--source include/wait_condition.inc +evalp show explain for $thr2; +connection con1; +reap; +set debug=''; +DROP TABLE t2; ## TODO: Test this: have several SHOW EXPLAIN requests be queued up for a ## thread and served together. -DROP TABLE t2; - drop table t0,t1; diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 4e9a0507bba..89d80dc1ec2 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -3039,7 +3039,7 @@ void Show_explain_request::get_explain_data(void *arg) target_thd->query_length(), &my_charset_bin); - if (target_thd->lex->unit.print_explain(req->explain_buf)) + if (target_thd->lex->unit.print_explain(req->explain_buf, 0 /* explain flags */)) req->failed_to_produce= TRUE; target_thd->restore_active_arena((Query_arena*)req->request_thd, diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index a47ba9b5024..69bb822f05a 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -3746,12 +3746,13 @@ bool st_select_lex::is_merged_child_of(st_select_lex *ancestor) } -int st_select_lex::print_explain(select_result_sink *output) +int st_select_lex::print_explain(select_result_sink *output, + uint8 explain_flags) { int res; if (join && join->optimized == 2) { - res= join->print_explain(output, TRUE, + res= join->print_explain(output, explain_flags, TRUE, join->need_tmp, // need_tmp_table (join->order != 0 && !join->skip_sort_order), // bool need_order join->select_distinct, // bool distinct @@ -3769,7 +3770,7 @@ int st_select_lex::print_explain(select_result_sink *output) */ if (!(unit->item && unit->item->eliminated)) { - unit->print_explain(output); + unit->print_explain(output, explain_flags); } } } @@ -3777,7 +3778,7 @@ int st_select_lex::print_explain(select_result_sink *output) { /* Produce "not yet optimized" line */ const char *msg="Not yet optimized"; - res= join->print_explain(output, TRUE, + res= join->print_explain(output, explain_flags, TRUE, FALSE, // need_tmp_table, FALSE, // bool need_order, FALSE, // bool distinct, @@ -3788,7 +3789,8 @@ err: } -int st_select_lex_unit::print_explain(select_result_sink *output) +int st_select_lex_unit::print_explain(select_result_sink *output, + uint8 explain_flags) { int res= 0; SELECT_LEX *first= first_select(); @@ -3804,7 +3806,7 @@ int st_select_lex_unit::print_explain(select_result_sink *output) for (SELECT_LEX *sl= first; sl; sl= sl->next_select()) { - if ((res= sl->print_explain(output))) + if ((res= sl->print_explain(output, explain_flags))) break; } @@ -3814,7 +3816,7 @@ int st_select_lex_unit::print_explain(select_result_sink *output) if (fake_select_lex && !fake_select_lex->join) { res= print_fake_select_lex_join(output, TRUE /* on the fly */, - fake_select_lex, 0 /* flags */); + fake_select_lex, explain_flags); } return res; } diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 6f72c9e3f7b..8a7547691e9 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -595,7 +595,7 @@ public: friend int subselect_union_engine::exec(); List *get_unit_column_types(); - int print_explain(select_result_sink *output); + int print_explain(select_result_sink *output, uint8 explain_flags); }; typedef class st_select_lex_unit SELECT_LEX_UNIT; @@ -918,7 +918,7 @@ public: bool save_prep_leaf_tables(THD *thd); bool is_merged_child_of(st_select_lex *ancestor); - int print_explain(select_result_sink *output); + int print_explain(select_result_sink *output, uint8 explain_flags); /* For MODE_ONLY_FULL_GROUP_BY we need to maintain two flags: - Non-aggregated fields are used in this select. diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 6a842ead2a4..f48f180d1dd 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -2123,6 +2123,14 @@ void JOIN::exec() */ thd->apc_target.enable(); 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(); } @@ -21075,7 +21083,7 @@ void JOIN::clear() int print_fake_select_lex_join(select_result_sink *result, bool on_the_fly, - SELECT_LEX *select_lex, uint8 select_options) + SELECT_LEX *select_lex, uint8 explain_flags) { const CHARSET_INFO *cs= system_charset_info; Item *item_null= new Item_null(); @@ -21121,7 +21129,7 @@ int print_fake_select_lex_join(select_result_sink *result, bool on_the_fly, item_list.push_back(new Item_string(table_name_buffer, len, cs)); } /* partitions */ - if (/*join->thd->lex->describe*/ select_options & DESCRIBE_PARTITIONS) + if (explain_flags & DESCRIBE_PARTITIONS) item_list.push_back(item_null); /* type */ item_list.push_back(new Item_string(join_type_str[JT_ALL], @@ -21136,7 +21144,7 @@ int print_fake_select_lex_join(select_result_sink *result, bool on_the_fly, /* ref */ item_list.push_back(item_null); /* in_rows */ - if (select_options & DESCRIBE_EXTENDED) + if (explain_flags & DESCRIBE_EXTENDED) item_list.push_back(item_null); /* rows */ item_list.push_back(item_null); @@ -21162,7 +21170,8 @@ int print_fake_select_lex_join(select_result_sink *result, bool on_the_fly, modifications to any select's data structures */ -int JOIN::print_explain(select_result_sink *result, bool on_the_fly, +int JOIN::print_explain(select_result_sink *result, uint8 explain_flags, + bool on_the_fly, bool need_tmp_table, bool need_order, bool distinct, const char *message) { @@ -21199,9 +21208,9 @@ int JOIN::print_explain(select_result_sink *result, bool on_the_fly, strlen(join->select_lex->type), cs)); for (uint i=0 ; i < 7; i++) item_list.push_back(item_null); - if (join->thd->lex->describe & DESCRIBE_PARTITIONS) + if (explain_flags & DESCRIBE_PARTITIONS) item_list.push_back(item_null); - if (join->thd->lex->describe & DESCRIBE_EXTENDED) + if (explain_flags & DESCRIBE_EXTENDED) item_list.push_back(item_null); item_list.push_back(new Item_string(message,strlen(message),cs)); @@ -21212,7 +21221,7 @@ int JOIN::print_explain(select_result_sink *result, bool on_the_fly, { if (print_fake_select_lex_join(result, on_the_fly, join->select_lex, - join->thd->lex->describe)) + explain_flags)) error= 1; } else if (!join->select_lex->master_unit()->derived || @@ -21317,7 +21326,7 @@ int JOIN::print_explain(select_result_sink *result, bool on_the_fly, cs)); } /* "partitions" column */ - if (join->thd->lex->describe & DESCRIBE_PARTITIONS) + if (explain_flags & DESCRIBE_PARTITIONS) { #ifdef WITH_PARTITION_STORAGE_ENGINE partition_info *part_info; @@ -21460,7 +21469,7 @@ int JOIN::print_explain(select_result_sink *result, bool on_the_fly, table_list->schema_table) { /* in_rows */ - if (join->thd->lex->describe & DESCRIBE_EXTENDED) + if (explain_flags & DESCRIBE_EXTENDED) item_list.push_back(item_null); /* rows */ item_list.push_back(item_null); @@ -21497,7 +21506,7 @@ int JOIN::print_explain(select_result_sink *result, bool on_the_fly, MY_INT64_NUM_DECIMAL_DIGITS)); /* Add "filtered" field to item_list. */ - if (join->thd->lex->describe & DESCRIBE_EXTENDED) + if (explain_flags & DESCRIBE_EXTENDED) { float f= 0.0; if (examined_rows) @@ -21577,7 +21586,7 @@ int JOIN::print_explain(select_result_sink *result, bool on_the_fly, { extra.append(STRING_WITH_LEN("; Using where with pushed " "condition")); - if (thd->lex->describe & DESCRIBE_EXTENDED) + if (explain_flags & DESCRIBE_EXTENDED) { extra.append(STRING_WITH_LEN(": ")); ((COND *)pushed_cond)->print(&extra, QT_ORDINARY); @@ -21732,7 +21741,8 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, THD *thd=join->thd; select_result *result=join->result; DBUG_ENTER("select_describe"); - join->error= join->print_explain(result, FALSE, /* Not on-the-fly */ + join->error= join->print_explain(result, thd->lex->describe, + FALSE, /* Not on-the-fly */ need_tmp_table, need_order, distinct, message); diff --git a/sql/sql_select.h b/sql/sql_select.h index c60419f1ffd..a15720b4b30 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -1405,7 +1405,8 @@ public: return (unit->item && unit->item->is_in_predicate()); } - int print_explain(select_result_sink *result, bool on_the_fly, + int print_explain(select_result_sink *result, uint8 explain_flags, + bool on_the_fly, bool need_tmp_table, bool need_order, bool distinct,const char *message); private: From 6bce336624e84f5ec377926b105ec2002b38c96b Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Fri, 11 May 2012 18:13:06 +0400 Subject: [PATCH 08/12] MDEV-240: SHOW EXPLAIN: Assertion `this->optimized == 2' failed - Fix the bug: SHOW EXPLAIN may hit a case where a join is partially optimized. - Change JOIN::optimized to use enum instead of numeric constants --- mysql-test/r/show_explain.result | 33 ++++++++++++++++++++++++++++++++ mysql-test/t/show_explain.test | 25 ++++++++++++++++++++++++ sql/item_subselect.cc | 6 ++++-- sql/sql_lex.cc | 2 +- sql/sql_select.cc | 17 ++++++++++------ sql/sql_select.h | 8 ++++++-- 6 files changed, 80 insertions(+), 11 deletions(-) diff --git a/mysql-test/r/show_explain.result b/mysql-test/r/show_explain.result index 4f0e89975ff..2051a409178 100644 --- a/mysql-test/r/show_explain.result +++ b/mysql-test/r/show_explain.result @@ -1,4 +1,5 @@ drop table if exists t0, t1, t2; +drop view if exists v1; create table t0 (a int); insert into t0 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); create table t1 (a int); @@ -337,4 +338,36 @@ Warnings: Note 1003 select `test`.`t2`.`a` AS `a` from `test`.`t2` join `test`.`t2` group by `test`.`t2`.`a` set debug=''; DROP TABLE t2; +# +# MDEV-240: SHOW EXPLAIN: Assertion `this->optimized == 2' failed in +# JOIN::print_explain on query with a JOIN, TEMPTABLE view, +# +CREATE TABLE t3 (a INT); +CREATE ALGORITHM=TEMPTABLE VIEW v1 AS SELECT * FROM t3; +INSERT INTO t3 VALUES (8); +CREATE TABLE t2 (b INT); +INSERT INTO t2 VALUES (4),(5),(6),(7),(8),(9); +explain SELECT * FROM v1, t2; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY system NULL NULL NULL NULL 1 +1 PRIMARY t2 ALL NULL NULL NULL NULL 6 +2 DERIVED t3 system NULL NULL NULL NULL 1 +set @show_explain_probe_select_id=2; +set debug='d,show_explain_probe_join_exec_end'; +SELECT * FROM v1, t2; +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 Not yet optimized +Warnings: +Note 1003 SELECT * FROM v1, t2 +a b +8 4 +8 5 +8 6 +8 7 +8 8 +8 9 +set debug=''; +DROP VIEW v1; +DROP TABLE t2, t3; drop table t0,t1; diff --git a/mysql-test/t/show_explain.test b/mysql-test/t/show_explain.test index 02850efedd8..36c34c8df5c 100644 --- a/mysql-test/t/show_explain.test +++ b/mysql-test/t/show_explain.test @@ -5,6 +5,7 @@ --disable_warnings drop table if exists t0, t1, t2; +drop view if exists v1; --enable_warnings # @@ -346,6 +347,30 @@ set debug=''; DROP TABLE t2; +--echo # +--echo # MDEV-240: SHOW EXPLAIN: Assertion `this->optimized == 2' failed in +--echo # JOIN::print_explain on query with a JOIN, TEMPTABLE view, +--echo # +CREATE TABLE t3 (a INT); +CREATE ALGORITHM=TEMPTABLE VIEW v1 AS SELECT * FROM t3; +INSERT INTO t3 VALUES (8); +CREATE TABLE t2 (b INT); +INSERT INTO t2 VALUES (4),(5),(6),(7),(8),(9); +explain SELECT * FROM v1, t2; + +set @show_explain_probe_select_id=2; +set debug='d,show_explain_probe_join_exec_end'; +send SELECT * FROM v1, t2; + +connection default; +--source include/wait_condition.inc +evalp show explain for $thr2; +connection con1; +reap; +set debug=''; +DROP VIEW v1; +DROP TABLE t2, t3; + ## TODO: Test this: have several SHOW EXPLAIN requests be queued up for a ## thread and served together. diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 8b720b350a5..1780953bd7e 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -2910,7 +2910,7 @@ int subselect_single_select_engine::exec() SELECT_LEX *save_select= thd->lex->current_select; thd->lex->current_select= select_lex; - if (!join->optimized) + if (join->optimized != JOIN::OPTIMIZATION_DONE) { SELECT_LEX_UNIT *unit= select_lex->master_unit(); @@ -4647,7 +4647,9 @@ int subselect_hash_sj_engine::exec() */ thd->lex->current_select= materialize_engine->select_lex; /* The subquery should be optimized, and materialized only once. */ - DBUG_ASSERT(materialize_join->optimized && !is_materialized); + DBUG_ASSERT(materialize_join->optimized == JOIN::OPTIMIZATION_DONE && + !is_materialized); + materialize_join->exec(); if ((res= test(materialize_join->error || thd->is_fatal_error || thd->is_error()))) diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 69bb822f05a..29d0a9e9888 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -3750,7 +3750,7 @@ int st_select_lex::print_explain(select_result_sink *output, uint8 explain_flags) { int res; - if (join && join->optimized == 2) + if (join && join->optimized == JOIN::OPTIMIZATION_DONE) { res= join->print_explain(output, explain_flags, TRUE, join->need_tmp, // need_tmp_table diff --git a/sql/sql_select.cc b/sql/sql_select.cc index f48f180d1dd..af7a06e424d 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -608,7 +608,7 @@ JOIN::prepare(Item ***rref_pointer_array, DBUG_ENTER("JOIN::prepare"); // to prevent double initialization on EXPLAIN - if (optimized) + if (optimized != JOIN::NOT_OPTIMIZED) DBUG_RETURN(0); conds= conds_init; @@ -936,7 +936,7 @@ err: int JOIN::optimize() { int res= optimize_inner(); - optimized= 2; + optimized= JOIN::OPTIMIZATION_DONE; return res; } /** @@ -960,9 +960,9 @@ JOIN::optimize_inner() DBUG_ENTER("JOIN::optimize"); do_send_rows = (unit->select_limit_cnt) ? 1 : 0; // to prevent double initialization on EXPLAIN - if (optimized) + if (optimized != JOIN::NOT_OPTIMIZED) DBUG_RETURN(0); - optimized= 1; + optimized= JOIN::OPTIMIZATION_IN_PROGRESS; thd_proc_info(thd, "optimizing"); set_allowed_join_cache_types(); @@ -1731,7 +1731,7 @@ int JOIN::init_execution() { DBUG_ENTER("JOIN::init_execution"); - DBUG_ASSERT(optimized); + DBUG_ASSERT(optimized == JOIN::OPTIMIZATION_DONE); DBUG_ASSERT(!(select_options & SELECT_DESCRIBE)); initialized= true; @@ -21187,7 +21187,7 @@ int JOIN::print_explain(select_result_sink *result, uint8 explain_flags, DBUG_PRINT("info", ("Select 0x%lx, type %s, message %s", (ulong)join->select_lex, join->select_lex->type, message ? message : "NULL")); - DBUG_ASSERT(this->optimized == 2); + /* Don't log this into the slow query log */ if (!on_the_fly) @@ -21204,6 +21204,10 @@ int JOIN::print_explain(select_result_sink *result, uint8 explain_flags, { item_list.push_back(new Item_int((int32) join->select_lex->select_number)); + + if (on_the_fly) + join->select_lex->set_explain_type(on_the_fly); + item_list.push_back(new Item_string(join->select_lex->type, strlen(join->select_lex->type), cs)); for (uint i=0 ; i < 7; i++) @@ -21227,6 +21231,7 @@ int JOIN::print_explain(select_result_sink *result, uint8 explain_flags, else if (!join->select_lex->master_unit()->derived || join->select_lex->master_unit()->derived->is_materialized_derived()) { + DBUG_ASSERT(optimized == JOIN::OPTIMIZATION_DONE); table_map used_tables=0; //if (!join->select_lex->type) if (on_the_fly) diff --git a/sql/sql_select.h b/sql/sql_select.h index a15720b4b30..3bb007f0d93 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -1178,7 +1178,11 @@ public: const char *zero_result_cause; ///< not 0 if exec must return zero result bool union_part; ///< this subselect is part of union - int optimized; ///< flag to avoid double optimization in EXPLAIN + + enum join_optimization_state { NOT_OPTIMIZED=0, + OPTIMIZATION_IN_PROGRESS=1, + OPTIMIZATION_DONE=2}; + join_optimization_state optimized; ///< flag to avoid double optimization in EXPLAIN bool initialized; ///< flag to avoid double init_execution calls /* @@ -1261,7 +1265,7 @@ public: ref_pointer_array= items0= items1= items2= items3= 0; ref_pointer_array_size= 0; zero_result_cause= 0; - optimized= 0; + optimized= JOIN::NOT_OPTIMIZED; initialized= 0; cond_equal= 0; having_equal= 0; From b3841ae9e4754e2aae1c106767d4ba50236cbf9b Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Mon, 14 May 2012 22:39:00 +0400 Subject: [PATCH 09/12] MDEV-267: SHOW EXPLAIN: Server crashes in JOIN::print_explain on most of queries - Make SHOW EXPLAIN code handle degenerate subquery cases (No tables, impossible where, etc) --- mysql-test/r/show_explain.result | 44 ++++++++++++++++++++++++++++++++ mysql-test/t/show_explain.test | 43 +++++++++++++++++++++++++++++++ sql/sql_lex.cc | 21 +++++++++++---- 3 files changed, 103 insertions(+), 5 deletions(-) diff --git a/mysql-test/r/show_explain.result b/mysql-test/r/show_explain.result index 2051a409178..b09903a617a 100644 --- a/mysql-test/r/show_explain.result +++ b/mysql-test/r/show_explain.result @@ -370,4 +370,48 @@ a b set debug=''; DROP VIEW v1; DROP TABLE t2, t3; +# +# MDEV-267: SHOW EXPLAIN: Server crashes in JOIN::print_explain on most of queries +# +set @show_explain_probe_select_id=1; +set debug='d,show_explain_probe_join_exec_end'; +select sleep(1); +show explain for $thr2; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select sleep(1) +sleep(1) +0 +set debug=''; +# +# Same as above, but try another reason for JOIN to be degenerate +# +set @show_explain_probe_select_id=1; +set debug='d,show_explain_probe_join_exec_end'; +select * from t0 where 1>10; +show explain for $thr2; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible WHERE +Warnings: +Note 1003 select * from t0 where 1>10 +a +set debug=''; +# +# Same as above, but try another reason for JOIN to be degenerate (2) +# +create table t3(a int primary key); +insert into t3 select a from t0; +set @show_explain_probe_select_id=1; +set debug='d,show_explain_probe_join_exec_end'; +select * from t0,t3 where t3.a=112233; +show explain for $thr2; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t3 const PRIMARY PRIMARY 4 0 unique row not found +1 SIMPLE t0 ALL NULL NULL NULL NULL 10 +Warnings: +Note 1003 select * from t0,t3 where t3.a=112233 +a a +set debug=''; +drop table t3; drop table t0,t1; diff --git a/mysql-test/t/show_explain.test b/mysql-test/t/show_explain.test index 36c34c8df5c..c9f715bbb67 100644 --- a/mysql-test/t/show_explain.test +++ b/mysql-test/t/show_explain.test @@ -371,6 +371,49 @@ set debug=''; DROP VIEW v1; DROP TABLE t2, t3; +--echo # +--echo # MDEV-267: SHOW EXPLAIN: Server crashes in JOIN::print_explain on most of queries +--echo # +set @show_explain_probe_select_id=1; +set debug='d,show_explain_probe_join_exec_end'; +send select sleep(1); +connection default; +--source include/wait_condition.inc +evalp show explain for $thr2; +connection con1; +reap; +set debug=''; + + +--echo # +--echo # Same as above, but try another reason for JOIN to be degenerate +--echo # +set @show_explain_probe_select_id=1; +set debug='d,show_explain_probe_join_exec_end'; +send select * from t0 where 1>10; +connection default; +--source include/wait_condition.inc +evalp show explain for $thr2; +connection con1; +reap; +set debug=''; + +--echo # +--echo # Same as above, but try another reason for JOIN to be degenerate (2) +--echo # +create table t3(a int primary key); +insert into t3 select a from t0; +set @show_explain_probe_select_id=1; +set debug='d,show_explain_probe_join_exec_end'; +send select * from t0,t3 where t3.a=112233; +connection default; +--source include/wait_condition.inc +evalp show explain for $thr2; +connection con1; +reap; +set debug=''; +drop table t3; + ## TODO: Test this: have several SHOW EXPLAIN requests be queued up for a ## thread and served together. diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 29d0a9e9888..8838abae735 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -3752,11 +3752,22 @@ int st_select_lex::print_explain(select_result_sink *output, int res; if (join && join->optimized == JOIN::OPTIMIZATION_DONE) { - res= join->print_explain(output, explain_flags, TRUE, - join->need_tmp, // need_tmp_table - (join->order != 0 && !join->skip_sort_order), // bool need_order - join->select_distinct, // bool distinct - NULL); //const char *message + if (!join->table_count) + { + /* It's a degenerate join */ + const char *cause= join->zero_result_cause ? join-> zero_result_cause : + "No tables used"; + res= join->print_explain(output, explain_flags, TRUE, FALSE, FALSE, + FALSE, cause); + } + else + { + res= join->print_explain(output, explain_flags, TRUE, + join->need_tmp, // need_tmp_table + (join->order != 0 && !join->skip_sort_order), // bool need_order + join->select_distinct, // bool distinct + NULL); //const char *message + } if (res) goto err; From 382e81ca84a51fded86df6f21e0b4e003fc94388 Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Tue, 15 May 2012 15:56:50 +0400 Subject: [PATCH 10/12] MDEV-270: SHOW EXPLAIN: server crashes in JOIN::print_explain on a query with select tables optimized away - Take into account, that for some degenerate joins instead of "join->table_count=0" the code sets "join->tables_list=0". --- mysql-test/r/show_explain.result | 39 ++++++++++++++++++++++++++++++++ mysql-test/t/show_explain.test | 34 ++++++++++++++++++++++++++++ sql/sql_lex.cc | 2 +- sql/sql_select.cc | 9 ++++++++ 4 files changed, 83 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/show_explain.result b/mysql-test/r/show_explain.result index b09903a617a..1b1c30b56df 100644 --- a/mysql-test/r/show_explain.result +++ b/mysql-test/r/show_explain.result @@ -414,4 +414,43 @@ Note 1003 select * from t0,t3 where t3.a=112233 a a set debug=''; drop table t3; +# +# MDEV-270: SHOW EXPLAIN: server crashes in JOIN::print_explain on a query with +# select tables optimized away +# +CREATE TABLE t2 (pk INT PRIMARY KEY, a INT ) ENGINE=MyISAM; +INSERT INTO t2 VALUES +(1,4),(2,62),(3,7),(4,1),(5,0),(6,7),(7,7),(8,1),(9,7),(10,1), +(11,5),(12,2),(13,0),(14,1),(15,8),(16,1),(17,1),(18,9),(19,1),(20,5) ; +explain SELECT * FROM t2 WHERE a = +(SELECT MAX(a) FROM t2 +WHERE pk= (SELECT MAX(pk) FROM t2 WHERE pk = 3) +); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t2 ALL NULL NULL NULL NULL 20 Using where +2 SUBQUERY t2 const PRIMARY PRIMARY 4 const 1 Using where +3 SUBQUERY NULL NULL NULL NULL NULL NULL NULL Select tables optimized away +set @show_explain_probe_select_id=2; +set debug='d,show_explain_probe_do_select'; +SELECT * FROM t2 WHERE a = +(SELECT MAX(a) FROM t2 +WHERE pk= (SELECT MAX(pk) FROM t2 WHERE pk = 3) +); +show explain for $thr2; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t2 ALL NULL NULL NULL NULL 20 Using where +2 SUBQUERY t2 const PRIMARY PRIMARY 4 1 Using where +3 SUBQUERY NULL NULL NULL NULL NULL NULL NULL Select tables optimized away +Warnings: +Note 1003 SELECT * FROM t2 WHERE a = +(SELECT MAX(a) FROM t2 +WHERE pk= (SELECT MAX(pk) FROM t2 WHERE pk = 3) +) +pk a +3 7 +6 7 +7 7 +9 7 +set debug=''; +drop table t2; drop table t0,t1; diff --git a/mysql-test/t/show_explain.test b/mysql-test/t/show_explain.test index c9f715bbb67..d813956a1fe 100644 --- a/mysql-test/t/show_explain.test +++ b/mysql-test/t/show_explain.test @@ -414,6 +414,40 @@ reap; set debug=''; drop table t3; +--echo # +--echo # MDEV-270: SHOW EXPLAIN: server crashes in JOIN::print_explain on a query with +--echo # select tables optimized away +--echo # + +CREATE TABLE t2 (pk INT PRIMARY KEY, a INT ) ENGINE=MyISAM; +INSERT INTO t2 VALUES + (1,4),(2,62),(3,7),(4,1),(5,0),(6,7),(7,7),(8,1),(9,7),(10,1), + (11,5),(12,2),(13,0),(14,1),(15,8),(16,1),(17,1),(18,9),(19,1),(20,5) ; + +explain SELECT * FROM t2 WHERE a = + (SELECT MAX(a) FROM t2 + WHERE pk= (SELECT MAX(pk) FROM t2 WHERE pk = 3) + ); + +set @show_explain_probe_select_id=2; +set debug='d,show_explain_probe_do_select'; +send SELECT * FROM t2 WHERE a = + (SELECT MAX(a) FROM t2 + WHERE pk= (SELECT MAX(pk) FROM t2 WHERE pk = 3) + ); +connection default; +--source include/wait_condition.inc +evalp show explain for $thr2; +connection con1; +reap; +set debug=''; +drop table t2; + + + + + + ## TODO: Test this: have several SHOW EXPLAIN requests be queued up for a ## thread and served together. diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 8838abae735..556077220de 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -3752,7 +3752,7 @@ int st_select_lex::print_explain(select_result_sink *output, int res; if (join && join->optimized == JOIN::OPTIMIZATION_DONE) { - if (!join->table_count) + if (!join->table_count || !join->tables_list) { /* It's a degenerate join */ const char *cause= join->zero_result_cause ? join-> zero_result_cause : diff --git a/sql/sql_select.cc b/sql/sql_select.cc index af7a06e424d..7af818ba147 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -15399,6 +15399,15 @@ do_select(JOIN *join,List *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 From 533e1d28459ab5aa0604560bf8885ae18e553295 Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Wed, 16 May 2012 12:49:22 +0400 Subject: [PATCH 11/12] MDEV-273: SHOW EXPLAIN: server crashes in JOIN::print_explain on a query with impossible WHERE - It turns out, there is a case where the join is degenerate, but join->table_count!= && join->tables_list!=NULL. Need to also check if join->zero_result_cause!=NULL, too. - There is a slight problem: The code sets zero_result_cause= "no matching row in const table" when NOT running EXPLAIN. The result is that SHOW EXPLAIN will show this line while regular EXPLAIN will not. --- mysql-test/r/show_explain.result | 56 ++++++++++++++++++++++++++++++-- mysql-test/t/show_explain.test | 43 +++++++++++++++++++++++- sql/sql_lex.cc | 6 +++- 3 files changed, 100 insertions(+), 5 deletions(-) diff --git a/mysql-test/r/show_explain.result b/mysql-test/r/show_explain.result index 1b1c30b56df..d19a6f29539 100644 --- a/mysql-test/r/show_explain.result +++ b/mysql-test/r/show_explain.result @@ -1,4 +1,4 @@ -drop table if exists t0, t1, t2; +drop table if exists t0, t1, t2, t3, t4; drop view if exists v1; create table t0 (a int); insert into t0 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); @@ -407,8 +407,7 @@ set debug='d,show_explain_probe_join_exec_end'; select * from t0,t3 where t3.a=112233; show explain for $thr2; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t3 const PRIMARY PRIMARY 4 0 unique row not found -1 SIMPLE t0 ALL NULL NULL NULL NULL 10 +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL no matching row in const table Warnings: Note 1003 select * from t0,t3 where t3.a=112233 a a @@ -453,4 +452,55 @@ pk a 9 7 set debug=''; drop table t2; +# +# MDEV-273: SHOW EXPLAIN: server crashes in JOIN::print_explain on a query with impossible WHERE +# +CREATE TABLE t2 (a1 INT, KEY(a1)) ENGINE=MyISAM; +INSERT INTO t2 VALUES +(4),(6),(7),(1),(0),(7),(7),(1),(7),(1), +(5),(2),(0),(1),(8),(1),(1),(9),(1),(5); +CREATE TABLE t3 (b1 INT) ENGINE=MyISAM; +INSERT INTO t3 VALUES +(4),(5),(8),(4),(8),(2),(9),(6),(4),(8), +(3),(5),(9),(6),(8),(3),(2),(6),(3),(1), +(4),(3),(1),(7),(0),(0),(9),(5),(9),(0), +(2),(2),(5),(9),(1),(4),(8),(6),(5),(5), +(1),(7),(2),(8),(9),(3),(2),(6),(6),(5), +(4),(3),(2),(7),(4),(6),(0),(8),(5),(8), +(2),(9),(7),(5),(7),(0),(4),(3),(1),(0), +(6),(2),(8),(3),(7),(3),(5),(5),(1),(2), +(1),(7),(1),(9),(9),(8),(3); +CREATE TABLE t4 (c1 INT) ENGINE=MyISAM; +EXPLAIN +SELECT count(*) FROM t2, t3 +WHERE a1 < ALL ( +SELECT a1 FROM t2 +WHERE a1 IN ( SELECT a1 FROM t2, t4 ) +); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t2 index NULL a1 5 NULL 20 Using where; Using index +1 PRIMARY t3 ALL NULL NULL NULL NULL 87 Using join buffer (flat, BNL join) +2 SUBQUERY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables +set @show_explain_probe_select_id=1; +set debug='d,show_explain_probe_do_select'; +SELECT count(*) FROM t2, t3 +WHERE a1 < ALL ( +SELECT a1 FROM t2 +WHERE a1 IN ( SELECT a1 FROM t2, t4 ) +); +show explain for $thr2; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t2 index NULL a1 5 NULL 20 Using where; Using index +1 PRIMARY t3 ALL NULL NULL NULL NULL 87 Using join buffer (flat, BNL join) +2 SUBQUERY NULL NULL NULL NULL NULL NULL NULL no matching row in const table +Warnings: +Note 1003 SELECT count(*) FROM t2, t3 +WHERE a1 < ALL ( +SELECT a1 FROM t2 +WHERE a1 IN ( SELECT a1 FROM t2, t4 ) +) +count(*) +1740 +set debug=''; +drop table t2, t3, t4; drop table t0,t1; diff --git a/mysql-test/t/show_explain.test b/mysql-test/t/show_explain.test index d813956a1fe..35694e97111 100644 --- a/mysql-test/t/show_explain.test +++ b/mysql-test/t/show_explain.test @@ -4,7 +4,7 @@ --source include/have_debug.inc --disable_warnings -drop table if exists t0, t1, t2; +drop table if exists t0, t1, t2, t3, t4; drop view if exists v1; --enable_warnings @@ -444,9 +444,50 @@ set debug=''; drop table t2; +--echo # +--echo # MDEV-273: SHOW EXPLAIN: server crashes in JOIN::print_explain on a query with impossible WHERE +--echo # +CREATE TABLE t2 (a1 INT, KEY(a1)) ENGINE=MyISAM; +INSERT INTO t2 VALUES + (4),(6),(7),(1),(0),(7),(7),(1),(7),(1), + (5),(2),(0),(1),(8),(1),(1),(9),(1),(5); +CREATE TABLE t3 (b1 INT) ENGINE=MyISAM; +INSERT INTO t3 VALUES + (4),(5),(8),(4),(8),(2),(9),(6),(4),(8), + (3),(5),(9),(6),(8),(3),(2),(6),(3),(1), + (4),(3),(1),(7),(0),(0),(9),(5),(9),(0), + (2),(2),(5),(9),(1),(4),(8),(6),(5),(5), + (1),(7),(2),(8),(9),(3),(2),(6),(6),(5), + (4),(3),(2),(7),(4),(6),(0),(8),(5),(8), + (2),(9),(7),(5),(7),(0),(4),(3),(1),(0), + (6),(2),(8),(3),(7),(3),(5),(5),(1),(2), + (1),(7),(1),(9),(9),(8),(3); +CREATE TABLE t4 (c1 INT) ENGINE=MyISAM; +EXPLAIN +SELECT count(*) FROM t2, t3 +WHERE a1 < ALL ( + SELECT a1 FROM t2 + WHERE a1 IN ( SELECT a1 FROM t2, t4 ) +); +set @show_explain_probe_select_id=1; +set debug='d,show_explain_probe_do_select'; +send +SELECT count(*) FROM t2, t3 +WHERE a1 < ALL ( + SELECT a1 FROM t2 + WHERE a1 IN ( SELECT a1 FROM t2, t4 ) +); + +connection default; +--source include/wait_condition.inc +evalp show explain for $thr2; +connection con1; +reap; +set debug=''; +drop table t2, t3, t4; ## TODO: Test this: have several SHOW EXPLAIN requests be queued up for a ## thread and served together. diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 556077220de..13972d56605 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -3752,7 +3752,11 @@ int st_select_lex::print_explain(select_result_sink *output, int res; if (join && join->optimized == JOIN::OPTIMIZATION_DONE) { - if (!join->table_count || !join->tables_list) + /* + There is a number of reasons join can be marked as degenerate, so all + three conditions below can happen simultaneously, or individually: + */ + if (!join->table_count || !join->tables_list || join->zero_result_cause) { /* It's a degenerate join */ const char *cause= join->zero_result_cause ? join-> zero_result_cause : From 483ae4bf81851a16e27cfc67b0eb474fed0a97dd Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Wed, 16 May 2012 20:22:54 +0400 Subject: [PATCH 12/12] Make SHOW EXPLAIN give a separate timeout error. --- sql/sql_show.cc | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/sql/sql_show.cc b/sql/sql_show.cc index a0c39ff6c8c..2a9e5fa4954 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -2121,9 +2121,18 @@ void mysqld_show_explain(THD *thd, ulong thread_id) if (bres || explain_req.failed_to_produce) { /* TODO not enabled or time out */ - my_error(ER_ERROR_WHEN_EXECUTING_COMMAND, MYF(0), - "SHOW EXPLAIN", - "Target is not running EXPLAINable command"); + if (timed_out) + { + my_error(ER_ERROR_WHEN_EXECUTING_COMMAND, MYF(0), + "SHOW EXPLAIN", + "Timeout"); + } + else + { + my_error(ER_ERROR_WHEN_EXECUTING_COMMAND, MYF(0), + "SHOW EXPLAIN", + "Target is not running EXPLAINable command"); + } bres= TRUE; } else