SHOW EXPLAIN: merge to 10.0-base.
This commit is contained in:
commit
f54cb33e4b
@ -1136,6 +1136,7 @@ plugin/handler_socket/perl-Net-HandlerSocket/Makefile.PL
|
|||||||
libmysqld/libmysqld_exports_file.cc
|
libmysqld/libmysqld_exports_file.cc
|
||||||
libmysqld/gcalc_slicescan.cc
|
libmysqld/gcalc_slicescan.cc
|
||||||
libmysqld/gcalc_tools.cc
|
libmysqld/gcalc_tools.cc
|
||||||
|
libmysqld/my_apc.cc
|
||||||
sql/share/errmsg.sys
|
sql/share/errmsg.sys
|
||||||
sql/share/mysql
|
sql/share/mysql
|
||||||
install_manifest.txt
|
install_manifest.txt
|
||||||
|
@ -285,6 +285,7 @@ IF(WITH_UNIT_TESTS)
|
|||||||
ADD_SUBDIRECTORY(unittest/strings)
|
ADD_SUBDIRECTORY(unittest/strings)
|
||||||
ADD_SUBDIRECTORY(unittest/examples)
|
ADD_SUBDIRECTORY(unittest/examples)
|
||||||
ADD_SUBDIRECTORY(unittest/mysys)
|
ADD_SUBDIRECTORY(unittest/mysys)
|
||||||
|
ADD_SUBDIRECTORY(unittest/sql)
|
||||||
ENDIF()
|
ENDIF()
|
||||||
|
|
||||||
IF(NOT WITHOUT_SERVER)
|
IF(NOT WITHOUT_SERVER)
|
||||||
|
@ -84,6 +84,8 @@ static my_bool non_blocking_api_enabled= 0;
|
|||||||
#define QUERY_SEND_FLAG 1
|
#define QUERY_SEND_FLAG 1
|
||||||
#define QUERY_REAP_FLAG 2
|
#define QUERY_REAP_FLAG 2
|
||||||
|
|
||||||
|
#define QUERY_PRINT_ORIGINAL_FLAG 4
|
||||||
|
|
||||||
#ifndef HAVE_SETENV
|
#ifndef HAVE_SETENV
|
||||||
static int setenv(const char *name, const char *value, int overwrite);
|
static int setenv(const char *name, const char *value, int overwrite);
|
||||||
#endif
|
#endif
|
||||||
@ -345,6 +347,7 @@ enum enum_commands {
|
|||||||
Q_SEND, Q_REAP,
|
Q_SEND, Q_REAP,
|
||||||
Q_DIRTY_CLOSE, Q_REPLACE, Q_REPLACE_COLUMN,
|
Q_DIRTY_CLOSE, Q_REPLACE, Q_REPLACE_COLUMN,
|
||||||
Q_PING, Q_EVAL,
|
Q_PING, Q_EVAL,
|
||||||
|
Q_EVALP,
|
||||||
Q_EVAL_RESULT,
|
Q_EVAL_RESULT,
|
||||||
Q_ENABLE_QUERY_LOG, Q_DISABLE_QUERY_LOG,
|
Q_ENABLE_QUERY_LOG, Q_DISABLE_QUERY_LOG,
|
||||||
Q_ENABLE_RESULT_LOG, Q_DISABLE_RESULT_LOG,
|
Q_ENABLE_RESULT_LOG, Q_DISABLE_RESULT_LOG,
|
||||||
@ -410,6 +413,7 @@ const char *command_names[]=
|
|||||||
"replace_column",
|
"replace_column",
|
||||||
"ping",
|
"ping",
|
||||||
"eval",
|
"eval",
|
||||||
|
"evalp",
|
||||||
"eval_result",
|
"eval_result",
|
||||||
/* Enable/disable that the _query_ is logged to result file */
|
/* Enable/disable that the _query_ is logged to result file */
|
||||||
"enable_query_log",
|
"enable_query_log",
|
||||||
@ -8316,7 +8320,8 @@ void run_query(struct st_connection *cn, struct st_command *command, int flags)
|
|||||||
/*
|
/*
|
||||||
Evaluate query if this is an eval command
|
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);
|
init_dynamic_string(&eval_query, "", command->query_len+256, 1024);
|
||||||
do_eval(&eval_query, command->query, command->end, FALSE);
|
do_eval(&eval_query, command->query, command->end, FALSE);
|
||||||
@ -8348,11 +8353,21 @@ void run_query(struct st_connection *cn, struct st_command *command, int flags)
|
|||||||
*/
|
*/
|
||||||
if (!disable_query_log && (flags & QUERY_SEND_FLAG))
|
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, delimiter, delimiter_length);
|
||||||
dynstr_append_mem(ds, "\n", 1);
|
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
|
Write the command to the result file before we execute the query
|
||||||
This is needed to be able to analyse the log if something goes
|
This is needed to be able to analyse the log if something goes
|
||||||
@ -9212,6 +9227,7 @@ int main(int argc, char **argv)
|
|||||||
case Q_EVAL_RESULT:
|
case Q_EVAL_RESULT:
|
||||||
die("'eval_result' command is deprecated");
|
die("'eval_result' command is deprecated");
|
||||||
case Q_EVAL:
|
case Q_EVAL:
|
||||||
|
case Q_EVALP:
|
||||||
case Q_QUERY_VERTICAL:
|
case Q_QUERY_VERTICAL:
|
||||||
case Q_QUERY_HORIZONTAL:
|
case Q_QUERY_HORIZONTAL:
|
||||||
if (command->query == command->query_buf)
|
if (command->query == command->query_buf)
|
||||||
@ -9239,6 +9255,9 @@ int main(int argc, char **argv)
|
|||||||
flags= QUERY_REAP_FLAG;
|
flags= QUERY_REAP_FLAG;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (command->type == Q_EVALP)
|
||||||
|
flags |= QUERY_PRINT_ORIGINAL_FLAG;
|
||||||
|
|
||||||
/* Check for special property for this query */
|
/* Check for special property for this query */
|
||||||
display_result_vertically|= (command->type == Q_QUERY_VERTICAL);
|
display_result_vertically|= (command->type == Q_QUERY_VERTICAL);
|
||||||
|
|
||||||
|
@ -96,6 +96,7 @@ SET(SQL_EMBEDDED_SOURCES emb_qcache.cc libmysqld.c lib_sql.cc
|
|||||||
../sql/create_options.cc ../sql/rpl_utility.cc
|
../sql/create_options.cc ../sql/rpl_utility.cc
|
||||||
../sql/rpl_reporting.cc
|
../sql/rpl_reporting.cc
|
||||||
../sql/sql_expression_cache.cc
|
../sql/sql_expression_cache.cc
|
||||||
|
../sql/my_apc.cc ../sql/my_apc.h
|
||||||
${GEN_SOURCES}
|
${GEN_SOURCES}
|
||||||
${MYSYS_LIBWRAP_SOURCE}
|
${MYSYS_LIBWRAP_SOURCE}
|
||||||
)
|
)
|
||||||
|
1078
mysql-test/r/show_explain.result
Normal file
1078
mysql-test/r/show_explain.result
Normal file
File diff suppressed because it is too large
Load Diff
27
mysql-test/r/show_explain_ps.result
Normal file
27
mysql-test/r/show_explain_ps.result
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
drop table if exists t0, t1;
|
||||||
|
select * from performance_schema.setup_instruments where name like '%show_explain%';
|
||||||
|
NAME ENABLED TIMED
|
||||||
|
wait/synch/cond/sql/show_explain YES YES
|
||||||
|
# We've got no instances
|
||||||
|
select * from performance_schema.cond_instances where name like '%show_explain%';
|
||||||
|
NAME OBJECT_INSTANCE_BEGIN
|
||||||
|
# Check out if our cond is hit.
|
||||||
|
create table t0 (a int);
|
||||||
|
insert into t0 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
|
||||||
|
set @show_explain_probe_select_id=1;
|
||||||
|
set debug_dbug='d,show_explain_probe_join_exec_start';
|
||||||
|
select count(*) from t0 where a < 100000;
|
||||||
|
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 where
|
||||||
|
Warnings:
|
||||||
|
Note 1003 select count(*) from t0 where a < 100000
|
||||||
|
count(*)
|
||||||
|
10
|
||||||
|
set debug_dbug='';
|
||||||
|
select event_name
|
||||||
|
from performance_schema.events_waits_history_long
|
||||||
|
where event_name='wait/synch/cond/sql/show_explain';
|
||||||
|
event_name
|
||||||
|
wait/synch/cond/sql/show_explain
|
||||||
|
drop table t0;
|
1115
mysql-test/t/show_explain.test
Normal file
1115
mysql-test/t/show_explain.test
Normal file
File diff suppressed because it is too large
Load Diff
51
mysql-test/t/show_explain_ps.test
Normal file
51
mysql-test/t/show_explain_ps.test
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
#
|
||||||
|
# Test how SHOW EXPLAIN is represented in performance schema
|
||||||
|
#
|
||||||
|
--source include/have_debug.inc
|
||||||
|
--source include/have_perfschema.inc
|
||||||
|
# Like all other perfschema tests, we don't work on embedded server:
|
||||||
|
--source include/not_embedded.inc
|
||||||
|
|
||||||
|
--disable_warnings
|
||||||
|
drop table if exists t0, t1;
|
||||||
|
--enable_warnings
|
||||||
|
|
||||||
|
select * from performance_schema.setup_instruments where name like '%show_explain%';
|
||||||
|
|
||||||
|
--echo # We've got no instances
|
||||||
|
select * from performance_schema.cond_instances where name like '%show_explain%';
|
||||||
|
|
||||||
|
--echo # Check out if our cond is hit.
|
||||||
|
|
||||||
|
create table t0 (a int);
|
||||||
|
insert into t0 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
|
||||||
|
|
||||||
|
let $thr1=`select connection_id()`;
|
||||||
|
connect (con1, localhost, root,,);
|
||||||
|
connection con1;
|
||||||
|
let $thr2=`select connection_id()`;
|
||||||
|
connection default;
|
||||||
|
|
||||||
|
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 t0 where a < 100000;
|
||||||
|
|
||||||
|
connection default;
|
||||||
|
--source include/wait_condition.inc
|
||||||
|
evalp show explain for $thr2;
|
||||||
|
connection con1;
|
||||||
|
reap;
|
||||||
|
|
||||||
|
set debug_dbug='';
|
||||||
|
|
||||||
|
select event_name
|
||||||
|
from performance_schema.events_waits_history_long
|
||||||
|
where event_name='wait/synch/cond/sql/show_explain';
|
||||||
|
|
||||||
|
drop table t0;
|
@ -86,6 +86,7 @@ SET (SQL_SOURCE
|
|||||||
gcalc_slicescan.cc gcalc_tools.cc
|
gcalc_slicescan.cc gcalc_tools.cc
|
||||||
threadpool_common.cc
|
threadpool_common.cc
|
||||||
../sql-common/mysql_async.c
|
../sql-common/mysql_async.c
|
||||||
|
my_apc.cc my_apc.h
|
||||||
${GEN_SOURCES}
|
${GEN_SOURCES}
|
||||||
${MYSYS_LIBWRAP_SOURCE}
|
${MYSYS_LIBWRAP_SOURCE}
|
||||||
)
|
)
|
||||||
|
@ -581,7 +581,6 @@ static ha_rows find_all_keys(Sort_param *param, SQL_SELECT *select,
|
|||||||
my_off_t record;
|
my_off_t record;
|
||||||
TABLE *sort_form;
|
TABLE *sort_form;
|
||||||
THD *thd= current_thd;
|
THD *thd= current_thd;
|
||||||
volatile killed_state *killed= &thd->killed;
|
|
||||||
handler *file;
|
handler *file;
|
||||||
MY_BITMAP *save_read_set, *save_write_set, *save_vcol_set;
|
MY_BITMAP *save_read_set, *save_write_set, *save_vcol_set;
|
||||||
|
|
||||||
@ -603,6 +602,11 @@ static ha_rows find_all_keys(Sort_param *param, SQL_SELECT *select,
|
|||||||
if (flag)
|
if (flag)
|
||||||
ref_pos= &file->ref[0];
|
ref_pos= &file->ref[0];
|
||||||
next_pos=ref_pos;
|
next_pos=ref_pos;
|
||||||
|
|
||||||
|
DBUG_EXECUTE_IF("show_explain_in_find_all_keys",
|
||||||
|
dbug_serve_apcs(thd, 1);
|
||||||
|
);
|
||||||
|
|
||||||
if (!quick_select)
|
if (!quick_select)
|
||||||
{
|
{
|
||||||
next_pos=(uchar*) 0; /* Find records in sequence */
|
next_pos=(uchar*) 0; /* Find records in sequence */
|
||||||
@ -665,7 +669,7 @@ static ha_rows find_all_keys(Sort_param *param, SQL_SELECT *select,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*killed)
|
if (thd->check_killed())
|
||||||
{
|
{
|
||||||
DBUG_PRINT("info",("Sort killed by user"));
|
DBUG_PRINT("info",("Sort killed by user"));
|
||||||
if (!quick_select)
|
if (!quick_select)
|
||||||
@ -1459,18 +1463,13 @@ int merge_buffers(Sort_param *param, IO_CACHE *from_file,
|
|||||||
void *first_cmp_arg;
|
void *first_cmp_arg;
|
||||||
element_count dupl_count= 0;
|
element_count dupl_count= 0;
|
||||||
uchar *src;
|
uchar *src;
|
||||||
killed_state not_killable;
|
|
||||||
uchar *unique_buff= param->unique_buff;
|
uchar *unique_buff= param->unique_buff;
|
||||||
volatile killed_state *killed= ¤t_thd->killed;
|
const bool killable= !param->not_killable;
|
||||||
|
THD* const thd=current_thd;
|
||||||
DBUG_ENTER("merge_buffers");
|
DBUG_ENTER("merge_buffers");
|
||||||
|
|
||||||
status_var_increment(current_thd->status_var.filesort_merge_passes);
|
status_var_increment(thd->status_var.filesort_merge_passes);
|
||||||
current_thd->query_plan_fsort_passes++;
|
thd->query_plan_fsort_passes++;
|
||||||
if (param->not_killable)
|
|
||||||
{
|
|
||||||
killed= ¬_killable;
|
|
||||||
not_killable= NOT_KILLED;
|
|
||||||
}
|
|
||||||
|
|
||||||
error=0;
|
error=0;
|
||||||
rec_length= param->rec_length;
|
rec_length= param->rec_length;
|
||||||
@ -1547,7 +1546,7 @@ int merge_buffers(Sort_param *param, IO_CACHE *from_file,
|
|||||||
|
|
||||||
while (queue.elements > 1)
|
while (queue.elements > 1)
|
||||||
{
|
{
|
||||||
if (*killed)
|
if (killable && thd->check_killed())
|
||||||
{
|
{
|
||||||
error= 1; goto err; /* purecov: inspected */
|
error= 1; goto err; /* purecov: inspected */
|
||||||
}
|
}
|
||||||
|
@ -600,6 +600,7 @@ enum enum_schema_tables
|
|||||||
SCH_COLUMN_PRIVILEGES,
|
SCH_COLUMN_PRIVILEGES,
|
||||||
SCH_ENGINES,
|
SCH_ENGINES,
|
||||||
SCH_EVENTS,
|
SCH_EVENTS,
|
||||||
|
SCH_EXPLAIN,
|
||||||
SCH_FILES,
|
SCH_FILES,
|
||||||
SCH_GLOBAL_STATUS,
|
SCH_GLOBAL_STATUS,
|
||||||
SCH_GLOBAL_VARIABLES,
|
SCH_GLOBAL_VARIABLES,
|
||||||
|
@ -4331,7 +4331,7 @@ longlong Item_func_sleep::val_int()
|
|||||||
|
|
||||||
#define extra_size sizeof(double)
|
#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)
|
bool create_if_not_exists)
|
||||||
{
|
{
|
||||||
user_var_entry *entry;
|
user_var_entry *entry;
|
||||||
|
@ -1919,7 +1919,7 @@ bool Item_allany_subselect::is_maxmin_applicable(JOIN *join)
|
|||||||
WHERE condition.
|
WHERE condition.
|
||||||
*/
|
*/
|
||||||
return (abort_on_null || (upper_item && upper_item->is_top_level_item())) &&
|
return (abort_on_null || (upper_item && upper_item->is_top_level_item())) &&
|
||||||
!join->select_lex->master_unit()->uncacheable && !func->eqne_op();
|
!(join->select_lex->master_unit()->uncacheable & ~UNCACHEABLE_EXPLAIN) && !func->eqne_op();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
270
sql/my_apc.cc
Normal file
270
sql/my_apc.cc
Normal file
@ -0,0 +1,270 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011 - 2012, Monty Program Ab
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; version 2 of the License.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef MY_APC_STANDALONE
|
||||||
|
|
||||||
|
#include "sql_priv.h"
|
||||||
|
#include "sql_class.h"
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* For standalone testing of APC system, see unittest/sql/my_apc-t.cc */
|
||||||
|
|
||||||
|
/*
|
||||||
|
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(mysql_mutex_t *target_mutex)
|
||||||
|
{
|
||||||
|
DBUG_ASSERT(!enabled);
|
||||||
|
LOCK_thd_data_ptr= target_mutex;
|
||||||
|
#ifndef DBUG_OFF
|
||||||
|
n_calls_processed= 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Destroy the target. The target must be disabled when this call is made.
|
||||||
|
*/
|
||||||
|
void Apc_target::destroy()
|
||||||
|
{
|
||||||
|
DBUG_ASSERT(!enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Enter ther state where the target is available for serving APC requests
|
||||||
|
*/
|
||||||
|
void Apc_target::enable()
|
||||||
|
{
|
||||||
|
/* Ok to do without getting/releasing the mutex: */
|
||||||
|
enabled++;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
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;
|
||||||
|
mysql_mutex_lock(LOCK_thd_data_ptr);
|
||||||
|
if (!(--enabled))
|
||||||
|
process= TRUE;
|
||||||
|
mysql_mutex_unlock(LOCK_thd_data_ptr);
|
||||||
|
if (process)
|
||||||
|
process_apc_requests();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* [internal] Put request qe into the request list */
|
||||||
|
|
||||||
|
void Apc_target::enqueue_request(Call_request *qe)
|
||||||
|
{
|
||||||
|
mysql_mutex_assert_owner(LOCK_thd_data_ptr);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
[internal] Remove request qe from the request queue.
|
||||||
|
|
||||||
|
The request is not necessarily first in the queue.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void Apc_target::dequeue_request(Call_request *qe)
|
||||||
|
{
|
||||||
|
mysql_mutex_assert_owner(LOCK_thd_data_ptr);
|
||||||
|
if (apc_calls == qe)
|
||||||
|
{
|
||||||
|
if ((apc_calls= apc_calls->next) == qe)
|
||||||
|
{
|
||||||
|
apc_calls= NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
qe->prev->next= qe->next;
|
||||||
|
qe->next->prev= qe->prev;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_PSI_INTERFACE
|
||||||
|
|
||||||
|
/* One key for all conds */
|
||||||
|
PSI_cond_key key_show_explain_request_COND;
|
||||||
|
|
||||||
|
static PSI_cond_info show_explain_psi_conds[]=
|
||||||
|
{
|
||||||
|
{ &key_show_explain_request_COND, "show_explain", 0 /* not using PSI_FLAG_GLOBAL*/ }
|
||||||
|
};
|
||||||
|
|
||||||
|
void init_show_explain_psi_keys(void)
|
||||||
|
{
|
||||||
|
if (PSI_server == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
PSI_server->register_cond("sql", show_explain_psi_conds,
|
||||||
|
array_elements(show_explain_psi_conds));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Make an APC (Async Procedure Call) to another thread.
|
||||||
|
|
||||||
|
@detail
|
||||||
|
Make an APC call: schedule it for execution and wait until the target
|
||||||
|
thread has executed it.
|
||||||
|
|
||||||
|
- The caller is responsible for making sure he's not posting request
|
||||||
|
to the thread he's calling this function from.
|
||||||
|
|
||||||
|
- The caller must have locked target_mutex. The function will release it.
|
||||||
|
|
||||||
|
@retval FALSE - Ok, the call has been made
|
||||||
|
@retval TRUE - Call wasnt made (either the target is in disabled state or
|
||||||
|
timeout occured)
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool Apc_target::make_apc_call(THD *caller_thd, Apc_call *call,
|
||||||
|
int timeout_sec, bool *timed_out)
|
||||||
|
{
|
||||||
|
bool res= TRUE;
|
||||||
|
*timed_out= FALSE;
|
||||||
|
|
||||||
|
if (enabled)
|
||||||
|
{
|
||||||
|
/* Create and post the request */
|
||||||
|
Call_request apc_request;
|
||||||
|
apc_request.call= call;
|
||||||
|
apc_request.processed= FALSE;
|
||||||
|
mysql_cond_init(key_show_explain_request_COND, &apc_request.COND_request,
|
||||||
|
NULL);
|
||||||
|
enqueue_request(&apc_request);
|
||||||
|
apc_request.what="enqueued by make_apc_call";
|
||||||
|
|
||||||
|
struct timespec abstime;
|
||||||
|
const int timeout= timeout_sec;
|
||||||
|
set_timespec(abstime, timeout);
|
||||||
|
|
||||||
|
int wait_res= 0;
|
||||||
|
const char *old_msg;
|
||||||
|
old_msg= caller_thd->enter_cond(&apc_request.COND_request,
|
||||||
|
LOCK_thd_data_ptr, "show_explain");
|
||||||
|
/* todo: how about processing other errors here? */
|
||||||
|
while (!apc_request.processed && (wait_res != ETIMEDOUT))
|
||||||
|
{
|
||||||
|
/* We own LOCK_thd_data_ptr */
|
||||||
|
wait_res= mysql_cond_timedwait(&apc_request.COND_request,
|
||||||
|
LOCK_thd_data_ptr, &abstime);
|
||||||
|
// &apc_request.LOCK_request, &abstime);
|
||||||
|
if (caller_thd->killed)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!apc_request.processed)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
The wait has timed out, or this thread was KILLed.
|
||||||
|
Remove the request from the queue (ok to do because we own
|
||||||
|
LOCK_thd_data_ptr)
|
||||||
|
*/
|
||||||
|
apc_request.processed= TRUE;
|
||||||
|
dequeue_request(&apc_request);
|
||||||
|
*timed_out= TRUE;
|
||||||
|
res= TRUE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Request was successfully executed and dequeued by the target thread */
|
||||||
|
res= FALSE;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
exit_cond() will call mysql_mutex_unlock(LOCK_thd_data_ptr) for us:
|
||||||
|
*/
|
||||||
|
caller_thd->exit_cond(old_msg);
|
||||||
|
|
||||||
|
/* Destroy all APC request data */
|
||||||
|
mysql_cond_destroy(&apc_request.COND_request);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mysql_mutex_unlock(LOCK_thd_data_ptr);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Process all APC requests.
|
||||||
|
This should be called periodically by the APC target thread.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void Apc_target::process_apc_requests()
|
||||||
|
{
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
Call_request *request;
|
||||||
|
|
||||||
|
mysql_mutex_lock(LOCK_thd_data_ptr);
|
||||||
|
if (!(request= get_first_in_queue()))
|
||||||
|
{
|
||||||
|
/* No requests in the queue */
|
||||||
|
mysql_mutex_unlock(LOCK_thd_data_ptr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Remove the request from the queue (we're holding queue 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->processed= TRUE;
|
||||||
|
|
||||||
|
request->call->call_in_target_thread();
|
||||||
|
request->what="func called by process_apc_requests";
|
||||||
|
|
||||||
|
#ifndef DBUG_OFF
|
||||||
|
n_calls_processed++;
|
||||||
|
#endif
|
||||||
|
mysql_cond_signal(&request->COND_request);
|
||||||
|
mysql_mutex_unlock(LOCK_thd_data_ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
138
sql/my_apc.h
Normal file
138
sql/my_apc.h
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
#ifndef INCLUDES_MY_APC_H
|
||||||
|
#define INCLUDES_MY_APC_H
|
||||||
|
/*
|
||||||
|
Copyright (c) 2011 - 2012, Monty Program Ab
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; version 2 of the License.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||||
|
|
||||||
|
/*
|
||||||
|
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
|
||||||
|
requestor.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class THD;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Target for asynchronous procedure calls (APCs).
|
||||||
|
- A target is running in some particular thread,
|
||||||
|
- One can make calls to it from other threads.
|
||||||
|
*/
|
||||||
|
class Apc_target
|
||||||
|
{
|
||||||
|
mysql_mutex_t *LOCK_thd_data_ptr;
|
||||||
|
public:
|
||||||
|
Apc_target() : enabled(0), apc_calls(NULL) {}
|
||||||
|
~Apc_target() { DBUG_ASSERT(!enabled && !apc_calls);}
|
||||||
|
|
||||||
|
void init(mysql_mutex_t *target_mutex);
|
||||||
|
void destroy();
|
||||||
|
void enable();
|
||||||
|
void disable();
|
||||||
|
|
||||||
|
void process_apc_requests();
|
||||||
|
/*
|
||||||
|
A lightweight function, intended to be used in frequent checks like this:
|
||||||
|
|
||||||
|
if (apc_target.have_requests()) apc_target.process_apc_requests()
|
||||||
|
*/
|
||||||
|
inline bool have_apc_requests()
|
||||||
|
{
|
||||||
|
return test(apc_calls);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Functor class for calls you can schedule */
|
||||||
|
class Apc_call
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/* This function will be called in the target thread */
|
||||||
|
virtual void call_in_target_thread()= 0;
|
||||||
|
virtual ~Apc_call() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Make a call in the target thread (see function definition for details) */
|
||||||
|
bool make_apc_call(THD *caller_thd, Apc_call *call, int timeout_sec, bool *timed_out);
|
||||||
|
|
||||||
|
#ifndef DBUG_OFF
|
||||||
|
int n_calls_processed; /* Number of calls served by this target */
|
||||||
|
#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: requests are added at the end of the
|
||||||
|
list and removed from the front. With circular list, we can keep one
|
||||||
|
pointer, and access both front an back of the list with it.
|
||||||
|
- a thread that has posted a request may time out (or be KILLed) and
|
||||||
|
cancel the request, which means we need a fast request-removal
|
||||||
|
operation.
|
||||||
|
*/
|
||||||
|
Call_request *apc_calls;
|
||||||
|
|
||||||
|
class Call_request
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Apc_call *call; /* Functor to be called */
|
||||||
|
|
||||||
|
/* The caller will actually wait for "processed==TRUE" */
|
||||||
|
bool processed;
|
||||||
|
|
||||||
|
/* Condition that will be signalled when the request has been served */
|
||||||
|
mysql_cond_t COND_request;
|
||||||
|
|
||||||
|
/* Double linked-list linkage */
|
||||||
|
Call_request *next;
|
||||||
|
Call_request *prev;
|
||||||
|
|
||||||
|
const char *what; /* (debug) 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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef HAVE_PSI_INTERFACE
|
||||||
|
void init_show_explain_psi_keys(void);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif //INCLUDES_MY_APC_H
|
||||||
|
|
@ -3349,6 +3349,7 @@ SHOW_VAR com_status_vars[]= {
|
|||||||
{"show_engine_status", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_ENGINE_STATUS]), SHOW_LONG_STATUS},
|
{"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_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_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},
|
{"show_fields", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_FIELDS]), SHOW_LONG_STATUS},
|
||||||
#ifndef DBUG_OFF
|
#ifndef DBUG_OFF
|
||||||
{"show_function_code", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_FUNC_CODE]), SHOW_LONG_STATUS},
|
{"show_function_code", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_FUNC_CODE]), SHOW_LONG_STATUS},
|
||||||
@ -3930,6 +3931,7 @@ static int init_thread_environment()
|
|||||||
#ifdef HAVE_EVENT_SCHEDULER
|
#ifdef HAVE_EVENT_SCHEDULER
|
||||||
Events::init_mutexes();
|
Events::init_mutexes();
|
||||||
#endif
|
#endif
|
||||||
|
init_show_explain_psi_keys();
|
||||||
/* Parameter for threads created for connections */
|
/* Parameter for threads created for connections */
|
||||||
(void) pthread_attr_init(&connection_attrib);
|
(void) pthread_attr_init(&connection_attrib);
|
||||||
(void) pthread_attr_setdetachstate(&connection_attrib,
|
(void) pthread_attr_setdetachstate(&connection_attrib,
|
||||||
|
@ -1657,6 +1657,7 @@ static bool convert_subq_to_sj(JOIN *parent_join, Item_in_subselect *subq_pred)
|
|||||||
parent_lex->ftfunc_list->push_front(ifm);
|
parent_lex->ftfunc_list->push_front(ifm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
parent_lex->have_merged_subqueries= TRUE;
|
||||||
DBUG_RETURN(FALSE);
|
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->
|
create_subquery_temptable_name(tbl_alias, hash_sj_engine->materialize_join->
|
||||||
select_lex->select_number);
|
select_lex->select_number);
|
||||||
jtbm->alias= tbl_alias;
|
jtbm->alias= tbl_alias;
|
||||||
|
|
||||||
|
parent_lex->have_merged_subqueries= TRUE;
|
||||||
#if 0
|
#if 0
|
||||||
/* Inject sj_on_expr into the parent's WHERE or ON */
|
/* Inject sj_on_expr into the parent's WHERE or ON */
|
||||||
if (emb_tbl_nest)
|
if (emb_tbl_nest)
|
||||||
|
@ -35,6 +35,7 @@ class Protocol
|
|||||||
protected:
|
protected:
|
||||||
THD *thd;
|
THD *thd;
|
||||||
String *packet;
|
String *packet;
|
||||||
|
/* Used by net_store_data() for charset conversions */
|
||||||
String *convert;
|
String *convert;
|
||||||
uint field_pos;
|
uint field_pos;
|
||||||
#ifndef DBUG_OFF
|
#ifndef DBUG_OFF
|
||||||
@ -49,6 +50,10 @@ protected:
|
|||||||
MYSQL_FIELD *next_mysql_field;
|
MYSQL_FIELD *next_mysql_field;
|
||||||
MEM_ROOT *alloc;
|
MEM_ROOT *alloc;
|
||||||
#endif
|
#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,
|
bool net_store_data(const uchar *from, size_t length,
|
||||||
CHARSET_INFO *fromcs, CHARSET_INFO *tocs);
|
CHARSET_INFO *fromcs, CHARSET_INFO *tocs);
|
||||||
bool store_string_aux(const char *from, size_t length,
|
bool store_string_aux(const char *from, size_t length,
|
||||||
|
@ -4345,13 +4345,13 @@ ER_TOO_MANY_USER_CONNECTIONS 42000
|
|||||||
ER_SET_CONSTANTS_ONLY
|
ER_SET_CONSTANTS_ONLY
|
||||||
dan "Du må kun bruge konstantudtryk med SET"
|
dan "Du må kun bruge konstantudtryk med SET"
|
||||||
nla "U mag alleen constante expressies gebruiken bij SET"
|
nla "U mag alleen constante expressies gebruiken bij SET"
|
||||||
eng "You may only use constant expressions with SET"
|
eng "You may only use constant expressions in this statement"
|
||||||
est "Ainult konstantsed suurused on lubatud SET klauslis"
|
est "Ainult konstantsed suurused on lubatud SET klauslis"
|
||||||
fre "Seules les expressions constantes sont autorisées avec SET"
|
fre "Seules les expressions constantes sont autorisées avec SET"
|
||||||
ger "Bei SET dürfen nur konstante Ausdrücke verwendet werden"
|
ger "Bei diesem Befehl dürfen nur konstante Ausdrücke verwendet werden"
|
||||||
ita "Si possono usare solo espressioni costanti con SET"
|
ita "Si possono usare solo espressioni costanti con SET"
|
||||||
por "Você pode usar apenas expressões constantes com SET"
|
por "Você pode usar apenas expressões constantes com SET"
|
||||||
rus "Вы можете использовать в SET только константные выражения"
|
rus "С этой командой вы можете использовать только константные выражения"
|
||||||
serbian "Možete upotrebiti samo konstantan iskaz sa komandom 'SET'"
|
serbian "Možete upotrebiti samo konstantan iskaz sa komandom 'SET'"
|
||||||
spa "Tu solo debes usar expresiones constantes con SET"
|
spa "Tu solo debes usar expresiones constantes con SET"
|
||||||
swe "Man kan endast använda konstantuttryck med SET"
|
swe "Man kan endast använda konstantuttryck med SET"
|
||||||
@ -6588,6 +6588,8 @@ ER_QUERY_EXCEEDED_ROWS_EXAMINED_LIMIT
|
|||||||
ER_NO_SUCH_TABLE_IN_ENGINE 42S02
|
ER_NO_SUCH_TABLE_IN_ENGINE 42S02
|
||||||
eng "Table '%-.192s.%-.192s' doesn't exist in engine"
|
eng "Table '%-.192s.%-.192s' doesn't exist in engine"
|
||||||
swe "Det finns ingen tabell som heter '%-.192s.%-.192s' i handlern"
|
swe "Det finns ingen tabell som heter '%-.192s.%-.192s' i handlern"
|
||||||
|
ER_TARGET_NOT_EXPLAINABLE
|
||||||
|
eng "Target is not running an EXPLAINable command"
|
||||||
ER_CONNECTION_ALREADY_EXISTS
|
ER_CONNECTION_ALREADY_EXISTS
|
||||||
eng "Connection '%.*s' conflicts with existing connection '%.*s'"
|
eng "Connection '%.*s' conflicts with existing connection '%.*s'"
|
||||||
ER_MASTER_LOG_PREFIX
|
ER_MASTER_LOG_PREFIX
|
||||||
|
@ -218,6 +218,7 @@ sp_get_flags_for_command(LEX *lex)
|
|||||||
case SQLCOM_SHOW_CREATE_TRIGGER:
|
case SQLCOM_SHOW_CREATE_TRIGGER:
|
||||||
case SQLCOM_SHOW_DATABASES:
|
case SQLCOM_SHOW_DATABASES:
|
||||||
case SQLCOM_SHOW_ERRORS:
|
case SQLCOM_SHOW_ERRORS:
|
||||||
|
case SQLCOM_SHOW_EXPLAIN:
|
||||||
case SQLCOM_SHOW_FIELDS:
|
case SQLCOM_SHOW_FIELDS:
|
||||||
case SQLCOM_SHOW_FUNC_CODE:
|
case SQLCOM_SHOW_FUNC_CODE:
|
||||||
case SQLCOM_SHOW_GRANTS:
|
case SQLCOM_SHOW_GRANTS:
|
||||||
|
@ -1206,6 +1206,7 @@ void THD::init(void)
|
|||||||
/* Initialize the Debug Sync Facility. See debug_sync.cc. */
|
/* Initialize the Debug Sync Facility. See debug_sync.cc. */
|
||||||
debug_sync_init_thread(this);
|
debug_sync_init_thread(this);
|
||||||
#endif /* defined(ENABLED_DEBUG_SYNC) */
|
#endif /* defined(ENABLED_DEBUG_SYNC) */
|
||||||
|
apc_target.init(&LOCK_thd_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1371,6 +1372,7 @@ void THD::cleanup(void)
|
|||||||
ull= NULL;
|
ull= NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
apc_target.destroy();
|
||||||
cleanup_done=1;
|
cleanup_done=1;
|
||||||
DBUG_VOID_RETURN;
|
DBUG_VOID_RETURN;
|
||||||
}
|
}
|
||||||
@ -2023,6 +2025,20 @@ CHANGED_TABLE_LIST* THD::changed_table_dup(const char *key, long key_length)
|
|||||||
int THD::send_explain_fields(select_result *result)
|
int THD::send_explain_fields(select_result *result)
|
||||||
{
|
{
|
||||||
List<Item> field_list;
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Populate the provided field_list with EXPLAIN output columns.
|
||||||
|
this->lex->describe has the EXPLAIN flags
|
||||||
|
*/
|
||||||
|
|
||||||
|
void THD::make_explain_field_list(List<Item> &field_list)
|
||||||
|
{
|
||||||
Item *item;
|
Item *item;
|
||||||
CHARSET_INFO *cs= system_charset_info;
|
CHARSET_INFO *cs= system_charset_info;
|
||||||
field_list.push_back(item= new Item_return_int("id",3, MYSQL_TYPE_LONGLONG));
|
field_list.push_back(item= new Item_return_int("id",3, MYSQL_TYPE_LONGLONG));
|
||||||
@ -2061,10 +2077,9 @@ int THD::send_explain_fields(select_result *result)
|
|||||||
}
|
}
|
||||||
item->maybe_null= 1;
|
item->maybe_null= 1;
|
||||||
field_list.push_back(new Item_empty_string("Extra", 255, cs));
|
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
|
#ifdef SIGNAL_WITH_VIO_CLOSE
|
||||||
void THD::close_active_vio()
|
void THD::close_active_vio()
|
||||||
{
|
{
|
||||||
@ -2296,6 +2311,7 @@ int select_send::send_data(List<Item> &items)
|
|||||||
DBUG_RETURN(0);
|
DBUG_RETURN(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool select_send::send_eof()
|
bool select_send::send_eof()
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
@ -3189,6 +3205,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)
|
void THD::set_n_backup_active_arena(Query_arena *set, Query_arena *backup)
|
||||||
{
|
{
|
||||||
DBUG_ENTER("THD::set_n_backup_active_arena");
|
DBUG_ENTER("THD::set_n_backup_active_arena");
|
||||||
@ -3203,6 +3223,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)
|
void THD::restore_active_arena(Query_arena *set, Query_arena *backup)
|
||||||
{
|
{
|
||||||
DBUG_ENTER("THD::restore_active_arena");
|
DBUG_ENTER("THD::restore_active_arena");
|
||||||
@ -3777,6 +3803,7 @@ void THD::restore_backup_open_tables_state(Open_tables_backup *backup)
|
|||||||
@retval 1 the user thread has been killed
|
@retval 1 the user thread has been killed
|
||||||
|
|
||||||
This is used to signal a storage engine if it should be killed.
|
This is used to signal a storage engine if it should be killed.
|
||||||
|
See also THD::check_killed().
|
||||||
*/
|
*/
|
||||||
|
|
||||||
extern "C" int thd_killed(const MYSQL_THD thd)
|
extern "C" int thd_killed(const MYSQL_THD thd)
|
||||||
@ -3784,6 +3811,10 @@ extern "C" int thd_killed(const MYSQL_THD thd)
|
|||||||
if (!thd)
|
if (!thd)
|
||||||
thd= current_thd;
|
thd= current_thd;
|
||||||
|
|
||||||
|
Apc_target *apc_target= (Apc_target*)&thd->apc_target;
|
||||||
|
if (apc_target->have_apc_requests())
|
||||||
|
apc_target->process_apc_requests();
|
||||||
|
|
||||||
if (!(thd->killed & KILL_HARD_BIT))
|
if (!(thd->killed & KILL_HARD_BIT))
|
||||||
return 0;
|
return 0;
|
||||||
return thd->killed;
|
return thd->killed;
|
||||||
|
@ -43,7 +43,7 @@
|
|||||||
#include "violite.h" /* vio_is_connected */
|
#include "violite.h" /* vio_is_connected */
|
||||||
#include "thr_lock.h" /* thr_lock_type, THR_LOCK_DATA,
|
#include "thr_lock.h" /* thr_lock_type, THR_LOCK_DATA,
|
||||||
THR_LOCK_INFO */
|
THR_LOCK_INFO */
|
||||||
|
#include "my_apc.h"
|
||||||
|
|
||||||
class Reprepare_observer;
|
class Reprepare_observer;
|
||||||
class Relay_log_info;
|
class Relay_log_info;
|
||||||
@ -1529,6 +1529,11 @@ private:
|
|||||||
|
|
||||||
extern "C" void my_message_sql(uint error, const char *str, myf MyFlags);
|
extern "C" void my_message_sql(uint error, const char *str, myf MyFlags);
|
||||||
|
|
||||||
|
class THD;
|
||||||
|
#ifndef DBUG_OFF
|
||||||
|
void dbug_serve_apcs(THD *thd, int n_calls);
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@class THD
|
@class THD
|
||||||
For each client connection we create a separate thread with THD serving as
|
For each client connection we create a separate thread with THD serving as
|
||||||
@ -2198,6 +2203,16 @@ public:
|
|||||||
*/
|
*/
|
||||||
killed_state volatile killed;
|
killed_state volatile killed;
|
||||||
|
|
||||||
|
/* See also thd_killed() */
|
||||||
|
inline bool check_killed()
|
||||||
|
{
|
||||||
|
if (killed)
|
||||||
|
return TRUE;
|
||||||
|
if (apc_target.have_apc_requests())
|
||||||
|
apc_target.process_apc_requests();
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
/* scramble - random string sent to client on handshake */
|
/* scramble - random string sent to client on handshake */
|
||||||
char scramble[SCRAMBLE_LENGTH+1];
|
char scramble[SCRAMBLE_LENGTH+1];
|
||||||
|
|
||||||
@ -2406,6 +2421,17 @@ public:
|
|||||||
/** Disconnect the associated communication endpoint. */
|
/** Disconnect the associated communication endpoint. */
|
||||||
void disconnect();
|
void disconnect();
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Allows this thread to serve as a target for others to schedule Async
|
||||||
|
Procedure Calls on.
|
||||||
|
|
||||||
|
It's possible to schedule any code to be executed this way, by
|
||||||
|
inheriting from the Apc_call object. Currently, only
|
||||||
|
Show_explain_request uses this.
|
||||||
|
*/
|
||||||
|
Apc_target apc_target;
|
||||||
|
|
||||||
#ifndef MYSQL_CLIENT
|
#ifndef MYSQL_CLIENT
|
||||||
enum enum_binlog_query_type {
|
enum enum_binlog_query_type {
|
||||||
/* The query can be logged in row format or in statement format. */
|
/* The query can be logged in row format or in statement format. */
|
||||||
@ -2599,7 +2625,7 @@ public:
|
|||||||
void add_changed_table(const char *key, long key_length);
|
void add_changed_table(const char *key, long key_length);
|
||||||
CHANGED_TABLE_LIST * changed_table_dup(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);
|
int send_explain_fields(select_result *result);
|
||||||
|
void make_explain_field_list(List<Item> &field_list);
|
||||||
/**
|
/**
|
||||||
Clear the current error, if any.
|
Clear the current error, if any.
|
||||||
We do not clear is_fatal_error or is_fatal_sub_stmt_error since we
|
We do not clear is_fatal_error or is_fatal_sub_stmt_error since we
|
||||||
@ -3240,10 +3266,42 @@ public:
|
|||||||
|
|
||||||
class JOIN;
|
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:
|
protected:
|
||||||
THD *thd;
|
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;
|
SELECT_LEX_UNIT *unit;
|
||||||
|
/* Something used only by the parser: */
|
||||||
public:
|
public:
|
||||||
select_result();
|
select_result();
|
||||||
virtual ~select_result() {};
|
virtual ~select_result() {};
|
||||||
@ -3261,11 +3319,6 @@ public:
|
|||||||
virtual uint field_count(List<Item> &fields) const
|
virtual uint field_count(List<Item> &fields) const
|
||||||
{ return fields.elements; }
|
{ return fields.elements; }
|
||||||
virtual bool send_result_set_metadata(List<Item> &list, uint flags)=0;
|
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 bool initialize_tables (JOIN *join=0) { return 0; }
|
||||||
virtual void send_error(uint errcode,const char *err);
|
virtual void send_error(uint errcode,const char *err);
|
||||||
virtual bool send_eof()=0;
|
virtual bool send_eof()=0;
|
||||||
@ -3292,6 +3345,32 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
This is a select_result_sink which simply writes all data into a (temporary)
|
||||||
|
table. Creation/deletion of the table is outside of the scope of the class
|
||||||
|
|
||||||
|
It is aimed at capturing SHOW EXPLAIN output, so:
|
||||||
|
- 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)
|
||||||
|
- We don't try to convert the target table to MyISAM
|
||||||
|
*/
|
||||||
|
|
||||||
|
class select_result_explain_buffer : public select_result_sink
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
select_result_explain_buffer(THD *thd_arg, TABLE *table_arg) :
|
||||||
|
thd(thd_arg), dst_table(table_arg) {};
|
||||||
|
|
||||||
|
THD *thd;
|
||||||
|
TABLE *dst_table; /* table to write into */
|
||||||
|
|
||||||
|
/* The following is called in the child thread: */
|
||||||
|
int send_data(List<Item> &items);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Base class for select_result descendands which intercept and
|
Base class for select_result descendands which intercept and
|
||||||
transform result set rows. As the rows are not sent to the client,
|
transform result set rows. As the rows are not sent to the client,
|
||||||
@ -3863,6 +3942,8 @@ class user_var_entry
|
|||||||
DTCollation collation;
|
DTCollation collation;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
user_var_entry *get_variable(HASH *hash, LEX_STRING &name,
|
||||||
|
bool create_if_not_exists);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Unique -- class for unique (removing of duplicates).
|
Unique -- class for unique (removing of duplicates).
|
||||||
|
@ -2236,7 +2236,7 @@ enum_nested_loop_state JOIN_CACHE::join_matching_records(bool skip_last)
|
|||||||
|
|
||||||
while (!(error= join_tab_scan->next()))
|
while (!(error= join_tab_scan->next()))
|
||||||
{
|
{
|
||||||
if (join->thd->killed)
|
if (join->thd->check_killed())
|
||||||
{
|
{
|
||||||
/* The user has aborted the execution of the query */
|
/* The user has aborted the execution of the query */
|
||||||
join->thd->send_kill_message();
|
join->thd->send_kill_message();
|
||||||
@ -2506,7 +2506,7 @@ enum_nested_loop_state JOIN_CACHE::join_null_complements(bool skip_last)
|
|||||||
|
|
||||||
for ( ; cnt; cnt--)
|
for ( ; cnt; cnt--)
|
||||||
{
|
{
|
||||||
if (join->thd->killed)
|
if (join->thd->check_killed())
|
||||||
{
|
{
|
||||||
/* The user has aborted the execution of the query */
|
/* The user has aborted the execution of the query */
|
||||||
join->thd->send_kill_message();
|
join->thd->send_kill_message();
|
||||||
@ -3356,7 +3356,7 @@ int JOIN_TAB_SCAN::next()
|
|||||||
update_virtual_fields(thd, table);
|
update_virtual_fields(thd, table);
|
||||||
while (!err && select && (skip_rc= select->skip_record(thd)) <= 0)
|
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;
|
return 1;
|
||||||
/*
|
/*
|
||||||
Move to the next record if the last retrieved record does not
|
Move to the next record if the last retrieved record does not
|
||||||
|
126
sql/sql_lex.cc
126
sql/sql_lex.cc
@ -1883,6 +1883,7 @@ void st_select_lex::init_query()
|
|||||||
nest_level= 0;
|
nest_level= 0;
|
||||||
link_next= 0;
|
link_next= 0;
|
||||||
is_prep_leaf_list_saved= FALSE;
|
is_prep_leaf_list_saved= FALSE;
|
||||||
|
have_merged_subqueries= FALSE;
|
||||||
bzero((char*) expr_cache_may_be_used, sizeof(expr_cache_may_be_used));
|
bzero((char*) expr_cache_may_be_used, sizeof(expr_cache_may_be_used));
|
||||||
m_non_agg_field_used= false;
|
m_non_agg_field_used= false;
|
||||||
m_agg_func_used= false;
|
m_agg_func_used= false;
|
||||||
@ -3474,7 +3475,7 @@ bool st_select_lex::optimize_unflattened_subqueries(bool const_only)
|
|||||||
if (options & SELECT_DESCRIBE)
|
if (options & SELECT_DESCRIBE)
|
||||||
{
|
{
|
||||||
/* Optimize the subquery in the context of EXPLAIN. */
|
/* Optimize the subquery in the context of EXPLAIN. */
|
||||||
sl->set_explain_type();
|
sl->set_explain_type(FALSE);
|
||||||
sl->options|= SELECT_DESCRIBE;
|
sl->options|= SELECT_DESCRIBE;
|
||||||
inner_join->select_options|= SELECT_DESCRIBE;
|
inner_join->select_options|= SELECT_DESCRIBE;
|
||||||
}
|
}
|
||||||
@ -3924,9 +3925,12 @@ void st_select_lex::update_correlated_cache()
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
Set the EXPLAIN type for this subquery.
|
Set the EXPLAIN type for this subquery.
|
||||||
|
|
||||||
|
@param on_the_fly TRUE<=> We're running a SHOW EXPLAIN command, so we must
|
||||||
|
not change any variables
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void st_select_lex::set_explain_type()
|
void st_select_lex::set_explain_type(bool on_the_fly)
|
||||||
{
|
{
|
||||||
bool is_primary= FALSE;
|
bool is_primary= FALSE;
|
||||||
if (next_select())
|
if (next_select())
|
||||||
@ -3948,6 +3952,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();
|
SELECT_LEX *first= master_unit()->first_select();
|
||||||
/* drop UNCACHEABLE_EXPLAIN, because it is for internal usage only */
|
/* drop UNCACHEABLE_EXPLAIN, because it is for internal usage only */
|
||||||
uint8 is_uncacheable= (uncacheable & ~UNCACHEABLE_EXPLAIN);
|
uint8 is_uncacheable= (uncacheable & ~UNCACHEABLE_EXPLAIN);
|
||||||
@ -4000,9 +4007,14 @@ void st_select_lex::set_explain_type()
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
type= is_uncacheable ? "UNCACHEABLE UNION": "UNION";
|
type= is_uncacheable ? "UNCACHEABLE UNION": "UNION";
|
||||||
|
if (this == master_unit()->fake_select_lex)
|
||||||
|
type= "UNION RESULT";
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!on_the_fly)
|
||||||
options|= SELECT_DESCRIBE;
|
options|= SELECT_DESCRIBE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4150,6 +4162,116 @@ 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,
|
||||||
|
uint8 explain_flags,
|
||||||
|
bool *printed_anything)
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
if (join && join->have_query_plan == JOIN::QEP_AVAILABLE)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
There is a number of reasons join can be marked as degenerate, so all
|
||||||
|
three conditions below can happen simultaneously, or individually:
|
||||||
|
*/
|
||||||
|
*printed_anything= TRUE;
|
||||||
|
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 :
|
||||||
|
"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->skip_sort_order && !join->no_order &&
|
||||||
|
(join->order || join->group_list), // bool need_order
|
||||||
|
join->select_distinct, // 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))
|
||||||
|
{
|
||||||
|
if ((res= unit->print_explain(output, explain_flags, printed_anything)))
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const char *msg;
|
||||||
|
if (!join)
|
||||||
|
DBUG_ASSERT(0); /* Seems not to be possible */
|
||||||
|
|
||||||
|
/* Not printing anything useful, don't touch *printed_anything here */
|
||||||
|
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 res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int st_select_lex_unit::print_explain(select_result_sink *output,
|
||||||
|
uint8 explain_flags, bool *printed_anything)
|
||||||
|
{
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
const char *msg="Query plan already deleted";
|
||||||
|
res= print_explain_message_line(output, first, TRUE /* on_the_fly */,
|
||||||
|
0, msg);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (SELECT_LEX *sl= first; sl; sl= sl->next_select())
|
||||||
|
{
|
||||||
|
if ((res= sl->print_explain(output, explain_flags, printed_anything)))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Note: fake_select_lex->join may be NULL or non-NULL at this point */
|
||||||
|
if (fake_select_lex)
|
||||||
|
{
|
||||||
|
res= print_fake_select_lex_join(output, TRUE /* on the fly */,
|
||||||
|
fake_select_lex, explain_flags);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
A routine used by the parser to decide whether we are specifying a full
|
A routine used by the parser to decide whether we are specifying a full
|
||||||
partitioning or if only partitions to add or to split.
|
partitioning or if only partitions to add or to split.
|
||||||
|
@ -194,6 +194,7 @@ enum enum_sql_command {
|
|||||||
SQLCOM_SHOW_RELAYLOG_EVENTS,
|
SQLCOM_SHOW_RELAYLOG_EVENTS,
|
||||||
SQLCOM_SHOW_USER_STATS, SQLCOM_SHOW_TABLE_STATS, SQLCOM_SHOW_INDEX_STATS,
|
SQLCOM_SHOW_USER_STATS, SQLCOM_SHOW_TABLE_STATS, SQLCOM_SHOW_INDEX_STATS,
|
||||||
SQLCOM_SHOW_CLIENT_STATS,
|
SQLCOM_SHOW_CLIENT_STATS,
|
||||||
|
SQLCOM_SHOW_EXPLAIN,
|
||||||
|
|
||||||
/*
|
/*
|
||||||
When a command is added here, be sure it's also added in mysqld.cc
|
When a command is added here, be sure it's also added in mysqld.cc
|
||||||
@ -357,6 +358,8 @@ typedef uchar index_clause_map;
|
|||||||
#define INDEX_HINT_MASK_ALL (INDEX_HINT_MASK_JOIN | INDEX_HINT_MASK_GROUP | \
|
#define INDEX_HINT_MASK_ALL (INDEX_HINT_MASK_JOIN | INDEX_HINT_MASK_GROUP | \
|
||||||
INDEX_HINT_MASK_ORDER)
|
INDEX_HINT_MASK_ORDER)
|
||||||
|
|
||||||
|
class select_result_sink;
|
||||||
|
|
||||||
/* Single element of an USE/FORCE/IGNORE INDEX list specified as a SQL hint */
|
/* Single element of an USE/FORCE/IGNORE INDEX list specified as a SQL hint */
|
||||||
class Index_hint : public Sql_alloc
|
class Index_hint : public Sql_alloc
|
||||||
{
|
{
|
||||||
@ -717,6 +720,8 @@ public:
|
|||||||
friend int subselect_union_engine::exec();
|
friend int subselect_union_engine::exec();
|
||||||
|
|
||||||
List<Item> *get_unit_column_types();
|
List<Item> *get_unit_column_types();
|
||||||
|
int print_explain(select_result_sink *output, uint8 explain_flags,
|
||||||
|
bool *printed_anything);
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef class st_select_lex_unit SELECT_LEX_UNIT;
|
typedef class st_select_lex_unit SELECT_LEX_UNIT;
|
||||||
@ -778,6 +783,12 @@ public:
|
|||||||
*/
|
*/
|
||||||
List<Item_in_subselect> sj_subselects;
|
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;
|
||||||
List<TABLE_LIST> leaf_tables_exec;
|
List<TABLE_LIST> leaf_tables_exec;
|
||||||
List<TABLE_LIST> leaf_tables_prep;
|
List<TABLE_LIST> leaf_tables_prep;
|
||||||
@ -1001,7 +1012,7 @@ public:
|
|||||||
bool is_part_of_union() { return master_unit()->is_union(); }
|
bool is_part_of_union() { return master_unit()->is_union(); }
|
||||||
bool optimize_unflattened_subqueries(bool const_only);
|
bool optimize_unflattened_subqueries(bool const_only);
|
||||||
/* Set the EXPLAIN type for this subquery. */
|
/* 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);
|
bool handle_derived(LEX *lex, uint phases);
|
||||||
void append_table_to_list(TABLE_LIST *TABLE_LIST::*link, TABLE_LIST *table);
|
void append_table_to_list(TABLE_LIST *TABLE_LIST::*link, TABLE_LIST *table);
|
||||||
bool get_free_table_map(table_map *map, uint *tablenr);
|
bool get_free_table_map(table_map *map, uint *tablenr);
|
||||||
@ -1025,8 +1036,10 @@ public:
|
|||||||
|
|
||||||
bool save_leaf_tables(THD *thd);
|
bool save_leaf_tables(THD *thd);
|
||||||
bool save_prep_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, uint8 explain_flags,
|
||||||
|
bool *printed_anything);
|
||||||
/*
|
/*
|
||||||
For MODE_ONLY_FULL_GROUP_BY we need to maintain two flags:
|
For MODE_ONLY_FULL_GROUP_BY we need to maintain two flags:
|
||||||
- Non-aggregated fields are used in this select.
|
- Non-aggregated fields are used in this select.
|
||||||
@ -2348,7 +2361,7 @@ struct LEX: public Query_tables_list
|
|||||||
char *backup_dir; /* For RESTORE/BACKUP */
|
char *backup_dir; /* For RESTORE/BACKUP */
|
||||||
char* to_log; /* For PURGE MASTER LOGS TO */
|
char* to_log; /* For PURGE MASTER LOGS TO */
|
||||||
char* x509_subject,*x509_issuer,*ssl_cipher;
|
char* x509_subject,*x509_issuer,*ssl_cipher;
|
||||||
String *wild;
|
String *wild; /* Wildcard in SHOW {something} LIKE 'wild'*/
|
||||||
sql_exchange *exchange;
|
sql_exchange *exchange;
|
||||||
select_result *result;
|
select_result *result;
|
||||||
Item *default_value, *on_update_value;
|
Item *default_value, *on_update_value;
|
||||||
|
@ -336,6 +336,7 @@ void init_update_queries(void)
|
|||||||
sql_command_flags[SQLCOM_SHOW_ENGINE_STATUS]= CF_STATUS_COMMAND;
|
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_MUTEX]= CF_STATUS_COMMAND;
|
||||||
sql_command_flags[SQLCOM_SHOW_ENGINE_LOGS]= 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_PROCESSLIST]= CF_STATUS_COMMAND;
|
||||||
sql_command_flags[SQLCOM_SHOW_GRANTS]= CF_STATUS_COMMAND;
|
sql_command_flags[SQLCOM_SHOW_GRANTS]= CF_STATUS_COMMAND;
|
||||||
sql_command_flags[SQLCOM_SHOW_CREATE_DB]= CF_STATUS_COMMAND;
|
sql_command_flags[SQLCOM_SHOW_CREATE_DB]= CF_STATUS_COMMAND;
|
||||||
@ -2175,6 +2176,33 @@ mysql_execute_command(THD *thd)
|
|||||||
execute_show_status(thd, all_tables);
|
execute_show_status(thd, all_tables);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case SQLCOM_SHOW_EXPLAIN:
|
||||||
|
{
|
||||||
|
if (!thd->security_ctx->priv_user[0] &&
|
||||||
|
check_global_access(thd,PROCESS_ACL))
|
||||||
|
break;
|
||||||
|
|
||||||
|
/*
|
||||||
|
The select should use only one table, it's the SHOW EXPLAIN pseudo-table
|
||||||
|
*/
|
||||||
|
if (lex->sroutines.records || lex->query_tables->next_global)
|
||||||
|
{
|
||||||
|
my_message(ER_SET_CONSTANTS_ONLY, ER(ER_SET_CONSTANTS_ONLY),
|
||||||
|
MYF(0));
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
Item **it= lex->value_list.head_ref();
|
||||||
|
if (!(*it)->basic_const_item() ||
|
||||||
|
(!(*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;
|
||||||
|
}
|
||||||
|
/* no break; fall through */
|
||||||
|
}
|
||||||
case SQLCOM_SHOW_DATABASES:
|
case SQLCOM_SHOW_DATABASES:
|
||||||
case SQLCOM_SHOW_TABLES:
|
case SQLCOM_SHOW_TABLES:
|
||||||
case SQLCOM_SHOW_TRIGGERS:
|
case SQLCOM_SHOW_TRIGGERS:
|
||||||
@ -6666,6 +6694,35 @@ void add_join_natural(TABLE_LIST *a, TABLE_LIST *b, List<String> *using_fields,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Find a thread by id and return it, locking it LOCK_thd_data
|
||||||
|
|
||||||
|
@param id Identifier of the thread we're looking for
|
||||||
|
|
||||||
|
@return NULL - not found
|
||||||
|
pointer - thread found, and its LOCK_thd_data is locked.
|
||||||
|
*/
|
||||||
|
|
||||||
|
THD *find_thread_by_id(ulong id)
|
||||||
|
{
|
||||||
|
THD *tmp;
|
||||||
|
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 == id)
|
||||||
|
{
|
||||||
|
mysql_mutex_lock(&tmp->LOCK_thd_data); // Lock from delete
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mysql_mutex_unlock(&LOCK_thread_count);
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
kill on thread.
|
kill on thread.
|
||||||
|
|
||||||
@ -6684,20 +6741,7 @@ uint kill_one_thread(THD *thd, ulong id, killed_state kill_signal)
|
|||||||
DBUG_ENTER("kill_one_thread");
|
DBUG_ENTER("kill_one_thread");
|
||||||
DBUG_PRINT("enter", ("id: %lu signal: %u", id, (uint) kill_signal));
|
DBUG_PRINT("enter", ("id: %lu signal: %u", id, (uint) kill_signal));
|
||||||
|
|
||||||
mysql_mutex_lock(&LOCK_thread_count); // For unlink from list
|
if ((tmp= find_thread_by_id(id)))
|
||||||
I_List_iterator<THD> it(threads);
|
|
||||||
while ((tmp=it++))
|
|
||||||
{
|
|
||||||
if (tmp->command == COM_DAEMON)
|
|
||||||
continue;
|
|
||||||
if (tmp->thread_id == id)
|
|
||||||
{
|
|
||||||
mysql_mutex_lock(&tmp->LOCK_thd_data); // Lock from delete
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mysql_mutex_unlock(&LOCK_thread_count);
|
|
||||||
if (tmp)
|
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
If we're SUPER, we can KILL anything, including system-threads.
|
If we're SUPER, we can KILL anything, including system-threads.
|
||||||
|
@ -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
|
Note that we don't need to have cases in this list if they are
|
||||||
marked with CF_STATUS_COMMAND in sql_command_flags
|
marked with CF_STATUS_COMMAND in sql_command_flags
|
||||||
*/
|
*/
|
||||||
|
case SQLCOM_SHOW_EXPLAIN:
|
||||||
case SQLCOM_DROP_TABLE:
|
case SQLCOM_DROP_TABLE:
|
||||||
case SQLCOM_RENAME_TABLE:
|
case SQLCOM_RENAME_TABLE:
|
||||||
case SQLCOM_ALTER_TABLE:
|
case SQLCOM_ALTER_TABLE:
|
||||||
|
@ -331,6 +331,7 @@ enum enum_yes_no_unknown
|
|||||||
External variables
|
External variables
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
/* sql_yacc.cc */
|
/* sql_yacc.cc */
|
||||||
#ifndef DBUG_OFF
|
#ifndef DBUG_OFF
|
||||||
extern void turn_parser_debug_on();
|
extern void turn_parser_debug_on();
|
||||||
|
@ -277,6 +277,57 @@ JOIN_TAB *first_breadth_first_tab(JOIN *join, enum enum_exec_or_opt tabs_kind);
|
|||||||
JOIN_TAB *next_breadth_first_tab(JOIN *join, enum enum_exec_or_opt tabs_kind,
|
JOIN_TAB *next_breadth_first_tab(JOIN *join, enum enum_exec_or_opt tabs_kind,
|
||||||
JOIN_TAB *tab);
|
JOIN_TAB *tab);
|
||||||
|
|
||||||
|
#ifndef DBUG_OFF
|
||||||
|
|
||||||
|
/*
|
||||||
|
SHOW EXPLAIN testing: wait for, and serve n_calls APC requests.
|
||||||
|
*/
|
||||||
|
void dbug_serve_apcs(THD *thd, int n_calls)
|
||||||
|
{
|
||||||
|
const char *save_proc_info= thd->proc_info;
|
||||||
|
/* This is so that mysqltest knows we're ready to serve requests: */
|
||||||
|
thd_proc_info(thd, "show_explain_trap");
|
||||||
|
|
||||||
|
/* Busy-wait for n_calls APC requests to arrive and be processed */
|
||||||
|
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.
|
This handles SELECT with and without UNION.
|
||||||
*/
|
*/
|
||||||
@ -939,6 +990,19 @@ err:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int JOIN::optimize()
|
||||||
|
{
|
||||||
|
int res= optimize_inner();
|
||||||
|
/*
|
||||||
|
If we're inside a non-correlated subquery, this function may be
|
||||||
|
called for the second time after the subquery has been executed
|
||||||
|
and deleted. The second call will not produce a valid query plan, it will
|
||||||
|
short-circuit because optimized==TRUE.
|
||||||
|
*/
|
||||||
|
if (!res && have_query_plan != QEP_DELETED)
|
||||||
|
have_query_plan= QEP_AVAILABLE;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
global select optimisation.
|
global select optimisation.
|
||||||
|
|
||||||
@ -952,7 +1016,7 @@ err:
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
int
|
int
|
||||||
JOIN::optimize()
|
JOIN::optimize_inner()
|
||||||
{
|
{
|
||||||
ulonglong select_opts_for_readinfo;
|
ulonglong select_opts_for_readinfo;
|
||||||
uint no_jbuf_after;
|
uint no_jbuf_after;
|
||||||
@ -2176,6 +2240,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.
|
Exec select.
|
||||||
|
|
||||||
@ -2187,8 +2277,8 @@ JOIN::save_join_tab()
|
|||||||
@todo
|
@todo
|
||||||
When can we have here thd->net.report_error not zero?
|
When can we have here thd->net.report_error not zero?
|
||||||
*/
|
*/
|
||||||
void
|
|
||||||
JOIN::exec()
|
void JOIN::exec_inner()
|
||||||
{
|
{
|
||||||
List<Item> *columns_list= &fields_list;
|
List<Item> *columns_list= &fields_list;
|
||||||
int tmp_error;
|
int tmp_error;
|
||||||
@ -2494,6 +2584,10 @@ JOIN::exec()
|
|||||||
DBUG_PRINT("info",("Creating group table"));
|
DBUG_PRINT("info",("Creating group table"));
|
||||||
|
|
||||||
/* Free first data from old join */
|
/* Free first data from old join */
|
||||||
|
|
||||||
|
/*
|
||||||
|
psergey-todo: this is the place of pre-mature JOIN::free call.
|
||||||
|
*/
|
||||||
curr_join->join_free();
|
curr_join->join_free();
|
||||||
if (curr_join->make_simple_join(this, curr_tmp_table))
|
if (curr_join->make_simple_join(this, curr_tmp_table))
|
||||||
DBUG_VOID_RETURN;
|
DBUG_VOID_RETURN;
|
||||||
@ -3766,7 +3860,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
|
|||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
/* Generate an execution plan from the found optimal join order. */
|
/* 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:
|
error:
|
||||||
/*
|
/*
|
||||||
@ -6840,7 +6934,7 @@ best_extension_by_limited_search(JOIN *join,
|
|||||||
DBUG_ENTER("best_extension_by_limited_search");
|
DBUG_ENTER("best_extension_by_limited_search");
|
||||||
|
|
||||||
THD *thd= join->thd;
|
THD *thd= join->thd;
|
||||||
if (thd->killed) // Abort
|
if (thd->check_killed()) // Abort
|
||||||
DBUG_RETURN(TRUE);
|
DBUG_RETURN(TRUE);
|
||||||
|
|
||||||
DBUG_EXECUTE("opt", print_plan(join, idx, read_time, record_count, idx,
|
DBUG_EXECUTE("opt", print_plan(join, idx, read_time, record_count, idx,
|
||||||
@ -6997,7 +7091,7 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count,
|
|||||||
{
|
{
|
||||||
DBUG_ENTER("find_best");
|
DBUG_ENTER("find_best");
|
||||||
THD *thd= join->thd;
|
THD *thd= join->thd;
|
||||||
if (thd->killed)
|
if (thd->check_killed())
|
||||||
DBUG_RETURN(TRUE);
|
DBUG_RETURN(TRUE);
|
||||||
if (!rest_tables)
|
if (!rest_tables)
|
||||||
{
|
{
|
||||||
@ -7693,6 +7787,7 @@ get_best_combination(JOIN *join)
|
|||||||
join->table_access_tabs= join->join_tab;
|
join->table_access_tabs= join->join_tab;
|
||||||
join->top_table_access_tabs_count= join->top_join_tab_count;
|
join->top_table_access_tabs_count= join->top_join_tab_count;
|
||||||
|
|
||||||
|
|
||||||
update_depend_map(join);
|
update_depend_map(join);
|
||||||
DBUG_RETURN(0);
|
DBUG_RETURN(0);
|
||||||
}
|
}
|
||||||
@ -7936,6 +8031,7 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j,
|
|||||||
j->ref.null_rejecting= 0;
|
j->ref.null_rejecting= 0;
|
||||||
j->ref.disable_cache= FALSE;
|
j->ref.disable_cache= FALSE;
|
||||||
j->ref.null_ref_part= NO_REF_PART;
|
j->ref.null_ref_part= NO_REF_PART;
|
||||||
|
j->ref.const_ref_part_map= 0;
|
||||||
keyuse=org_keyuse;
|
keyuse=org_keyuse;
|
||||||
|
|
||||||
store_key **ref_key= j->ref.key_copy;
|
store_key **ref_key= j->ref.key_copy;
|
||||||
@ -7971,6 +8067,13 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j,
|
|||||||
if (keyuse->null_rejecting)
|
if (keyuse->null_rejecting)
|
||||||
j->ref.null_rejecting |= 1 << i;
|
j->ref.null_rejecting |= 1 << i;
|
||||||
keyuse_uses_no_tables= keyuse_uses_no_tables && !keyuse->used_tables;
|
keyuse_uses_no_tables= keyuse_uses_no_tables && !keyuse->used_tables;
|
||||||
|
/*
|
||||||
|
Todo: we should remove this check for thd->lex->describe on the next
|
||||||
|
line. With SHOW EXPLAIN code, EXPLAIN printout code no longer depends
|
||||||
|
on it. However, removing the check caused change in lots of query
|
||||||
|
plans! Does the optimizer depend on the contents of
|
||||||
|
table_ref->key_copy ? If yes, do we produce incorrect EXPLAINs?
|
||||||
|
*/
|
||||||
if (!keyuse->val->used_tables() && !thd->lex->describe)
|
if (!keyuse->val->used_tables() && !thd->lex->describe)
|
||||||
{ // Compare against constant
|
{ // Compare against constant
|
||||||
store_key_item tmp(thd,
|
store_key_item tmp(thd,
|
||||||
@ -7983,6 +8086,7 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j,
|
|||||||
if (thd->is_fatal_error)
|
if (thd->is_fatal_error)
|
||||||
DBUG_RETURN(TRUE);
|
DBUG_RETURN(TRUE);
|
||||||
tmp.copy();
|
tmp.copy();
|
||||||
|
j->ref.const_ref_part_map |= key_part_map(1) << i ;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
*ref_key++= get_store_key(thd,
|
*ref_key++= get_store_key(thd,
|
||||||
@ -8094,6 +8198,7 @@ JOIN::make_simple_join(JOIN *parent, TABLE *temp_table)
|
|||||||
!(parent->join_tab_reexec= (JOIN_TAB*) thd->alloc(sizeof(JOIN_TAB))))
|
!(parent->join_tab_reexec= (JOIN_TAB*) thd->alloc(sizeof(JOIN_TAB))))
|
||||||
DBUG_RETURN(TRUE); /* purecov: inspected */
|
DBUG_RETURN(TRUE); /* purecov: inspected */
|
||||||
|
|
||||||
|
// psergey-todo: here, save the pointer for original join_tabs.
|
||||||
join_tab= parent->join_tab_reexec;
|
join_tab= parent->join_tab_reexec;
|
||||||
table= &parent->table_reexec[0]; parent->table_reexec[0]= temp_table;
|
table= &parent->table_reexec[0]; parent->table_reexec[0]= temp_table;
|
||||||
table_count= top_join_tab_count= 1;
|
table_count= top_join_tab_count= 1;
|
||||||
@ -10357,7 +10462,7 @@ ha_rows JOIN_TAB::get_examined_rows()
|
|||||||
{
|
{
|
||||||
ha_rows examined_rows;
|
ha_rows examined_rows;
|
||||||
|
|
||||||
if (select && select->quick)
|
if (select && select->quick && use_quick != 2)
|
||||||
examined_rows= select->quick->records;
|
examined_rows= select->quick->records;
|
||||||
else if (type == JT_NEXT || type == JT_ALL ||
|
else if (type == JT_NEXT || type == JT_ALL ||
|
||||||
type == JT_HASH || type ==JT_HASH_NEXT)
|
type == JT_HASH || type ==JT_HASH_NEXT)
|
||||||
@ -10411,9 +10516,18 @@ bool JOIN_TAB::preread_init()
|
|||||||
mysql_handle_single_derived(join->thd->lex,
|
mysql_handle_single_derived(join->thd->lex,
|
||||||
derived, DT_CREATE | DT_FILL))
|
derived, DT_CREATE | DT_FILL))
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
preread_init_done= TRUE;
|
preread_init_done= TRUE;
|
||||||
if (select && select->quick)
|
if (select && select->quick)
|
||||||
select->quick->replace_handler(table->file);
|
select->quick->replace_handler(table->file);
|
||||||
|
|
||||||
|
DBUG_EXECUTE_IF("show_explain_probe_join_tab_preread",
|
||||||
|
if (dbug_user_var_equals_int(join->thd,
|
||||||
|
"show_explain_probe_select_id",
|
||||||
|
join->select_lex->select_number))
|
||||||
|
dbug_serve_apcs(join->thd, 1);
|
||||||
|
);
|
||||||
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -10622,6 +10736,8 @@ void JOIN::cleanup(bool full)
|
|||||||
DBUG_ENTER("JOIN::cleanup");
|
DBUG_ENTER("JOIN::cleanup");
|
||||||
DBUG_PRINT("enter", ("full %u", (uint) full));
|
DBUG_PRINT("enter", ("full %u", (uint) full));
|
||||||
|
|
||||||
|
have_query_plan= QEP_DELETED;
|
||||||
|
|
||||||
if (table)
|
if (table)
|
||||||
{
|
{
|
||||||
JOIN_TAB *tab;
|
JOIN_TAB *tab;
|
||||||
@ -10641,10 +10757,23 @@ void JOIN::cleanup(bool full)
|
|||||||
|
|
||||||
if (full)
|
if (full)
|
||||||
{
|
{
|
||||||
|
JOIN_TAB *sort_tab= first_linear_tab(this, WITHOUT_CONST_TABLES);
|
||||||
|
if (pre_sort_join_tab)
|
||||||
|
{
|
||||||
|
if (sort_tab && sort_tab->select == pre_sort_join_tab->select)
|
||||||
|
{
|
||||||
|
pre_sort_join_tab->select= NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
clean_pre_sort_join_tab();
|
||||||
|
}
|
||||||
|
|
||||||
for (tab= first_linear_tab(this, WITH_CONST_TABLES); tab;
|
for (tab= first_linear_tab(this, WITH_CONST_TABLES); tab;
|
||||||
tab= next_linear_tab(this, tab, WITH_BUSH_ROOTS))
|
tab= next_linear_tab(this, tab, WITH_BUSH_ROOTS))
|
||||||
|
{
|
||||||
tab->cleanup();
|
tab->cleanup();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (tab= first_linear_tab(this, WITH_CONST_TABLES); tab;
|
for (tab= first_linear_tab(this, WITH_CONST_TABLES); tab;
|
||||||
@ -15539,7 +15668,7 @@ create_internal_tmp_table_from_heap2(THD *thd, TABLE *table,
|
|||||||
DBUG_EXECUTE_IF("raise_error", write_err= HA_ERR_FOUND_DUPP_KEY ;);
|
DBUG_EXECUTE_IF("raise_error", write_err= HA_ERR_FOUND_DUPP_KEY ;);
|
||||||
if (write_err)
|
if (write_err)
|
||||||
goto err;
|
goto err;
|
||||||
if (thd->killed)
|
if (thd->check_killed())
|
||||||
{
|
{
|
||||||
thd->send_kill_message();
|
thd->send_kill_message();
|
||||||
goto err_killed;
|
goto err_killed;
|
||||||
@ -15798,6 +15927,14 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
DBUG_ASSERT(join->table_count);
|
DBUG_ASSERT(join->table_count);
|
||||||
|
|
||||||
|
DBUG_EXECUTE_IF("show_explain_probe_do_select",
|
||||||
|
if (dbug_user_var_equals_int(join->thd,
|
||||||
|
"show_explain_probe_select_id",
|
||||||
|
join->select_lex->select_number))
|
||||||
|
dbug_serve_apcs(join->thd, 1);
|
||||||
|
);
|
||||||
|
|
||||||
if (join->outer_ref_cond && !join->outer_ref_cond->val_int())
|
if (join->outer_ref_cond && !join->outer_ref_cond->val_int())
|
||||||
error= NESTED_LOOP_NO_MORE_ROWS;
|
error= NESTED_LOOP_NO_MORE_ROWS;
|
||||||
else
|
else
|
||||||
@ -15928,7 +16065,7 @@ sub_select_cache(JOIN *join, JOIN_TAB *join_tab, bool end_of_records)
|
|||||||
rc= sub_select(join, join_tab, end_of_records);
|
rc= sub_select(join, join_tab, end_of_records);
|
||||||
DBUG_RETURN(rc);
|
DBUG_RETURN(rc);
|
||||||
}
|
}
|
||||||
if (join->thd->killed)
|
if (join->thd->check_killed())
|
||||||
{
|
{
|
||||||
/* The user has aborted the execution of the query */
|
/* The user has aborted the execution of the query */
|
||||||
join->thd->send_kill_message();
|
join->thd->send_kill_message();
|
||||||
@ -16215,7 +16352,7 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab,
|
|||||||
DBUG_RETURN(NESTED_LOOP_ERROR);
|
DBUG_RETURN(NESTED_LOOP_ERROR);
|
||||||
if (error < 0)
|
if (error < 0)
|
||||||
DBUG_RETURN(NESTED_LOOP_NO_MORE_ROWS);
|
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();
|
join->thd->send_kill_message();
|
||||||
DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
|
DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
|
||||||
@ -16960,6 +17097,14 @@ int read_first_record_seq(JOIN_TAB *tab)
|
|||||||
static int
|
static int
|
||||||
test_if_quick_select(JOIN_TAB *tab)
|
test_if_quick_select(JOIN_TAB *tab)
|
||||||
{
|
{
|
||||||
|
DBUG_EXECUTE_IF("show_explain_probe_test_if_quick_select",
|
||||||
|
if (dbug_user_var_equals_int(tab->join->thd,
|
||||||
|
"show_explain_probe_select_id",
|
||||||
|
tab->join->select_lex->select_number))
|
||||||
|
dbug_serve_apcs(tab->join->thd, 1);
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
delete tab->select->quick;
|
delete tab->select->quick;
|
||||||
tab->select->quick=0;
|
tab->select->quick=0;
|
||||||
return tab->select->test_quick_select(tab->join->thd, tab->keys,
|
return tab->select->test_quick_select(tab->join->thd, tab->keys,
|
||||||
@ -17438,7 +17583,7 @@ end_write(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
end:
|
end:
|
||||||
if (join->thd->killed)
|
if (join->thd->check_killed())
|
||||||
{
|
{
|
||||||
join->thd->send_kill_message();
|
join->thd->send_kill_message();
|
||||||
DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
|
DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
|
||||||
@ -17521,7 +17666,7 @@ end_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
|
|||||||
}
|
}
|
||||||
join->send_records++;
|
join->send_records++;
|
||||||
end:
|
end:
|
||||||
if (join->thd->killed)
|
if (join->thd->check_killed())
|
||||||
{
|
{
|
||||||
join->thd->send_kill_message();
|
join->thd->send_kill_message();
|
||||||
DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
|
DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
|
||||||
@ -17571,7 +17716,7 @@ end_unique_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
|
|||||||
DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */
|
DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (join->thd->killed)
|
if (join->thd->check_killed())
|
||||||
{
|
{
|
||||||
join->thd->send_kill_message();
|
join->thd->send_kill_message();
|
||||||
DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
|
DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
|
||||||
@ -17649,7 +17794,7 @@ end_write_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
|
|||||||
if (join->procedure)
|
if (join->procedure)
|
||||||
join->procedure->add();
|
join->procedure->add();
|
||||||
end:
|
end:
|
||||||
if (join->thd->killed)
|
if (join->thd->check_killed())
|
||||||
{
|
{
|
||||||
join->thd->send_kill_message();
|
join->thd->send_kill_message();
|
||||||
DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
|
DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
|
||||||
@ -18907,6 +19052,8 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order,
|
|||||||
TABLE *table;
|
TABLE *table;
|
||||||
SQL_SELECT *select;
|
SQL_SELECT *select;
|
||||||
JOIN_TAB *tab;
|
JOIN_TAB *tab;
|
||||||
|
int err= 0;
|
||||||
|
bool quick_created= FALSE;
|
||||||
DBUG_ENTER("create_sort_index");
|
DBUG_ENTER("create_sort_index");
|
||||||
|
|
||||||
if (join->table_count == join->const_tables)
|
if (join->table_count == join->const_tables)
|
||||||
@ -18915,17 +19062,46 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order,
|
|||||||
table= tab->table;
|
table= tab->table;
|
||||||
select= tab->select;
|
select= tab->select;
|
||||||
|
|
||||||
|
JOIN_TAB *save_pre_sort_join_tab= NULL;
|
||||||
|
if (join->pre_sort_join_tab)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
we've already been in this function, and stashed away the original access
|
||||||
|
method in join->pre_sort_join_tab, restore it now.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* First, restore state of the handler */
|
||||||
|
if (join->pre_sort_index != MAX_KEY)
|
||||||
|
{
|
||||||
|
if (table->file->ha_index_or_rnd_end())
|
||||||
|
goto err;
|
||||||
|
if (join->pre_sort_idx_pushed_cond)
|
||||||
|
{
|
||||||
|
table->file->idx_cond_push(join->pre_sort_index,
|
||||||
|
join->pre_sort_idx_pushed_cond);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (table->file->ha_index_or_rnd_end() ||
|
||||||
|
table->file->ha_rnd_init(TRUE))
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Second, restore access method parameters */
|
||||||
|
tab->records= join->pre_sort_join_tab->records;
|
||||||
|
tab->select= join->pre_sort_join_tab->select;
|
||||||
|
tab->select_cond= join->pre_sort_join_tab->select_cond;
|
||||||
|
tab->type= join->pre_sort_join_tab->type;
|
||||||
|
tab->read_first_record= join->pre_sort_join_tab->read_first_record;
|
||||||
|
|
||||||
|
save_pre_sort_join_tab= join->pre_sort_join_tab;
|
||||||
|
join->pre_sort_join_tab= NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/* Currently ORDER BY ... LIMIT is not supported in subqueries. */
|
/* Currently ORDER BY ... LIMIT is not supported in subqueries. */
|
||||||
DBUG_ASSERT(join->group_list || !join->is_in_subquery());
|
DBUG_ASSERT(join->group_list || !join->is_in_subquery());
|
||||||
|
|
||||||
/*
|
|
||||||
If we have a select->quick object that is created outside of
|
|
||||||
create_sort_index() and this is part of a subquery that
|
|
||||||
potentially can be executed multiple times then we should not
|
|
||||||
delete the quick object on exit from this function.
|
|
||||||
*/
|
|
||||||
bool keep_quick= select && select->quick && join->join_tab_save;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
When there is SQL_BIG_RESULT do not sort using index for GROUP BY,
|
When there is SQL_BIG_RESULT do not sort using index for GROUP BY,
|
||||||
and thus force sorting on disk unless a group min-max optimization
|
and thus force sorting on disk unless a group min-max optimization
|
||||||
@ -18977,7 +19153,7 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order,
|
|||||||
get_quick_select_for_ref(thd, table, &tab->ref,
|
get_quick_select_for_ref(thd, table, &tab->ref,
|
||||||
tab->found_records))))
|
tab->found_records))))
|
||||||
goto err;
|
goto err;
|
||||||
DBUG_ASSERT(!keep_quick);
|
quick_created= TRUE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -18995,6 +19171,37 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order,
|
|||||||
&examined_rows, &found_rows);
|
&examined_rows, &found_rows);
|
||||||
table->sort.found_records= filesort_retval;
|
table->sort.found_records= filesort_retval;
|
||||||
tab->records= found_rows; // For SQL_CALC_ROWS
|
tab->records= found_rows; // For SQL_CALC_ROWS
|
||||||
|
|
||||||
|
if (quick_created)
|
||||||
|
{
|
||||||
|
/* This will delete the quick select. */
|
||||||
|
select->cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!join->pre_sort_join_tab)
|
||||||
|
{
|
||||||
|
if (save_pre_sort_join_tab)
|
||||||
|
join->pre_sort_join_tab= save_pre_sort_join_tab;
|
||||||
|
else if (!(join->pre_sort_join_tab= (JOIN_TAB*)thd->alloc(sizeof(JOIN_TAB))))
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
*(join->pre_sort_join_tab)= *tab;
|
||||||
|
|
||||||
|
if (table->file->inited == handler::INDEX)
|
||||||
|
{
|
||||||
|
// Save index #, save index condition
|
||||||
|
join->pre_sort_index= table->file->active_index;
|
||||||
|
join->pre_sort_idx_pushed_cond= table->file->pushed_idx_cond;
|
||||||
|
// no need to save key_read?
|
||||||
|
err= table->file->ha_index_end();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
join->pre_sort_index= MAX_KEY;
|
||||||
|
|
||||||
|
/*TODO: here, close the index scan, cancel index-only read. */
|
||||||
|
#if 0
|
||||||
|
/* MariaDB doesn't need the following: */
|
||||||
if (select)
|
if (select)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
@ -19011,6 +19218,7 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order,
|
|||||||
tablesort_result_cache= table->sort.io_cache;
|
tablesort_result_cache= table->sort.io_cache;
|
||||||
table->sort.io_cache= NULL;
|
table->sort.io_cache= NULL;
|
||||||
|
|
||||||
|
// select->cleanup(); // filesort did select
|
||||||
/*
|
/*
|
||||||
If a quick object was created outside of create_sort_index()
|
If a quick object was created outside of create_sort_index()
|
||||||
that might be reused, then do not call select->cleanup() since
|
that might be reused, then do not call select->cleanup() since
|
||||||
@ -19033,18 +19241,60 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order,
|
|||||||
// Restore the output resultset
|
// Restore the output resultset
|
||||||
table->sort.io_cache= tablesort_result_cache;
|
table->sort.io_cache= tablesort_result_cache;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
tab->select=NULL;
|
||||||
tab->set_select_cond(NULL, __LINE__);
|
tab->set_select_cond(NULL, __LINE__);
|
||||||
tab->last_inner= 0;
|
// tab->last_inner= 0;
|
||||||
tab->first_unmatched= 0;
|
// tab->first_unmatched= 0;
|
||||||
tab->type=JT_ALL; // Read with normal read_record
|
tab->type=JT_ALL; // Read with normal read_record
|
||||||
tab->read_first_record= join_init_read_record;
|
tab->read_first_record= join_init_read_record;
|
||||||
|
tab->table->file->ha_index_or_rnd_end();
|
||||||
|
|
||||||
|
if (err)
|
||||||
|
goto err;
|
||||||
|
|
||||||
tab->join->examined_rows+=examined_rows;
|
tab->join->examined_rows+=examined_rows;
|
||||||
table->disable_keyread(); // Restore if we used indexes
|
|
||||||
DBUG_RETURN(filesort_retval == HA_POS_ERROR);
|
DBUG_RETURN(filesort_retval == HA_POS_ERROR);
|
||||||
err:
|
err:
|
||||||
DBUG_RETURN(-1);
|
DBUG_RETURN(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void JOIN::clean_pre_sort_join_tab()
|
||||||
|
{
|
||||||
|
//TABLE *table= pre_sort_join_tab->table;
|
||||||
|
/*
|
||||||
|
Note: we can come here for fake_select_lex object. That object will have
|
||||||
|
the table already deleted by st_select_lex_unit::cleanup().
|
||||||
|
We rely on that fake_select_lex didn't have quick select.
|
||||||
|
*/
|
||||||
|
#if 0
|
||||||
|
if (pre_sort_join_tab->select && pre_sort_join_tab->select->quick)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
We need to preserve tablesort's output resultset here, because
|
||||||
|
QUICK_INDEX_MERGE_SELECT::~QUICK_INDEX_MERGE_SELECT (called by
|
||||||
|
SQL_SELECT::cleanup()) may free it assuming it's the result of the quick
|
||||||
|
select operation that we no longer need. Note that all the other parts of
|
||||||
|
this data structure are cleaned up when
|
||||||
|
QUICK_INDEX_MERGE_SELECT::get_next encounters end of data, so the next
|
||||||
|
SQL_SELECT::cleanup() call changes sort.io_cache alone.
|
||||||
|
*/
|
||||||
|
IO_CACHE *tablesort_result_cache;
|
||||||
|
|
||||||
|
tablesort_result_cache= table->sort.io_cache;
|
||||||
|
table->sort.io_cache= NULL;
|
||||||
|
pre_sort_join_tab->select->cleanup();
|
||||||
|
table->quick_keys.clear_all(); // as far as we cleanup select->quick
|
||||||
|
table->intersect_keys.clear_all();
|
||||||
|
table->sort.io_cache= tablesort_result_cache;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
//table->disable_keyread(); // Restore if we used indexes
|
||||||
|
if (pre_sort_join_tab->select && pre_sort_join_tab->select->quick)
|
||||||
|
{
|
||||||
|
pre_sort_join_tab->select->cleanup();
|
||||||
|
}
|
||||||
|
}
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
Remove duplicates from tmp table
|
Remove duplicates from tmp table
|
||||||
This should be recoded to add a unique index to the table and remove
|
This should be recoded to add a unique index to the table and remove
|
||||||
@ -19163,7 +19413,7 @@ static int remove_dup_with_compare(THD *thd, TABLE *table, Field **first_field,
|
|||||||
error= file->ha_rnd_next(record);
|
error= file->ha_rnd_next(record);
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
if (thd->killed)
|
if (thd->check_killed())
|
||||||
{
|
{
|
||||||
thd->send_kill_message();
|
thd->send_kill_message();
|
||||||
error=0;
|
error=0;
|
||||||
@ -19295,7 +19545,7 @@ static int remove_dup_with_hash_index(THD *thd, TABLE *table,
|
|||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
uchar *org_key_pos;
|
uchar *org_key_pos;
|
||||||
if (thd->killed)
|
if (thd->check_killed())
|
||||||
{
|
{
|
||||||
thd->send_kill_message();
|
thd->send_kill_message();
|
||||||
error=0;
|
error=0;
|
||||||
@ -21317,53 +21567,50 @@ void JOIN::clear()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
EXPLAIN handling.
|
|
||||||
|
|
||||||
Send a description about what how the select will be done to stdout.
|
|
||||||
*/
|
|
||||||
|
|
||||||
static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
|
|
||||||
bool distinct,const char *message)
|
|
||||||
{
|
|
||||||
List<Item> field_list;
|
|
||||||
List<Item> item_list;
|
|
||||||
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");
|
|
||||||
DBUG_PRINT("info", ("Select 0x%lx, type %s, message %s",
|
|
||||||
(ulong)join->select_lex, join->select_lex->type,
|
|
||||||
message ? message : "NULL"));
|
|
||||||
/* 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;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
NOTE: the number/types of items pushed into item_list must be in sync with
|
Print an EXPLAIN line with all NULLs and given message in the 'Extra' column
|
||||||
EXPLAIN column types as they're "defined" in THD::send_explain_fields()
|
|
||||||
*/
|
*/
|
||||||
if (message)
|
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)
|
item_list.push_back(new Item_int((int32)
|
||||||
join->select_lex->select_number));
|
select_lex->select_number));
|
||||||
item_list.push_back(new Item_string(join->select_lex->type,
|
item_list.push_back(new Item_string(select_lex->type,
|
||||||
strlen(join->select_lex->type), cs));
|
strlen(select_lex->type), cs));
|
||||||
for (uint i=0 ; i < 7; i++)
|
for (uint i=0 ; i < 7; i++)
|
||||||
item_list.push_back(item_null);
|
item_list.push_back(item_null);
|
||||||
if (join->thd->lex->describe & DESCRIBE_PARTITIONS)
|
if (options & DESCRIBE_PARTITIONS)
|
||||||
item_list.push_back(item_null);
|
item_list.push_back(item_null);
|
||||||
if (join->thd->lex->describe & DESCRIBE_EXTENDED)
|
if (options & DESCRIBE_EXTENDED)
|
||||||
item_list.push_back(item_null);
|
item_list.push_back(item_null);
|
||||||
|
|
||||||
item_list.push_back(new Item_string(message,strlen(message),cs));
|
item_list.push_back(new Item_string(message,strlen(message),cs));
|
||||||
|
|
||||||
if (result->send_data(item_list))
|
if (result->send_data(item_list))
|
||||||
join->error= 1;
|
return 1;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
else if (join->select_lex == join->unit->fake_select_lex)
|
|
||||||
|
|
||||||
|
int print_fake_select_lex_join(select_result_sink *result, bool on_the_fly,
|
||||||
|
SELECT_LEX *select_lex, uint8 explain_flags)
|
||||||
{
|
{
|
||||||
|
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);
|
||||||
/*
|
/*
|
||||||
here we assume that the query will return at least two rows, so we
|
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
|
show "filesort" in EXPLAIN. Of course, sometimes we'll be wrong
|
||||||
@ -21376,12 +21623,12 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
|
|||||||
/* id */
|
/* id */
|
||||||
item_list.push_back(new Item_null);
|
item_list.push_back(new Item_null);
|
||||||
/* select_type */
|
/* select_type */
|
||||||
item_list.push_back(new Item_string(join->select_lex->type,
|
item_list.push_back(new Item_string(select_lex->type,
|
||||||
strlen(join->select_lex->type),
|
strlen(select_lex->type),
|
||||||
cs));
|
cs));
|
||||||
/* table */
|
/* table */
|
||||||
{
|
{
|
||||||
SELECT_LEX *sl= join->unit->first_select();
|
SELECT_LEX *sl= select_lex->master_unit()->first_select();
|
||||||
uint len= 6, lastop= 0;
|
uint len= 6, lastop= 0;
|
||||||
memcpy(table_name_buffer, STRING_WITH_LEN("<union"));
|
memcpy(table_name_buffer, STRING_WITH_LEN("<union"));
|
||||||
for (; sl && len + lastop + 5 < NAME_LEN; sl= sl->next_select())
|
for (; sl && len + lastop + 5 < NAME_LEN; sl= sl->next_select())
|
||||||
@ -21403,7 +21650,7 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
|
|||||||
item_list.push_back(new Item_string(table_name_buffer, len, cs));
|
item_list.push_back(new Item_string(table_name_buffer, len, cs));
|
||||||
}
|
}
|
||||||
/* partitions */
|
/* partitions */
|
||||||
if (join->thd->lex->describe & DESCRIBE_PARTITIONS)
|
if (explain_flags & DESCRIBE_PARTITIONS)
|
||||||
item_list.push_back(item_null);
|
item_list.push_back(item_null);
|
||||||
/* type */
|
/* type */
|
||||||
item_list.push_back(new Item_string(join_type_str[JT_ALL],
|
item_list.push_back(new Item_string(join_type_str[JT_ALL],
|
||||||
@ -21418,27 +21665,87 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
|
|||||||
/* ref */
|
/* ref */
|
||||||
item_list.push_back(item_null);
|
item_list.push_back(item_null);
|
||||||
/* in_rows */
|
/* in_rows */
|
||||||
if (join->thd->lex->describe & DESCRIBE_EXTENDED)
|
if (explain_flags & DESCRIBE_EXTENDED)
|
||||||
item_list.push_back(item_null);
|
item_list.push_back(item_null);
|
||||||
/* rows */
|
/* rows */
|
||||||
item_list.push_back(item_null);
|
item_list.push_back(item_null);
|
||||||
/* extra */
|
/* extra */
|
||||||
if (join->unit->global_parameters->order_list.first)
|
if (select_lex->master_unit()->global_parameters->order_list.first)
|
||||||
item_list.push_back(new Item_string("Using filesort",
|
item_list.push_back(new Item_string("Using filesort",
|
||||||
14, cs));
|
14, cs));
|
||||||
else
|
else
|
||||||
item_list.push_back(new Item_string("", 0, cs));
|
item_list.push_back(new Item_string("", 0, cs));
|
||||||
|
|
||||||
if (result->send_data(item_list))
|
if (result->send_data(item_list))
|
||||||
join->error= 1;
|
return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
EXPLAIN handling.
|
||||||
|
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
Item *item_null= new Item_null();
|
||||||
|
CHARSET_INFO *cs= system_charset_info;
|
||||||
|
int quick_type;
|
||||||
|
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(on_the_fly? have_query_plan == QEP_AVAILABLE: TRUE);
|
||||||
|
/* Don't log this into the slow query log */
|
||||||
|
|
||||||
|
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
|
||||||
|
EXPLAIN column types as they're "defined" in THD::send_explain_fields()
|
||||||
|
*/
|
||||||
|
if (message)
|
||||||
|
{
|
||||||
|
if (print_explain_message_line(result, join->select_lex, on_the_fly,
|
||||||
|
explain_flags, message))
|
||||||
|
error= 1;
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (join->select_lex == join->unit->fake_select_lex)
|
||||||
|
{
|
||||||
|
if (print_fake_select_lex_join(result, on_the_fly,
|
||||||
|
join->select_lex,
|
||||||
|
explain_flags))
|
||||||
|
error= 1;
|
||||||
}
|
}
|
||||||
else if (!join->select_lex->master_unit()->derived ||
|
else if (!join->select_lex->master_unit()->derived ||
|
||||||
join->select_lex->master_unit()->derived->is_materialized_derived())
|
join->select_lex->master_unit()->derived->is_materialized_derived())
|
||||||
{
|
{
|
||||||
table_map used_tables=0;
|
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;
|
bool printing_materialize_nest= FALSE;
|
||||||
uint select_id= join->select_lex->select_number;
|
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, WALK_OPTIMIZATION_TABS); tab;
|
for (JOIN_TAB *tab= first_breadth_first_tab(join, WALK_OPTIMIZATION_TABS); tab;
|
||||||
tab= next_breadth_first_tab(join, WALK_OPTIMIZATION_TABS, tab))
|
tab= next_breadth_first_tab(join, WALK_OPTIMIZATION_TABS, tab))
|
||||||
@ -21473,6 +21780,8 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
|
|||||||
tmp3.length(0);
|
tmp3.length(0);
|
||||||
tmp4.length(0);
|
tmp4.length(0);
|
||||||
quick_type= -1;
|
quick_type= -1;
|
||||||
|
QUICK_SELECT_I *quick= NULL;
|
||||||
|
JOIN_TAB *saved_join_tab= NULL;
|
||||||
|
|
||||||
/* Don't show eliminated tables */
|
/* Don't show eliminated tables */
|
||||||
if (table->map & join->eliminated_tables)
|
if (table->map & join->eliminated_tables)
|
||||||
@ -21481,6 +21790,13 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (join->table_access_tabs == join->join_tab &&
|
||||||
|
tab == (first_top_tab + join->const_tables) && pre_sort_join_tab)
|
||||||
|
{
|
||||||
|
saved_join_tab= tab;
|
||||||
|
tab= pre_sort_join_tab;
|
||||||
|
}
|
||||||
|
|
||||||
item_list.empty();
|
item_list.empty();
|
||||||
/* id */
|
/* id */
|
||||||
item_list.push_back(new Item_uint((uint32)select_id));
|
item_list.push_back(new Item_uint((uint32)select_id));
|
||||||
@ -21489,17 +21805,19 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
|
|||||||
join->select_lex->type;
|
join->select_lex->type;
|
||||||
item_list.push_back(new Item_string(stype, strlen(stype), cs));
|
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) &&
|
if ((tab->type == JT_ALL || tab->type == JT_HASH) &&
|
||||||
tab->select && tab->select->quick)
|
tab->select && tab->select->quick && tab->use_quick != 2)
|
||||||
{
|
{
|
||||||
|
quick= tab->select->quick;
|
||||||
quick_type= tab->select->quick->get_type();
|
quick_type= tab->select->quick->get_type();
|
||||||
if ((quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_MERGE) ||
|
if ((quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_MERGE) ||
|
||||||
(quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_INTERSECT) ||
|
(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_INTERSECT) ||
|
||||||
(quick_type == QUICK_SELECT_I::QS_TYPE_ROR_UNION))
|
(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
|
else
|
||||||
tab->type= tab->type == JT_ALL ? JT_RANGE : JT_HASH_RANGE;
|
tab_type= tab->type == JT_ALL ? JT_RANGE : JT_HASH_RANGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* table */
|
/* table */
|
||||||
@ -21528,7 +21846,7 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
|
|||||||
strlen(real_table->alias), cs));
|
strlen(real_table->alias), cs));
|
||||||
}
|
}
|
||||||
/* "partitions" column */
|
/* "partitions" column */
|
||||||
if (join->thd->lex->describe & DESCRIBE_PARTITIONS)
|
if (explain_flags & DESCRIBE_PARTITIONS)
|
||||||
{
|
{
|
||||||
#ifdef WITH_PARTITION_STORAGE_ENGINE
|
#ifdef WITH_PARTITION_STORAGE_ENGINE
|
||||||
partition_info *part_info;
|
partition_info *part_info;
|
||||||
@ -21547,8 +21865,8 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
/* "type" column */
|
/* "type" column */
|
||||||
item_list.push_back(new Item_string(join_type_str[tab->type],
|
item_list.push_back(new Item_string(join_type_str[tab_type],
|
||||||
strlen(join_type_str[tab->type]),
|
strlen(join_type_str[tab_type]),
|
||||||
cs));
|
cs));
|
||||||
/* Build "possible_keys" value and add it to item_list */
|
/* Build "possible_keys" value and add it to item_list */
|
||||||
if (!tab->keys.is_clear_all())
|
if (!tab->keys.is_clear_all())
|
||||||
@ -21572,7 +21890,7 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
|
|||||||
item_list.push_back(item_null);
|
item_list.push_back(item_null);
|
||||||
|
|
||||||
/* Build "key", "key_len", and "ref" values and add them to item_list */
|
/* 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_info= table->key_info+tab->index;
|
||||||
key_len= key_info->key_length;
|
key_len= key_info->key_length;
|
||||||
@ -21591,22 +21909,30 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
|
|||||||
length= (longlong10_to_str(key_len, keylen_str_buf, 10) -
|
length= (longlong10_to_str(key_len, keylen_str_buf, 10) -
|
||||||
keylen_str_buf);
|
keylen_str_buf);
|
||||||
tmp3.append(keylen_str_buf, length, cs);
|
tmp3.append(keylen_str_buf, length, cs);
|
||||||
if (tab->ref.key_parts)
|
if (tab->ref.key_parts && tab_type != JT_FT)
|
||||||
{
|
{
|
||||||
for (store_key **ref=tab->ref.key_copy ; *ref ; ref++)
|
store_key **ref=tab->ref.key_copy;
|
||||||
|
for (uint kp= 0; kp < tab->ref.key_parts; kp++)
|
||||||
{
|
{
|
||||||
if (tmp4.length())
|
if (tmp4.length())
|
||||||
tmp4.append(',');
|
tmp4.append(',');
|
||||||
|
|
||||||
|
if ((key_part_map(1) << kp) & tab->ref.const_ref_part_map)
|
||||||
|
tmp4.append("const");
|
||||||
|
else
|
||||||
|
{
|
||||||
tmp4.append((*ref)->name(), strlen((*ref)->name()), cs);
|
tmp4.append((*ref)->name(), strlen((*ref)->name()), cs);
|
||||||
|
ref++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (is_hj && tab->type != JT_HASH)
|
}
|
||||||
|
if (is_hj && tab_type != JT_HASH)
|
||||||
{
|
{
|
||||||
tmp2.append(':');
|
tmp2.append(':');
|
||||||
tmp3.append(':');
|
tmp3.append(':');
|
||||||
}
|
}
|
||||||
if (tab->type == JT_HASH_NEXT)
|
if (tab_type == JT_HASH_NEXT)
|
||||||
{
|
{
|
||||||
register uint length;
|
register uint length;
|
||||||
key_info= table->key_info+tab->index;
|
key_info= table->key_info+tab->index;
|
||||||
@ -21616,9 +21942,9 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
|
|||||||
keylen_str_buf);
|
keylen_str_buf);
|
||||||
tmp3.append(keylen_str_buf, length, cs);
|
tmp3.append(keylen_str_buf, length, cs);
|
||||||
}
|
}
|
||||||
if (tab->type != JT_CONST && tab->select && tab->select->quick)
|
if (tab->type != JT_CONST && tab->select && quick)
|
||||||
tab->select->quick->add_keys_and_lengths(&tmp2, &tmp3);
|
tab->select->quick->add_keys_and_lengths(&tmp2, &tmp3);
|
||||||
if (key_info || (tab->select && tab->select->quick))
|
if (key_info || (tab->select && quick))
|
||||||
{
|
{
|
||||||
if (tmp2.length())
|
if (tmp2.length())
|
||||||
item_list.push_back(new Item_string(tmp2.ptr(),tmp2.length(),cs));
|
item_list.push_back(new Item_string(tmp2.ptr(),tmp2.length(),cs));
|
||||||
@ -21628,7 +21954,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));
|
item_list.push_back(new Item_string(tmp3.ptr(),tmp3.length(),cs));
|
||||||
else
|
else
|
||||||
item_list.push_back(item_null);
|
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));
|
item_list.push_back(new Item_string(tmp4.ptr(),tmp4.length(),cs));
|
||||||
else
|
else
|
||||||
item_list.push_back(item_null);
|
item_list.push_back(item_null);
|
||||||
@ -21671,7 +21997,7 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
|
|||||||
table_list->schema_table)
|
table_list->schema_table)
|
||||||
{
|
{
|
||||||
/* in_rows */
|
/* in_rows */
|
||||||
if (join->thd->lex->describe & DESCRIBE_EXTENDED)
|
if (explain_flags & DESCRIBE_EXTENDED)
|
||||||
item_list.push_back(item_null);
|
item_list.push_back(item_null);
|
||||||
/* rows */
|
/* rows */
|
||||||
item_list.push_back(item_null);
|
item_list.push_back(item_null);
|
||||||
@ -21684,7 +22010,7 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
|
|||||||
MY_INT64_NUM_DECIMAL_DIGITS));
|
MY_INT64_NUM_DECIMAL_DIGITS));
|
||||||
|
|
||||||
/* Add "filtered" field to item_list. */
|
/* Add "filtered" field to item_list. */
|
||||||
if (join->thd->lex->describe & DESCRIBE_EXTENDED)
|
if (explain_flags & DESCRIBE_EXTENDED)
|
||||||
{
|
{
|
||||||
float f= 0.0;
|
float f= 0.0;
|
||||||
if (examined_rows)
|
if (examined_rows)
|
||||||
@ -21696,11 +22022,11 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
|
|||||||
|
|
||||||
/* Build "Extra" field and add it to item_list. */
|
/* Build "Extra" field and add it to item_list. */
|
||||||
key_read=table->key_read;
|
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))
|
table->covering_keys.is_set(tab->index))
|
||||||
key_read=1;
|
key_read=1;
|
||||||
if (quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT &&
|
if (quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT &&
|
||||||
!((QUICK_ROR_INTERSECT_SELECT*)tab->select->quick)->need_to_fetch_row)
|
!((QUICK_ROR_INTERSECT_SELECT*)quick)->need_to_fetch_row)
|
||||||
key_read=1;
|
key_read=1;
|
||||||
|
|
||||||
if (tab->info)
|
if (tab->info)
|
||||||
@ -21728,8 +22054,8 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
|
|||||||
uint keyno= MAX_KEY;
|
uint keyno= MAX_KEY;
|
||||||
if (tab->ref.key_parts)
|
if (tab->ref.key_parts)
|
||||||
keyno= tab->ref.key;
|
keyno= tab->ref.key;
|
||||||
else if (tab->select && tab->select->quick)
|
else if (tab->select && quick)
|
||||||
keyno = tab->select->quick->index;
|
keyno = quick->index;
|
||||||
|
|
||||||
if (keyno != MAX_KEY && keyno == table->file->pushed_idx_cond_keyno &&
|
if (keyno != MAX_KEY && keyno == table->file->pushed_idx_cond_keyno &&
|
||||||
table->file->pushed_idx_cond)
|
table->file->pushed_idx_cond)
|
||||||
@ -21768,7 +22094,7 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
|
|||||||
{
|
{
|
||||||
extra.append(STRING_WITH_LEN("; Using where with pushed "
|
extra.append(STRING_WITH_LEN("; Using where with pushed "
|
||||||
"condition"));
|
"condition"));
|
||||||
if (thd->lex->describe & DESCRIBE_EXTENDED)
|
if (explain_flags & DESCRIBE_EXTENDED)
|
||||||
{
|
{
|
||||||
extra.append(STRING_WITH_LEN(": "));
|
extra.append(STRING_WITH_LEN(": "));
|
||||||
((COND *)pushed_cond)->print(&extra, QT_ORDINARY);
|
((COND *)pushed_cond)->print(&extra, QT_ORDINARY);
|
||||||
@ -21861,7 +22187,7 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
|
|||||||
extra.append(STRING_WITH_LEN("; End temporary"));
|
extra.append(STRING_WITH_LEN("; End temporary"));
|
||||||
else if (tab->do_firstmatch)
|
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"));
|
extra.append(STRING_WITH_LEN("; FirstMatch"));
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -21908,12 +22234,34 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
|
|||||||
item_list.push_back(new Item_string(str, len, cs));
|
item_list.push_back(new Item_string(str, len, cs));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (saved_join_tab)
|
||||||
|
tab= saved_join_tab;
|
||||||
|
|
||||||
// For next iteration
|
// For next iteration
|
||||||
used_tables|=table->map;
|
used_tables|=table->map;
|
||||||
if (result->send_data(item_list))
|
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, thd->lex->describe,
|
||||||
|
FALSE, /* Not on-the-fly */
|
||||||
|
need_tmp_table, need_order, distinct,
|
||||||
|
message);
|
||||||
|
|
||||||
for (SELECT_LEX_UNIT *unit= join->select_lex->first_inner_unit();
|
for (SELECT_LEX_UNIT *unit= join->select_lex->first_inner_unit();
|
||||||
unit;
|
unit;
|
||||||
unit= unit->next_unit())
|
unit= unit->next_unit())
|
||||||
@ -21956,7 +22304,7 @@ bool mysql_explain_union(THD *thd, SELECT_LEX_UNIT *unit, select_result *result)
|
|||||||
|
|
||||||
for (SELECT_LEX *sl= first; sl; sl= sl->next_select())
|
for (SELECT_LEX *sl= first; sl; sl= sl->next_select())
|
||||||
{
|
{
|
||||||
sl->set_explain_type();
|
sl->set_explain_type(FALSE);
|
||||||
sl->options|= SELECT_DESCRIBE;
|
sl->options|= SELECT_DESCRIBE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,6 +101,13 @@ typedef struct st_table_ref
|
|||||||
uchar *key_buff; ///< value to look for with key
|
uchar *key_buff; ///< value to look for with key
|
||||||
uchar *key_buff2; ///< key_buff+key_length
|
uchar *key_buff2; ///< key_buff+key_length
|
||||||
store_key **key_copy; //
|
store_key **key_copy; //
|
||||||
|
|
||||||
|
/*
|
||||||
|
Bitmap of key parts which refer to constants. key_copy only has copiers for
|
||||||
|
non-const key parts.
|
||||||
|
*/
|
||||||
|
key_part_map const_ref_part_map;
|
||||||
|
|
||||||
Item **items; ///< val()'s for each keypart
|
Item **items; ///< val()'s for each keypart
|
||||||
/*
|
/*
|
||||||
Array of pointers to trigger variables. Some/all of the pointers may be
|
Array of pointers to trigger variables. Some/all of the pointers may be
|
||||||
@ -897,7 +904,6 @@ protected:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
JOIN_TAB *join_tab, **best_ref;
|
JOIN_TAB *join_tab, **best_ref;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
For "Using temporary+Using filesort" queries, JOIN::join_tab can point to
|
For "Using temporary+Using filesort" queries, JOIN::join_tab can point to
|
||||||
either:
|
either:
|
||||||
@ -910,6 +916,15 @@ public:
|
|||||||
JOIN_TAB *table_access_tabs;
|
JOIN_TAB *table_access_tabs;
|
||||||
uint top_table_access_tabs_count;
|
uint top_table_access_tabs_count;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Saved join_tab for pre_sorting. create_sort_index() will save here..
|
||||||
|
*/
|
||||||
|
JOIN_TAB *pre_sort_join_tab;
|
||||||
|
uint pre_sort_index;
|
||||||
|
Item *pre_sort_idx_pushed_cond;
|
||||||
|
void clean_pre_sort_join_tab();
|
||||||
|
|
||||||
|
|
||||||
JOIN_TAB **map2table; ///< mapping between table indexes and JOIN_TABs
|
JOIN_TAB **map2table; ///< mapping between table indexes and JOIN_TABs
|
||||||
JOIN_TAB *join_tab_save; ///< saved join_tab for subquery reexecution
|
JOIN_TAB *join_tab_save; ///< saved join_tab for subquery reexecution
|
||||||
|
|
||||||
@ -1175,9 +1190,15 @@ public:
|
|||||||
const char *zero_result_cause; ///< not 0 if exec must return zero result
|
const char *zero_result_cause; ///< not 0 if exec must return zero result
|
||||||
|
|
||||||
bool union_part; ///< this subselect is part of union
|
bool union_part; ///< this subselect is part of union
|
||||||
|
|
||||||
|
enum join_optimization_state { NOT_OPTIMIZED=0,
|
||||||
|
OPTIMIZATION_IN_PROGRESS=1,
|
||||||
|
OPTIMIZATION_DONE=2};
|
||||||
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
|
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
|
Additional WHERE and HAVING predicates to be considered for IN=>EXISTS
|
||||||
subquery transformation of a JOIN object.
|
subquery transformation of a JOIN object.
|
||||||
@ -1259,6 +1280,7 @@ public:
|
|||||||
ref_pointer_array_size= 0;
|
ref_pointer_array_size= 0;
|
||||||
zero_result_cause= 0;
|
zero_result_cause= 0;
|
||||||
optimized= 0;
|
optimized= 0;
|
||||||
|
have_query_plan= QEP_NOT_PRESENT_YET;
|
||||||
initialized= 0;
|
initialized= 0;
|
||||||
cond_equal= 0;
|
cond_equal= 0;
|
||||||
having_equal= 0;
|
having_equal= 0;
|
||||||
@ -1279,6 +1301,8 @@ public:
|
|||||||
outer_ref_cond= pseudo_bits_cond= NULL;
|
outer_ref_cond= pseudo_bits_cond= NULL;
|
||||||
in_to_exists_where= NULL;
|
in_to_exists_where= NULL;
|
||||||
in_to_exists_having= NULL;
|
in_to_exists_having= NULL;
|
||||||
|
|
||||||
|
pre_sort_join_tab= NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int prepare(Item ***rref_pointer_array, TABLE_LIST *tables, uint wind_num,
|
int prepare(Item ***rref_pointer_array, TABLE_LIST *tables, uint wind_num,
|
||||||
@ -1287,9 +1311,11 @@ public:
|
|||||||
SELECT_LEX_UNIT *unit);
|
SELECT_LEX_UNIT *unit);
|
||||||
bool prepare_stage2();
|
bool prepare_stage2();
|
||||||
int optimize();
|
int optimize();
|
||||||
|
int optimize_inner();
|
||||||
int reinit();
|
int reinit();
|
||||||
int init_execution();
|
int init_execution();
|
||||||
void exec();
|
void exec();
|
||||||
|
void exec_inner();
|
||||||
int destroy();
|
int destroy();
|
||||||
void restore_tmp();
|
void restore_tmp();
|
||||||
bool alloc_func_list();
|
bool alloc_func_list();
|
||||||
@ -1403,6 +1429,11 @@ public:
|
|||||||
{
|
{
|
||||||
return (unit->item && unit->item->is_in_predicate());
|
return (unit->item && unit->item->is_in_predicate());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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:
|
private:
|
||||||
/**
|
/**
|
||||||
TRUE if the query contains an aggregate function but has no GROUP
|
TRUE if the query contains an aggregate function but has no GROUP
|
||||||
@ -1754,6 +1785,9 @@ inline bool optimizer_flag(THD *thd, uint flag)
|
|||||||
return (thd->variables.optimizer_switch & 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,
|
uint get_index_for_order(ORDER *order, TABLE *table, SQL_SELECT *select,
|
||||||
ha_rows limit, bool *need_sort, bool *reverse);
|
ha_rows limit, bool *need_sort, bool *reverse);
|
||||||
ORDER *simple_remove_const(ORDER *order, COND *where);
|
ORDER *simple_remove_const(ORDER *order, COND *where);
|
||||||
|
188
sql/sql_show.cc
188
sql/sql_show.cc
@ -2003,6 +2003,166 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose)
|
|||||||
DBUG_VOID_RETURN;
|
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::call_in_target_thread()
|
||||||
|
{
|
||||||
|
Query_arena backup_arena;
|
||||||
|
bool printed_anything= FALSE;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Change the arena because JOIN::print_explain and co. are going to allocate
|
||||||
|
items. Let them allocate them on our arena.
|
||||||
|
*/
|
||||||
|
target_thd->set_n_backup_active_arena((Query_arena*)request_thd,
|
||||||
|
&backup_arena);
|
||||||
|
|
||||||
|
query_str.copy(target_thd->query(),
|
||||||
|
target_thd->query_length(),
|
||||||
|
target_thd->query_charset());
|
||||||
|
|
||||||
|
if (target_thd->lex->unit.print_explain(explain_buf, 0 /* explain flags*/,
|
||||||
|
&printed_anything))
|
||||||
|
{
|
||||||
|
failed_to_produce= TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!printed_anything)
|
||||||
|
failed_to_produce= TRUE;
|
||||||
|
|
||||||
|
target_thd->restore_active_arena((Query_arena*)request_thd, &backup_arena);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int select_result_explain_buffer::send_data(List<Item> &items)
|
||||||
|
{
|
||||||
|
fill_record(thd, dst_table->field, items, TRUE, FALSE);
|
||||||
|
if ((dst_table->file->ha_write_tmp_row(dst_table->record[0])))
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Store the SHOW EXPLAIN output in the temporary table.
|
||||||
|
*/
|
||||||
|
|
||||||
|
int fill_show_explain(THD *thd, TABLE_LIST *table, COND *cond)
|
||||||
|
{
|
||||||
|
const char *calling_user;
|
||||||
|
THD *tmp;
|
||||||
|
my_thread_id thread_id;
|
||||||
|
DBUG_ENTER("fill_show_explain");
|
||||||
|
|
||||||
|
DBUG_ASSERT(cond==NULL);
|
||||||
|
thread_id= thd->lex->value_list.head()->val_int();
|
||||||
|
calling_user= (thd->security_ctx->master_access & PROCESS_ACL) ? NullS :
|
||||||
|
thd->security_ctx->priv_user;
|
||||||
|
|
||||||
|
if ((tmp= find_thread_by_id(thread_id)))
|
||||||
|
{
|
||||||
|
Security_context *tmp_sctx= tmp->security_ctx;
|
||||||
|
/*
|
||||||
|
If calling_user==NULL, calling thread has SUPER or PROCESS
|
||||||
|
privilege, and so can do SHOW EXPLAIN on any user.
|
||||||
|
|
||||||
|
if calling_user!=NULL, he's only allowed to view SHOW EXPLAIN on
|
||||||
|
his own threads.
|
||||||
|
*/
|
||||||
|
if (calling_user && (!tmp_sctx->user || strcmp(calling_user,
|
||||||
|
tmp_sctx->user)))
|
||||||
|
{
|
||||||
|
my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "PROCESS");
|
||||||
|
mysql_mutex_unlock(&tmp->LOCK_thd_data);
|
||||||
|
DBUG_RETURN(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tmp == thd)
|
||||||
|
{
|
||||||
|
mysql_mutex_unlock(&tmp->LOCK_thd_data);
|
||||||
|
my_error(ER_TARGET_NOT_EXPLAINABLE, MYF(0));
|
||||||
|
DBUG_RETURN(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 a SHOW EXPLAIN request.
|
||||||
|
*/
|
||||||
|
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(thd, table->table);
|
||||||
|
|
||||||
|
explain_req.explain_buf= explain_buf;
|
||||||
|
explain_req.target_thd= tmp;
|
||||||
|
explain_req.request_thd= thd;
|
||||||
|
explain_req.failed_to_produce= FALSE;
|
||||||
|
|
||||||
|
/* Ok, we have a lock on target->LOCK_thd_data, can call: */
|
||||||
|
bres= tmp->apc_target.make_apc_call(thd, &explain_req, timeout_sec, &timed_out);
|
||||||
|
|
||||||
|
if (bres || explain_req.failed_to_produce)
|
||||||
|
{
|
||||||
|
if (thd->killed)
|
||||||
|
thd->send_kill_message();
|
||||||
|
else if (timed_out)
|
||||||
|
my_error(ER_LOCK_WAIT_TIMEOUT, MYF(0));
|
||||||
|
else
|
||||||
|
my_error(ER_TARGET_NOT_EXPLAINABLE, MYF(0));
|
||||||
|
|
||||||
|
bres= TRUE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
Push the query string as a warning. The query may be in a different
|
||||||
|
charset than the charset that's used for error messages, so, convert it
|
||||||
|
if needed.
|
||||||
|
*/
|
||||||
|
CHARSET_INFO *fromcs= explain_req.query_str.charset();
|
||||||
|
CHARSET_INFO *tocs= error_message_charset_info;
|
||||||
|
char *warning_text;
|
||||||
|
if (!my_charset_same(fromcs, tocs))
|
||||||
|
{
|
||||||
|
uint conv_length= 1 + tocs->mbmaxlen * explain_req.query_str.length() /
|
||||||
|
fromcs->mbminlen;
|
||||||
|
uint dummy_errors;
|
||||||
|
char *to, *p;
|
||||||
|
if (!(to= (char*)thd->alloc(conv_length + 1)))
|
||||||
|
DBUG_RETURN(1);
|
||||||
|
p= to;
|
||||||
|
p+= copy_and_convert(to, conv_length, tocs,
|
||||||
|
explain_req.query_str.c_ptr(),
|
||||||
|
explain_req.query_str.length(), fromcs,
|
||||||
|
&dummy_errors);
|
||||||
|
*p= 0;
|
||||||
|
warning_text= to;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
warning_text= explain_req.query_str.c_ptr_safe();
|
||||||
|
|
||||||
|
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
|
||||||
|
ER_YES, warning_text);
|
||||||
|
}
|
||||||
|
DBUG_RETURN(bres);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
my_error(ER_NO_SUCH_THREAD, MYF(0), thread_id);
|
||||||
|
DBUG_RETURN(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond)
|
int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond)
|
||||||
{
|
{
|
||||||
TABLE *table= tables->table;
|
TABLE *table= tables->table;
|
||||||
@ -8333,6 +8493,32 @@ ST_FIELD_INFO keycache_fields_info[]=
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
ST_FIELD_INFO show_explain_fields_info[]=
|
||||||
|
{
|
||||||
|
/* field_name, length, type, value, field_flags, old_name*/
|
||||||
|
{"id", 3, MYSQL_TYPE_LONGLONG, 0 /*value*/, MY_I_S_MAYBE_NULL, "id",
|
||||||
|
SKIP_OPEN_TABLE},
|
||||||
|
{"select_type", 19, MYSQL_TYPE_STRING, 0 /*value*/, 0, "select_type",
|
||||||
|
SKIP_OPEN_TABLE},
|
||||||
|
{"table", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0 /*value*/, MY_I_S_MAYBE_NULL,
|
||||||
|
"table", SKIP_OPEN_TABLE},
|
||||||
|
{"type", 15, MYSQL_TYPE_STRING, 0, MY_I_S_MAYBE_NULL, "type", SKIP_OPEN_TABLE},
|
||||||
|
{"possible_keys", NAME_CHAR_LEN*MAX_KEY, MYSQL_TYPE_STRING, 0/*value*/,
|
||||||
|
MY_I_S_MAYBE_NULL, "possible_keys", SKIP_OPEN_TABLE},
|
||||||
|
{"key", NAME_CHAR_LEN*MAX_KEY, MYSQL_TYPE_STRING, 0/*value*/,
|
||||||
|
MY_I_S_MAYBE_NULL, "key", SKIP_OPEN_TABLE},
|
||||||
|
{"key_len", NAME_CHAR_LEN*MAX_KEY, MYSQL_TYPE_STRING, 0/*value*/,
|
||||||
|
MY_I_S_MAYBE_NULL, "key_len", SKIP_OPEN_TABLE},
|
||||||
|
{"ref", NAME_CHAR_LEN*MAX_REF_PARTS, MYSQL_TYPE_STRING, 0/*value*/,
|
||||||
|
MY_I_S_MAYBE_NULL, "ref", SKIP_OPEN_TABLE},
|
||||||
|
{"rows", 10, MYSQL_TYPE_LONGLONG, 0/*value*/, MY_I_S_MAYBE_NULL, "rows",
|
||||||
|
SKIP_OPEN_TABLE},
|
||||||
|
{"Extra", 255, MYSQL_TYPE_STRING, 0/*value*/, 0 /*flags*/, "Extra",
|
||||||
|
SKIP_OPEN_TABLE},
|
||||||
|
{0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Description of ST_FIELD_INFO in table.h
|
Description of ST_FIELD_INFO in table.h
|
||||||
|
|
||||||
@ -8364,6 +8550,8 @@ ST_SCHEMA_TABLE schema_tables[]=
|
|||||||
{"EVENTS", events_fields_info, create_schema_table,
|
{"EVENTS", events_fields_info, create_schema_table,
|
||||||
0, make_old_format, 0, -1, -1, 0, 0},
|
0, make_old_format, 0, -1, -1, 0, 0},
|
||||||
#endif
|
#endif
|
||||||
|
{"EXPLAIN", show_explain_fields_info, create_schema_table, fill_show_explain,
|
||||||
|
make_old_format, 0, -1, -1, TRUE /*hidden*/ , 0},
|
||||||
{"FILES", files_fields_info, create_schema_table,
|
{"FILES", files_fields_info, create_schema_table,
|
||||||
hton_fill_schema_table, 0, 0, -1, -1, 0, 0},
|
hton_fill_schema_table, 0, 0, -1, -1, 0, 0},
|
||||||
{"GLOBAL_STATUS", variables_fields_info, create_schema_table,
|
{"GLOBAL_STATUS", variables_fields_info, create_schema_table,
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#include "sql_list.h" /* List */
|
#include "sql_list.h" /* List */
|
||||||
#include "handler.h" /* enum_schema_tables */
|
#include "handler.h" /* enum_schema_tables */
|
||||||
#include "table.h" /* enum_schema_table_state */
|
#include "table.h" /* enum_schema_table_state */
|
||||||
|
#include "my_apc.h"
|
||||||
|
|
||||||
/* Forward declarations */
|
/* Forward declarations */
|
||||||
class JOIN;
|
class JOIN;
|
||||||
@ -131,5 +132,30 @@ enum enum_schema_tables get_schema_table_idx(ST_SCHEMA_TABLE *schema_table);
|
|||||||
|
|
||||||
/* These functions were under INNODB_COMPATIBILITY_HOOKS */
|
/* These functions were under INNODB_COMPATIBILITY_HOOKS */
|
||||||
int get_quote_char_for_identifier(THD *thd, const char *name, uint length);
|
int get_quote_char_for_identifier(THD *thd, const char *name, uint length);
|
||||||
|
THD *find_thread_by_id(ulong id);
|
||||||
|
|
||||||
|
class select_result_explain_buffer;
|
||||||
|
/*
|
||||||
|
SHOW EXPLAIN request object.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class Show_explain_request : public Apc_target::Apc_call
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
THD *target_thd; /* thd that we're running SHOW EXPLAIN for */
|
||||||
|
THD *request_thd; /* thd that run SHOW EXPLAIN command */
|
||||||
|
|
||||||
|
/* If true, there was some error when producing EXPLAIN output. */
|
||||||
|
bool failed_to_produce;
|
||||||
|
|
||||||
|
/* SHOW EXPLAIN will be stored here */
|
||||||
|
select_result_explain_buffer *explain_buf;
|
||||||
|
|
||||||
|
/* Query that we've got SHOW EXPLAIN for */
|
||||||
|
String query_str;
|
||||||
|
|
||||||
|
/* Overloaded virtual function */
|
||||||
|
void call_in_target_thread();
|
||||||
|
};
|
||||||
|
|
||||||
#endif /* SQL_SHOW_H */
|
#endif /* SQL_SHOW_H */
|
||||||
|
@ -722,6 +722,8 @@ bool st_select_lex_unit::exec()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DBUG_EXECUTE_IF("show_explain_probe_union_read",
|
||||||
|
dbug_serve_apcs(thd, 1););
|
||||||
/* Send result to 'result' */
|
/* Send result to 'result' */
|
||||||
saved_error= TRUE;
|
saved_error= TRUE;
|
||||||
{
|
{
|
||||||
|
@ -11692,6 +11692,14 @@ show_param:
|
|||||||
Lex->spname= $3;
|
Lex->spname= $3;
|
||||||
Lex->sql_command = SQLCOM_SHOW_CREATE_EVENT;
|
Lex->sql_command = SQLCOM_SHOW_CREATE_EVENT;
|
||||||
}
|
}
|
||||||
|
| describe_command FOR_SYM expr
|
||||||
|
{
|
||||||
|
THD *thd= YYTHD;
|
||||||
|
Lex->sql_command= SQLCOM_SHOW_EXPLAIN;
|
||||||
|
if (prepare_schema_table(thd, Lex, 0, SCH_EXPLAIN))
|
||||||
|
MYSQL_YYABORT;
|
||||||
|
add_value_to_list(thd, $3);
|
||||||
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
show_engine_param:
|
show_engine_param:
|
||||||
|
@ -1146,7 +1146,12 @@ public:
|
|||||||
See TABLE_LIST::process_index_hints().
|
See TABLE_LIST::process_index_hints().
|
||||||
*/
|
*/
|
||||||
bool force_index_group;
|
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
|
If set, the optimizer has found that row retrieval should access index
|
||||||
|
3
unittest/sql/CMakeLists.txt
Normal file
3
unittest/sql/CMakeLists.txt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
|
||||||
|
MY_ADD_TESTS(my_apc LINK_LIBRARIES mysys EXT cc)
|
||||||
|
|
227
unittest/sql/my_apc-t.cc
Normal file
227
unittest/sql/my_apc-t.cc
Normal file
@ -0,0 +1,227 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2012, Monty Program Ab
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; version 2 of the License.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||||
|
|
||||||
|
/*
|
||||||
|
This file does standalone APC system tests.
|
||||||
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <my_global.h>
|
||||||
|
#include <my_pthread.h>
|
||||||
|
#include <my_sys.h>
|
||||||
|
|
||||||
|
#include <tap.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
A fake THD with enter_cond/exit_cond and some other members.
|
||||||
|
*/
|
||||||
|
class THD
|
||||||
|
{
|
||||||
|
mysql_mutex_t* thd_mutex;
|
||||||
|
public:
|
||||||
|
bool killed;
|
||||||
|
|
||||||
|
THD() : killed(FALSE) {}
|
||||||
|
inline const char* enter_cond(mysql_cond_t *cond, mysql_mutex_t* mutex,
|
||||||
|
const char* msg)
|
||||||
|
{
|
||||||
|
mysql_mutex_assert_owner(mutex);
|
||||||
|
thd_mutex= mutex;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
inline void exit_cond(const char* old_msg)
|
||||||
|
{
|
||||||
|
mysql_mutex_unlock(thd_mutex);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#include "../sql/my_apc.h"
|
||||||
|
|
||||||
|
#define MY_APC_STANDALONE 1
|
||||||
|
#include "../sql/my_apc.cc"
|
||||||
|
|
||||||
|
volatile bool started= FALSE;
|
||||||
|
volatile bool service_should_exit= FALSE;
|
||||||
|
volatile bool requestors_should_exit=FALSE;
|
||||||
|
|
||||||
|
/* Counters for APC calls */
|
||||||
|
int apcs_served= 0;
|
||||||
|
int apcs_missed=0;
|
||||||
|
int apcs_timed_out=0;
|
||||||
|
mysql_mutex_t apc_counters_mutex;
|
||||||
|
|
||||||
|
inline void increment_counter(int *var)
|
||||||
|
{
|
||||||
|
mysql_mutex_lock(&apc_counters_mutex);
|
||||||
|
*var= *var+1;
|
||||||
|
mysql_mutex_unlock(&apc_counters_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
volatile bool have_errors= false;
|
||||||
|
|
||||||
|
Apc_target apc_target;
|
||||||
|
mysql_mutex_t target_mutex;
|
||||||
|
|
||||||
|
int int_rand(int size)
|
||||||
|
{
|
||||||
|
return (int) (0.5 + ((double)rand() / RAND_MAX) * size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
APC target thread (the one that will serve the APC requests). We will have
|
||||||
|
one target.
|
||||||
|
*/
|
||||||
|
void *test_apc_service_thread(void *ptr)
|
||||||
|
{
|
||||||
|
my_thread_init();
|
||||||
|
mysql_mutex_init(0, &target_mutex, MY_MUTEX_INIT_FAST);
|
||||||
|
apc_target.init(&target_mutex);
|
||||||
|
apc_target.enable();
|
||||||
|
started= TRUE;
|
||||||
|
diag("test_apc_service_thread started");
|
||||||
|
while (!service_should_exit)
|
||||||
|
{
|
||||||
|
//apc_target.disable();
|
||||||
|
my_sleep(10000);
|
||||||
|
//apc_target.enable();
|
||||||
|
for (int i = 0; i < 10 && !service_should_exit; i++)
|
||||||
|
{
|
||||||
|
apc_target.process_apc_requests();
|
||||||
|
my_sleep(int_rand(30));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
apc_target.disable();
|
||||||
|
apc_target.destroy();
|
||||||
|
mysql_mutex_destroy(&target_mutex);
|
||||||
|
my_thread_end();
|
||||||
|
pthread_exit(0);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
One APC request (to write 'value' into *where_to)
|
||||||
|
*/
|
||||||
|
class Apc_order : public Apc_target::Apc_call
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
int value; // The value
|
||||||
|
int *where_to; // Where to write it
|
||||||
|
Apc_order(int a, int *b) : value(a), where_to(b) {}
|
||||||
|
|
||||||
|
void call_in_target_thread()
|
||||||
|
{
|
||||||
|
my_sleep(int_rand(1000));
|
||||||
|
*where_to = value;
|
||||||
|
increment_counter(&apcs_served);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
APC requestor thread. It makes APC requests, and checks if they were actually
|
||||||
|
executed.
|
||||||
|
*/
|
||||||
|
void *test_apc_requestor_thread(void *ptr)
|
||||||
|
{
|
||||||
|
my_thread_init();
|
||||||
|
diag("test_apc_requestor_thread started");
|
||||||
|
THD my_thd;
|
||||||
|
|
||||||
|
while (!requestors_should_exit)
|
||||||
|
{
|
||||||
|
int dst_value= 0;
|
||||||
|
int src_value= int_rand(4*1000*100);
|
||||||
|
/* Create an APC to do "dst_value= src_value" assignment */
|
||||||
|
Apc_order apc_order(src_value, &dst_value);
|
||||||
|
bool timed_out;
|
||||||
|
|
||||||
|
mysql_mutex_lock(&target_mutex);
|
||||||
|
bool res= apc_target.make_apc_call(&my_thd, &apc_order, 60, &timed_out);
|
||||||
|
if (res)
|
||||||
|
{
|
||||||
|
if (timed_out)
|
||||||
|
increment_counter(&apcs_timed_out);
|
||||||
|
else
|
||||||
|
increment_counter(&apcs_missed);
|
||||||
|
|
||||||
|
if (dst_value != 0)
|
||||||
|
{
|
||||||
|
diag("APC was done even though return value says it wasnt!");
|
||||||
|
have_errors= true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (dst_value != src_value)
|
||||||
|
{
|
||||||
|
diag("APC was not done even though return value says it was!");
|
||||||
|
have_errors= true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//my_sleep(300);
|
||||||
|
}
|
||||||
|
diag("test_apc_requestor_thread exiting");
|
||||||
|
my_thread_end();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Number of APC requestor threads */
|
||||||
|
const int N_THREADS=23;
|
||||||
|
|
||||||
|
|
||||||
|
int main(int args, char **argv)
|
||||||
|
{
|
||||||
|
pthread_t service_thr;
|
||||||
|
pthread_t request_thr[N_THREADS];
|
||||||
|
int i;
|
||||||
|
|
||||||
|
my_thread_global_init();
|
||||||
|
|
||||||
|
mysql_mutex_init(0, &apc_counters_mutex, MY_MUTEX_INIT_FAST);
|
||||||
|
|
||||||
|
plan(1);
|
||||||
|
diag("Testing APC delivery and execution");
|
||||||
|
|
||||||
|
pthread_create(&service_thr, NULL, test_apc_service_thread, (void*)NULL);
|
||||||
|
while (!started)
|
||||||
|
my_sleep(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++)
|
||||||
|
{
|
||||||
|
my_sleep(500*1000);
|
||||||
|
diag("%d APCs served %d missed", apcs_served, apcs_missed);
|
||||||
|
}
|
||||||
|
diag("Shutting down requestors");
|
||||||
|
requestors_should_exit= TRUE;
|
||||||
|
for (i = 0; i < N_THREADS; i++)
|
||||||
|
pthread_join(request_thr[i], NULL);
|
||||||
|
|
||||||
|
diag("Shutting down service");
|
||||||
|
service_should_exit= TRUE;
|
||||||
|
pthread_join(service_thr, NULL);
|
||||||
|
|
||||||
|
mysql_mutex_destroy(&apc_counters_mutex);
|
||||||
|
|
||||||
|
diag("Done");
|
||||||
|
my_thread_end();
|
||||||
|
my_thread_global_end();
|
||||||
|
|
||||||
|
ok1(!have_errors);
|
||||||
|
return exit_status();
|
||||||
|
}
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user