[SHOW] EXPLAIN UPDATE/DELETE, code re-structuring
- Update view.result (old EXPLAIN didn't match the execution) - Put in a stub code to work around the SELECT ... UNION SELECT ... ORDER BY (subuqery) problem
This commit is contained in:
parent
79392b9383
commit
99a8bfe68c
@ -16,4 +16,4 @@ read_many_rows_innodb : Bug#11748886 2010-11-15 mattiasj report already exist
|
||||
archive-big : Bug#11817185 2011-03-10 Anitha Disabled since this leads to timeout on Solaris Sparc
|
||||
log_tables-big : Bug#11756699 2010-11-15 mattiasj report already exists
|
||||
mysql_embedded : Bug#12561297 2011-05-14 Anitha Dependent on PB2 changes - eventum#41836
|
||||
show_explain : Psergey: random timeout in range-checked-for-each record query.
|
||||
#show_explain : Psergey: random timeout in range-checked-for-each record query.
|
||||
|
@ -3584,7 +3584,7 @@ View Create View character_set_client collation_connection
|
||||
v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select `t1`.`a` AS `a`,`t1`.`b` AS `b` from `t1` FORCE INDEX (PRIMARY) FORCE INDEX (`b`) order by `t1`.`a` latin1 latin1_swedish_ci
|
||||
EXPLAIN SELECT * FROM v1;
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t1 index NULL PRIMARY 4 NULL 15
|
||||
1 SIMPLE t1 ALL NULL NULL NULL NULL 15
|
||||
CREATE VIEW v2 AS SELECT * FROM t1 USE KEY () ORDER BY a;
|
||||
SHOW CREATE VIEW v2;
|
||||
View Create View character_set_client collation_connection
|
||||
|
@ -159,7 +159,9 @@ void Update_plan::save_query_plan_footprint_intern(QPF_query *query, QPF_update
|
||||
qpf->add_child(unit->first_select()->select_number);
|
||||
|
||||
//TODO: temporary?:
|
||||
unit->save_qpf(query);
|
||||
// A: yes. optimizing children subqueries has caused them to save QPFs,
|
||||
// automatically.
|
||||
//unit->save_qpf(query);
|
||||
}
|
||||
}
|
||||
|
||||
|
194
sql/sql_lex.cc
194
sql/sql_lex.cc
@ -4172,29 +4172,32 @@ bool st_select_lex::is_merged_child_of(st_select_lex *ancestor)
|
||||
return all_merged;
|
||||
}
|
||||
|
||||
/*
|
||||
This is used by SHOW EXPLAIN. It assuses query plan has been already
|
||||
collected into QPF structures and we only need to print it out.
|
||||
*/
|
||||
|
||||
int LEX::print_explain(select_result_sink *output, uint8 explain_flags,
|
||||
bool *printed_anything)
|
||||
bool *printed_anything) //TODO: remove printed_anything
|
||||
{
|
||||
/* if (upd_del_plan)
|
||||
int res;
|
||||
if (query_plan_footprint)
|
||||
{
|
||||
upd_del_plan->print_explain(output, explain_flags, printed_anything);
|
||||
return 0;
|
||||
}*/
|
||||
//int res= unit.print_explain(output, explain_flags, printed_anything);
|
||||
|
||||
//psergey-todo: here, we should make Query Plan Footprint, and then produce
|
||||
// an EXPLAIN output from it.
|
||||
/*
|
||||
The new, QueryPlanFootprint way:
|
||||
*/
|
||||
QPF_query qpf;
|
||||
unit.save_qpf(&qpf);
|
||||
//return res;
|
||||
return 0;
|
||||
res= query_plan_footprint->print_explain(output, explain_flags);
|
||||
*printed_anything= true;
|
||||
}
|
||||
else
|
||||
{
|
||||
res= 0;
|
||||
*printed_anything= false;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
|
||||
*/
|
||||
void st_select_lex::save_qpf(QPF_query *output)
|
||||
{
|
||||
int res;
|
||||
@ -4261,92 +4264,12 @@ err:
|
||||
return ;//res;
|
||||
}
|
||||
|
||||
#if 0
|
||||
int st_select_lex::print_explain(select_result_sink *output,
|
||||
uint8 explain_flags,
|
||||
bool *printed_anything)
|
||||
|
||||
int st_select_lex_unit::save_union_qpf(QPF_query *output)
|
||||
{
|
||||
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";
|
||||
}
|
||||
set_explain_type(TRUE/* on_the_fly */);
|
||||
res= print_explain_message_line(output, 0/*options*/, select_number, type,
|
||||
msg);
|
||||
}
|
||||
err:
|
||||
return res;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
int st_select_lex_unit::save_qpf(QPF_query *output)
|
||||
{
|
||||
//int res= 0;
|
||||
SELECT_LEX *first= first_select();
|
||||
|
||||
if (!first->next_select())
|
||||
{
|
||||
/* This is a 1-way UNION, i.e. not really a UNION */
|
||||
if (!output->get_select(first->select_number))
|
||||
first->save_qpf(output);
|
||||
return 0;
|
||||
}
|
||||
|
||||
QPF_union *qpfu= new (output->mem_root) QPF_union;
|
||||
|
||||
/*
|
||||
TODO: The following code should be eliminated. If we have a capability to
|
||||
save Query Plan Footprints, we should just save them, and never need to
|
||||
@ -4380,53 +4303,48 @@ int st_select_lex_unit::save_qpf(QPF_query *output)
|
||||
// Save the UNION node
|
||||
output->add_node(qpfu);
|
||||
|
||||
#if 0
|
||||
/* 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;
|
||||
#endif
|
||||
qpfu->fake_select_type= "UNION RESULT";
|
||||
qpfu->using_filesort= test(global_parameters->order_list.first);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if 0
|
||||
int st_select_lex_unit::print_explain(select_result_sink *output,
|
||||
uint8 explain_flags, bool *printed_anything)
|
||||
|
||||
int st_select_lex_unit::save_union_qpf_part2(QPF_query *output)
|
||||
{
|
||||
int res= 0;
|
||||
SELECT_LEX *first= first_select();
|
||||
|
||||
if (first && !first->next_select() && !first->join)
|
||||
{
|
||||
/*
|
||||
If there is only one child, 'first', and it has join==NULL, emit "not in
|
||||
EXPLAIN state" error.
|
||||
*/
|
||||
const char *msg="Query plan already deleted";
|
||||
first->set_explain_type(TRUE/* on_the_fly */);
|
||||
res= print_explain_message_line(output, 0/*options*/, first->select_number,
|
||||
first->type, msg);
|
||||
return res;
|
||||
}
|
||||
|
||||
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 */
|
||||
QPF_union *qpfu= output->get_union(first_select()->select_number);
|
||||
if (fake_select_lex)
|
||||
{
|
||||
res= print_fake_select_lex_join(output, TRUE /* on the fly */,
|
||||
fake_select_lex, explain_flags);
|
||||
for (SELECT_LEX_UNIT *unit= fake_select_lex->first_inner_unit();
|
||||
unit; unit= unit->next_unit())
|
||||
{
|
||||
if (!(unit->item && unit->item->eliminated))
|
||||
{
|
||||
qpfu->add_child(unit->first_select()->select_number);
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
int st_select_lex_unit::save_qpf(QPF_query *output)
|
||||
{
|
||||
//int res= 0;
|
||||
SELECT_LEX *first= first_select();
|
||||
|
||||
if (!first->next_select())
|
||||
{
|
||||
/* This is a 1-way UNION, i.e. not really a UNION */
|
||||
if (!output->get_select(first->select_number))
|
||||
first->save_qpf(output);
|
||||
return 0;
|
||||
}
|
||||
|
||||
save_union_qpf(output);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
A routine used by the parser to decide whether we are specifying a full
|
||||
|
@ -733,6 +733,8 @@ public:
|
||||
List<Item> *get_unit_column_types();
|
||||
|
||||
int save_qpf(QPF_query *output);
|
||||
int save_union_qpf(QPF_query *output);
|
||||
int save_union_qpf_part2(QPF_query *output);
|
||||
};
|
||||
|
||||
typedef class st_select_lex_unit SELECT_LEX_UNIT;
|
||||
|
@ -1002,6 +1002,7 @@ err:
|
||||
DBUG_RETURN(res); /* purecov: inspected */
|
||||
}
|
||||
|
||||
void join_save_qpf(JOIN *join);
|
||||
|
||||
int JOIN::optimize()
|
||||
{
|
||||
@ -1020,7 +1021,10 @@ int JOIN::optimize()
|
||||
return 0, even though we never had a query plan.
|
||||
*/
|
||||
if (was_optimized != optimized && !res && have_query_plan != QEP_DELETED)
|
||||
{
|
||||
have_query_plan= QEP_AVAILABLE;
|
||||
join_save_qpf(this);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
@ -1611,8 +1615,10 @@ TODO: make view to decide if it is possible to write to WHERE directly or make S
|
||||
JOIN_TAB *tab= &join_tab[const_tables];
|
||||
bool all_order_fields_used;
|
||||
if (order)
|
||||
{
|
||||
skip_sort_order= test_if_skip_sort_order(tab, order, select_limit, 1,
|
||||
&tab->table->keys_in_use_for_order_by);
|
||||
}
|
||||
if ((group_list=create_distinct_group(thd, select_lex->ref_pointer_array,
|
||||
order, fields_list, all_fields,
|
||||
&all_order_fields_used)))
|
||||
@ -2289,11 +2295,48 @@ JOIN::save_join_tab()
|
||||
}
|
||||
|
||||
|
||||
void join_save_qpf(JOIN *join)
|
||||
{
|
||||
THD *thd= join->thd;
|
||||
//TODO: why not call st_select_lex::save_qpf here?
|
||||
|
||||
if (join->select_lex->select_number != UINT_MAX &&
|
||||
join->select_lex->select_number != INT_MAX /* this is not a UNION's "fake select */ &&
|
||||
join->have_query_plan != JOIN::QEP_NOT_PRESENT_YET &&
|
||||
join->have_query_plan != JOIN::QEP_DELETED && // this happens when there was no QEP ever, but then
|
||||
//cleanup() is called multiple times
|
||||
|
||||
thd->lex->query_plan_footprint && // for "SET" command in SPs.
|
||||
!thd->lex->query_plan_footprint->get_select(join->select_lex->select_number))
|
||||
{
|
||||
const char *message= NULL;
|
||||
|
||||
if (!join->table_count || !join->tables_list || join->zero_result_cause)
|
||||
{
|
||||
/* It's a degenerate join */
|
||||
message= join->zero_result_cause ? join->zero_result_cause : "No tables used";
|
||||
}
|
||||
|
||||
join->save_qpf(thd->lex->query_plan_footprint,
|
||||
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
|
||||
message); // message
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void JOIN::exec()
|
||||
{
|
||||
/*
|
||||
Enable SHOW EXPLAIN only if we're in the top-level query.
|
||||
*/
|
||||
|
||||
/*
|
||||
psergey: we can produce SHOW explain at this point. This means, we're ready
|
||||
to save the query plan.
|
||||
*/
|
||||
thd->apc_target.enable();
|
||||
DBUG_EXECUTE_IF("show_explain_probe_join_exec_start",
|
||||
if (dbug_user_var_equals_int(thd,
|
||||
@ -11109,6 +11152,7 @@ void JOIN::cleanup(bool full)
|
||||
if (full)
|
||||
{
|
||||
//
|
||||
#if 0
|
||||
if (select_lex->select_number != UINT_MAX &&
|
||||
select_lex->select_number != INT_MAX /* this is not a UNION's "fake select */ &&
|
||||
have_query_plan != QEP_NOT_PRESENT_YET &&
|
||||
@ -11133,7 +11177,7 @@ void JOIN::cleanup(bool full)
|
||||
select_distinct, // bool distinct
|
||||
message); // message
|
||||
}
|
||||
|
||||
#endif
|
||||
//
|
||||
|
||||
have_query_plan= QEP_DELETED; //psergey: this is a problem!
|
||||
@ -22352,589 +22396,6 @@ void explain_append_mrr_info(QUICK_RANGE_SELECT *quick, String *res)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
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
|
||||
|
||||
psergey-todo: should this produce a data structure with a query plan? Or, the
|
||||
data structure with the query plan should be produced in any way?
|
||||
*/
|
||||
#if 0
|
||||
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 (on_the_fly)
|
||||
join->select_lex->set_explain_type(on_the_fly);
|
||||
|
||||
if (print_explain_message_line(result, explain_flags,
|
||||
join->select_lex->select_number,
|
||||
join->select_lex->type, 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 ||
|
||||
join->select_lex->master_unit()->derived->is_materialized_derived())
|
||||
{
|
||||
table_map used_tables=0;
|
||||
|
||||
if (on_the_fly)
|
||||
join->select_lex->set_explain_type(on_the_fly);
|
||||
|
||||
bool printing_materialize_nest= FALSE;
|
||||
uint select_id= join->select_lex->select_number;
|
||||
|
||||
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;
|
||||
tab= next_breadth_first_tab(join, WALK_OPTIMIZATION_TABS, tab))
|
||||
{
|
||||
if (tab->bush_root_tab)
|
||||
{
|
||||
JOIN_TAB *first_sibling= tab->bush_root_tab->bush_children->start;
|
||||
select_id= first_sibling->emb_sj_nest->sj_subq_pred->get_identifier();
|
||||
printing_materialize_nest= TRUE;
|
||||
}
|
||||
|
||||
TABLE *table=tab->table;
|
||||
TABLE_LIST *table_list= tab->table->pos_in_table_list;
|
||||
char buff[512];
|
||||
char buff1[512], buff2[512], buff3[512], buff4[512];
|
||||
char keylen_str_buf[64];
|
||||
my_bool key_read;
|
||||
String extra(buff, sizeof(buff),cs);
|
||||
char table_name_buffer[SAFE_NAME_LEN];
|
||||
String tmp1(buff1,sizeof(buff1),cs);
|
||||
String tmp2(buff2,sizeof(buff2),cs);
|
||||
String tmp3(buff3,sizeof(buff3),cs);
|
||||
String tmp4(buff4,sizeof(buff4),cs);
|
||||
char hash_key_prefix[]= "#hash#";
|
||||
KEY *key_info= 0;
|
||||
uint key_len= 0;
|
||||
bool is_hj= tab->type == JT_HASH || tab->type ==JT_HASH_NEXT;
|
||||
|
||||
extra.length(0);
|
||||
tmp1.length(0);
|
||||
tmp2.length(0);
|
||||
tmp3.length(0);
|
||||
tmp4.length(0);
|
||||
quick_type= -1;
|
||||
QUICK_SELECT_I *quick= NULL;
|
||||
JOIN_TAB *saved_join_tab= NULL;
|
||||
|
||||
/* Don't show eliminated tables */
|
||||
if (table->map & join->eliminated_tables)
|
||||
{
|
||||
used_tables|=table->map;
|
||||
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();
|
||||
/* id */
|
||||
item_list.push_back(new Item_uint((uint32)select_id));
|
||||
/* select_type */
|
||||
const char* stype= printing_materialize_nest? "MATERIALIZED" :
|
||||
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 && tab->use_quick != 2)
|
||||
{
|
||||
quick= tab->select->quick;
|
||||
quick_type= tab->select->quick->get_type();
|
||||
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_ROR_INTERSECT) ||
|
||||
(quick_type == QUICK_SELECT_I::QS_TYPE_ROR_UNION))
|
||||
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;
|
||||
}
|
||||
|
||||
/* table */
|
||||
if (table->derived_select_number)
|
||||
{
|
||||
/* Derived table name generation */
|
||||
int len= my_snprintf(table_name_buffer, sizeof(table_name_buffer)-1,
|
||||
"<derived%u>",
|
||||
table->derived_select_number);
|
||||
item_list.push_back(new Item_string(table_name_buffer, len, cs));
|
||||
}
|
||||
else if (tab->bush_children)
|
||||
{
|
||||
JOIN_TAB *ctab= tab->bush_children->start;
|
||||
/* table */
|
||||
int len= my_snprintf(table_name_buffer,
|
||||
sizeof(table_name_buffer)-1,
|
||||
"<subquery%d>",
|
||||
ctab->emb_sj_nest->sj_subq_pred->get_identifier());
|
||||
item_list.push_back(new Item_string(table_name_buffer, len, cs));
|
||||
}
|
||||
else
|
||||
{
|
||||
TABLE_LIST *real_table= table->pos_in_table_list;
|
||||
item_list.push_back(new Item_string(real_table->alias,
|
||||
strlen(real_table->alias), cs));
|
||||
}
|
||||
/* "partitions" column */
|
||||
if (explain_flags & DESCRIBE_PARTITIONS)
|
||||
{
|
||||
#ifdef WITH_PARTITION_STORAGE_ENGINE
|
||||
partition_info *part_info;
|
||||
if (!table->derived_select_number &&
|
||||
(part_info= table->part_info))
|
||||
{
|
||||
Item_string *item_str= new Item_string(cs);
|
||||
make_used_partitions_str(part_info, &item_str->str_value);
|
||||
item_list.push_back(item_str);
|
||||
}
|
||||
else
|
||||
item_list.push_back(item_null);
|
||||
#else
|
||||
/* just produce empty column if partitioning is not compiled in */
|
||||
item_list.push_back(item_null);
|
||||
#endif
|
||||
}
|
||||
/* "type" column */
|
||||
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())
|
||||
{
|
||||
uint j;
|
||||
for (j=0 ; j < table->s->keys ; j++)
|
||||
{
|
||||
if (tab->keys.is_set(j))
|
||||
{
|
||||
if (tmp1.length())
|
||||
tmp1.append(',');
|
||||
tmp1.append(table->key_info[j].name,
|
||||
strlen(table->key_info[j].name),
|
||||
system_charset_info);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (tmp1.length())
|
||||
item_list.push_back(new Item_string(tmp1.ptr(),tmp1.length(),cs));
|
||||
else
|
||||
item_list.push_back(item_null);
|
||||
|
||||
/* Build "key", "key_len", and "ref" values and add them to item_list */
|
||||
if (tab_type == JT_NEXT)
|
||||
{
|
||||
key_info= table->key_info+tab->index;
|
||||
key_len= key_info->key_length;
|
||||
}
|
||||
else if (tab->ref.key_parts)
|
||||
{
|
||||
key_info= tab->get_keyinfo_by_key_no(tab->ref.key);
|
||||
key_len= tab->ref.key_length;
|
||||
}
|
||||
if (key_info)
|
||||
{
|
||||
register uint length;
|
||||
if (is_hj)
|
||||
tmp2.append(hash_key_prefix, strlen(hash_key_prefix), cs);
|
||||
tmp2.append(key_info->name, strlen(key_info->name), cs);
|
||||
length= (longlong10_to_str(key_len, keylen_str_buf, 10) -
|
||||
keylen_str_buf);
|
||||
tmp3.append(keylen_str_buf, length, cs);
|
||||
if (tab->ref.key_parts && tab_type != JT_FT)
|
||||
{
|
||||
store_key **ref=tab->ref.key_copy;
|
||||
for (uint kp= 0; kp < tab->ref.key_parts; kp++)
|
||||
{
|
||||
if (tmp4.length())
|
||||
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);
|
||||
ref++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (is_hj && tab_type != JT_HASH)
|
||||
{
|
||||
tmp2.append(':');
|
||||
tmp3.append(':');
|
||||
}
|
||||
if (tab_type == JT_HASH_NEXT)
|
||||
{
|
||||
register uint length;
|
||||
key_info= table->key_info+tab->index;
|
||||
key_len= key_info->key_length;
|
||||
tmp2.append(key_info->name, strlen(key_info->name), cs);
|
||||
length= (longlong10_to_str(key_len, keylen_str_buf, 10) -
|
||||
keylen_str_buf);
|
||||
tmp3.append(keylen_str_buf, length, cs);
|
||||
}
|
||||
if (tab->type != JT_CONST && tab->select && quick)
|
||||
tab->select->quick->add_keys_and_lengths(&tmp2, &tmp3);
|
||||
if (key_info || (tab->select && quick))
|
||||
{
|
||||
if (tmp2.length())
|
||||
item_list.push_back(new Item_string(tmp2.ptr(),tmp2.length(),cs));
|
||||
else
|
||||
item_list.push_back(item_null);
|
||||
if (tmp3.length())
|
||||
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)
|
||||
item_list.push_back(new Item_string(tmp4.ptr(),tmp4.length(),cs));
|
||||
else
|
||||
item_list.push_back(item_null);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (table_list && /* SJM bushes don't have table_list */
|
||||
table_list->schema_table &&
|
||||
table_list->schema_table->i_s_requested_object & OPTIMIZE_I_S_TABLE)
|
||||
{
|
||||
const char *tmp_buff;
|
||||
int f_idx;
|
||||
if (table_list->has_db_lookup_value)
|
||||
{
|
||||
f_idx= table_list->schema_table->idx_field1;
|
||||
tmp_buff= table_list->schema_table->fields_info[f_idx].field_name;
|
||||
tmp2.append(tmp_buff, strlen(tmp_buff), cs);
|
||||
}
|
||||
if (table_list->has_table_lookup_value)
|
||||
{
|
||||
if (table_list->has_db_lookup_value)
|
||||
tmp2.append(',');
|
||||
f_idx= table_list->schema_table->idx_field2;
|
||||
tmp_buff= table_list->schema_table->fields_info[f_idx].field_name;
|
||||
tmp2.append(tmp_buff, strlen(tmp_buff), cs);
|
||||
}
|
||||
if (tmp2.length())
|
||||
item_list.push_back(new Item_string(tmp2.ptr(),tmp2.length(),cs));
|
||||
else
|
||||
item_list.push_back(item_null);
|
||||
}
|
||||
else
|
||||
item_list.push_back(item_null);
|
||||
item_list.push_back(item_null);
|
||||
item_list.push_back(item_null);
|
||||
}
|
||||
|
||||
/* Add "rows" field to item_list. */
|
||||
if (table_list /* SJM bushes don't have table_list */ &&
|
||||
table_list->schema_table)
|
||||
{
|
||||
/* in_rows */
|
||||
if (explain_flags & DESCRIBE_EXTENDED)
|
||||
item_list.push_back(item_null);
|
||||
/* rows */
|
||||
item_list.push_back(item_null);
|
||||
}
|
||||
else
|
||||
{
|
||||
ha_rows examined_rows= tab->get_examined_rows();
|
||||
|
||||
item_list.push_back(new Item_int((longlong) (ulonglong) examined_rows,
|
||||
MY_INT64_NUM_DECIMAL_DIGITS));
|
||||
|
||||
/* Add "filtered" field to item_list. */
|
||||
if (explain_flags & DESCRIBE_EXTENDED)
|
||||
{
|
||||
float f= 0.0;
|
||||
if (examined_rows)
|
||||
{
|
||||
double pushdown_cond_selectivity= tab->cond_selectivity;
|
||||
if (pushdown_cond_selectivity == 1.0)
|
||||
f= (float) (100.0 * tab->records_read / examined_rows);
|
||||
else
|
||||
f= (float) (100.0 * pushdown_cond_selectivity);
|
||||
}
|
||||
set_if_smaller(f, 100.0);
|
||||
item_list.push_back(new Item_float(f, 2));
|
||||
}
|
||||
}
|
||||
|
||||
/* Build "Extra" field and add it to item_list. */
|
||||
key_read=table->key_read;
|
||||
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 &&
|
||||
!((QUICK_ROR_INTERSECT_SELECT*)quick)->need_to_fetch_row)
|
||||
key_read=1;
|
||||
|
||||
if (tab->info)
|
||||
{
|
||||
const char *reason;
|
||||
switch (tab->info) {
|
||||
case ET_CONST_ROW_NOT_FOUND:
|
||||
reason= "const row not found";
|
||||
break;
|
||||
case ET_UNIQUE_ROW_NOT_FOUND:
|
||||
reason= "unique row not found";
|
||||
break;
|
||||
case ET_IMPOSSIBLE_ON_CONDITION:
|
||||
reason= "Impossible ON condition";
|
||||
break;
|
||||
default:
|
||||
DBUG_ASSERT(0);
|
||||
}
|
||||
item_list.push_back(new Item_string(reason,strlen(reason),cs));
|
||||
}
|
||||
else if (tab->packed_info & TAB_INFO_HAVE_VALUE)
|
||||
{
|
||||
if (tab->packed_info & TAB_INFO_USING_INDEX)
|
||||
extra.append(STRING_WITH_LEN("; Using index"));
|
||||
if (tab->packed_info & TAB_INFO_USING_WHERE)
|
||||
extra.append(STRING_WITH_LEN("; Using where"));
|
||||
if (tab->packed_info & TAB_INFO_FULL_SCAN_ON_NULL)
|
||||
extra.append(STRING_WITH_LEN("; Full scan on NULL key"));
|
||||
/* Skip initial "; "*/
|
||||
const char *str= extra.ptr();
|
||||
uint32 len= extra.length();
|
||||
if (len)
|
||||
{
|
||||
str += 2;
|
||||
len -= 2;
|
||||
}
|
||||
item_list.push_back(new Item_string(str, len, cs));
|
||||
}
|
||||
else
|
||||
{
|
||||
uint keyno= MAX_KEY;
|
||||
if (tab->ref.key_parts)
|
||||
keyno= tab->ref.key;
|
||||
else if (tab->select && quick)
|
||||
keyno = quick->index;
|
||||
|
||||
if (keyno != MAX_KEY && keyno == table->file->pushed_idx_cond_keyno &&
|
||||
table->file->pushed_idx_cond)
|
||||
extra.append(STRING_WITH_LEN("; Using index condition"));
|
||||
else if (tab->cache_idx_cond)
|
||||
extra.append(STRING_WITH_LEN("; Using index condition(BKA)"));
|
||||
|
||||
if (quick_type == QUICK_SELECT_I::QS_TYPE_ROR_UNION ||
|
||||
quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT ||
|
||||
quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_INTERSECT ||
|
||||
quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_MERGE)
|
||||
{
|
||||
extra.append(STRING_WITH_LEN("; Using "));
|
||||
tab->select->quick->add_info_string(&extra);
|
||||
}
|
||||
if (tab->select)
|
||||
{
|
||||
if (tab->use_quick == 2)
|
||||
{
|
||||
/* 4 bits per 1 hex digit + terminating '\0' */
|
||||
char buf[MAX_KEY / 4 + 1];
|
||||
extra.append(STRING_WITH_LEN("; Range checked for each "
|
||||
"record (index map: 0x"));
|
||||
extra.append(tab->keys.print(buf));
|
||||
extra.append(')');
|
||||
}
|
||||
else if (tab->select->cond)
|
||||
{
|
||||
const COND *pushed_cond= tab->table->file->pushed_cond;
|
||||
|
||||
if (((thd->variables.optimizer_switch &
|
||||
OPTIMIZER_SWITCH_ENGINE_CONDITION_PUSHDOWN) ||
|
||||
(tab->table->file->ha_table_flags() &
|
||||
HA_MUST_USE_TABLE_CONDITION_PUSHDOWN)) &&
|
||||
pushed_cond)
|
||||
{
|
||||
extra.append(STRING_WITH_LEN("; Using where with pushed "
|
||||
"condition"));
|
||||
if (explain_flags & DESCRIBE_EXTENDED)
|
||||
{
|
||||
extra.append(STRING_WITH_LEN(": "));
|
||||
((COND *)pushed_cond)->print(&extra, QT_ORDINARY);
|
||||
}
|
||||
}
|
||||
else
|
||||
extra.append(STRING_WITH_LEN("; Using where"));
|
||||
}
|
||||
}
|
||||
if (table_list /* SJM bushes don't have table_list */ &&
|
||||
table_list->schema_table &&
|
||||
table_list->schema_table->i_s_requested_object & OPTIMIZE_I_S_TABLE)
|
||||
{
|
||||
if (!table_list->table_open_method)
|
||||
extra.append(STRING_WITH_LEN("; Skip_open_table"));
|
||||
else if (table_list->table_open_method == OPEN_FRM_ONLY)
|
||||
extra.append(STRING_WITH_LEN("; Open_frm_only"));
|
||||
else
|
||||
extra.append(STRING_WITH_LEN("; Open_full_table"));
|
||||
if (table_list->has_db_lookup_value &&
|
||||
table_list->has_table_lookup_value)
|
||||
extra.append(STRING_WITH_LEN("; Scanned 0 databases"));
|
||||
else if (table_list->has_db_lookup_value ||
|
||||
table_list->has_table_lookup_value)
|
||||
extra.append(STRING_WITH_LEN("; Scanned 1 database"));
|
||||
else
|
||||
extra.append(STRING_WITH_LEN("; Scanned all databases"));
|
||||
}
|
||||
if (key_read)
|
||||
{
|
||||
if (quick_type == QUICK_SELECT_I::QS_TYPE_GROUP_MIN_MAX)
|
||||
{
|
||||
QUICK_GROUP_MIN_MAX_SELECT *qgs=
|
||||
(QUICK_GROUP_MIN_MAX_SELECT *) tab->select->quick;
|
||||
extra.append(STRING_WITH_LEN("; Using index for group-by"));
|
||||
qgs->append_loose_scan_type(&extra);
|
||||
}
|
||||
else
|
||||
extra.append(STRING_WITH_LEN("; Using index"));
|
||||
}
|
||||
if (table->reginfo.not_exists_optimize)
|
||||
extra.append(STRING_WITH_LEN("; Not exists"));
|
||||
|
||||
/*
|
||||
if (quick_type == QUICK_SELECT_I::QS_TYPE_RANGE &&
|
||||
!(((QUICK_RANGE_SELECT*)(tab->select->quick))->mrr_flags &
|
||||
HA_MRR_USE_DEFAULT_IMPL))
|
||||
{
|
||||
extra.append(STRING_WITH_LEN("; Using MRR"));
|
||||
}
|
||||
*/
|
||||
if (quick_type == QUICK_SELECT_I::QS_TYPE_RANGE)
|
||||
{
|
||||
explain_append_mrr_info((QUICK_RANGE_SELECT*)(tab->select->quick),
|
||||
&extra);
|
||||
}
|
||||
|
||||
if (need_tmp_table)
|
||||
{
|
||||
need_tmp_table=0;
|
||||
extra.append(STRING_WITH_LEN("; Using temporary"));
|
||||
}
|
||||
if (need_order)
|
||||
{
|
||||
need_order=0;
|
||||
extra.append(STRING_WITH_LEN("; Using filesort"));
|
||||
}
|
||||
if (distinct & test_all_bits(used_tables,
|
||||
join->select_list_used_tables))
|
||||
extra.append(STRING_WITH_LEN("; Distinct"));
|
||||
if (tab->loosescan_match_tab)
|
||||
{
|
||||
extra.append(STRING_WITH_LEN("; LooseScan"));
|
||||
}
|
||||
|
||||
if (tab->first_weedout_table)
|
||||
extra.append(STRING_WITH_LEN("; Start temporary"));
|
||||
if (tab->check_weed_out_table)
|
||||
extra.append(STRING_WITH_LEN("; End temporary"));
|
||||
else if (tab->do_firstmatch)
|
||||
{
|
||||
if (tab->do_firstmatch == /*join->join_tab*/ first_top_tab - 1)
|
||||
extra.append(STRING_WITH_LEN("; FirstMatch"));
|
||||
else
|
||||
{
|
||||
extra.append(STRING_WITH_LEN("; FirstMatch("));
|
||||
TABLE *prev_table=tab->do_firstmatch->table;
|
||||
if (prev_table->derived_select_number)
|
||||
{
|
||||
char namebuf[NAME_LEN];
|
||||
/* Derived table name generation */
|
||||
int len= my_snprintf(namebuf, sizeof(namebuf)-1,
|
||||
"<derived%u>",
|
||||
prev_table->derived_select_number);
|
||||
extra.append(namebuf, len);
|
||||
}
|
||||
else
|
||||
extra.append(prev_table->pos_in_table_list->alias);
|
||||
extra.append(STRING_WITH_LEN(")"));
|
||||
}
|
||||
}
|
||||
|
||||
for (uint part= 0; part < tab->ref.key_parts; part++)
|
||||
{
|
||||
if (tab->ref.cond_guards[part])
|
||||
{
|
||||
extra.append(STRING_WITH_LEN("; Full scan on NULL key"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (tab->cache)
|
||||
{
|
||||
extra.append(STRING_WITH_LEN("; Using join buffer"));
|
||||
tab->cache->print_explain_comment(&extra);
|
||||
}
|
||||
|
||||
/* Skip initial "; "*/
|
||||
const char *str= extra.ptr();
|
||||
uint32 len= extra.length();
|
||||
if (len)
|
||||
{
|
||||
str += 2;
|
||||
len -= 2;
|
||||
}
|
||||
item_list.push_back(new Item_string(str, len, cs));
|
||||
}
|
||||
|
||||
if (saved_join_tab)
|
||||
tab= saved_join_tab;
|
||||
|
||||
// For next iteration
|
||||
used_tables|=table->map;
|
||||
if (result->send_data(item_list))
|
||||
error= 1;
|
||||
}
|
||||
}
|
||||
DBUG_RETURN(error);
|
||||
}
|
||||
#endif
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void QPF_table_access::push_extra(enum Extra_tag extra_tag)
|
||||
{
|
||||
@ -23001,6 +22462,7 @@ int JOIN::save_qpf(QPF_query *output, bool need_tmp_table, bool need_order,
|
||||
}
|
||||
else if (join->select_lex == join->unit->fake_select_lex)
|
||||
{
|
||||
#if 0
|
||||
select_lex->set_explain_type(on_the_fly);
|
||||
QPF_union *qp_union= new (output->mem_root) QPF_union;
|
||||
qp_node= qp_union;
|
||||
@ -23017,6 +22479,7 @@ int JOIN::save_qpf(QPF_query *output, bool need_tmp_table, bool need_order,
|
||||
test(select_lex->master_unit()->global_parameters->order_list.first);
|
||||
|
||||
output->add_node(qp_union);
|
||||
#endif
|
||||
}
|
||||
else if (!join->select_lex->master_unit()->derived ||
|
||||
join->select_lex->master_unit()->derived->is_materialized_derived())
|
||||
@ -23555,16 +23018,21 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
|
||||
THD *thd=join->thd;
|
||||
select_result *result=join->result;
|
||||
DBUG_ENTER("select_describe");
|
||||
#if 0
|
||||
join->error= join->print_explain(result, thd->lex->describe,
|
||||
FALSE, /* Not on-the-fly */
|
||||
need_tmp_table, need_order, distinct,
|
||||
message);
|
||||
#endif
|
||||
//psergey-todo: save QPF here, too.
|
||||
|
||||
// Update the QPF:
|
||||
QPF_select *qp;
|
||||
if ((qp= thd->lex->query_plan_footprint->get_select(join->select_lex->select_number)))
|
||||
{
|
||||
qp->using_temporary= need_tmp_table;
|
||||
qp->using_filesort= need_order;
|
||||
}
|
||||
|
||||
/*
|
||||
WE DONT NEED THIS here anymore:
|
||||
|
||||
join->save_qpf(thd->lex->query_plan_footprint, need_tmp_table, need_order,
|
||||
distinct, message);
|
||||
|
||||
*/
|
||||
for (SELECT_LEX_UNIT *unit= join->select_lex->first_inner_unit();
|
||||
unit;
|
||||
unit= unit->next_unit())
|
||||
|
@ -626,6 +626,9 @@ bool st_select_lex_unit::exec()
|
||||
|
||||
saved_error= optimize();
|
||||
|
||||
|
||||
save_union_qpf(thd->lex->query_plan_footprint);
|
||||
|
||||
if (uncacheable || !item || !item->assigned() || describe)
|
||||
{
|
||||
for (SELECT_LEX *sl= select_cursor; sl; sl= sl->next_select())
|
||||
@ -772,6 +775,9 @@ bool st_select_lex_unit::exec()
|
||||
*/
|
||||
if (!fake_select_lex->ref_pointer_array)
|
||||
fake_select_lex->n_child_sum_items+= global_parameters->n_sum_items;
|
||||
|
||||
|
||||
save_union_qpf_part2(thd->lex->query_plan_footprint);
|
||||
|
||||
saved_error= mysql_select(thd, &fake_select_lex->ref_pointer_array,
|
||||
&result_table_list,
|
||||
|
Loading…
x
Reference in New Issue
Block a user