1
0
mirror of https://github.com/MariaDB/server.git synced 2025-07-30 16:24:05 +03:00

MWL#182: Explain running statements

- Merge with 5.3-main
This commit is contained in:
Sergey Petrunya
2011-10-27 21:41:19 +04:00
25 changed files with 1365 additions and 126 deletions

View File

@ -245,6 +245,51 @@ Item_equal *find_item_equal(COND_EQUAL *cond_equal, Field *field,
JOIN_TAB *first_depth_first_tab(JOIN* join);
JOIN_TAB *next_depth_first_tab(JOIN* join, JOIN_TAB* tab);
#ifndef DBUG_OFF
// psergey:
void dbug_serve_apcs(THD *thd, int n_calls)
{
// TODO how do we signal that we're SHOW-EXPLAIN-READY?
const char *save_proc_info= thd->proc_info;
thd_proc_info(thd, "show_explain_trap");
int n_apcs= thd->apc_target.n_calls_processed + n_calls;
while (thd->apc_target.n_calls_processed < n_apcs)
{
my_sleep(300);
if (thd->check_killed())
break;
}
thd_proc_info(thd, save_proc_info);
}
/*
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.
*/
@ -846,6 +891,12 @@ inject_jtbm_conds(JOIN *join, List<TABLE_LIST> *join_list, Item **join_where)
DBUG_VOID_RETURN;
}
int JOIN::optimize()
{
int res= optimize_inner();
optimized= 2;
return res;
}
/**
global select optimisation.
@ -859,7 +910,7 @@ inject_jtbm_conds(JOIN *join, List<TABLE_LIST> *join_list, Item **join_where)
*/
int
JOIN::optimize()
JOIN::optimize_inner()
{
ulonglong select_opts_for_readinfo;
uint no_jbuf_after;
@ -2022,6 +2073,17 @@ JOIN::save_join_tab()
}
void JOIN::exec()
{
/*
Enable SHOW EXPLAIN only if we're in the top-level query.
*/
thd->apc_target.enable();
exec_inner();
thd->apc_target.disable();
}
/**
Exec select.
@ -2033,12 +2095,20 @@ JOIN::save_join_tab()
@todo
When can we have here thd->net.report_error not zero?
*/
void
JOIN::exec()
void JOIN::exec_inner()
{
List<Item> *columns_list= &fields_list;
int tmp_error;
DBUG_ENTER("JOIN::exec");
DBUG_EXECUTE_IF("show_explain_probe_2", dbug_serve_apcs(thd, 1););
DBUG_EXECUTE_IF("show_explain_probe_1",
if (dbug_user_var_equals_int(thd,
"show_explain_probe_select_id",
select_lex->select_number))
dbug_serve_apcs(thd, 1);
);
thd_proc_info(thd, "executing");
error= 0;
@ -3545,7 +3615,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
goto error;
/* Generate an execution plan from the found optimal join order. */
DBUG_RETURN(join->thd->killed || get_best_combination(join));
DBUG_RETURN(join->thd->check_killed() || get_best_combination(join));
error:
/*
@ -6307,7 +6377,7 @@ best_extension_by_limited_search(JOIN *join,
DBUG_ENTER("best_extension_by_limited_search");
THD *thd= join->thd;
if (thd->killed) // Abort
if (thd->check_killed()) // Abort
DBUG_RETURN(TRUE);
DBUG_EXECUTE("opt", print_plan(join, idx, read_time, record_count, idx,
@ -6467,7 +6537,7 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count,
{
DBUG_ENTER("find_best");
THD *thd= join->thd;
if (thd->killed)
if (thd->check_killed())
DBUG_RETURN(TRUE);
if (!rest_tables)
{
@ -14541,7 +14611,7 @@ create_internal_tmp_table_from_heap2(THD *thd, TABLE *table,
DBUG_EXECUTE_IF("raise_error", write_err= HA_ERR_FOUND_DUPP_KEY ;);
if (write_err)
goto err;
if (thd->killed)
if (thd->check_killed())
{
thd->send_kill_message();
goto err_killed;
@ -14913,7 +14983,7 @@ sub_select_cache(JOIN *join, JOIN_TAB *join_tab, bool end_of_records)
rc= sub_select(join, join_tab, end_of_records);
DBUG_RETURN(rc);
}
if (join->thd->killed)
if (join->thd->check_killed())
{
/* The user has aborted the execution of the query */
join->thd->send_kill_message();
@ -15212,7 +15282,7 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab,
DBUG_RETURN(NESTED_LOOP_ERROR);
if (error < 0)
DBUG_RETURN(NESTED_LOOP_NO_MORE_ROWS);
if (join->thd->killed) // Aborted by user
if (join->thd->check_killed()) // Aborted by user
{
join->thd->send_kill_message();
DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
@ -16339,7 +16409,7 @@ end_write(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
TABLE *table=join->tmp_table;
DBUG_ENTER("end_write");
if (join->thd->killed) // Aborted by user
if (join->thd->check_killed()) // Aborted by user
{
join->thd->send_kill_message();
DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
@ -16410,7 +16480,7 @@ end_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
if (end_of_records)
DBUG_RETURN(NESTED_LOOP_OK);
if (join->thd->killed) // Aborted by user
if (join->thd->check_killed()) // Aborted by user
{
join->thd->send_kill_message();
DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
@ -16491,7 +16561,7 @@ end_unique_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
if (end_of_records)
DBUG_RETURN(NESTED_LOOP_OK);
if (join->thd->killed) // Aborted by user
if (join->thd->check_killed()) // Aborted by user
{
join->thd->send_kill_message();
DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
@ -16538,7 +16608,7 @@ end_write_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
int idx= -1;
DBUG_ENTER("end_write_group");
if (join->thd->killed)
if (join->thd->check_killed())
{ // Aborted by user
join->thd->send_kill_message();
DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
@ -18309,7 +18379,7 @@ static int remove_dup_with_compare(THD *thd, TABLE *table, Field **first_field,
error= file->ha_rnd_next(record);
for (;;)
{
if (thd->killed)
if (thd->check_killed())
{
thd->send_kill_message();
error=0;
@ -18441,7 +18511,7 @@ static int remove_dup_with_hash_index(THD *thd, TABLE *table,
for (;;)
{
uchar *org_key_pos;
if (thd->killed)
if (thd->check_killed())
{
thd->send_kill_message();
error=0;
@ -20446,29 +20516,117 @@ void JOIN::clear()
}
}
int print_fake_select_lex_join(select_result_sink *result, bool on_the_fly,
SELECT_LEX *select_lex, uint8 select_options)
{
const CHARSET_INFO *cs= system_charset_info;
Item *item_null= new Item_null();
List<Item> item_list;
if (on_the_fly)
select_lex->set_explain_type(on_the_fly); //psergey
/*
here we assume that the query will return at least two rows, so we
show "filesort" in EXPLAIN. Of course, sometimes we'll be wrong
and no filesort will be actually done, but executing all selects in
the UNION to provide precise EXPLAIN information will hardly be
appreciated :)
*/
char table_name_buffer[SAFE_NAME_LEN];
item_list.empty();
/* id */
item_list.push_back(new Item_null);
/* select_type */
item_list.push_back(new Item_string(select_lex->type,
strlen(select_lex->type),
cs));
/* table */
{
SELECT_LEX *sl= select_lex->master_unit()->first_select();
uint len= 6, lastop= 0;
memcpy(table_name_buffer, STRING_WITH_LEN("<union"));
for (; sl && len + lastop + 5 < NAME_LEN; sl= sl->next_select())
{
len+= lastop;
lastop= my_snprintf(table_name_buffer + len, NAME_LEN - len,
"%u,", sl->select_number);
}
if (sl || len + lastop >= NAME_LEN)
{
memcpy(table_name_buffer + len, STRING_WITH_LEN("...>") + 1);
len+= 4;
}
else
{
len+= lastop;
table_name_buffer[len - 1]= '>'; // change ',' to '>'
}
item_list.push_back(new Item_string(table_name_buffer, len, cs));
}
/* partitions */
if (/*join->thd->lex->describe*/ select_options & DESCRIBE_PARTITIONS)
item_list.push_back(item_null);
/* type */
item_list.push_back(new Item_string(join_type_str[JT_ALL],
strlen(join_type_str[JT_ALL]),
cs));
/* possible_keys */
item_list.push_back(item_null);
/* key*/
item_list.push_back(item_null);
/* key_len */
item_list.push_back(item_null);
/* ref */
item_list.push_back(item_null);
/* in_rows */
if (select_options & DESCRIBE_EXTENDED)
item_list.push_back(item_null);
/* rows */
item_list.push_back(item_null);
/* extra */
if (select_lex->master_unit()->global_parameters->order_list.first)
item_list.push_back(new Item_string("Using filesort",
14, cs));
else
item_list.push_back(new Item_string("", 0, cs));
if (result->send_data(item_list))
return 1;
return 0;
}
/**
EXPLAIN handling.
Send a description about what how the select will be done to stdout.
Produce lines explaining execution of *this* select (not including children
selects)
@param on_the_fly TRUE <=> we're being executed on-the-fly, so don't make
modifications to any select's data structures
*/
static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
bool distinct,const char *message)
int JOIN::print_explain(select_result_sink *result, bool on_the_fly,
bool need_tmp_table, bool need_order,
bool distinct, const char *message)
{
List<Item> field_list;
List<Item> item_list;
JOIN *join= this; /* Legacy: this code used to be a non-member function */
THD *thd=join->thd;
select_result *result=join->result;
Item *item_null= new Item_null();
CHARSET_INFO *cs= system_charset_info;
int quick_type;
DBUG_ENTER("select_describe");
int error= 0;
DBUG_ENTER("JOIN::print_explain");
DBUG_PRINT("info", ("Select 0x%lx, type %s, message %s",
(ulong)join->select_lex, join->select_lex->type,
message ? message : "NULL"));
DBUG_ASSERT(this->optimized == 2);
/* Don't log this into the slow query log */
thd->server_status&= ~(SERVER_QUERY_NO_INDEX_USED | SERVER_QUERY_NO_GOOD_INDEX_USED);
join->unit->offset_limit_cnt= 0;
if (!on_the_fly)
{
thd->server_status&= ~(SERVER_QUERY_NO_INDEX_USED | SERVER_QUERY_NO_GOOD_INDEX_USED);
join->unit->offset_limit_cnt= 0;
}
/*
NOTE: the number/types of items pushed into item_list must be in sync with
@ -20489,82 +20647,22 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
item_list.push_back(new Item_string(message,strlen(message),cs));
if (result->send_data(item_list))
join->error= 1;
error= 1;
}
else if (join->select_lex == join->unit->fake_select_lex)
{
/*
here we assume that the query will return at least two rows, so we
show "filesort" in EXPLAIN. Of course, sometimes we'll be wrong
and no filesort will be actually done, but executing all selects in
the UNION to provide precise EXPLAIN information will hardly be
appreciated :)
*/
char table_name_buffer[SAFE_NAME_LEN];
item_list.empty();
/* id */
item_list.push_back(new Item_null);
/* select_type */
item_list.push_back(new Item_string(join->select_lex->type,
strlen(join->select_lex->type),
cs));
/* table */
{
SELECT_LEX *sl= join->unit->first_select();
uint len= 6, lastop= 0;
memcpy(table_name_buffer, STRING_WITH_LEN("<union"));
for (; sl && len + lastop + 5 < NAME_LEN; sl= sl->next_select())
{
len+= lastop;
lastop= my_snprintf(table_name_buffer + len, NAME_LEN - len,
"%u,", sl->select_number);
}
if (sl || len + lastop >= NAME_LEN)
{
memcpy(table_name_buffer + len, STRING_WITH_LEN("...>") + 1);
len+= 4;
}
else
{
len+= lastop;
table_name_buffer[len - 1]= '>'; // change ',' to '>'
}
item_list.push_back(new Item_string(table_name_buffer, len, cs));
}
/* partitions */
if (join->thd->lex->describe & DESCRIBE_PARTITIONS)
item_list.push_back(item_null);
/* type */
item_list.push_back(new Item_string(join_type_str[JT_ALL],
strlen(join_type_str[JT_ALL]),
cs));
/* possible_keys */
item_list.push_back(item_null);
/* key*/
item_list.push_back(item_null);
/* key_len */
item_list.push_back(item_null);
/* ref */
item_list.push_back(item_null);
/* in_rows */
if (join->thd->lex->describe & DESCRIBE_EXTENDED)
item_list.push_back(item_null);
/* rows */
item_list.push_back(item_null);
/* extra */
if (join->unit->global_parameters->order_list.first)
item_list.push_back(new Item_string("Using filesort",
14, cs));
else
item_list.push_back(new Item_string("", 0, cs));
if (result->send_data(item_list))
join->error= 1;
if (print_fake_select_lex_join(result, on_the_fly,
join->select_lex,
join->thd->lex->describe))
error= 1;
}
else if (!join->select_lex->master_unit()->derived ||
join->select_lex->master_unit()->derived->is_materialized_derived())
{
table_map used_tables=0;
//if (!join->select_lex->type)
if (on_the_fly)
join->select_lex->set_explain_type(on_the_fly); //psergey-todo: this adds SELECT_DESCRIBE to options! bad for on-the-fly
bool printing_materialize_nest= FALSE;
uint select_id= join->select_lex->select_number;
@ -20618,6 +20716,7 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
join->select_lex->type;
item_list.push_back(new Item_string(stype, strlen(stype), cs));
enum join_type tab_type= tab->type;
if ((tab->type == JT_ALL || tab->type == JT_HASH) &&
tab->select && tab->select->quick)
{
@ -20626,9 +20725,9 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
(quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_INTERSECT) ||
(quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT) ||
(quick_type == QUICK_SELECT_I::QS_TYPE_ROR_UNION))
tab->type= tab->type == JT_ALL ? JT_INDEX_MERGE : JT_HASH_INDEX_MERGE;
tab_type= tab->type == JT_ALL ? JT_INDEX_MERGE : JT_HASH_INDEX_MERGE;
else
tab->type= tab->type == JT_ALL ? JT_RANGE : JT_HASH_RANGE;
tab_type= tab->type == JT_ALL ? JT_RANGE : JT_HASH_RANGE;
}
/* table */
@ -20677,8 +20776,8 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
#endif
}
/* "type" column */
item_list.push_back(new Item_string(join_type_str[tab->type],
strlen(join_type_str[tab->type]),
item_list.push_back(new Item_string(join_type_str[tab_type],
strlen(join_type_str[tab_type]),
cs));
/* Build "possible_keys" value and add it to item_list */
if (!tab->keys.is_clear_all())
@ -20702,7 +20801,7 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
item_list.push_back(item_null);
/* Build "key", "key_len", and "ref" values and add them to item_list */
if (tab->type == JT_NEXT)
if (tab_type == JT_NEXT)
{
key_info= table->key_info+tab->index;
key_len= key_info->key_length;
@ -20731,12 +20830,12 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
}
}
}
if (is_hj && tab->type != JT_HASH)
if (is_hj && tab_type != JT_HASH)
{
tmp2.append(':');
tmp3.append(':');
}
if (tab->type == JT_HASH_NEXT)
if (tab_type == JT_HASH_NEXT)
{
register uint length;
key_info= table->key_info+tab->index;
@ -20758,7 +20857,7 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
item_list.push_back(new Item_string(tmp3.ptr(),tmp3.length(),cs));
else
item_list.push_back(item_null);
if (key_info && tab->type != JT_NEXT)
if (key_info && tab_type != JT_NEXT)
item_list.push_back(new Item_string(tmp4.ptr(),tmp4.length(),cs));
else
item_list.push_back(item_null);
@ -20811,7 +20910,7 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
ha_rows examined_rows;
if (tab->select && tab->select->quick)
examined_rows= tab->select->quick->records;
else if (tab->type == JT_NEXT || tab->type == JT_ALL || is_hj)
else if (tab_type == JT_NEXT || tab_type == JT_ALL || is_hj)
{
if (tab->limit)
examined_rows= tab->limit;
@ -20850,7 +20949,7 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
/* Build "Extra" field and add it to item_list. */
key_read=table->key_read;
if ((tab->type == JT_NEXT || tab->type == JT_CONST) &&
if ((tab_type == JT_NEXT || tab_type == JT_CONST) &&
table->covering_keys.is_set(tab->index))
key_read=1;
if (quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT &&
@ -21056,9 +21155,23 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
// For next iteration
used_tables|=table->map;
if (result->send_data(item_list))
join->error= 1;
error= 1;
}
}
DBUG_RETURN(error);
}
static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
bool distinct,const char *message)
{
THD *thd=join->thd;
select_result *result=join->result;
DBUG_ENTER("select_describe");
join->error= join->print_explain(result, FALSE, /* Not on-the-fly */
need_tmp_table, need_order, distinct,
message);
for (SELECT_LEX_UNIT *unit= join->select_lex->first_inner_unit();
unit;
unit= unit->next_unit())
@ -21101,7 +21214,7 @@ bool mysql_explain_union(THD *thd, SELECT_LEX_UNIT *unit, select_result *result)
for (SELECT_LEX *sl= first; sl; sl= sl->next_select())
{
sl->set_explain_type();
sl->set_explain_type(FALSE); //psergey-todo: maybe remove this from here?
sl->options|= SELECT_DESCRIBE;
}