mirror of
https://github.com/MariaDB/server.git
synced 2025-08-07 00:04:31 +03:00
MDEV-406: ANALYZE $stmt: get ANALYZE work for subqueries
- "ANALYZE $stmt" should discard select's output, but it should still evaluate the output columns (otherwise, subqueries in select list are not executed) - SHOW EXPLAIN's code practice of calling JOIN::save_explain_data() after JOIN::exec() is disastrous for ANALYZE, because it resets all counters after the first execution. It is stopped = "Late" test_if_skip_sort_order() calls explicitly update their part of the query plan. = Also, I had to rewrite I_S optimization to actually have optimization and execution stages.
This commit is contained in:
@@ -61,3 +61,17 @@ id select_type table type possible_keys key key_len ref rows r_rows filtered r_f
|
|||||||
NULL UNION RESULT <union1,2> ALL NULL NULL NULL NULL NULL 5 NULL NULL
|
NULL UNION RESULT <union1,2> ALL NULL NULL NULL NULL NULL 5 NULL NULL
|
||||||
drop table t1;
|
drop table t1;
|
||||||
drop table t0;
|
drop table t0;
|
||||||
|
#
|
||||||
|
# Try a subquery.
|
||||||
|
#
|
||||||
|
create table t0 (a int, b int);
|
||||||
|
insert into t0 values
|
||||||
|
(0,0),(1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8),(9,9);
|
||||||
|
create table t1 (a int, b int);
|
||||||
|
insert into t1 values (1,1),(2,2),(3,3);
|
||||||
|
# See .test file for the right values of r_rows and r_filtered.
|
||||||
|
analyze select a, a in (select t0.b from t0 where t0.b+1=t1.b+1) from t1;
|
||||||
|
id select_type table type possible_keys key key_len ref rows r_rows filtered r_filtered Extra
|
||||||
|
1 PRIMARY t1 ALL NULL NULL NULL NULL 3 3 100.00 100.00
|
||||||
|
2 DEPENDENT SUBQUERY t0 ALL NULL NULL NULL NULL 10 3 100.00 33.33 Using where
|
||||||
|
drop table t0,t1;
|
||||||
|
@@ -641,7 +641,7 @@ set debug_dbug='+d,show_explain_probe_join_exec_start';
|
|||||||
SHOW INDEX FROM t1;
|
SHOW INDEX FROM t1;
|
||||||
show explain for $thr2;
|
show explain for $thr2;
|
||||||
id select_type table type possible_keys key key_len ref rows Extra
|
id select_type table type possible_keys key key_len ref rows Extra
|
||||||
1 SIMPLE STATISTICS ALL NULL NULL NULL NULL NULL Skip_open_table; Scanned all databases
|
1 SIMPLE STATISTICS ALL NULL TABLE_SCHEMA,TABLE_NAME NULL NULL NULL Open_full_table; Scanned 0 databases
|
||||||
Warnings:
|
Warnings:
|
||||||
Note 1003 SHOW INDEX FROM t1
|
Note 1003 SHOW INDEX FROM t1
|
||||||
Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment Index_comment
|
Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment Index_comment
|
||||||
|
@@ -34,5 +34,29 @@ insert into t1 select a,a from t0;
|
|||||||
analyze (select * from t1 A where a<5) union (select * from t1 B where a in (5,6));
|
analyze (select * from t1 A where a<5) union (select * from t1 B where a in (5,6));
|
||||||
analyze (select * from t1 A where a<5) union (select * from t1 B where a in (1,2));
|
analyze (select * from t1 A where a<5) union (select * from t1 B where a in (1,2));
|
||||||
drop table t1;
|
drop table t1;
|
||||||
|
|
||||||
drop table t0;
|
drop table t0;
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # Try a subquery.
|
||||||
|
--echo #
|
||||||
|
create table t0 (a int, b int);
|
||||||
|
insert into t0 values
|
||||||
|
(0,0),(1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8),(9,9);
|
||||||
|
|
||||||
|
create table t1 (a int, b int);
|
||||||
|
insert into t1 values (1,1),(2,2),(3,3);
|
||||||
|
|
||||||
|
#
|
||||||
|
# t1 t0
|
||||||
|
# a=1 (0,1) 2 rows
|
||||||
|
# a=2 (0,1,2) 3 rows
|
||||||
|
# a=3 (0,1,2,3) 4 rows
|
||||||
|
#
|
||||||
|
# TOTAL TOTAL= 9 rows. 3 executions, avg=3 rows.
|
||||||
|
# WHERE is satisfied for 1 row per query, which gives filtered=33.3
|
||||||
|
|
||||||
|
--echo # See .test file for the right values of r_rows and r_filtered.
|
||||||
|
analyze select a, a in (select t0.b from t0 where t0.b+1=t1.b+1) from t1;
|
||||||
|
|
||||||
|
drop table t0,t1;
|
||||||
|
|
||||||
|
@@ -210,6 +210,42 @@ public:
|
|||||||
virtual enum enum_protocol_type type() { return PROTOCOL_BINARY; };
|
virtual enum enum_protocol_type type() { return PROTOCOL_BINARY; };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
A helper for "ANALYZE $stmt" which looks a real network procotol but doesn't
|
||||||
|
write results to the network.
|
||||||
|
|
||||||
|
At first glance, class select_send looks like a more appropriate place to
|
||||||
|
implement the "write nothing" hook. This is not true, because
|
||||||
|
- we need to evaluate the value of every item, and do it the way
|
||||||
|
select_send does it (i.e. call item->val_int() or val_real() or...)
|
||||||
|
- select_send::send_data() has some other code, like telling the storage
|
||||||
|
engine that the row can be unlocked. We want to keep that also.
|
||||||
|
as a result, "ANALYZE $stmt" uses a select_send_analyze which still uses
|
||||||
|
select_send::send_data() & co., and also uses Protocol_discard object.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class Protocol_discard : public Protocol_text
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Protocol_discard(THD *thd_arg) : Protocol_text(thd_arg) {}
|
||||||
|
/* The real writing is done only in write() */
|
||||||
|
virtual bool write() { return 0; }
|
||||||
|
virtual bool send_result_set_metadata(List<Item> *list, uint flags)
|
||||||
|
{
|
||||||
|
// Don't pas Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF flags
|
||||||
|
return Protocol_text::send_result_set_metadata(list, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// send_error is intentionally not overloaded.
|
||||||
|
virtual bool send_eof(uint server_status, uint statement_warn_count)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
void send_warning(THD *thd, uint sql_errno, const char *err=0);
|
void send_warning(THD *thd, uint sql_errno, const char *err=0);
|
||||||
bool net_send_error(THD *thd, uint sql_errno, const char *err,
|
bool net_send_error(THD *thd, uint sql_errno, const char *err,
|
||||||
const char* sqlstate);
|
const char* sqlstate);
|
||||||
|
@@ -3959,19 +3959,21 @@ public:
|
|||||||
virtual void cleanup();
|
virtual void cleanup();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
We need this class, because select_send::send_eof() will call ::my_eof.
|
||||||
|
|
||||||
|
See also class Protocol_discard.
|
||||||
|
*/
|
||||||
|
|
||||||
class select_send_analyze : public select_send
|
class select_send_analyze : public select_send
|
||||||
{
|
{
|
||||||
bool discard_data;
|
|
||||||
bool send_result_set_metadata(List<Item> &list, uint flags) { return 0; }
|
bool send_result_set_metadata(List<Item> &list, uint flags) { return 0; }
|
||||||
/*
|
|
||||||
ANALYZE-todo: we should call val_int() (or val_str() or whatever) to
|
|
||||||
compute the columns. If we don't, it's not full execution.
|
|
||||||
*/
|
|
||||||
int send_data(List<Item> &items) { return 0; }
|
|
||||||
bool send_eof() { return 0; }
|
bool send_eof() { return 0; }
|
||||||
void abort_result_set() {}
|
void abort_result_set() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class select_to_file :public select_result_interceptor {
|
class select_to_file :public select_result_interceptor {
|
||||||
protected:
|
protected:
|
||||||
sql_exchange *exchange;
|
sql_exchange *exchange;
|
||||||
|
@@ -345,6 +345,13 @@ int Explain_node::print_explain_for_children(Explain_query *query,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Explain_select::replace_table(uint idx, Explain_table_access *new_tab)
|
||||||
|
{
|
||||||
|
delete join_tabs[idx];
|
||||||
|
join_tabs[idx]= new_tab;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Explain_select::~Explain_select()
|
Explain_select::~Explain_select()
|
||||||
{
|
{
|
||||||
if (join_tabs)
|
if (join_tabs)
|
||||||
|
@@ -130,6 +130,12 @@ public:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
This is used to save the results of "late" test_if_skip_sort_order() calls
|
||||||
|
that are made from JOIN::exec
|
||||||
|
*/
|
||||||
|
void replace_table(uint idx, Explain_table_access *new_tab);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
int select_id;
|
int select_id;
|
||||||
const char *select_type;
|
const char *select_type;
|
||||||
|
@@ -5260,10 +5260,13 @@ static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables)
|
|||||||
{
|
{
|
||||||
//psergey-todo: ANALYZE should hook in here...
|
//psergey-todo: ANALYZE should hook in here...
|
||||||
select_result *save_result;
|
select_result *save_result;
|
||||||
|
Protocol *save_protocol;
|
||||||
if (lex->analyze_stmt)
|
if (lex->analyze_stmt)
|
||||||
{
|
{
|
||||||
save_result= result;
|
save_result= result;
|
||||||
result= new select_send_analyze();
|
result= new select_send_analyze();
|
||||||
|
save_protocol= thd->protocol;
|
||||||
|
thd->protocol= new Protocol_discard(thd);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -5280,6 +5283,7 @@ static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables)
|
|||||||
result= save_result;
|
result= save_result;
|
||||||
if (!result && !(result= new select_send()))
|
if (!result && !(result= new select_send()))
|
||||||
return 1;
|
return 1;
|
||||||
|
thd->protocol= save_protocol;
|
||||||
thd->lex->explain->send_explain(thd);
|
thd->lex->explain->send_explain(thd);
|
||||||
|
|
||||||
if (result != lex->result)
|
if (result != lex->result)
|
||||||
|
@@ -1873,6 +1873,9 @@ TODO: make view to decide if it is possible to write to WHERE directly or make S
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((select_lex->options & OPTION_SCHEMA_TABLE))
|
||||||
|
optimize_schema_tables_reads(this);
|
||||||
|
|
||||||
tmp_having= having;
|
tmp_having= having;
|
||||||
if (select_options & SELECT_DESCRIBE)
|
if (select_options & SELECT_DESCRIBE)
|
||||||
{
|
{
|
||||||
@@ -1919,6 +1922,7 @@ setup_subq_exit:
|
|||||||
error= 0;
|
error= 0;
|
||||||
|
|
||||||
derived_exit:
|
derived_exit:
|
||||||
|
|
||||||
select_lex->mark_const_derived(zero_result_cause);
|
select_lex->mark_const_derived(zero_result_cause);
|
||||||
DBUG_RETURN(0);
|
DBUG_RETURN(0);
|
||||||
}
|
}
|
||||||
@@ -2059,6 +2063,7 @@ int JOIN::init_execution()
|
|||||||
&join_tab[const_tables].table->
|
&join_tab[const_tables].table->
|
||||||
keys_in_use_for_order_by))
|
keys_in_use_for_order_by))
|
||||||
order=0;
|
order=0;
|
||||||
|
join_tab[const_tables].update_explain_data(const_tables);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2383,10 +2388,12 @@ void JOIN::exec()
|
|||||||
|
|
||||||
if (!exec_saved_explain)
|
if (!exec_saved_explain)
|
||||||
{
|
{
|
||||||
|
#if 0
|
||||||
save_explain_data(thd->lex->explain, true /* can overwrite */,
|
save_explain_data(thd->lex->explain, true /* can overwrite */,
|
||||||
need_tmp,
|
need_tmp,
|
||||||
order != 0 && !skip_sort_order,
|
order != 0 && !skip_sort_order,
|
||||||
select_distinct);
|
select_distinct);
|
||||||
|
#endif
|
||||||
exec_saved_explain= true;
|
exec_saved_explain= true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2564,15 +2571,19 @@ void JOIN::exec_inner()
|
|||||||
simple_order= simple_group;
|
simple_order= simple_group;
|
||||||
skip_sort_order= 0;
|
skip_sort_order= 0;
|
||||||
}
|
}
|
||||||
|
bool made_call= false;
|
||||||
if (order &&
|
if (order &&
|
||||||
(order != group_list || !(select_options & SELECT_BIG_RESULT)) &&
|
(order != group_list || !(select_options & SELECT_BIG_RESULT)) &&
|
||||||
(const_tables == table_count ||
|
(const_tables == table_count ||
|
||||||
((simple_order || skip_sort_order) &&
|
((simple_order || skip_sort_order) &&
|
||||||
test_if_skip_sort_order(&join_tab[const_tables], order,
|
(made_call=true) &&
|
||||||
|
test_if_skip_sort_order(&join_tab[const_tables], order,
|
||||||
select_limit, 0,
|
select_limit, 0,
|
||||||
&join_tab[const_tables].table->
|
&join_tab[const_tables].table->
|
||||||
keys_in_use_for_query))))
|
keys_in_use_for_query))))
|
||||||
order=0;
|
order=0;
|
||||||
|
if (made_call)
|
||||||
|
join_tab[const_tables].update_explain_data(const_tables);
|
||||||
having= tmp_having;
|
having= tmp_having;
|
||||||
select_describe(this, need_tmp,
|
select_describe(this, need_tmp,
|
||||||
order != 0 && !skip_sort_order,
|
order != 0 && !skip_sort_order,
|
||||||
@@ -20535,7 +20546,12 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order,
|
|||||||
test_if_skip_sort_order(tab,order,select_limit,0,
|
test_if_skip_sort_order(tab,order,select_limit,0,
|
||||||
is_order_by ? &table->keys_in_use_for_order_by :
|
is_order_by ? &table->keys_in_use_for_order_by :
|
||||||
&table->keys_in_use_for_group_by))
|
&table->keys_in_use_for_group_by))
|
||||||
|
{
|
||||||
|
tab->update_explain_data(join->const_tables);
|
||||||
DBUG_RETURN(0);
|
DBUG_RETURN(0);
|
||||||
|
}
|
||||||
|
tab->update_explain_data(join->const_tables);
|
||||||
|
|
||||||
for (ORDER *ord= join->order; ord; ord= ord->next)
|
for (ORDER *ord= join->order; ord; ord= ord->next)
|
||||||
length++;
|
length++;
|
||||||
if (!(join->sortorder=
|
if (!(join->sortorder=
|
||||||
@@ -23248,6 +23264,416 @@ void append_possible_keys(String *str, TABLE *table, key_map possible_keys)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: this function is only applicable for the first non-const optimization
|
||||||
|
// join tab.
|
||||||
|
void JOIN_TAB::update_explain_data(uint idx)
|
||||||
|
{
|
||||||
|
if (this == first_breadth_first_tab(join, WALK_OPTIMIZATION_TABS) &&
|
||||||
|
join->select_lex->select_number != INT_MAX &&
|
||||||
|
join->select_lex->select_number != UINT_MAX)
|
||||||
|
{
|
||||||
|
Explain_table_access *eta= new Explain_table_access();
|
||||||
|
JOIN_TAB* const first_top_tab= first_breadth_first_tab(join, WALK_OPTIMIZATION_TABS);
|
||||||
|
save_explain_data(eta, join->const_table_map, join->select_distinct, first_top_tab);
|
||||||
|
|
||||||
|
Explain_select *sel= join->thd->lex->explain->get_select(join->select_lex->select_number);
|
||||||
|
sel->replace_table(idx, eta);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void JOIN_TAB::save_explain_data(Explain_table_access *eta, table_map prefix_tables,
|
||||||
|
bool distinct, JOIN_TAB *first_top_tab)
|
||||||
|
{
|
||||||
|
int quick_type;
|
||||||
|
const CHARSET_INFO *cs= system_charset_info;
|
||||||
|
|
||||||
|
JOIN_TAB *tab= this;
|
||||||
|
THD *thd=join->thd;
|
||||||
|
|
||||||
|
TABLE *table=tab->table;
|
||||||
|
TABLE_LIST *table_list= tab->table->pos_in_table_list;
|
||||||
|
char buff4[512];
|
||||||
|
my_bool key_read;
|
||||||
|
char table_name_buffer[SAFE_NAME_LEN];
|
||||||
|
String tmp4(buff4,sizeof(buff4),cs);
|
||||||
|
KEY *key_info= 0;
|
||||||
|
uint key_len= 0;
|
||||||
|
tmp4.length(0);
|
||||||
|
quick_type= -1;
|
||||||
|
QUICK_SELECT_I *quick= NULL;
|
||||||
|
|
||||||
|
eta->key.set(thd->mem_root, NULL, (uint)-1);
|
||||||
|
eta->quick_info= NULL;
|
||||||
|
|
||||||
|
tab->tracker= &eta->tracker;
|
||||||
|
|
||||||
|
/* id */
|
||||||
|
if (tab->bush_root_tab)
|
||||||
|
{
|
||||||
|
JOIN_TAB *first_sibling= tab->bush_root_tab->bush_children->start;
|
||||||
|
eta->sjm_nest_select_id= first_sibling->emb_sj_nest->sj_subq_pred->get_identifier();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
eta->sjm_nest_select_id= 0;
|
||||||
|
|
||||||
|
/* select_type is kept in Explain_select */
|
||||||
|
|
||||||
|
/* 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);
|
||||||
|
eta->table_name.copy(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());
|
||||||
|
eta->table_name.copy(table_name_buffer, len, cs);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TABLE_LIST *real_table= table->pos_in_table_list;
|
||||||
|
eta->table_name.copy(real_table->alias, strlen(real_table->alias), cs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* "partitions" column */
|
||||||
|
{
|
||||||
|
#ifdef WITH_PARTITION_STORAGE_ENGINE
|
||||||
|
partition_info *part_info;
|
||||||
|
if (!table->derived_select_number &&
|
||||||
|
(part_info= table->part_info))
|
||||||
|
{
|
||||||
|
make_used_partitions_str(part_info, &eta->used_partitions);
|
||||||
|
eta->used_partitions_set= true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
eta->used_partitions_set= false;
|
||||||
|
#else
|
||||||
|
/* just produce empty column if partitioning is not compiled in */
|
||||||
|
eta->used_partitions_set= false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/* "type" column */
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
eta->type= tab_type;
|
||||||
|
|
||||||
|
/* Build "possible_keys" value */
|
||||||
|
append_possible_keys(&eta->possible_keys_str, table, tab->keys);
|
||||||
|
|
||||||
|
/* Build "key", "key_len", and "ref" */
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
In STRAIGHT_JOIN queries, there can be join tabs with JT_CONST type
|
||||||
|
that still have quick selects.
|
||||||
|
*/
|
||||||
|
if (tab->select && tab->select->quick && tab_type != JT_CONST)
|
||||||
|
{
|
||||||
|
eta->quick_info= tab->select->quick->get_explain(thd->mem_root);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key_info) /* 'index' or 'ref' access */
|
||||||
|
{
|
||||||
|
eta->key.set(thd->mem_root, key_info->name, key_len);
|
||||||
|
|
||||||
|
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 (tab_type == JT_HASH_NEXT) /* full index scan + hash join */
|
||||||
|
{
|
||||||
|
eta->hash_next_key.set(thd->mem_root,
|
||||||
|
table->key_info[tab->index].name,
|
||||||
|
table->key_info[tab->index].key_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key_info)
|
||||||
|
{
|
||||||
|
if (key_info && tab_type != JT_NEXT)
|
||||||
|
{
|
||||||
|
eta->ref.copy(tmp4);
|
||||||
|
eta->ref_set= true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
eta->ref_set= false;
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
IS_table_read_plan *is_table_read_plan= table_list->is_table_read_plan;
|
||||||
|
const char *tmp_buff;
|
||||||
|
int f_idx;
|
||||||
|
StringBuffer<64> key_name_buf;
|
||||||
|
if (is_table_read_plan->has_db_lookup_value())
|
||||||
|
{
|
||||||
|
/* The "key" has the name of the column referring to the database */
|
||||||
|
f_idx= table_list->schema_table->idx_field1;
|
||||||
|
tmp_buff= table_list->schema_table->fields_info[f_idx].field_name;
|
||||||
|
key_name_buf.append(tmp_buff, strlen(tmp_buff), cs);
|
||||||
|
}
|
||||||
|
if (is_table_read_plan->has_table_lookup_value())
|
||||||
|
{
|
||||||
|
if (is_table_read_plan->has_db_lookup_value())
|
||||||
|
key_name_buf.append(',');
|
||||||
|
|
||||||
|
f_idx= table_list->schema_table->idx_field2;
|
||||||
|
tmp_buff= table_list->schema_table->fields_info[f_idx].field_name;
|
||||||
|
key_name_buf.append(tmp_buff, strlen(tmp_buff), cs);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key_name_buf.length())
|
||||||
|
eta->key.set(thd->mem_root, key_name_buf.c_ptr_safe(), -1);
|
||||||
|
}
|
||||||
|
eta->ref_set= false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* "rows" */
|
||||||
|
if (table_list /* SJM bushes don't have table_list */ &&
|
||||||
|
table_list->schema_table)
|
||||||
|
{
|
||||||
|
/* I_S tables have rows=extra=NULL */
|
||||||
|
eta->rows_set= false;
|
||||||
|
eta->filtered_set= false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
double examined_rows= tab->get_examined_rows();
|
||||||
|
|
||||||
|
eta->rows_set= true;
|
||||||
|
eta->rows= (ha_rows) examined_rows;
|
||||||
|
|
||||||
|
/* "filtered" */
|
||||||
|
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);
|
||||||
|
eta->filtered_set= true;
|
||||||
|
eta->filtered= f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Build "Extra" field and save it */
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
eta->push_extra(tab->info);
|
||||||
|
}
|
||||||
|
else if (tab->packed_info & TAB_INFO_HAVE_VALUE)
|
||||||
|
{
|
||||||
|
if (tab->packed_info & TAB_INFO_USING_INDEX)
|
||||||
|
eta->push_extra(ET_USING_INDEX);
|
||||||
|
if (tab->packed_info & TAB_INFO_USING_WHERE)
|
||||||
|
eta->push_extra(ET_USING_WHERE);
|
||||||
|
if (tab->packed_info & TAB_INFO_FULL_SCAN_ON_NULL)
|
||||||
|
eta->push_extra(ET_FULL_SCAN_ON_NULL_KEY);
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
eta->push_extra(ET_USING_INDEX_CONDITION);
|
||||||
|
else if (tab->cache_idx_cond)
|
||||||
|
eta->push_extra(ET_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)
|
||||||
|
{
|
||||||
|
eta->push_extra(ET_USING);
|
||||||
|
}
|
||||||
|
if (tab->select)
|
||||||
|
{
|
||||||
|
if (tab->use_quick == 2)
|
||||||
|
{
|
||||||
|
eta->push_extra(ET_RANGE_CHECKED_FOR_EACH_RECORD);
|
||||||
|
eta->range_checked_map= tab->keys;
|
||||||
|
}
|
||||||
|
else if (tab->select->cond ||
|
||||||
|
(tab->cache_select && tab->cache_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)
|
||||||
|
{
|
||||||
|
eta->push_extra(ET_USING_WHERE_WITH_PUSHED_CONDITION);
|
||||||
|
/*
|
||||||
|
psergey-todo: what to do? This was useful with NDB only.
|
||||||
|
|
||||||
|
if (explain_flags & DESCRIBE_EXTENDED)
|
||||||
|
{
|
||||||
|
extra.append(STRING_WITH_LEN(": "));
|
||||||
|
((COND *)pushed_cond)->print(&extra, QT_ORDINARY);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
else
|
||||||
|
eta->push_extra(ET_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)
|
||||||
|
eta->push_extra(ET_SKIP_OPEN_TABLE);
|
||||||
|
else if (table_list->table_open_method == OPEN_FRM_ONLY)
|
||||||
|
eta->push_extra(ET_OPEN_FRM_ONLY);
|
||||||
|
else
|
||||||
|
eta->push_extra(ET_OPEN_FULL_TABLE);
|
||||||
|
/* psergey-note: the following has a bug.*/
|
||||||
|
if (table_list->is_table_read_plan->has_db_lookup_value() &&
|
||||||
|
table_list->is_table_read_plan->has_table_lookup_value())
|
||||||
|
eta->push_extra(ET_SCANNED_0_DATABASES);
|
||||||
|
else if (table_list->is_table_read_plan->has_db_lookup_value() ||
|
||||||
|
table_list->is_table_read_plan->has_table_lookup_value())
|
||||||
|
eta->push_extra(ET_SCANNED_1_DATABASE);
|
||||||
|
else
|
||||||
|
eta->push_extra(ET_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;
|
||||||
|
eta->push_extra(ET_USING_INDEX_FOR_GROUP_BY);
|
||||||
|
eta->loose_scan_is_scanning= qgs->loose_scan_is_scanning();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
eta->push_extra(ET_USING_INDEX);
|
||||||
|
}
|
||||||
|
if (table->reginfo.not_exists_optimize)
|
||||||
|
eta->push_extra(ET_NOT_EXISTS);
|
||||||
|
|
||||||
|
if (quick_type == QUICK_SELECT_I::QS_TYPE_RANGE)
|
||||||
|
{
|
||||||
|
explain_append_mrr_info((QUICK_RANGE_SELECT*)(tab->select->quick),
|
||||||
|
&eta->mrr_type);
|
||||||
|
if (eta->mrr_type.length() > 0)
|
||||||
|
eta->push_extra(ET_USING_MRR);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (distinct & test_all_bits(prefix_tables, join->select_list_used_tables))
|
||||||
|
eta->push_extra(ET_DISTINCT);
|
||||||
|
if (tab->loosescan_match_tab)
|
||||||
|
{
|
||||||
|
eta->push_extra(ET_LOOSESCAN);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tab->first_weedout_table)
|
||||||
|
eta->push_extra(ET_START_TEMPORARY);
|
||||||
|
if (tab->check_weed_out_table)
|
||||||
|
eta->push_extra(ET_END_TEMPORARY);
|
||||||
|
else if (tab->do_firstmatch)
|
||||||
|
{
|
||||||
|
if (tab->do_firstmatch == /*join->join_tab*/ first_top_tab - 1)
|
||||||
|
eta->push_extra(ET_FIRST_MATCH);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
eta->push_extra(ET_FIRST_MATCH);
|
||||||
|
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);
|
||||||
|
eta->firstmatch_table_name.append(namebuf, len);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
eta->firstmatch_table_name.append(prev_table->pos_in_table_list->alias);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint part= 0; part < tab->ref.key_parts; part++)
|
||||||
|
{
|
||||||
|
if (tab->ref.cond_guards[part])
|
||||||
|
{
|
||||||
|
eta->push_extra(ET_FULL_SCAN_ON_NULL_KEY);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tab->cache)
|
||||||
|
{
|
||||||
|
eta->push_extra(ET_USING_JOIN_BUFFER);
|
||||||
|
tab->cache->save_explain_data(&eta->bka_type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Save Query Plan Footprint
|
Save Query Plan Footprint
|
||||||
@@ -23262,9 +23688,6 @@ int JOIN::save_explain_data_intern(Explain_query *output, bool need_tmp_table,
|
|||||||
{
|
{
|
||||||
Explain_node *explain_node;
|
Explain_node *explain_node;
|
||||||
JOIN *join= this; /* Legacy: this code used to be a non-member function */
|
JOIN *join= this; /* Legacy: this code used to be a non-member function */
|
||||||
THD *thd=join->thd;
|
|
||||||
const CHARSET_INFO *cs= system_charset_info;
|
|
||||||
int quick_type;
|
|
||||||
int error= 0;
|
int error= 0;
|
||||||
DBUG_ENTER("JOIN::save_explain_data_intern");
|
DBUG_ENTER("JOIN::save_explain_data_intern");
|
||||||
DBUG_PRINT("info", ("Select 0x%lx, type %s, message %s",
|
DBUG_PRINT("info", ("Select 0x%lx, type %s, message %s",
|
||||||
@@ -23305,27 +23728,9 @@ int JOIN::save_explain_data_intern(Explain_query *output, bool need_tmp_table,
|
|||||||
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))
|
||||||
{
|
{
|
||||||
uint select_id;
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
select_id= join->select_lex->select_number;
|
|
||||||
|
|
||||||
TABLE *table=tab->table;
|
|
||||||
TABLE_LIST *table_list= tab->table->pos_in_table_list;
|
|
||||||
char buff4[512];
|
|
||||||
my_bool key_read;
|
|
||||||
char table_name_buffer[SAFE_NAME_LEN];
|
|
||||||
String tmp4(buff4,sizeof(buff4),cs);
|
|
||||||
KEY *key_info= 0;
|
|
||||||
uint key_len= 0;
|
|
||||||
tmp4.length(0);
|
|
||||||
quick_type= -1;
|
|
||||||
QUICK_SELECT_I *quick= NULL;
|
|
||||||
JOIN_TAB *saved_join_tab= NULL;
|
JOIN_TAB *saved_join_tab= NULL;
|
||||||
|
TABLE *table=tab->table;
|
||||||
|
|
||||||
/* Don't show eliminated tables */
|
/* Don't show eliminated tables */
|
||||||
if (table->map & join->eliminated_tables)
|
if (table->map & join->eliminated_tables)
|
||||||
@@ -23343,383 +23748,18 @@ int JOIN::save_explain_data_intern(Explain_query *output, bool need_tmp_table,
|
|||||||
|
|
||||||
Explain_table_access *eta= new (output->mem_root) Explain_table_access;
|
Explain_table_access *eta= new (output->mem_root) Explain_table_access;
|
||||||
xpl_sel->add_table(eta);
|
xpl_sel->add_table(eta);
|
||||||
eta->key.set(thd->mem_root, NULL, (uint)-1);
|
|
||||||
eta->quick_info= NULL;
|
|
||||||
|
|
||||||
tab->tracker= &eta->tracker;
|
tab->save_explain_data(eta, used_tables, distinct, first_top_tab);
|
||||||
|
|
||||||
/* id */
|
if (need_tmp_table)
|
||||||
if (tab->bush_root_tab)
|
|
||||||
eta->sjm_nest_select_id= select_id;
|
|
||||||
else
|
|
||||||
eta->sjm_nest_select_id= 0;
|
|
||||||
|
|
||||||
/* select_type */
|
|
||||||
xpl_sel->select_type= join->select_lex->type;
|
|
||||||
|
|
||||||
/* table */
|
|
||||||
if (table->derived_select_number)
|
|
||||||
{
|
{
|
||||||
/* Derived table name generation */
|
need_tmp_table=0;
|
||||||
int len= my_snprintf(table_name_buffer, sizeof(table_name_buffer)-1,
|
xpl_sel->using_temporary= true;
|
||||||
"<derived%u>",
|
|
||||||
table->derived_select_number);
|
|
||||||
eta->table_name.copy(table_name_buffer, len, cs);
|
|
||||||
}
|
}
|
||||||
else if (tab->bush_children)
|
if (need_order)
|
||||||
{
|
{
|
||||||
JOIN_TAB *ctab= tab->bush_children->start;
|
need_order=0;
|
||||||
/* table */
|
xpl_sel->using_filesort= true;
|
||||||
int len= my_snprintf(table_name_buffer,
|
|
||||||
sizeof(table_name_buffer)-1,
|
|
||||||
"<subquery%d>",
|
|
||||||
ctab->emb_sj_nest->sj_subq_pred->get_identifier());
|
|
||||||
eta->table_name.copy(table_name_buffer, len, cs);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
TABLE_LIST *real_table= table->pos_in_table_list;
|
|
||||||
eta->table_name.copy(real_table->alias, strlen(real_table->alias), cs);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* "partitions" column */
|
|
||||||
{
|
|
||||||
#ifdef WITH_PARTITION_STORAGE_ENGINE
|
|
||||||
partition_info *part_info;
|
|
||||||
if (!table->derived_select_number &&
|
|
||||||
(part_info= table->part_info))
|
|
||||||
{
|
|
||||||
make_used_partitions_str(part_info, &eta->used_partitions);
|
|
||||||
eta->used_partitions_set= true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
eta->used_partitions_set= false;
|
|
||||||
#else
|
|
||||||
/* just produce empty column if partitioning is not compiled in */
|
|
||||||
eta->used_partitions_set= false;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/* "type" column */
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
eta->type= tab_type;
|
|
||||||
|
|
||||||
/* Build "possible_keys" value */
|
|
||||||
append_possible_keys(&eta->possible_keys_str, table, tab->keys);
|
|
||||||
|
|
||||||
/* Build "key", "key_len", and "ref" */
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
In STRAIGHT_JOIN queries, there can be join tabs with JT_CONST type
|
|
||||||
that still have quick selects.
|
|
||||||
*/
|
|
||||||
if (tab->select && tab->select->quick && tab_type != JT_CONST)
|
|
||||||
{
|
|
||||||
eta->quick_info= tab->select->quick->get_explain(thd->mem_root);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (key_info) /* 'index' or 'ref' access */
|
|
||||||
{
|
|
||||||
eta->key.set(thd->mem_root, key_info->name, key_len);
|
|
||||||
|
|
||||||
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 (tab_type == JT_HASH_NEXT) /* full index scan + hash join */
|
|
||||||
{
|
|
||||||
eta->hash_next_key.set(thd->mem_root,
|
|
||||||
table->key_info[tab->index].name,
|
|
||||||
table->key_info[tab->index].key_length);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (key_info)
|
|
||||||
{
|
|
||||||
if (key_info && tab_type != JT_NEXT)
|
|
||||||
{
|
|
||||||
eta->ref.copy(tmp4);
|
|
||||||
eta->ref_set= true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
eta->ref_set= false;
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
StringBuffer<64> key_name_buf;
|
|
||||||
if (table_list->has_db_lookup_value)
|
|
||||||
{
|
|
||||||
/* The "key" has the name of the column referring to the database */
|
|
||||||
f_idx= table_list->schema_table->idx_field1;
|
|
||||||
tmp_buff= table_list->schema_table->fields_info[f_idx].field_name;
|
|
||||||
key_name_buf.append(tmp_buff, strlen(tmp_buff), cs);
|
|
||||||
}
|
|
||||||
if (table_list->has_table_lookup_value)
|
|
||||||
{
|
|
||||||
if (table_list->has_db_lookup_value)
|
|
||||||
key_name_buf.append(',');
|
|
||||||
|
|
||||||
f_idx= table_list->schema_table->idx_field2;
|
|
||||||
tmp_buff= table_list->schema_table->fields_info[f_idx].field_name;
|
|
||||||
key_name_buf.append(tmp_buff, strlen(tmp_buff), cs);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (key_name_buf.length())
|
|
||||||
eta->key.set(thd->mem_root, key_name_buf.c_ptr_safe(), -1);
|
|
||||||
}
|
|
||||||
eta->ref_set= false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* "rows" */
|
|
||||||
if (table_list /* SJM bushes don't have table_list */ &&
|
|
||||||
table_list->schema_table)
|
|
||||||
{
|
|
||||||
/* I_S tables have rows=extra=NULL */
|
|
||||||
eta->rows_set= false;
|
|
||||||
eta->filtered_set= false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
double examined_rows= tab->get_examined_rows();
|
|
||||||
|
|
||||||
eta->rows_set= true;
|
|
||||||
eta->rows= (ha_rows) examined_rows;
|
|
||||||
|
|
||||||
/* "filtered" */
|
|
||||||
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);
|
|
||||||
eta->filtered_set= true;
|
|
||||||
eta->filtered= f;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Build "Extra" field and save it */
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
eta->push_extra(tab->info);
|
|
||||||
}
|
|
||||||
else if (tab->packed_info & TAB_INFO_HAVE_VALUE)
|
|
||||||
{
|
|
||||||
if (tab->packed_info & TAB_INFO_USING_INDEX)
|
|
||||||
eta->push_extra(ET_USING_INDEX);
|
|
||||||
if (tab->packed_info & TAB_INFO_USING_WHERE)
|
|
||||||
eta->push_extra(ET_USING_WHERE);
|
|
||||||
if (tab->packed_info & TAB_INFO_FULL_SCAN_ON_NULL)
|
|
||||||
eta->push_extra(ET_FULL_SCAN_ON_NULL_KEY);
|
|
||||||
}
|
|
||||||
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)
|
|
||||||
eta->push_extra(ET_USING_INDEX_CONDITION);
|
|
||||||
else if (tab->cache_idx_cond)
|
|
||||||
eta->push_extra(ET_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)
|
|
||||||
{
|
|
||||||
eta->push_extra(ET_USING);
|
|
||||||
}
|
|
||||||
if (tab->select)
|
|
||||||
{
|
|
||||||
if (tab->use_quick == 2)
|
|
||||||
{
|
|
||||||
eta->push_extra(ET_RANGE_CHECKED_FOR_EACH_RECORD);
|
|
||||||
eta->range_checked_map= tab->keys;
|
|
||||||
}
|
|
||||||
else if (tab->select->cond ||
|
|
||||||
(tab->cache_select && tab->cache_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)
|
|
||||||
{
|
|
||||||
eta->push_extra(ET_USING_WHERE_WITH_PUSHED_CONDITION);
|
|
||||||
/*
|
|
||||||
psergey-todo: what to do? This was useful with NDB only.
|
|
||||||
|
|
||||||
if (explain_flags & DESCRIBE_EXTENDED)
|
|
||||||
{
|
|
||||||
extra.append(STRING_WITH_LEN(": "));
|
|
||||||
((COND *)pushed_cond)->print(&extra, QT_ORDINARY);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
else
|
|
||||||
eta->push_extra(ET_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)
|
|
||||||
eta->push_extra(ET_SKIP_OPEN_TABLE);
|
|
||||||
else if (table_list->table_open_method == OPEN_FRM_ONLY)
|
|
||||||
eta->push_extra(ET_OPEN_FRM_ONLY);
|
|
||||||
else
|
|
||||||
eta->push_extra(ET_OPEN_FULL_TABLE);
|
|
||||||
/* psergey-note: the following has a bug.*/
|
|
||||||
if (table_list->has_db_lookup_value &&
|
|
||||||
table_list->has_table_lookup_value)
|
|
||||||
eta->push_extra(ET_SCANNED_0_DATABASES);
|
|
||||||
else if (table_list->has_db_lookup_value ||
|
|
||||||
table_list->has_table_lookup_value)
|
|
||||||
eta->push_extra(ET_SCANNED_1_DATABASE);
|
|
||||||
else
|
|
||||||
eta->push_extra(ET_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;
|
|
||||||
eta->push_extra(ET_USING_INDEX_FOR_GROUP_BY);
|
|
||||||
eta->loose_scan_is_scanning= qgs->loose_scan_is_scanning();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
eta->push_extra(ET_USING_INDEX);
|
|
||||||
}
|
|
||||||
if (table->reginfo.not_exists_optimize)
|
|
||||||
eta->push_extra(ET_NOT_EXISTS);
|
|
||||||
|
|
||||||
if (quick_type == QUICK_SELECT_I::QS_TYPE_RANGE)
|
|
||||||
{
|
|
||||||
explain_append_mrr_info((QUICK_RANGE_SELECT*)(tab->select->quick),
|
|
||||||
&eta->mrr_type);
|
|
||||||
if (eta->mrr_type.length() > 0)
|
|
||||||
eta->push_extra(ET_USING_MRR);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (need_tmp_table)
|
|
||||||
{
|
|
||||||
need_tmp_table=0;
|
|
||||||
xpl_sel->using_temporary= true;
|
|
||||||
}
|
|
||||||
if (need_order)
|
|
||||||
{
|
|
||||||
need_order=0;
|
|
||||||
xpl_sel->using_filesort= true;
|
|
||||||
}
|
|
||||||
if (distinct & test_all_bits(used_tables,
|
|
||||||
join->select_list_used_tables))
|
|
||||||
eta->push_extra(ET_DISTINCT);
|
|
||||||
if (tab->loosescan_match_tab)
|
|
||||||
{
|
|
||||||
eta->push_extra(ET_LOOSESCAN);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tab->first_weedout_table)
|
|
||||||
eta->push_extra(ET_START_TEMPORARY);
|
|
||||||
if (tab->check_weed_out_table)
|
|
||||||
eta->push_extra(ET_END_TEMPORARY);
|
|
||||||
else if (tab->do_firstmatch)
|
|
||||||
{
|
|
||||||
if (tab->do_firstmatch == /*join->join_tab*/ first_top_tab - 1)
|
|
||||||
eta->push_extra(ET_FIRST_MATCH);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
eta->push_extra(ET_FIRST_MATCH);
|
|
||||||
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);
|
|
||||||
eta->firstmatch_table_name.append(namebuf, len);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
eta->firstmatch_table_name.append(prev_table->pos_in_table_list->alias);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (uint part= 0; part < tab->ref.key_parts; part++)
|
|
||||||
{
|
|
||||||
if (tab->ref.cond_guards[part])
|
|
||||||
{
|
|
||||||
eta->push_extra(ET_FULL_SCAN_ON_NULL_KEY);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tab->cache)
|
|
||||||
{
|
|
||||||
eta->push_extra(ET_USING_JOIN_BUFFER);
|
|
||||||
tab->cache->save_explain_data(&eta->bka_type);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (saved_join_tab)
|
if (saved_join_tab)
|
||||||
|
@@ -537,6 +537,11 @@ typedef struct st_join_table {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void remove_redundant_bnl_scan_conds();
|
void remove_redundant_bnl_scan_conds();
|
||||||
|
|
||||||
|
void save_explain_data(Explain_table_access *eta, table_map prefix_tables,
|
||||||
|
bool distinct, struct st_join_table *first_top_tab);
|
||||||
|
|
||||||
|
void update_explain_data(uint idx);
|
||||||
} JOIN_TAB;
|
} JOIN_TAB;
|
||||||
|
|
||||||
|
|
||||||
|
198
sql/sql_show.cc
198
sql/sql_show.cc
@@ -121,12 +121,6 @@ append_algorithm(TABLE_LIST *table, String *buff);
|
|||||||
|
|
||||||
static COND * make_cond_for_info_schema(COND *cond, TABLE_LIST *table);
|
static COND * make_cond_for_info_schema(COND *cond, TABLE_LIST *table);
|
||||||
|
|
||||||
typedef struct st_lookup_field_values
|
|
||||||
{
|
|
||||||
LEX_STRING db_value, table_value;
|
|
||||||
bool wild_db_value, wild_table_value;
|
|
||||||
} LOOKUP_FIELD_VALUES;
|
|
||||||
|
|
||||||
bool get_lookup_field_values(THD *, COND *, TABLE_LIST *, LOOKUP_FIELD_VALUES *);
|
bool get_lookup_field_values(THD *, COND *, TABLE_LIST *, LOOKUP_FIELD_VALUES *);
|
||||||
|
|
||||||
/***************************************************************************
|
/***************************************************************************
|
||||||
@@ -4628,6 +4622,10 @@ public:
|
|||||||
from frm files and storage engine are filled by the function
|
from frm files and storage engine are filled by the function
|
||||||
get_all_tables().
|
get_all_tables().
|
||||||
|
|
||||||
|
@note This function assumes optimize_for_get_all_tables() has been
|
||||||
|
run for the table and produced a "read plan" in
|
||||||
|
tables->is_table_read_plan.
|
||||||
|
|
||||||
@param[in] thd thread handler
|
@param[in] thd thread handler
|
||||||
@param[in] tables I_S table
|
@param[in] tables I_S table
|
||||||
@param[in] cond 'WHERE' condition
|
@param[in] cond 'WHERE' condition
|
||||||
@@ -4644,16 +4642,16 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
|
|||||||
TABLE_LIST table_acl_check;
|
TABLE_LIST table_acl_check;
|
||||||
SELECT_LEX *lsel= tables->schema_select_lex;
|
SELECT_LEX *lsel= tables->schema_select_lex;
|
||||||
ST_SCHEMA_TABLE *schema_table= tables->schema_table;
|
ST_SCHEMA_TABLE *schema_table= tables->schema_table;
|
||||||
LOOKUP_FIELD_VALUES lookup_field_vals;
|
IS_table_read_plan *plan= tables->is_table_read_plan;
|
||||||
enum enum_schema_tables schema_table_idx;
|
enum enum_schema_tables schema_table_idx;
|
||||||
Dynamic_array<LEX_STRING*> db_names;
|
Dynamic_array<LEX_STRING*> db_names;
|
||||||
COND *partial_cond= 0;
|
Item *partial_cond= plan->partial_cond;
|
||||||
int error= 1;
|
int error= 1;
|
||||||
Open_tables_backup open_tables_state_backup;
|
Open_tables_backup open_tables_state_backup;
|
||||||
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
||||||
Security_context *sctx= thd->security_ctx;
|
Security_context *sctx= thd->security_ctx;
|
||||||
#endif
|
#endif
|
||||||
uint table_open_method;
|
uint table_open_method= tables->table_open_method;
|
||||||
bool can_deadlock;
|
bool can_deadlock;
|
||||||
DBUG_ENTER("get_all_tables");
|
DBUG_ENTER("get_all_tables");
|
||||||
|
|
||||||
@@ -4677,9 +4675,6 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
|
|||||||
thd->reset_n_backup_open_tables_state(&open_tables_state_backup);
|
thd->reset_n_backup_open_tables_state(&open_tables_state_backup);
|
||||||
|
|
||||||
schema_table_idx= get_schema_table_idx(schema_table);
|
schema_table_idx= get_schema_table_idx(schema_table);
|
||||||
tables->table_open_method= table_open_method=
|
|
||||||
get_table_open_method(tables, schema_table, schema_table_idx);
|
|
||||||
DBUG_PRINT("open_method", ("%d", tables->table_open_method));
|
|
||||||
/*
|
/*
|
||||||
this branch processes SHOW FIELDS, SHOW INDEXES commands.
|
this branch processes SHOW FIELDS, SHOW INDEXES commands.
|
||||||
see sql_parse.cc, prepare_schema_table() function where
|
see sql_parse.cc, prepare_schema_table() function where
|
||||||
@@ -4703,44 +4698,12 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
|
|||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (get_lookup_field_values(thd, cond, tables, &lookup_field_vals))
|
if (plan->no_rows)
|
||||||
{
|
{
|
||||||
error= 0;
|
error= 0;
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
DBUG_PRINT("info",("db_name='%s', table_name='%s'",
|
|
||||||
lookup_field_vals.db_value.str,
|
|
||||||
lookup_field_vals.table_value.str));
|
|
||||||
|
|
||||||
if (!lookup_field_vals.wild_db_value && !lookup_field_vals.wild_table_value)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
if lookup value is empty string then
|
|
||||||
it's impossible table name or db name
|
|
||||||
*/
|
|
||||||
if ((lookup_field_vals.db_value.str &&
|
|
||||||
!lookup_field_vals.db_value.str[0]) ||
|
|
||||||
(lookup_field_vals.table_value.str &&
|
|
||||||
!lookup_field_vals.table_value.str[0]))
|
|
||||||
{
|
|
||||||
error= 0;
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lookup_field_vals.db_value.length &&
|
|
||||||
!lookup_field_vals.wild_db_value)
|
|
||||||
tables->has_db_lookup_value= TRUE;
|
|
||||||
if (lookup_field_vals.table_value.length &&
|
|
||||||
!lookup_field_vals.wild_table_value)
|
|
||||||
tables->has_table_lookup_value= TRUE;
|
|
||||||
|
|
||||||
if (tables->has_db_lookup_value && tables->has_table_lookup_value)
|
|
||||||
partial_cond= 0;
|
|
||||||
else
|
|
||||||
partial_cond= make_cond_for_info_schema(cond, tables);
|
|
||||||
|
|
||||||
if (lex->describe)
|
if (lex->describe)
|
||||||
{
|
{
|
||||||
/* EXPLAIN SELECT */
|
/* EXPLAIN SELECT */
|
||||||
@@ -4750,7 +4713,7 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
|
|||||||
|
|
||||||
bzero((char*) &table_acl_check, sizeof(table_acl_check));
|
bzero((char*) &table_acl_check, sizeof(table_acl_check));
|
||||||
|
|
||||||
if (make_db_list(thd, &db_names, &lookup_field_vals))
|
if (make_db_list(thd, &db_names, &plan->lookup_field_vals))
|
||||||
goto err;
|
goto err;
|
||||||
for (size_t i=0; i < db_names.elements(); i++)
|
for (size_t i=0; i < db_names.elements(); i++)
|
||||||
{
|
{
|
||||||
@@ -4765,7 +4728,7 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
|
|||||||
{
|
{
|
||||||
Dynamic_array<LEX_STRING*> table_names;
|
Dynamic_array<LEX_STRING*> table_names;
|
||||||
int res= make_table_name_list(thd, &table_names, lex,
|
int res= make_table_name_list(thd, &table_names, lex,
|
||||||
&lookup_field_vals, db_name);
|
&plan->lookup_field_vals, db_name);
|
||||||
if (res == 2) /* Not fatal error, continue */
|
if (res == 2) /* Not fatal error, continue */
|
||||||
continue;
|
continue;
|
||||||
if (res)
|
if (res)
|
||||||
@@ -4802,8 +4765,8 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
|
|||||||
already created by make_table_name_list() function).
|
already created by make_table_name_list() function).
|
||||||
*/
|
*/
|
||||||
if (!table_open_method && schema_table_idx == SCH_TABLES &&
|
if (!table_open_method && schema_table_idx == SCH_TABLES &&
|
||||||
(!lookup_field_vals.table_value.length ||
|
(!plan->lookup_field_vals.table_value.length ||
|
||||||
lookup_field_vals.wild_table_value))
|
plan->lookup_field_vals.wild_table_value))
|
||||||
{
|
{
|
||||||
table->field[0]->store(STRING_WITH_LEN("def"), system_charset_info);
|
table->field[0]->store(STRING_WITH_LEN("def"), system_charset_info);
|
||||||
if (schema_table_store_record(thd, table))
|
if (schema_table_store_record(thd, table))
|
||||||
@@ -7979,6 +7942,137 @@ int make_schema_select(THD *thd, SELECT_LEX *sel,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Optimize reading from an I_S table.
|
||||||
|
|
||||||
|
@detail
|
||||||
|
This function prepares a plan for populating an I_S table with
|
||||||
|
get_all_tables().
|
||||||
|
|
||||||
|
The plan is in IS_table_read_plan structure, it is saved in
|
||||||
|
tables->is_table_read_plan.
|
||||||
|
|
||||||
|
@return
|
||||||
|
false - Ok
|
||||||
|
true - Out Of Memory
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
static bool optimize_for_get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
|
||||||
|
{
|
||||||
|
SELECT_LEX *lsel= tables->schema_select_lex;
|
||||||
|
ST_SCHEMA_TABLE *schema_table= tables->schema_table;
|
||||||
|
enum enum_schema_tables schema_table_idx;
|
||||||
|
IS_table_read_plan *plan;
|
||||||
|
DBUG_ENTER("get_all_tables");
|
||||||
|
|
||||||
|
if (!(plan= new IS_table_read_plan()))
|
||||||
|
DBUG_RETURN(1);
|
||||||
|
|
||||||
|
tables->is_table_read_plan= plan;
|
||||||
|
|
||||||
|
schema_table_idx= get_schema_table_idx(schema_table);
|
||||||
|
tables->table_open_method= get_table_open_method(tables, schema_table,
|
||||||
|
schema_table_idx);
|
||||||
|
DBUG_PRINT("open_method", ("%d", tables->table_open_method));
|
||||||
|
|
||||||
|
/*
|
||||||
|
this branch processes SHOW FIELDS, SHOW INDEXES commands.
|
||||||
|
see sql_parse.cc, prepare_schema_table() function where
|
||||||
|
this values are initialized
|
||||||
|
*/
|
||||||
|
if (lsel && lsel->table_list.first)
|
||||||
|
{
|
||||||
|
/* These do not need to have a query plan */
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (get_lookup_field_values(thd, cond, tables, &plan->lookup_field_vals))
|
||||||
|
{
|
||||||
|
plan->no_rows= true;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
DBUG_PRINT("info",("db_name='%s', table_name='%s'",
|
||||||
|
plan->lookup_field_vals.db_value.str,
|
||||||
|
plan->lookup_field_vals.table_value.str));
|
||||||
|
|
||||||
|
if (!plan->lookup_field_vals.wild_db_value &&
|
||||||
|
!plan->lookup_field_vals.wild_table_value)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
if lookup value is empty string then
|
||||||
|
it's impossible table name or db name
|
||||||
|
*/
|
||||||
|
if ((plan->lookup_field_vals.db_value.str &&
|
||||||
|
!plan->lookup_field_vals.db_value.str[0]) ||
|
||||||
|
(plan->lookup_field_vals.table_value.str &&
|
||||||
|
!plan->lookup_field_vals.table_value.str[0]))
|
||||||
|
{
|
||||||
|
plan->no_rows= true;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (plan->has_db_lookup_value() && plan->has_table_lookup_value())
|
||||||
|
plan->partial_cond= 0;
|
||||||
|
else
|
||||||
|
plan->partial_cond= make_cond_for_info_schema(cond, tables);
|
||||||
|
|
||||||
|
end:
|
||||||
|
DBUG_RETURN(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
This is the optimizer part of get_schema_tables_result().
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool optimize_schema_tables_reads(JOIN *join)
|
||||||
|
{
|
||||||
|
THD *thd= join->thd;
|
||||||
|
bool result= 0;
|
||||||
|
DBUG_ENTER("optimize_schema_tables_reads");
|
||||||
|
|
||||||
|
for (JOIN_TAB *tab= first_linear_tab(join, WITH_CONST_TABLES);
|
||||||
|
tab;
|
||||||
|
tab= next_linear_tab(join, tab, WITH_BUSH_ROOTS))
|
||||||
|
{
|
||||||
|
if (!tab->table || !tab->table->pos_in_table_list)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
TABLE_LIST *table_list= tab->table->pos_in_table_list;
|
||||||
|
if (table_list->schema_table && thd->fill_information_schema_tables())
|
||||||
|
{
|
||||||
|
/* A value of 0 indicates a dummy implementation */
|
||||||
|
if (table_list->schema_table->fill_table == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* skip I_S optimizations specific to get_all_tables */
|
||||||
|
if (table_list->schema_table->fill_table != get_all_tables)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Item *cond= tab->select_cond;
|
||||||
|
if (tab->cache_select && tab->cache_select->cond)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
If join buffering is used, we should use the condition that is
|
||||||
|
attached to the join cache. Cache condition has a part of WHERE that
|
||||||
|
can be checked when we're populating this table.
|
||||||
|
join_tab->select_cond is of no interest, because it only has
|
||||||
|
conditions that depend on both this table and previous tables in the
|
||||||
|
join order.
|
||||||
|
*/
|
||||||
|
cond= tab->cache_select->cond;
|
||||||
|
}
|
||||||
|
|
||||||
|
optimize_for_get_all_tables(thd, table_list, cond);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DBUG_RETURN(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Fill temporary schema tables before SELECT
|
Fill temporary schema tables before SELECT
|
||||||
|
|
||||||
@@ -7987,6 +8081,10 @@ int make_schema_select(THD *thd, SELECT_LEX *sel,
|
|||||||
join join which use schema tables
|
join join which use schema tables
|
||||||
executed_place place where I_S table processed
|
executed_place place where I_S table processed
|
||||||
|
|
||||||
|
SEE ALSO
|
||||||
|
The optimization part is done by get_schema_tables_result(). This function
|
||||||
|
is run on query execution.
|
||||||
|
|
||||||
RETURN
|
RETURN
|
||||||
FALSE success
|
FALSE success
|
||||||
TRUE error
|
TRUE error
|
||||||
@@ -8007,7 +8105,7 @@ bool get_schema_tables_result(JOIN *join,
|
|||||||
|
|
||||||
for (JOIN_TAB *tab= first_linear_tab(join, WITH_CONST_TABLES);
|
for (JOIN_TAB *tab= first_linear_tab(join, WITH_CONST_TABLES);
|
||||||
tab;
|
tab;
|
||||||
tab= next_linear_tab(join, tab, WITHOUT_BUSH_ROOTS))
|
tab= next_linear_tab(join, tab, WITH_BUSH_ROOTS))
|
||||||
{
|
{
|
||||||
if (!tab->table || !tab->table->pos_in_table_list)
|
if (!tab->table || !tab->table->pos_in_table_list)
|
||||||
break;
|
break;
|
||||||
|
@@ -150,6 +150,41 @@ public:
|
|||||||
void call_in_target_thread();
|
void call_in_target_thread();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef struct st_lookup_field_values
|
||||||
|
{
|
||||||
|
LEX_STRING db_value, table_value;
|
||||||
|
bool wild_db_value, wild_table_value;
|
||||||
|
} LOOKUP_FIELD_VALUES;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
INFORMATION_SCHEMA: Execution plan for get_all_tables() call
|
||||||
|
*/
|
||||||
|
|
||||||
|
class IS_table_read_plan : public Sql_alloc
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
IS_table_read_plan() : no_rows(false) {}
|
||||||
|
|
||||||
|
bool no_rows;
|
||||||
|
|
||||||
|
LOOKUP_FIELD_VALUES lookup_field_vals;
|
||||||
|
Item *partial_cond;
|
||||||
|
|
||||||
|
bool has_db_lookup_value()
|
||||||
|
{
|
||||||
|
return (lookup_field_vals.db_value.length &&
|
||||||
|
!lookup_field_vals.wild_db_value);
|
||||||
|
}
|
||||||
|
bool has_table_lookup_value()
|
||||||
|
{
|
||||||
|
return (lookup_field_vals.table_value.length &&
|
||||||
|
!lookup_field_vals.wild_table_value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
bool optimize_schema_tables_reads(JOIN *join);
|
||||||
|
|
||||||
/* Handle the ignored database directories list for SHOW/I_S. */
|
/* Handle the ignored database directories list for SHOW/I_S. */
|
||||||
bool ignore_db_dirs_init();
|
bool ignore_db_dirs_init();
|
||||||
void ignore_db_dirs_free();
|
void ignore_db_dirs_free();
|
||||||
|
16
sql/table.h
16
sql/table.h
@@ -1499,6 +1499,7 @@ typedef struct st_schema_table
|
|||||||
uint i_s_requested_object; /* the object we need to open(TABLE | VIEW) */
|
uint i_s_requested_object; /* the object we need to open(TABLE | VIEW) */
|
||||||
} ST_SCHEMA_TABLE;
|
} ST_SCHEMA_TABLE;
|
||||||
|
|
||||||
|
class IS_table_read_plan;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Types of derived tables. The ending part is a bitmap of phases that are
|
Types of derived tables. The ending part is a bitmap of phases that are
|
||||||
@@ -2044,12 +2045,23 @@ struct TABLE_LIST
|
|||||||
/* TRUE <=> this table is a const one and was optimized away. */
|
/* TRUE <=> this table is a const one and was optimized away. */
|
||||||
bool optimized_away;
|
bool optimized_away;
|
||||||
|
|
||||||
|
/* I_S: Flags to open_table (e.g. OPEN_TABLE_ONLY or OPEN_VIEW_ONLY) */
|
||||||
uint i_s_requested_object;
|
uint i_s_requested_object;
|
||||||
bool has_db_lookup_value;
|
|
||||||
bool has_table_lookup_value;
|
/*
|
||||||
|
I_S: how to read the tables (SKIP_OPEN_TABLE/OPEN_FRM_ONLY/OPEN_FULL_TABLE)
|
||||||
|
*/
|
||||||
uint table_open_method;
|
uint table_open_method;
|
||||||
|
/*
|
||||||
|
I_S: where the schema table was filled
|
||||||
|
(this is a hack. The code should be able to figure out whether reading
|
||||||
|
from I_S should be done by create_sort_index() or by JOIN::exec.)
|
||||||
|
*/
|
||||||
enum enum_schema_table_state schema_table_state;
|
enum enum_schema_table_state schema_table_state;
|
||||||
|
|
||||||
|
/* Something like a "query plan" for reading INFORMATION_SCHEMA table */
|
||||||
|
IS_table_read_plan *is_table_read_plan;
|
||||||
|
|
||||||
MDL_request mdl_request;
|
MDL_request mdl_request;
|
||||||
|
|
||||||
#ifdef WITH_PARTITION_STORAGE_ENGINE
|
#ifdef WITH_PARTITION_STORAGE_ENGINE
|
||||||
|
Reference in New Issue
Block a user