summaryrefslogtreecommitdiff
path: root/sql/sql_select.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/sql_select.cc')
-rw-r--r--sql/sql_select.cc2005
1 files changed, 1454 insertions, 551 deletions
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 35267e6617c..93e67c484e3 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -51,6 +51,7 @@
#include "opt_subselect.h"
#include "log_slow.h"
#include "sql_derived.h"
+#include "sql_statistics.h"
#include "debug_sync.h" // DEBUG_SYNC
#include <m_ctype.h>
@@ -88,12 +89,14 @@ void best_access_path(JOIN *join, JOIN_TAB *s,
POSITION *pos, POSITION *loose_scan_pos);
static void optimize_straight_join(JOIN *join, table_map join_tables);
static bool greedy_search(JOIN *join, table_map remaining_tables,
- uint depth, uint prune_level);
+ uint depth, uint prune_level,
+ uint use_cond_selectivity);
static bool best_extension_by_limited_search(JOIN *join,
table_map remaining_tables,
uint idx, double record_count,
double read_time, uint depth,
- uint prune_level);
+ uint prune_level,
+ uint use_cond_selectivity);
static uint determine_search_depth(JOIN* join);
C_MODE_START
static int join_tab_cmp(const void *dummy, const void* ptr1, const void* ptr2);
@@ -105,7 +108,7 @@ C_MODE_END
tested and approved.
*/
static bool find_best(JOIN *join,table_map rest_tables,uint index,
- double record_count,double read_time);
+ double record_count,double read_time, uint use_cond_selectivity);
static uint cache_record_length(JOIN *join,uint index);
bool get_best_combination(JOIN *join);
static store_key *get_store_key(THD *thd,
@@ -133,7 +136,8 @@ static COND *build_equal_items(JOIN *join, COND *cond,
COND_EQUAL *inherited,
List<TABLE_LIST> *join_list,
bool ignore_on_conds,
- COND_EQUAL **cond_equal_ref);
+ COND_EQUAL **cond_equal_ref,
+ bool link_equal_fields= FALSE);
static COND* substitute_for_best_equal_field(JOIN_TAB *context_tab,
COND *cond,
COND_EQUAL *cond_equal,
@@ -149,8 +153,9 @@ static uint build_bitmap_for_nested_joins(List<TABLE_LIST> *join_list,
static COND *optimize_cond(JOIN *join, COND *conds,
List<TABLE_LIST> *join_list,
bool ignore_on_conds,
- Item::cond_result *cond_value,
- COND_EQUAL **cond_equal);
+ Item::cond_result *cond_value,
+ COND_EQUAL **cond_equal,
+ int flags= 0);
bool const_expression_in_where(COND *conds,Item *item, Item **comp_item);
static bool create_internal_tmp_table_from_heap2(THD *, TABLE *,
ENGINE_COLUMNDEF *, ENGINE_COLUMNDEF **,
@@ -279,6 +284,59 @@ enum enum_exec_or_opt {WALK_OPTIMIZATION_TABS , WALK_EXECUTION_TABS};
JOIN_TAB *first_breadth_first_tab(JOIN *join, enum enum_exec_or_opt tabs_kind);
JOIN_TAB *next_breadth_first_tab(JOIN *join, enum enum_exec_or_opt tabs_kind,
JOIN_TAB *tab);
+static double table_cond_selectivity(JOIN *join, uint idx, JOIN_TAB *s,
+ table_map rem_tables);
+
+#ifndef DBUG_OFF
+
+/*
+ SHOW EXPLAIN testing: wait for, and serve n_calls APC requests.
+*/
+void dbug_serve_apcs(THD *thd, int n_calls)
+{
+ const char *save_proc_info= thd->proc_info;
+ /* This is so that mysqltest knows we're ready to serve requests: */
+ thd_proc_info(thd, "show_explain_trap");
+
+ /* Busy-wait for n_calls APC requests to arrive and be processed */
+ int n_apcs= thd->apc_target.n_calls_processed + n_calls;
+ while (thd->apc_target.n_calls_processed < n_apcs)
+ {
+ my_sleep(300);
+ if (thd->check_killed())
+ break;
+ }
+ thd_proc_info(thd, save_proc_info);
+}
+
+
+/*
+ Debugging: check if @name=value, comparing as integer
+
+ Intended usage:
+
+ DBUG_EXECUTE_IF("show_explain_probe_2",
+ if (dbug_user_var_equals_int(thd, "select_id", select_id))
+ dbug_serve_apcs(thd, 1);
+ );
+
+*/
+
+bool dbug_user_var_equals_int(THD *thd, const char *name, int value)
+{
+ user_var_entry *var;
+ LEX_STRING varname= {(char*)name, strlen(name)};
+ if ((var= get_variable(&thd->user_vars, varname, FALSE)))
+ {
+ bool null_value;
+ longlong var_value= var->val_int(&null_value);
+ if (!null_value && var_value == value)
+ return TRUE;
+ }
+ return FALSE;
+}
+#endif
+
/**
This handles SELECT with and without UNION.
@@ -553,7 +611,9 @@ inline int setup_without_group(THD *thd, Item **ref_pointer_array,
List<Item> &all_fields,
COND **conds,
ORDER *order,
- ORDER *group, bool *hidden_group_fields)
+ ORDER *group,
+ bool *hidden_group_fields,
+ uint *reserved)
{
int res;
st_select_lex *const select= thd->lex->current_select;
@@ -567,6 +627,13 @@ inline int setup_without_group(THD *thd, Item **ref_pointer_array,
thd->lex->allow_sum_func&= ~((nesting_map)1 << select->nest_level);
res= setup_conds(thd, tables, leaves, conds);
+ if (thd->lex->current_select->first_cond_optimization)
+ {
+ if (!res && *conds)
+ (*reserved)= (*conds)->exists2in_reserved_items();
+ else
+ (*reserved)= 0;
+ }
/* it's not wrong to have non-aggregated columns in a WHERE */
select->set_non_agg_field_used(saved_non_agg_field_used);
@@ -714,7 +781,7 @@ JOIN::prepare(Item ***rref_pointer_array,
setup_without_group(thd, (*rref_pointer_array), tables_list,
select_lex->leaf_tables, fields_list,
all_fields, &conds, order, group_list,
- &hidden_group_fields))
+ &hidden_group_fields, &select_lex->select_n_reserved))
DBUG_RETURN(-1); /* purecov: inspected */
ref_pointer_array= *rref_pointer_array;
@@ -945,6 +1012,34 @@ err:
DBUG_RETURN(res); /* purecov: inspected */
}
+int JOIN::optimize()
+{
+ bool was_optimized= optimized;
+ int res= optimize_inner();
+ /*
+ If we're inside a non-correlated subquery, this function may be
+ called for the second time after the subquery has been executed
+ and deleted. The second call will not produce a valid query plan, it will
+ short-circuit because optimized==TRUE.
+
+ "was_optimized != optimized" is here to handle this case:
+ - first optimization starts, gets an error (from a const. cheap
+ subquery), returns 1
+ - another JOIN::optimize() call made, and now join->optimize() will
+ return 0, even though we never had a query plan.
+ */
+ if (was_optimized != optimized && !res && have_query_plan != QEP_DELETED)
+ {
+ create_explain_query_if_not_exists(thd->lex, thd->mem_root);
+ have_query_plan= QEP_AVAILABLE;
+ save_explain_data(thd->lex->explain, false /* can overwrite */,
+ need_tmp,
+ !skip_sort_order && !no_order && (order || group_list),
+ select_distinct);
+ }
+ return res;
+}
+
/**
global select optimisation.
@@ -959,7 +1054,7 @@ err:
*/
int
-JOIN::optimize()
+JOIN::optimize_inner()
{
ulonglong select_opts_for_readinfo;
uint no_jbuf_after;
@@ -992,6 +1087,24 @@ JOIN::optimize()
// Update used tables after all handling derived table procedures
select_lex->update_used_tables();
+ /*
+ In fact we transform underlying subqueries after their 'prepare' phase and
+ before 'optimize' from upper query 'optimize' to allow semijoin
+ conversion happened (which done in the same way.
+ */
+ if(select_lex->first_cond_optimization &&
+ conds && conds->walk(&Item::exists2in_processor, 0, (uchar *)thd))
+ DBUG_RETURN(1);
+ /*
+TODO: make view to decide if it is possible to write to WHERE directly or make Semi-Joins able to process ON condition if it is possible
+ for (TABLE_LIST *tbl= tables_list; tbl; tbl= tbl->next_local)
+ {
+ if (tbl->on_expr &&
+ tbl->on_expr->walk(&Item::exists2in_processor, 0, (uchar *)thd))
+ DBUG_RETURN(1);
+ }
+ */
+
if (transform_max_min_subquery())
DBUG_RETURN(1); /* purecov: inspected */
@@ -1075,7 +1188,7 @@ JOIN::optimize()
DBUG_RETURN(1);
conds= optimize_cond(this, conds, join_list, FALSE,
- &cond_value, &cond_equal);
+ &cond_value, &cond_equal, OPT_LINK_EQUAL_FIELDS);
if (thd->is_error())
{
@@ -1466,9 +1579,10 @@ JOIN::optimize()
We have found that grouping can be removed since groups correspond to
only one row anyway, but we still have to guarantee correct result
order. The line below effectively rewrites the query from GROUP BY
- <fields> to ORDER BY <fields>. There are two exceptions:
+ <fields> to ORDER BY <fields>. There are three exceptions:
- if skip_sort_order is set (see above), then we can simply skip
GROUP BY;
+ - if we are in a subquery, we don't have to maintain order
- we can only rewrite ORDER BY if the ORDER BY fields are 'compatible'
with the GROUP BY ones, i.e. either one is a prefix of another.
We only check if the ORDER BY is a prefix of GROUP BY. In this case
@@ -1478,7 +1592,13 @@ JOIN::optimize()
'order' as is.
*/
if (!order || test_if_subpart(group_list, order))
- order= skip_sort_order ? 0 : group_list;
+ {
+ if (skip_sort_order ||
+ select_lex->master_unit()->item) // This is a subquery
+ order= NULL;
+ else
+ order= group_list;
+ }
/*
If we have an IGNORE INDEX FOR GROUP BY(fields) clause, this must be
rewritten to IGNORE INDEX FOR ORDER BY(fields).
@@ -1522,8 +1642,10 @@ JOIN::optimize()
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)))
@@ -1891,6 +2013,7 @@ int JOIN::init_execution()
if (!group_list && ! exec_tmp_table1->distinct && order && simple_order)
{
+ DBUG_PRINT("info",("Sorting for order"));
thd_proc_info(thd, "Sorting for order");
if (create_sort_index(thd, this, order,
HA_POS_ERROR, HA_POS_ERROR, TRUE))
@@ -2102,8 +2225,7 @@ JOIN::reinit()
DBUG_ENTER("JOIN::reinit");
unit->offset_limit_cnt= (ha_rows)(select_lex->offset_limit ?
- select_lex->offset_limit->val_uint() :
- ULL(0));
+ select_lex->offset_limit->val_uint() : 0);
first_record= 0;
cleaned= false;
@@ -2201,6 +2323,59 @@ JOIN::save_join_tab()
}
+void JOIN::save_explain_data(Explain_query *output, bool can_overwrite,
+ bool need_tmp_table, bool need_order,
+ bool distinct)
+{
+ if (select_lex->select_number != UINT_MAX &&
+ select_lex->select_number != INT_MAX /* this is not a UNION's "fake select */ &&
+ have_query_plan != JOIN::QEP_NOT_PRESENT_YET &&
+ have_query_plan != JOIN::QEP_DELETED && // this happens when there was
+ // no QEP ever, but then
+ //cleanup() is called multiple times
+ output && // for "SET" command in SPs.
+ (can_overwrite? true: !output->get_select(select_lex->select_number)))
+ {
+ const char *message= NULL;
+ if (!table_count || !tables_list || zero_result_cause)
+ {
+ /* It's a degenerate join */
+ message= zero_result_cause ? zero_result_cause : "No tables used";
+ }
+ save_explain_data_intern(thd->lex->explain, need_tmp_table, need_order,
+ distinct, message);
+ }
+}
+
+
+void JOIN::exec()
+{
+ DBUG_EXECUTE_IF("show_explain_probe_join_exec_start",
+ if (dbug_user_var_equals_int(thd,
+ "show_explain_probe_select_id",
+ select_lex->select_number))
+ dbug_serve_apcs(thd, 1);
+ );
+ exec_inner();
+
+ if (!exec_saved_explain)
+ {
+ save_explain_data(thd->lex->explain, true /* can overwrite */,
+ need_tmp,
+ order != 0 && !skip_sort_order,
+ select_distinct);
+ exec_saved_explain= true;
+ }
+
+ DBUG_EXECUTE_IF("show_explain_probe_join_exec_end",
+ if (dbug_user_var_equals_int(thd,
+ "show_explain_probe_select_id",
+ select_lex->select_number))
+ dbug_serve_apcs(thd, 1);
+ );
+}
+
+
/**
Exec select.
@@ -2212,14 +2387,16 @@ JOIN::save_join_tab()
@todo
When can we have here thd->net.report_error not zero?
*/
-void
-JOIN::exec()
+
+void JOIN::exec_inner()
{
List<Item> *columns_list= &fields_list;
int tmp_error;
DBUG_ENTER("JOIN::exec");
+ const bool has_group_by= this->group;
+
thd_proc_info(thd, "executing");
error= 0;
if (procedure)
@@ -2523,6 +2700,10 @@ JOIN::exec()
DBUG_PRINT("info",("Creating group table"));
/* Free first data from old join */
+
+ /*
+ psergey-todo: this is the place of pre-mature JOIN::free call.
+ */
curr_join->join_free();
if (curr_join->make_simple_join(this, curr_tmp_table))
DBUG_VOID_RETURN;
@@ -2563,11 +2744,12 @@ JOIN::exec()
}
if (curr_join->group_list)
{
- thd_proc_info(thd, "Creating sort index");
if (curr_join->join_tab == join_tab && save_join_tab())
{
DBUG_VOID_RETURN;
}
+ DBUG_PRINT("info",("Sorting for index"));
+ thd_proc_info(thd, "Creating sort index");
if (create_sort_index(thd, curr_join, curr_join->group_list,
HA_POS_ERROR, HA_POS_ERROR, FALSE) ||
make_group_fields(this, curr_join))
@@ -2731,6 +2913,7 @@ JOIN::exec()
JOIN_TAB *curr_table= &curr_join->join_tab[curr_join->const_tables];
table_map used_tables= (curr_join->const_table_map |
curr_table->table->map);
+ curr_join->tmp_having->update_used_tables();
Item* sort_table_cond= make_cond_for_table(thd, curr_join->tmp_having,
used_tables,
@@ -2820,13 +3003,39 @@ JOIN::exec()
the query. XXX: it's never shown in EXPLAIN!
OPTION_FOUND_ROWS supersedes LIMIT and is taken into account.
*/
- if (create_sort_index(thd, curr_join,
- curr_join->group_list ?
- curr_join->group_list : curr_join->order,
- curr_join->select_limit,
- (select_options & OPTION_FOUND_ROWS ?
- HA_POS_ERROR : unit->select_limit_cnt),
- curr_join->group_list ? TRUE : FALSE))
+ DBUG_PRINT("info",("Sorting for order by/group by"));
+ ORDER *order_arg=
+ curr_join->group_list ? curr_join->group_list : curr_join->order;
+ /*
+ filesort_limit: Return only this many rows from filesort().
+ We can use select_limit_cnt only if we have no group_by and 1 table.
+ This allows us to use Bounded_queue for queries like:
+ "select SQL_CALC_FOUND_ROWS * from t1 order by b desc limit 1;"
+ select_limit == HA_POS_ERROR (we need a full table scan)
+ unit->select_limit_cnt == 1 (we only need one row in the result set)
+ */
+ const ha_rows filesort_limit_arg=
+ (has_group_by || curr_join->table_count > 1)
+ ? curr_join->select_limit : unit->select_limit_cnt;
+ const ha_rows select_limit_arg=
+ select_options & OPTION_FOUND_ROWS
+ ? HA_POS_ERROR : unit->select_limit_cnt;
+
+ DBUG_PRINT("info", ("has_group_by %d "
+ "curr_join->table_count %d "
+ "curr_join->m_select_limit %d "
+ "unit->select_limit_cnt %d",
+ has_group_by,
+ curr_join->table_count,
+ (int) curr_join->select_limit,
+ (int) unit->select_limit_cnt));
+
+ if (create_sort_index(thd,
+ curr_join,
+ order_arg,
+ filesort_limit_arg,
+ select_limit_arg,
+ curr_join->group_list ? FALSE : TRUE))
DBUG_VOID_RETURN;
sortorder= curr_join->sortorder;
if (curr_join->const_tables != curr_join->table_count &&
@@ -2858,6 +3067,14 @@ JOIN::exec()
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF);
error= do_select(curr_join, curr_fields_list, NULL, procedure);
thd->limit_found_rows= curr_join->send_records;
+ if (curr_join->order &&
+ curr_join->sortorder)
+ {
+ /* Use info provided by filesort. */
+ DBUG_ASSERT(curr_join->table_count > curr_join->const_tables);
+ JOIN_TAB *tab= curr_join->join_tab + curr_join->const_tables;
+ thd->limit_found_rows= tab->records;
+ }
/* Accumulate the counts from all join iterations of all join parts. */
thd->examined_row_count+= curr_join->examined_rows;
@@ -3206,6 +3423,8 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
table_vector[i]=s->table=table=tables->table;
table->pos_in_table_list= tables;
error= tables->fetch_number_of_rows();
+ set_statistics_for_table(join->thd, table);
+ bitmap_clear_all(&table->cond_set);
#ifdef WITH_PARTITION_STORAGE_ENGINE
const bool no_partitions_used= table->no_partitions_used;
@@ -3231,8 +3450,8 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
s->dependent= tables->dep_tables;
if (tables->schema_table)
- table->file->stats.records= 2;
- table->quick_condition_rows= table->file->stats.records;
+ table->file->stats.records= table->used_stat_records= 2;
+ table->quick_condition_rows= table->stat_records();
s->on_expr_ref= &tables->on_expr;
if (*s->on_expr_ref)
@@ -3670,6 +3889,8 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
all select distinct fields participate in one index.
*/
add_group_and_distinct_keys(join, s);
+
+ s->table->cond_selectivity= 1.0;
/*
Perform range analysis if there are keys it could use (1).
@@ -3678,7 +3899,8 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
Don't do range analysis for materialized subqueries (4).
Don't do range analysis for materialized derived tables (5)
*/
- if (!s->const_keys.is_clear_all() && // (1)
+ if ((!s->const_keys.is_clear_all() ||
+ !bitmap_is_clear_all(&s->table->cond_set)) && // (1)
(!s->table->pos_in_table_list->embedding || // (2)
(s->table->pos_in_table_list->embedding && // (3)
s->table->pos_in_table_list->embedding->sj_on_expr)) && // (3)
@@ -3686,20 +3908,37 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
!(s->table->pos_in_table_list->derived && // (5)
s->table->pos_in_table_list->is_materialized_derived())) // (5)
{
- ha_rows records;
- SQL_SELECT *select;
- select= make_select(s->table, found_const_table_map,
- found_const_table_map,
- *s->on_expr_ref ? *s->on_expr_ref : conds,
- 1, &error);
- if (!select)
- goto error;
- records= get_quick_record_count(join->thd, select, s->table,
- &s->const_keys, join->row_limit);
- s->quick=select->quick;
- s->needed_reg=select->needed_reg;
- select->quick=0;
- if (records == 0 && s->table->reginfo.impossible_range)
+ bool impossible_range= FALSE;
+ ha_rows records= HA_POS_ERROR;
+ SQL_SELECT *select= 0;
+ if (!s->const_keys.is_clear_all())
+ {
+ select= make_select(s->table, found_const_table_map,
+ found_const_table_map,
+ *s->on_expr_ref ? *s->on_expr_ref : conds,
+ 1, &error);
+ if (!select)
+ goto error;
+ records= get_quick_record_count(join->thd, select, s->table,
+ &s->const_keys, join->row_limit);
+ s->quick=select->quick;
+ s->needed_reg=select->needed_reg;
+ select->quick=0;
+ impossible_range= records == 0 && s->table->reginfo.impossible_range;
+ }
+ if (!impossible_range)
+ {
+ if (join->thd->variables.optimizer_use_condition_selectivity > 1)
+ calculate_cond_selectivity_for_table(join->thd, s->table,
+ *s->on_expr_ref ?
+ *s->on_expr_ref : conds);
+ if (s->table->reginfo.impossible_range)
+ {
+ impossible_range= TRUE;
+ records= 0;
+ }
+ }
+ if (impossible_range)
{
/*
Impossible WHERE or ON expression
@@ -3713,7 +3952,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
if (*s->on_expr_ref)
{
/* Generate empty row */
- s->info= "Impossible ON condition";
+ s->info= ET_IMPOSSIBLE_ON_CONDITION;
found_const_table_map|= s->table->map;
s->type= JT_CONST;
mark_as_null_row(s->table); // All fields are NULL
@@ -3724,8 +3963,10 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
s->found_records=records;
s->read_time= s->quick ? s->quick->read_time : 0.0;
}
- delete select;
+ if (select)
+ delete select;
}
+
}
if (pull_out_semijoin_tables(join))
@@ -3796,7 +4037,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
DEBUG_SYNC(join->thd, "inside_make_join_statistics");
/* Generate an execution plan from the found optimal join order. */
- DBUG_RETURN(join->thd->killed || get_best_combination(join));
+ DBUG_RETURN(join->thd->check_killed() || get_best_combination(join));
error:
/*
@@ -4095,11 +4336,12 @@ add_key_field(JOIN *join,
else if (!(field->flags & PART_KEY_FLAG))
{
// Don't remove column IS NULL on a LEFT JOIN table
- if (!eq_func || (*value)->type() != Item::NULL_ITEM ||
- !field->table->maybe_null || field->null_ptr)
- return; // Not a key. Skip it
- optimize= KEY_OPTIMIZE_EXISTS;
- DBUG_ASSERT(num_values == 1);
+ if (eq_func && (*value)->type() == Item::NULL_ITEM &&
+ field->table->maybe_null && !field->null_ptr)
+ {
+ optimize= KEY_OPTIMIZE_EXISTS;
+ DBUG_ASSERT(num_values == 1);
+ }
}
if (optimize != KEY_OPTIMIZE_EXISTS)
{
@@ -4148,7 +4390,11 @@ add_key_field(JOIN *join,
break;
}
if (is_const)
+ {
stat[0].const_keys.merge(possible_keys);
+ if (possible_keys.is_clear_all())
+ bitmap_set_bit(&field->table->cond_set, field->field_index);
+ }
else if (!eq_func)
{
/*
@@ -4927,7 +5173,8 @@ update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab,
/* set a barrier for the array of SARGABLE_PARAM */
(*sargables)[0].field= 0;
- if (my_init_dynamic_array(keyuse,sizeof(KEYUSE),20,64))
+ if (my_init_dynamic_array(keyuse,sizeof(KEYUSE),20,64,
+ MYF(MY_THREAD_SPECIFIC)))
return TRUE;
if (cond)
@@ -5290,6 +5537,7 @@ void set_position(JOIN *join,uint idx,JOIN_TAB *table,KEYUSE *key)
join->positions[idx].table= table;
join->positions[idx].key=key;
join->positions[idx].records_read=1.0; /* This is a const table */
+ join->positions[idx].cond_selectivity= 1.0;
join->positions[idx].ref_depend_map= 0;
// join->positions[idx].loosescan_key= MAX_KEY; /* Not a LooseScan */
@@ -5564,7 +5812,7 @@ best_access_path(JOIN *join,
else
{
uint key_parts= table->actual_n_key_parts(keyinfo);
- if (!(records=keyinfo->rec_per_key[key_parts-1]))
+ if (!(records= keyinfo->actual_rec_per_key(key_parts-1)))
{ /* Prefer longer keys */
records=
((double) s->records / (double) rec *
@@ -5665,7 +5913,7 @@ best_access_path(JOIN *join,
else
{
/* Check if we have statistic about the distribution */
- if ((records= keyinfo->rec_per_key[max_key_part-1]))
+ if ((records= keyinfo->actual_rec_per_key(max_key_part-1)))
{
/*
Fix for the case where the index statistics is too
@@ -6011,6 +6259,7 @@ static void choose_initial_table_order(JOIN *join)
TABLE_LIST *emb_subq;
JOIN_TAB **tab= join->best_ref + join->const_tables;
JOIN_TAB **tabs_end= tab + join->table_count - join->const_tables;
+ DBUG_ENTER("choose_initial_table_order");
/* Find where the top-level JOIN_TABs end and subquery JOIN_TABs start */
for (; tab != tabs_end; tab++)
{
@@ -6020,7 +6269,7 @@ static void choose_initial_table_order(JOIN *join)
uint n_subquery_tabs= tabs_end - tab;
if (!n_subquery_tabs)
- return;
+ DBUG_VOID_RETURN;
/* Copy the subquery JOIN_TABs to a separate array */
JOIN_TAB *subquery_tabs[MAX_TABLES];
@@ -6075,6 +6324,7 @@ static void choose_initial_table_order(JOIN *join)
subq_tab += n_subquery_tables - 1;
}
}
+ DBUG_VOID_RETURN;
}
@@ -6106,6 +6356,8 @@ choose_plan(JOIN *join, table_map join_tables)
{
uint search_depth= join->thd->variables.optimizer_search_depth;
uint prune_level= join->thd->variables.optimizer_prune_level;
+ uint use_cond_selectivity=
+ join->thd->variables.optimizer_use_condition_selectivity;
bool straight_join= test(join->select_options & SELECT_STRAIGHT_JOIN);
DBUG_ENTER("choose_plan");
@@ -6162,15 +6414,19 @@ choose_plan(JOIN *join, table_map join_tables)
the greedy version. Will be removed when greedy_search is approved.
*/
join->best_read= DBL_MAX;
- if (find_best(join, join_tables, join->const_tables, 1.0, 0.0))
+ if (find_best(join, join_tables, join->const_tables, 1.0, 0.0,
+ use_cond_selectivity))
+ {
DBUG_RETURN(TRUE);
+ }
}
else
{
if (search_depth == 0)
/* Automatically determine a reasonable value for 'search_depth' */
search_depth= determine_search_depth(join);
- if (greedy_search(join, join_tables, search_depth, prune_level))
+ if (greedy_search(join, join_tables, search_depth, prune_level,
+ use_cond_selectivity))
DBUG_RETURN(TRUE);
}
}
@@ -6444,6 +6700,8 @@ optimize_straight_join(JOIN *join, table_map join_tables)
bool disable_jbuf= join->thd->variables.join_cache_level == 0;
double record_count= 1.0;
double read_time= 0.0;
+ uint use_cond_selectivity=
+ join->thd->variables.optimizer_use_condition_selectivity;
POSITION loose_scan_pos;
for (JOIN_TAB **pos= join->best_ref + idx ; (s= *pos) ; pos++)
@@ -6460,6 +6718,11 @@ optimize_straight_join(JOIN *join, table_map join_tables)
&loose_scan_pos);
join_tables&= ~(s->table->map);
+ double pushdown_cond_selectivity= 1.0;
+ if (use_cond_selectivity > 1)
+ pushdown_cond_selectivity= table_cond_selectivity(join, idx, s,
+ join_tables);
+ join->positions[idx].cond_selectivity= pushdown_cond_selectivity;
++idx;
}
@@ -6547,6 +6810,8 @@ optimize_straight_join(JOIN *join, table_map join_tables)
@param search_depth controlls the exhaustiveness of the search
@param prune_level the pruning heuristics that should be applied during
search
+ @param use_cond_selectivity specifies how the selectivity of the conditions
+ pushed to a table should be taken into account
@retval
FALSE ok
@@ -6558,7 +6823,8 @@ static bool
greedy_search(JOIN *join,
table_map remaining_tables,
uint search_depth,
- uint prune_level)
+ uint prune_level,
+ uint use_cond_selectivity)
{
double record_count= 1.0;
double read_time= 0.0;
@@ -6569,7 +6835,6 @@ greedy_search(JOIN *join,
JOIN_TAB *best_table; // the next plan node to be added to the curr QEP
// ==join->tables or # tables in the sj-mat nest we're optimizing
uint n_tables __attribute__((unused));
-
DBUG_ENTER("greedy_search");
/* number of tables that remain to be optimized */
@@ -6584,7 +6849,8 @@ greedy_search(JOIN *join,
/* Find the extension of the current QEP with the lowest cost */
join->best_read= DBL_MAX;
if (best_extension_by_limited_search(join, remaining_tables, idx, record_count,
- read_time, search_depth, prune_level))
+ read_time, search_depth, prune_level,
+ use_cond_selectivity))
DBUG_RETURN(TRUE);
/*
'best_read < DBL_MAX' means that optimizer managed to find
@@ -6824,6 +7090,240 @@ double JOIN::get_examined_rows()
/**
+ @brief
+ Get the selectivity of equalities between columns when joining a table
+
+ @param join The optimized join
+ @param idx The number of tables in the evaluated partual join
+ @param s The table to be joined for evaluation
+ @param rem_tables The bitmap of tables to be joined later
+ @param keyparts The number of key parts to used when joining s
+ @param ref_keyuse_steps Array of references to keyuses employed to join s
+*/
+
+static
+double table_multi_eq_cond_selectivity(JOIN *join, uint idx, JOIN_TAB *s,
+ table_map rem_tables, uint keyparts,
+ uint16 *ref_keyuse_steps)
+{
+ double sel= 1.0;
+ COND_EQUAL *cond_equal= join->cond_equal;
+
+ if (!cond_equal || !cond_equal->current_level.elements)
+ return sel;
+
+ if (!s->keyuse)
+ return sel;
+
+ Item_equal *item_equal;
+ List_iterator_fast<Item_equal> it(cond_equal->current_level);
+ TABLE *table= s->table;
+ table_map table_bit= table->map;
+ POSITION *pos= &join->positions[idx];
+
+ while ((item_equal= it++))
+ {
+ /*
+ Check whether we need to take into account the selectivity of
+ multiple equality item_equal. If this is the case multiply
+ the current value of sel by this selectivity
+ */
+ table_map used_tables= item_equal->used_tables();
+ if (!(used_tables & table_bit))
+ continue;
+ if (item_equal->get_const())
+ continue;
+
+ Field *fld;
+ bool adjust_sel= FALSE;
+ Item_equal_fields_iterator fi(*item_equal);
+ while((fi++) && !adjust_sel)
+ {
+ Field *fld= fi.get_curr_field();
+ if (fld->table->map != table_bit)
+ continue;
+ if (pos->key == 0)
+ adjust_sel= TRUE;
+ else
+ {
+ uint i;
+ KEYUSE *keyuse= pos->key;
+ uint key= keyuse->key;
+
+ for (i= 0; i < keyparts; i++)
+ {
+ uint fldno;
+ if (is_hash_join_key_no(key))
+ fldno= keyuse->keypart;
+ else
+ fldno= table->key_info[key].key_part[keyparts-1].fieldnr - 1;
+ if (fld->field_index == fldno)
+ break;
+ }
+ if (i == keyparts)
+ {
+ /*
+ Field fld is included in multiple equality item_equal
+ and is not a part of the ref key.
+ The selectivity of the multiple equality must be taken
+ into account unless one of the ref arguments is
+ equal to fld.
+ */
+ adjust_sel= TRUE;
+ for (uint j= 0; j < keyparts && adjust_sel; j++)
+ {
+ if (j > 0)
+ keyuse+= ref_keyuse_steps[j-1];
+ Item *ref_item= keyuse->val;
+ if (ref_item->real_item()->type() == Item::FIELD_ITEM)
+ {
+ Item_field *field_item= (Item_field *) (ref_item->real_item());
+ if (item_equal->contains(field_item->field))
+ adjust_sel= FALSE;
+ }
+ }
+ }
+ }
+ }
+ if (adjust_sel)
+ {
+ /*
+ If ref == 0 and there are no fields in the multiple equality
+ item_equal that belong to the tables joined prior to s
+ then the selectivity of multiple equality will be set to 1.0.
+ */
+ double eq_fld_sel= 1.0;
+ fi.rewind();
+ while ((fi++))
+ {
+ double curr_eq_fld_sel;
+ fld= fi.get_curr_field();
+ if (!fld->table->map & ~(table_bit | rem_tables))
+ continue;
+ curr_eq_fld_sel= get_column_avg_frequency(fld) /
+ fld->table->stat_records();
+ if (curr_eq_fld_sel < 1.0)
+ set_if_bigger(eq_fld_sel, curr_eq_fld_sel);
+ }
+ sel*= eq_fld_sel;
+ }
+ }
+ return sel;
+}
+
+
+/**
+ @brief
+ Get the selectivity of conditions when joining a table
+
+ @param join The optimized join
+ @param s The table to be joined for evaluation
+ @param rem_tables The bitmap of tables to be joined later
+
+ @retval
+ selectivity of the conditions imposed on the rows of s
+*/
+
+static
+double table_cond_selectivity(JOIN *join, uint idx, JOIN_TAB *s,
+ table_map rem_tables)
+{
+ uint16 ref_keyuse_steps[MAX_REF_PARTS - 1];
+ Field *field;
+ TABLE *table= s->table;
+ MY_BITMAP *read_set= table->read_set;
+ double sel= s->table->cond_selectivity;
+ double table_records= table->stat_records();
+ POSITION *pos= &join->positions[idx];
+ uint keyparts= 0;
+ uint found_part_ref_or_null= 0;
+
+ /* Discount the selectivity of the access method used to join table s */
+ if (s->quick && s->quick->index != MAX_KEY)
+ {
+ if (pos->key == 0 && table_records > 0)
+ {
+ sel/= table->quick_rows[s->quick->index]/table_records;
+ }
+ }
+ else if (pos->key != 0)
+ {
+ /* A ref/ access or hash join is used to join table */
+ KEYUSE *keyuse= pos->key;
+ KEYUSE *prev_ref_keyuse= keyuse;
+ uint key= keyuse->key;
+ do
+ {
+ if (!(keyuse->used_tables & (rem_tables | table->map)))
+ {
+ if (are_tables_local(s, keyuse->val->used_tables()))
+ {
+ if (is_hash_join_key_no(key))
+ {
+ if (keyparts == keyuse->keypart)
+ keyparts++;
+ }
+ else
+ {
+ if (keyparts == keyuse->keypart &&
+ !(~(keyuse->val->used_tables()) & pos->ref_depend_map) &&
+ !(found_part_ref_or_null & keyuse->optimize))
+ {
+ keyparts++;
+ found_part_ref_or_null|= keyuse->optimize & ~KEY_OPTIMIZE_EQ;
+ }
+ }
+ if (keyparts > keyuse->keypart)
+ {
+ uint fldno;
+ if (is_hash_join_key_no(key))
+ fldno= keyuse->keypart;
+ else
+ fldno= table->key_info[key].key_part[keyparts-1].fieldnr - 1;
+ if (keyuse->val->const_item())
+ sel*= table->field[fldno]->cond_selectivity;
+ if (keyparts > 1)
+ {
+ ref_keyuse_steps[keyparts-2]= keyuse - prev_ref_keyuse;
+ prev_ref_keyuse= keyuse;
+ }
+ }
+ }
+ }
+ keyuse++;
+ } while (keyuse->table == table && keyuse->key == key);
+ }
+
+ /*
+ If the field f from the table is equal to a field from one the
+ earlier joined tables then the selectivity of the range conditions
+ over the field f must be discounted.
+ */
+ for (Field **f_ptr=table->field ; (field= *f_ptr) ; f_ptr++)
+ {
+ if (!bitmap_is_set(read_set, field->field_index) ||
+ !field->next_equal_field)
+ continue;
+ for (Field *next_field= field->next_equal_field;
+ next_field != field;
+ next_field= next_field->next_equal_field)
+ {
+ if (!(next_field->table->map & rem_tables) && next_field->table != table)
+ {
+ sel/= field->cond_selectivity;
+ break;
+ }
+ }
+ }
+
+ sel*= table_multi_eq_cond_selectivity(join, idx, s, rem_tables,
+ keyparts, ref_keyuse_steps);
+
+ return sel;
+}
+
+
+/**
Find a good, possibly optimal, query execution plan (QEP) by a possibly
exhaustive search.
@@ -6933,6 +7433,8 @@ double JOIN::get_examined_rows()
@param prune_level pruning heuristics that should be applied during
optimization
(values: 0 = EXHAUSTIVE, 1 = PRUNE_BY_TIME_OR_ROWS)
+ @param use_cond_selectivity specifies how the selectivity of the conditions
+ pushed to a table should be taken into account
@retval
FALSE ok
@@ -6947,12 +7449,13 @@ best_extension_by_limited_search(JOIN *join,
double record_count,
double read_time,
uint search_depth,
- uint prune_level)
+ uint prune_level,
+ uint use_cond_selectivity)
{
DBUG_ENTER("best_extension_by_limited_search");
THD *thd= join->thd;
- if (thd->killed) // Abort
+ if (thd->check_killed()) // Abort
DBUG_RETURN(TRUE);
DBUG_EXECUTE("opt", print_plan(join, idx, read_time, record_count, idx,
@@ -7050,16 +7553,25 @@ best_extension_by_limited_search(JOIN *join,
}
}
+ double pushdown_cond_selectivity= 1.0;
+ if (use_cond_selectivity > 1)
+ pushdown_cond_selectivity= table_cond_selectivity(join, idx, s,
+ remaining_tables &
+ ~real_table_bit);
+ join->positions[idx].cond_selectivity= pushdown_cond_selectivity;
+ double partial_join_cardinality= current_record_count *
+ pushdown_cond_selectivity;
if ( (search_depth > 1) && (remaining_tables & ~real_table_bit) & allowed_tables )
{ /* Recursively expand the current partial plan */
swap_variables(JOIN_TAB*, join->best_ref[idx], *pos);
if (best_extension_by_limited_search(join,
remaining_tables & ~real_table_bit,
idx + 1,
- current_record_count,
+ partial_join_cardinality,
current_read_time,
search_depth - 1,
- prune_level))
+ prune_level,
+ use_cond_selectivity))
DBUG_RETURN(TRUE);
swap_variables(JOIN_TAB*, join->best_ref[idx], *pos);
}
@@ -7077,7 +7589,7 @@ best_extension_by_limited_search(JOIN *join,
{
memcpy((uchar*) join->best_positions, (uchar*) join->positions,
sizeof(POSITION) * (idx + 1));
- join->record_count= current_record_count;
+ join->record_count= partial_join_cardinality;
join->best_read= current_read_time - 0.001;
}
DBUG_EXECUTE("opt", print_plan(join, idx+1,
@@ -7105,11 +7617,11 @@ best_extension_by_limited_search(JOIN *join,
*/
static bool
find_best(JOIN *join,table_map rest_tables,uint idx,double record_count,
- double read_time)
+ double read_time, uint use_cond_selectivity)
{
DBUG_ENTER("find_best");
THD *thd= join->thd;
- if (thd->killed)
+ if (thd->check_killed())
DBUG_RETURN(TRUE);
if (!rest_tables)
{
@@ -7156,20 +7668,30 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count,
advance_sj_state(join, rest_tables, idx, &current_record_count,
&current_read_time, &loose_scan_pos);
- if (best_record_count > current_record_count ||
+ double pushdown_cond_selectivity= 1.0;
+ if (use_cond_selectivity > 1)
+ pushdown_cond_selectivity= table_cond_selectivity(join, idx, s,
+ rest_tables &
+ ~real_table_bit);
+ join->positions[idx].cond_selectivity= pushdown_cond_selectivity;
+ double partial_join_cardinality= current_record_count *
+ pushdown_cond_selectivity;
+
+ if (best_record_count > partial_join_cardinality ||
best_read_time > current_read_time ||
(idx == join->const_tables && s->table == join->sort_by_table))
{
- if (best_record_count >= current_record_count &&
+ if (best_record_count >= partial_join_cardinality &&
best_read_time >= current_read_time &&
(!(s->key_dependent & rest_tables) || records < 2.0))
{
- best_record_count=current_record_count;
+ best_record_count= partial_join_cardinality;
best_read_time=current_read_time;
}
swap_variables(JOIN_TAB*, join->best_ref[idx], *pos);
if (find_best(join,rest_tables & ~real_table_bit,idx+1,
- current_record_count,current_read_time))
+ partial_join_cardinality,current_read_time,
+ use_cond_selectivity))
DBUG_RETURN(TRUE);
swap_variables(JOIN_TAB*, join->best_ref[idx], *pos);
}
@@ -7722,6 +8244,7 @@ get_best_combination(JOIN *join)
*/
SJ_MATERIALIZATION_INFO *sjm= cur_pos->table->emb_sj_nest->sj_mat_info;
j->records= j->records_read= (ha_rows)(sjm->is_sj_scan? sjm->rows : 1);
+ j->cond_selectivity= 1.0;
JOIN_TAB *jt;
JOIN_TAB_RANGE *jt_range;
if (!(jt= (JOIN_TAB*)join->thd->alloc(sizeof(JOIN_TAB)*sjm->tables)) ||
@@ -7785,6 +8308,7 @@ get_best_combination(JOIN *join)
to access join->best_positions[].
*/
j->records_read= (ha_rows)join->best_positions[tablenr].records_read;
+ j->cond_selectivity= join->best_positions[tablenr].cond_selectivity;
join->map2table[j->table->tablenr]= j;
/* If we've reached the end of sjm nest, switch back to main sequence */
@@ -7806,6 +8330,7 @@ get_best_combination(JOIN *join)
join->table_access_tabs= join->join_tab;
join->top_table_access_tabs_count= join->top_join_tab_count;
+
update_depend_map(join);
DBUG_RETURN(0);
}
@@ -7882,6 +8407,7 @@ static bool create_hj_key_for_table(JOIN *join, JOIN_TAB *join_tab,
keyinfo->key_length=0;
keyinfo->algorithm= HA_KEY_ALG_UNDEF;
keyinfo->flags= HA_GENERATED_KEY;
+ keyinfo->is_statistics_from_stat_tables= FALSE;
keyinfo->name= (char *) "$hj";
keyinfo->rec_per_key= (ulong*) thd->calloc(sizeof(ulong)*key_parts);
if (!keyinfo->rec_per_key)
@@ -8051,6 +8577,7 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j,
j->ref.null_rejecting= 0;
j->ref.disable_cache= FALSE;
j->ref.null_ref_part= NO_REF_PART;
+ j->ref.const_ref_part_map= 0;
keyuse=org_keyuse;
store_key **ref_key= j->ref.key_copy;
@@ -8088,6 +8615,13 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j,
if (keyuse->null_rejecting)
j->ref.null_rejecting|= (key_part_map)1 << i;
keyuse_uses_no_tables= keyuse_uses_no_tables && !keyuse->used_tables;
+ /*
+ Todo: we should remove this check for thd->lex->describe on the next
+ line. With SHOW EXPLAIN code, EXPLAIN printout code no longer depends
+ on it. However, removing the check caused change in lots of query
+ plans! Does the optimizer depend on the contents of
+ table_ref->key_copy ? If yes, do we produce incorrect EXPLAINs?
+ */
if (!keyuse->val->used_tables() && !thd->lex->describe)
{ // Compare against constant
store_key_item tmp(thd,
@@ -8100,6 +8634,7 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j,
if (thd->is_fatal_error)
DBUG_RETURN(TRUE);
tmp.copy();
+ j->ref.const_ref_part_map |= key_part_map(1) << i ;
}
else
*ref_key++= get_store_key(thd,
@@ -8214,6 +8749,7 @@ JOIN::make_simple_join(JOIN *parent, TABLE *temp_table)
!(parent->join_tab_reexec= (JOIN_TAB*) thd->alloc(sizeof(JOIN_TAB))))
DBUG_RETURN(TRUE); /* purecov: inspected */
+ // psergey-todo: here, save the pointer for original join_tabs.
join_tab= parent->join_tab_reexec;
table= &parent->table_reexec[0]; parent->table_reexec[0]= temp_table;
table_count= top_join_tab_count= 1;
@@ -8616,10 +9152,6 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
{ /* there may be a select without a cond. */
if (join->table_count > 1)
cond->update_used_tables(); // Tablenr may have changed
- if (join->const_tables == join->table_count &&
- thd->lex->current_select->master_unit() ==
- &thd->lex->unit) // not upper level SELECT
- join->const_table_map|=RAND_TABLE_BIT;
/*
Extract expressions that depend on constant tables
@@ -9585,7 +10117,7 @@ end_sj_materialize(JOIN *join, JOIN_TAB *join_tab, bool end_of_records)
if (item->is_null())
DBUG_RETURN(NESTED_LOOP_OK);
}
- fill_record(thd, table->field, sjm->sjm_table_cols, TRUE, FALSE);
+ fill_record(thd, table, table->field, sjm->sjm_table_cols, TRUE, FALSE);
if (thd->is_error())
DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */
if ((error= table->file->ha_write_tmp_row(table->record[0])))
@@ -10041,6 +10573,86 @@ restart:
}
}
+/**
+ Remove pushdown conditions that are already checked by the scan phase
+ of BNL/BNLH joins.
+
+ @note
+ If the single-table condition for this table will be used by a
+ blocked join to pre-filter this table's rows, there is no need
+ to re-check the same single-table condition for each joined record.
+
+ This method removes from JOIN_TAB::select_cond and JOIN_TAB::select::cond
+ all top-level conjuncts that also appear in in JOIN_TAB::cache_select::cond.
+*/
+
+void JOIN_TAB::remove_redundant_bnl_scan_conds()
+{
+ if (!(select_cond && cache_select && cache &&
+ (cache->get_join_alg() == JOIN_CACHE::BNL_JOIN_ALG ||
+ cache->get_join_alg() == JOIN_CACHE::BNLH_JOIN_ALG)))
+ return;
+
+ /*
+ select->cond is not processed separately. This method assumes it is always
+ the same as select_cond.
+ */
+ DBUG_ASSERT(!select || !select->cond ||
+ (select->cond == select_cond));
+
+ if (is_cond_and(select_cond))
+ {
+ List_iterator<Item> pushed_cond_li(*((Item_cond*) select_cond)->argument_list());
+ Item *pushed_item;
+ Item_cond_and *reduced_select_cond= new Item_cond_and;
+
+ if (is_cond_and(cache_select->cond))
+ {
+ List_iterator<Item> scan_cond_li(*((Item_cond*) cache_select->cond)->argument_list());
+ Item *scan_item;
+ while ((pushed_item= pushed_cond_li++))
+ {
+ bool found= false;
+ scan_cond_li.rewind();
+ while ((scan_item= scan_cond_li++))
+ {
+ if (pushed_item->eq(scan_item, 0))
+ {
+ found= true;
+ break;
+ }
+ }
+ if (!found)
+ reduced_select_cond->add(pushed_item);
+ }
+ }
+ else
+ {
+ while ((pushed_item= pushed_cond_li++))
+ {
+ if (!pushed_item->eq(cache_select->cond, 0))
+ reduced_select_cond->add(pushed_item);
+ }
+ }
+
+ /*
+ JOIN_CACHE::check_match uses JOIN_TAB::select->cond instead of
+ JOIN_TAB::select_cond. set_cond() sets both pointers.
+ */
+ if (reduced_select_cond->argument_list()->is_empty())
+ set_cond(NULL);
+ else if (reduced_select_cond->argument_list()->elements == 1)
+ set_cond(reduced_select_cond->argument_list()->head());
+ else
+ {
+ reduced_select_cond->quick_fix_field();
+ set_cond(reduced_select_cond);
+ }
+ }
+ else if (select_cond->eq(cache_select->cond, 0))
+ set_cond(NULL);
+}
+
/*
Plan refinement stage: do various setup things for the executor
@@ -10292,6 +10904,15 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after)
abort();
/* purecov: end */
}
+
+ tab->remove_redundant_bnl_scan_conds();
+ DBUG_EXECUTE("where",
+ char buff[256];
+ String str(buff,sizeof(buff),system_charset_info);
+ str.length(0);
+ str.append(tab->table? tab->table->alias.c_ptr() :"<no_table_name>");
+ str.append(" final_pushdown_cond");
+ print_where(tab->select_cond, str.c_ptr_safe(), QT_ORDINARY););
}
uint n_top_tables= join->join_tab_ranges.head()->end -
join->join_tab_ranges.head()->start;
@@ -10465,7 +11086,7 @@ double JOIN_TAB::scan_time()
}
else
{
- found_records= records= table->file->stats.records;
+ found_records= records= table->stat_records();
read_time= table->file->scan_time();
/*
table->quick_condition_rows has already been set to
@@ -10476,7 +11097,7 @@ double JOIN_TAB::scan_time()
}
else
{
- found_records= records=table->file->stats.records;
+ found_records= records=table->stat_records();
read_time= found_records ? (double)found_records: 10.0;// TODO:fix this stub
res= read_time;
}
@@ -10494,7 +11115,7 @@ ha_rows JOIN_TAB::get_examined_rows()
{
ha_rows examined_rows;
- if (select && select->quick)
+ if (select && select->quick && use_quick != 2)
examined_rows= select->quick->records;
else if (type == JT_NEXT || type == JT_ALL ||
type == JT_HASH || type ==JT_HASH_NEXT)
@@ -10517,7 +11138,7 @@ ha_rows JOIN_TAB::get_examined_rows()
handler->info(HA_STATUS_VARIABLE) has been called in
make_join_statistics()
*/
- examined_rows= table->file->stats.records;
+ examined_rows= table->stat_records();
}
}
}
@@ -10548,10 +11169,18 @@ bool JOIN_TAB::preread_init()
mysql_handle_single_derived(join->thd->lex,
derived, DT_CREATE | DT_FILL))
return TRUE;
+
preread_init_done= TRUE;
if (select && select->quick)
select->quick->replace_handler(table->file);
+ DBUG_EXECUTE_IF("show_explain_probe_join_tab_preread",
+ if (dbug_user_var_equals_int(join->thd,
+ "show_explain_probe_select_id",
+ join->select_lex->select_number))
+ dbug_serve_apcs(join->thd, 1);
+ );
+
/* init ftfuns for just initialized derived table */
if (table->fulltext_searched)
init_ftfuncs(join->thd, join->select_lex, test(join->order));
@@ -10762,6 +11391,9 @@ void JOIN::cleanup(bool full)
{
DBUG_ENTER("JOIN::cleanup");
DBUG_PRINT("enter", ("full %u", (uint) full));
+
+ if (full)
+ have_query_plan= QEP_DELETED;
if (table)
{
@@ -11311,14 +11943,6 @@ public:
COND_CMP(Item *a,Item_func *b) :and_level(a),cmp_func(b) {}
};
-#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION
-template class I_List<COND_CMP>;
-template class I_List_iterator<COND_CMP>;
-template class List<Item_func_match>;
-template class List_iterator<Item_func_match>;
-#endif
-
-
/**
Find the multiple equality predicate containing a field.
@@ -11803,7 +12427,8 @@ static bool check_equality(THD *thd, Item *item, COND_EQUAL *cond_equal,
*/
static COND *build_equal_items_for_cond(THD *thd, COND *cond,
- COND_EQUAL *inherited)
+ COND_EQUAL *inherited,
+ bool link_item_fields)
{
Item_equal *item_equal;
COND_EQUAL cond_equal;
@@ -11850,6 +12475,7 @@ static COND *build_equal_items_for_cond(THD *thd, COND *cond,
List_iterator_fast<Item_equal> it(cond_equal.current_level);
while ((item_equal= it++))
{
+ item_equal->set_link_equal_fields(link_item_fields);
item_equal->fix_fields(thd, NULL);
item_equal->update_used_tables();
set_if_bigger(thd->lex->current_select->max_equal_elems,
@@ -11869,7 +12495,8 @@ static COND *build_equal_items_for_cond(THD *thd, COND *cond,
while ((item= li++))
{
Item *new_item;
- if ((new_item= build_equal_items_for_cond(thd, item, inherited)) != item)
+ if ((new_item= build_equal_items_for_cond(thd, item, inherited, FALSE))
+ != item)
{
/* This replacement happens only for standalone equalities */
/*
@@ -12012,9 +12639,9 @@ static COND *build_equal_items_for_cond(THD *thd, COND *cond,
@endcode
Thus, applying equalities from the where condition we basically
can get more freedom in performing join operations.
- Althogh we don't use this property now, it probably makes sense to use
+ Although we don't use this property now, it probably makes sense to use
it in the future.
- @param thd Thread handler
+ @param thd Thread handler
@param cond condition to build the multiple equalities for
@param inherited path to all inherited multiple equality items
@param join_list list of join tables to which the condition
@@ -12023,6 +12650,7 @@ static COND *build_equal_items_for_cond(THD *thd, COND *cond,
for on expressions
@param[out] cond_equal_ref pointer to the structure to place built
equalities in
+ @param link_equal_items equal fields are to be linked
@return
pointer to the transformed condition containing multiple equalities
@@ -12032,14 +12660,15 @@ static COND *build_equal_items(JOIN *join, COND *cond,
COND_EQUAL *inherited,
List<TABLE_LIST> *join_list,
bool ignore_on_conds,
- COND_EQUAL **cond_equal_ref)
+ COND_EQUAL **cond_equal_ref,
+ bool link_equal_fields)
{
THD *thd= join->thd;
COND_EQUAL *cond_equal= 0;
if (cond)
{
- cond= build_equal_items_for_cond(thd, cond, inherited);
+ cond= build_equal_items_for_cond(thd, cond, inherited, link_equal_fields);
cond->update_used_tables();
if (cond->type() == Item::COND_ITEM &&
((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC)
@@ -13558,9 +14187,10 @@ void optimize_wo_join_buffering(JOIN *join, uint first_tab, uint last_tab,
static COND *
-optimize_cond(JOIN *join, COND *conds,
+optimize_cond(JOIN *join, COND *conds,
List<TABLE_LIST> *join_list, bool ignore_on_conds,
- Item::cond_result *cond_value, COND_EQUAL **cond_equal)
+ Item::cond_result *cond_value, COND_EQUAL **cond_equal,
+ int flags)
{
THD *thd= join->thd;
DBUG_ENTER("optimize_cond");
@@ -13583,9 +14213,10 @@ optimize_cond(JOIN *join, COND *conds,
multiple equality contains a constant.
*/
DBUG_EXECUTE("where", print_where(conds, "original", QT_ORDINARY););
- conds= build_equal_items(join, conds, NULL, join_list, ignore_on_conds,
- cond_equal);
- DBUG_EXECUTE("where",print_where(conds,"after equal_items", QT_ORDINARY););
+ conds= build_equal_items(join, conds, NULL, join_list,
+ ignore_on_conds, cond_equal,
+ test(flags & OPT_LINK_EQUAL_FIELDS));
+ DBUG_EXECUTE("where",print_where(conds,"after equal_items", QT_ORDINARY););
/* change field = field to field = const for each found field = const */
propagate_cond_constants(thd, (I_List<COND_CMP> *) 0, conds, conds);
@@ -14377,6 +15008,8 @@ Field *create_tmp_field_from_field(THD *thd, Field *org_field,
((Field_double *) new_field)->not_fixed= TRUE;
new_field->vcol_info= 0;
new_field->stored_in_db= TRUE;
+ new_field->cond_selectivity= 1.0;
+ new_field->next_equal_field= NULL;
}
return new_field;
}
@@ -14721,6 +15354,9 @@ void setup_tmp_table_column_bitmaps(TABLE *table, uchar *bitmaps)
bitmap_init(&table->eq_join_set,
(my_bitmap_map*) (bitmaps+ 3*bitmap_buffer_size(field_count)),
field_count, FALSE);
+ bitmap_init(&table->cond_set,
+ (my_bitmap_map*) (bitmaps+ 4*bitmap_buffer_size(field_count)),
+ field_count, FALSE);
/* write_set and all_set are copies of read_set */
table->def_write_set= table->def_read_set;
table->s->all_set= table->def_read_set;
@@ -14868,7 +15504,7 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
if (param->precomputed_group_by)
copy_func_count+= param->sum_func_count;
- init_sql_alloc(&own_root, TABLE_ALLOC_BLOCK_SIZE, 0);
+ init_sql_alloc(&own_root, TABLE_ALLOC_BLOCK_SIZE, 0, MYF(MY_THREAD_SPECIFIC));
if (!multi_alloc_root(&own_root,
&table, sizeof(*table),
@@ -14886,7 +15522,7 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
&tmpname, (uint) strlen(path)+1,
&group_buff, (group && ! using_unique_constraint ?
param->group_length : 0),
- &bitmaps, bitmap_buffer_size(field_count)*4,
+ &bitmaps, bitmap_buffer_size(field_count)*5,
NullS))
{
if (temp_pool_slot != MY_BIT_NONE)
@@ -15349,8 +15985,11 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
keyinfo->usable_key_parts=keyinfo->key_parts= param->group_parts;
keyinfo->ext_key_parts= keyinfo->key_parts;
keyinfo->key_length=0;
- keyinfo->rec_per_key=0;
+ keyinfo->rec_per_key=NULL;
+ keyinfo->read_stats= NULL;
+ keyinfo->collected_stats= NULL;
keyinfo->algorithm= HA_KEY_ALG_UNDEF;
+ keyinfo->is_statistics_from_stat_tables= FALSE;
keyinfo->name= (char*) "group_key";
ORDER *cur_group= group;
for (; cur_group ; cur_group= cur_group->next, key_part_info++)
@@ -15463,7 +16102,10 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
keyinfo->key_length= 0; // Will compute the sum of the parts below.
keyinfo->name= (char*) "distinct_key";
keyinfo->algorithm= HA_KEY_ALG_UNDEF;
+ keyinfo->is_statistics_from_stat_tables= FALSE;
keyinfo->rec_per_key=0;
+ keyinfo->read_stats= NULL;
+ keyinfo->collected_stats= NULL;
/*
Create an extra field to hold NULL bits so that unique indexes on
@@ -15620,7 +16262,7 @@ TABLE *create_virtual_tmp_table(THD *thd, List<Create_field> &field_list)
&share, sizeof(*share),
&field, (field_count + 1) * sizeof(Field*),
&blob_field, (field_count+1) *sizeof(uint),
- &bitmaps, bitmap_buffer_size(field_count)*4,
+ &bitmaps, bitmap_buffer_size(field_count)*5,
NullS))
return 0;
@@ -16174,7 +16816,7 @@ create_internal_tmp_table_from_heap2(THD *thd, TABLE *table,
DBUG_EXECUTE_IF("raise_error", write_err= HA_ERR_FOUND_DUPP_KEY ;);
if (write_err)
goto err;
- if (thd->killed)
+ if (thd->check_killed())
{
thd->send_kill_message();
goto err_killed;
@@ -16441,6 +17083,14 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure)
else
{
DBUG_ASSERT(join->table_count);
+
+ DBUG_EXECUTE_IF("show_explain_probe_do_select",
+ if (dbug_user_var_equals_int(join->thd,
+ "show_explain_probe_select_id",
+ join->select_lex->select_number))
+ dbug_serve_apcs(join->thd, 1);
+ );
+
if (join->outer_ref_cond && !join->outer_ref_cond->val_int())
error= NESTED_LOOP_NO_MORE_ROWS;
else
@@ -16571,7 +17221,7 @@ sub_select_cache(JOIN *join, JOIN_TAB *join_tab, bool end_of_records)
rc= sub_select(join, join_tab, end_of_records);
DBUG_RETURN(rc);
}
- if (join->thd->killed)
+ if (join->thd->check_killed())
{
/* The user has aborted the execution of the query */
join->thd->send_kill_message();
@@ -16861,12 +17511,14 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab,
DBUG_ENTER("evaluate_join_record");
DBUG_PRINT("enter",
("evaluate_join_record join: %p join_tab: %p"
- " cond: %p error: %d", join, join_tab, select_cond, error));
+ " cond: %p error: %d alias %s",
+ join, join_tab, select_cond, error,
+ join_tab->table->alias.ptr()));
if (error > 0 || (join->thd->is_error())) // Fatal error
DBUG_RETURN(NESTED_LOOP_ERROR);
if (error < 0)
DBUG_RETURN(NESTED_LOOP_NO_MORE_ROWS);
- if (join->thd->killed) // Aborted by user
+ if (join->thd->check_killed()) // Aborted by user
{
join->thd->send_kill_message();
DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
@@ -16974,6 +17626,7 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab,
if (join_tab->check_weed_out_table && found)
{
int res= join_tab->check_weed_out_table->sj_weedout_check_row(join->thd);
+ DBUG_PRINT("info", ("weedout_check: %d", res));
if (res == -1)
DBUG_RETURN(NESTED_LOOP_ERROR);
else if (res == 1)
@@ -16994,8 +17647,8 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab,
(See above join->return_tab= tab).
*/
join->examined_rows++;
- DBUG_PRINT("counts", ("join->examined_rows++: %lu",
- (ulong) join->examined_rows));
+ DBUG_PRINT("counts", ("join->examined_rows++: %lu found: %d",
+ (ulong) join->examined_rows, (int) found));
if (found)
{
@@ -17212,7 +17865,7 @@ join_read_const_table(JOIN_TAB *tab, POSITION *pos)
{
if ((error=join_read_system(tab)))
{ // Info for DESCRIBE
- tab->info="const row not found";
+ tab->info= ET_CONST_ROW_NOT_FOUND;
/* Mark for EXPLAIN that the row was not found */
pos->records_read=0.0;
pos->ref_depend_map= 0;
@@ -17238,7 +17891,7 @@ join_read_const_table(JOIN_TAB *tab, POSITION *pos)
table->disable_keyread();
if (error)
{
- tab->info="unique row not found";
+ tab->info= ET_UNIQUE_ROW_NOT_FOUND;
/* Mark for EXPLAIN that the row was not found */
pos->records_read=0.0;
pos->ref_depend_map= 0;
@@ -17643,6 +18296,14 @@ int read_first_record_seq(JOIN_TAB *tab)
static int
test_if_quick_select(JOIN_TAB *tab)
{
+ DBUG_EXECUTE_IF("show_explain_probe_test_if_quick_select",
+ if (dbug_user_var_equals_int(tab->join->thd,
+ "show_explain_probe_select_id",
+ tab->join->select_lex->select_number))
+ dbug_serve_apcs(tab->join->thd, 1);
+ );
+
+
delete tab->select->quick;
tab->select->quick=0;
return tab->select->test_quick_select(tab->join->thd, tab->keys,
@@ -17893,7 +18554,25 @@ end_send(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
if ((error= join->result->send_data(*join->fields)))
DBUG_RETURN(error < 0 ? NESTED_LOOP_OK : NESTED_LOOP_ERROR);
}
- if (++join->send_records >= join->unit->select_limit_cnt &&
+
+ ++join->send_records;
+ if (join->send_records >= join->unit->select_limit_cnt &&
+ !join->do_send_rows)
+ {
+ /*
+ If filesort is used for sorting, stop after select_limit_cnt+1
+ records are read. Because of optimization in some cases it can
+ provide only select_limit_cnt+1 records.
+ */
+ if (join->order &&
+ join->sortorder &&
+ join->select_options & OPTION_FOUND_ROWS)
+ {
+ DBUG_PRINT("info", ("filesort NESTED_LOOP_QUERY_LIMIT"));
+ DBUG_RETURN(NESTED_LOOP_QUERY_LIMIT);
+ }
+ }
+ if (join->send_records >= join->unit->select_limit_cnt &&
join->do_send_rows)
{
if (join->select_options & OPTION_FOUND_ROWS)
@@ -18117,7 +18796,7 @@ end_write(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
}
}
end:
- if (join->thd->killed)
+ if (join->thd->check_killed())
{
join->thd->send_kill_message();
DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
@@ -18188,7 +18867,7 @@ end_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
}
join->send_records++;
end:
- if (join->thd->killed)
+ if (join->thd->check_killed())
{
join->thd->send_kill_message();
DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
@@ -18238,7 +18917,7 @@ end_unique_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */
}
}
- if (join->thd->killed)
+ if (join->thd->check_killed())
{
join->thd->send_kill_message();
DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
@@ -18316,7 +18995,7 @@ end_write_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
if (join->procedure)
join->procedure->add();
end:
- if (join->thd->killed)
+ if (join->thd->check_killed())
{
join->thd->send_kill_message();
DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
@@ -19323,7 +20002,7 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
uint saved_best_key_parts= 0;
int best_key_direction= 0;
JOIN *join= tab->join;
- ha_rows table_records= table->file->stats.records;
+ ha_rows table_records= table->stat_records();
test_if_cheaper_ordering(tab, order, table, usable_keys,
ref_key, select_limit,
@@ -19439,7 +20118,7 @@ check_reverse_order:
{
tab->ref.key= -1;
tab->ref.key_parts= 0;
- if (select_limit < table->file->stats.records)
+ if (select_limit < table->stat_records())
tab->limit= select_limit;
table->disable_keyread();
}
@@ -19594,6 +20273,8 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order,
{
uint length= 0;
ha_rows examined_rows;
+ ha_rows found_rows;
+ ha_rows filesort_retval= HA_POS_ERROR;
TABLE *table;
SQL_SELECT *select;
JOIN_TAB *tab;
@@ -19682,7 +20363,8 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order,
goto err; /* purecov: inspected */
table->sort.io_cache=(IO_CACHE*) my_malloc(sizeof(IO_CACHE),
- MYF(MY_WME | MY_ZEROFILL));
+ MYF(MY_WME | MY_ZEROFILL|
+ MY_THREAD_SPECIFIC));
table->status=0; // May be wrong if quick_select
if (!tab->preread_init_done && tab->preread_init())
@@ -19726,9 +20408,11 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order,
if (table->s->tmp_table)
table->file->info(HA_STATUS_VARIABLE); // Get record count
- table->sort.found_records=filesort(thd, table,join->sortorder, length,
- select, filesort_limit, 0,
- &examined_rows);
+ filesort_retval= filesort(thd, table, join->sortorder, length,
+ select, filesort_limit, 0,
+ &examined_rows, &found_rows);
+ table->sort.found_records= filesort_retval;
+ tab->records= found_rows; // For SQL_CALC_ROWS
if (quick_created)
{
@@ -19746,72 +20430,6 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order,
*(join->pre_sort_join_tab)= *tab;
- /*TODO: here, close the index scan, cancel index-only read. */
- tab->records= table->sort.found_records; // For SQL_CALC_ROWS
-#if 0
- /* MariaDB doesn't need the following: */
- if (select)
- {
- /*
- We need to preserve tablesort's output resultset here, because
- QUICK_INDEX_MERGE_SELECT::~QUICK_INDEX_MERGE_SELECT (called by
- SQL_SELECT::cleanup()) may free it assuming it's the result of the quick
- select operation that we no longer need. Note that all the other parts of
- this data structure are cleaned up when
- QUICK_INDEX_MERGE_SELECT::get_next encounters end of data, so the next
- SQL_SELECT::cleanup() call changes sort.io_cache alone.
- */
- IO_CACHE *tablesort_result_cache;
-
- tablesort_result_cache= table->sort.io_cache;
- table->sort.io_cache= NULL;
-
- if (select->quick &&
- select->quick->get_type() == QUICK_SELECT_I::QS_TYPE_GROUP_MIN_MAX)
- {
- tab->filesort_used_loose_index_scan= true;
-
- QUICK_GROUP_MIN_MAX_SELECT *minmax_quick=
- static_cast<QUICK_GROUP_MIN_MAX_SELECT*>(select->quick);
- if (minmax_quick->is_agg_distinct())
- tab->filesort_used_loose_index_scan_agg_distinct= true;
- }
-
- /*
- If a quick object was created outside of create_sort_index()
- that might be reused, then do not call select->cleanup() since
- it will delete the quick object.
- */
- if (!keep_quick)
- {
- select->cleanup();
-
- // If we deleted the quick object we need to clear quick_keys
- table->quick_keys.clear_all();
- table->intersect_keys.clear_all();
- }
- else
- {
- // Need to close the index scan in order to re-use the handler
- tab->select->quick->range_end();
- }
-
- /*
- The select object is now ready for the next use. To avoid that
- the select object is used when reading the records in sorted
- order we set the pointer to it to NULL. The select pointer will
- be restored from the saved_select pointer when this select
- operation is completed (@see JOIN::exec). This ensures that it
- will be re-used when filesort is used by subqueries that are
- executed multiple times.
- */
- tab->saved_select= tab->select;
- tab->select= NULL;
-
- // Restore the output resultset
- table->sort.io_cache= tablesort_result_cache;
- }
-#endif
tab->select=NULL;
tab->set_select_cond(NULL, __LINE__);
tab->type=JT_ALL; // Read with normal read_record
@@ -19822,12 +20440,11 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order,
goto err;
tab->join->examined_rows+=examined_rows;
- DBUG_RETURN(table->sort.found_records == HA_POS_ERROR);
+ DBUG_RETURN(filesort_retval == HA_POS_ERROR);
err:
DBUG_RETURN(-1);
}
-
void JOIN::clean_pre_sort_join_tab()
{
//TABLE *table= pre_sort_join_tab->table;
@@ -19836,29 +20453,6 @@ void JOIN::clean_pre_sort_join_tab()
the table already deleted by st_select_lex_unit::cleanup().
We rely on that fake_select_lex didn't have quick select.
*/
-#if 0
- if (pre_sort_join_tab->select && pre_sort_join_tab->select->quick)
- {
- /*
- We need to preserve tablesort's output resultset here, because
- QUICK_INDEX_MERGE_SELECT::~QUICK_INDEX_MERGE_SELECT (called by
- SQL_SELECT::cleanup()) may free it assuming it's the result of the quick
- select operation that we no longer need. Note that all the other parts of
- this data structure are cleaned up when
- QUICK_INDEX_MERGE_SELECT::get_next encounters end of data, so the next
- SQL_SELECT::cleanup() call changes sort.io_cache alone.
- */
- IO_CACHE *tablesort_result_cache;
-
- tablesort_result_cache= table->sort.io_cache;
- table->sort.io_cache= NULL;
- pre_sort_join_tab->select->cleanup();
- table->quick_keys.clear_all(); // as far as we cleanup select->quick
- table->intersect_keys.clear_all();
- table->sort.io_cache= tablesort_result_cache;
- }
-#endif
- //table->disable_keyread(); // Restore if we used indexes
if (pre_sort_join_tab->select && pre_sort_join_tab->select->quick)
{
pre_sort_join_tab->select->cleanup();
@@ -19982,7 +20576,7 @@ static int remove_dup_with_compare(THD *thd, TABLE *table, Field **first_field,
error= file->ha_rnd_next(record);
for (;;)
{
- if (thd->killed)
+ if (thd->check_killed())
{
thd->send_kill_message();
error=0;
@@ -20103,7 +20697,7 @@ static int remove_dup_with_hash_index(THD *thd, TABLE *table,
for (;;)
{
uchar *org_key_pos;
- if (thd->killed)
+ if (thd->check_killed())
{
thd->send_kill_message();
error=0;
@@ -20179,7 +20773,7 @@ SORT_FIELD *make_unireg_sortorder(ORDER *order, uint *length,
pos= sort= sortorder;
if (!pos)
- return 0;
+ DBUG_RETURN(0);
for (;order;order=order->next,pos++)
{
@@ -20196,6 +20790,7 @@ SORT_FIELD *make_unireg_sortorder(ORDER *order, uint *length,
else
pos->item= *order->item;
pos->reverse=! order->asc;
+ DBUG_ASSERT(pos->field != NULL || pos->item != NULL);
}
*length=count;
DBUG_RETURN(sort);
@@ -22166,162 +22761,372 @@ void JOIN::clear()
}
}
-/**
- EXPLAIN handling.
- Send a description about what how the select will be done to stdout.
+/*
+ Print an EXPLAIN line with all NULLs and given message in the 'Extra' column
*/
-static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
- bool distinct,const char *message)
+int print_explain_message_line(select_result_sink *result,
+ uint8 options,
+ uint select_number,
+ const char *select_type,
+ ha_rows *rows,
+ const char *message)
{
- List<Item> field_list;
- List<Item> item_list;
- THD *thd=join->thd;
- select_result *result=join->result;
+ const CHARSET_INFO *cs= system_charset_info;
Item *item_null= new Item_null();
- CHARSET_INFO *cs= system_charset_info;
- int quick_type;
- DBUG_ENTER("select_describe");
- DBUG_PRINT("info", ("Select 0x%lx, type %s, message %s",
- (ulong)join->select_lex, join->select_lex->type,
- message ? message : "NULL"));
- /* Don't log this into the slow query log */
- thd->server_status&= ~(SERVER_QUERY_NO_INDEX_USED | SERVER_QUERY_NO_GOOD_INDEX_USED);
- join->unit->offset_limit_cnt= 0;
+ List<Item> item_list;
- /*
- 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)
- {
- item_list.push_back(new Item_int((int32)
- join->select_lex->select_number));
- item_list.push_back(new Item_string(join->select_lex->type,
- strlen(join->select_lex->type), cs));
- for (uint i=0 ; i < 7; i++)
- item_list.push_back(item_null);
- if (join->thd->lex->describe & DESCRIBE_PARTITIONS)
- item_list.push_back(item_null);
- if (join->thd->lex->describe & DESCRIBE_EXTENDED)
- item_list.push_back(item_null);
+ item_list.push_back(new Item_int((int32) select_number));
+ item_list.push_back(new Item_string(select_type,
+ strlen(select_type), cs));
+ /* `table` */
+ item_list.push_back(item_null);
- item_list.push_back(new Item_string(message,strlen(message),cs));
- if (result->send_data(item_list))
- join->error= 1;
+ /* `partitions` */
+ if (options & DESCRIBE_PARTITIONS)
+ item_list.push_back(item_null);
+
+ /* type, possible_keys, key, key_len, ref */
+ for (uint i=0 ; i < 5; i++)
+ item_list.push_back(item_null);
+
+ /* `rows` */
+ if (rows)
+ {
+ item_list.push_back(new Item_int(*rows,
+ MY_INT64_NUM_DECIMAL_DIGITS));
}
- else if (join->select_lex == join->unit->fake_select_lex)
+ else
+ item_list.push_back(item_null);
+
+ /* `filtered` */
+ if (options & DESCRIBE_EXTENDED)
+ item_list.push_back(item_null);
+
+ /* `Extra` */
+ if (message)
+ item_list.push_back(new Item_string(message,strlen(message),cs));
+ else
+ item_list.push_back(item_null);
+
+ if (result->send_data(item_list))
+ return 1;
+ return 0;
+}
+
+
+/*
+ Make a comma-separated list of possible_keys names and add it into the string
+*/
+
+void make_possible_keys_line(TABLE *table, key_map possible_keys, String *line)
+{
+ if (!possible_keys.is_clear_all())
{
- /*
- here we assume that the query will return at least two rows, so we
- show "filesort" in EXPLAIN. Of course, sometimes we'll be wrong
- and no filesort will be actually done, but executing all selects in
- the UNION to provide precise EXPLAIN information will hardly be
- appreciated :)
- */
- char table_name_buffer[SAFE_NAME_LEN];
- item_list.empty();
- /* id */
- item_list.push_back(new Item_null);
- /* select_type */
- item_list.push_back(new Item_string(join->select_lex->type,
- strlen(join->select_lex->type),
- cs));
- /* table */
- {
- SELECT_LEX *sl= join->unit->first_select();
- uint len= 6, lastop= 0;
- memcpy(table_name_buffer, STRING_WITH_LEN("<union"));
- for (; sl && len + lastop + 5 < NAME_LEN; sl= sl->next_select())
- {
- len+= lastop;
- lastop= my_snprintf(table_name_buffer + len, NAME_LEN - len,
- "%u,", sl->select_number);
- }
- if (sl || len + lastop >= NAME_LEN)
- {
- memcpy(table_name_buffer + len, STRING_WITH_LEN("...>") + 1);
- len+= 4;
- }
- else
+ uint j;
+ for (j=0 ; j < table->s->keys ; j++)
+ {
+ if (possible_keys.is_set(j))
{
- len+= lastop;
- table_name_buffer[len - 1]= '>'; // change ',' to '>'
+ if (line->length())
+ line->append(',');
+ line->append(table->key_info[j].name,
+ strlen(table->key_info[j].name),
+ system_charset_info);
}
- item_list.push_back(new Item_string(table_name_buffer, len, cs));
}
- /* partitions */
- if (join->thd->lex->describe & DESCRIBE_PARTITIONS)
+ }
+}
+
+/*
+ Print an EXPLAIN output row, based on information provided in the parameters
+
+ @note
+ Parameters that may have NULL value in EXPLAIN output, should be passed
+ (char*)NULL.
+
+ @return
+ 0 - OK
+ 1 - OOM Error
+*/
+
+int print_explain_row(select_result_sink *result,
+ uint8 options,
+ uint select_number,
+ const char *select_type,
+ const char *table_name,
+ const char *partitions,
+ enum join_type jtype,
+ const char *possible_keys,
+ const char *index,
+ const char *key_len,
+ const char *ref,
+ ha_rows *rows,
+ const char *extra)
+{
+ const CHARSET_INFO *cs= system_charset_info;
+ Item *item_null= new Item_null();
+ List<Item> item_list;
+ Item *item;
+
+ item_list.push_back(new Item_int((int32) select_number));
+ item_list.push_back(new Item_string(select_type,
+ strlen(select_type), cs));
+ item_list.push_back(new Item_string(table_name,
+ strlen(table_name), cs));
+ if (options & DESCRIBE_PARTITIONS)
+ {
+ if (partitions)
+ {
+ item_list.push_back(new Item_string(partitions,
+ strlen(partitions), cs));
+ }
+ else
item_list.push_back(item_null);
- /* type */
- item_list.push_back(new Item_string(join_type_str[JT_ALL],
- strlen(join_type_str[JT_ALL]),
- cs));
- /* possible_keys */
- item_list.push_back(item_null);
- /* key*/
+ }
+
+ const char *jtype_str= join_type_str[jtype];
+ item_list.push_back(new Item_string(jtype_str,
+ strlen(jtype_str), cs));
+
+ item= possible_keys? new Item_string(possible_keys, strlen(possible_keys),
+ cs) : item_null;
+ item_list.push_back(item);
+
+ /* 'index */
+ item= index ? new Item_string(index, strlen(index), cs) : item_null;
+ item_list.push_back(item);
+
+ /* 'key_len */
+ item= key_len ? new Item_string(key_len, strlen(key_len), cs) : item_null;
+ item_list.push_back(item);
+
+ /* 'ref' */
+ item= ref ? new Item_string(ref, strlen(ref), cs) : item_null;
+ item_list.push_back(item);
+
+ /* 'rows' */
+ if (rows)
+ {
+ item_list.push_back(new Item_int(*rows,
+ MY_INT64_NUM_DECIMAL_DIGITS));
+ }
+ else
item_list.push_back(item_null);
- /* key_len */
+
+ /* 'filtered' */
+ const double filtered=100.0;
+ if (options & DESCRIBE_EXTENDED)
+ item_list.push_back(new Item_float(filtered, 2));
+
+ /* 'Extra' */
+ if (extra)
+ item_list.push_back(new Item_string(extra, strlen(extra), cs));
+ else
item_list.push_back(item_null);
- /* ref */
+
+ if (result->send_data(item_list))
+ return 1;
+ return 0;
+}
+
+
+int print_fake_select_lex_join(select_result_sink *result, bool on_the_fly,
+ SELECT_LEX *select_lex, uint8 explain_flags)
+{
+ const CHARSET_INFO *cs= system_charset_info;
+ Item *item_null= new Item_null();
+ List<Item> item_list;
+ if (on_the_fly)
+ select_lex->set_explain_type(on_the_fly);
+ /*
+ here we assume that the query will return at least two rows, so we
+ show "filesort" in EXPLAIN. Of course, sometimes we'll be wrong
+ and no filesort will be actually done, but executing all selects in
+ the UNION to provide precise EXPLAIN information will hardly be
+ appreciated :)
+ */
+ char table_name_buffer[SAFE_NAME_LEN];
+ item_list.empty();
+ /* id */
+ item_list.push_back(new Item_null);
+ /* select_type */
+ item_list.push_back(new Item_string(select_lex->type,
+ strlen(select_lex->type),
+ cs));
+ /* table */
+ {
+ SELECT_LEX *sl= select_lex->master_unit()->first_select();
+ uint len= 6, lastop= 0;
+ memcpy(table_name_buffer, STRING_WITH_LEN("<union"));
+ for (; sl && len + lastop + 5 < NAME_LEN; sl= sl->next_select())
+ {
+ len+= lastop;
+ lastop= my_snprintf(table_name_buffer + len, NAME_LEN - len,
+ "%u,", sl->select_number);
+ }
+ if (sl || len + lastop >= NAME_LEN)
+ {
+ memcpy(table_name_buffer + len, STRING_WITH_LEN("...>") + 1);
+ len+= 4;
+ }
+ else
+ {
+ len+= lastop;
+ table_name_buffer[len - 1]= '>'; // change ',' to '>'
+ }
+ item_list.push_back(new Item_string(table_name_buffer, len, cs));
+ }
+ /* partitions */
+ if (explain_flags & DESCRIBE_PARTITIONS)
item_list.push_back(item_null);
- /* in_rows */
- if (join->thd->lex->describe & DESCRIBE_EXTENDED)
- item_list.push_back(item_null);
- /* rows */
+ /* type */
+ item_list.push_back(new Item_string(join_type_str[JT_ALL],
+ strlen(join_type_str[JT_ALL]),
+ cs));
+ /* possible_keys */
+ item_list.push_back(item_null);
+ /* key*/
+ item_list.push_back(item_null);
+ /* key_len */
+ item_list.push_back(item_null);
+ /* ref */
+ item_list.push_back(item_null);
+ /* in_rows */
+ if (explain_flags & DESCRIBE_EXTENDED)
item_list.push_back(item_null);
- /* extra */
- if (join->unit->global_parameters->order_list.first)
- item_list.push_back(new Item_string("Using filesort",
- 14, cs));
- else
- item_list.push_back(new Item_string("", 0, cs));
+ /* rows */
+ item_list.push_back(item_null);
+ /* extra */
+ if (select_lex->master_unit()->global_parameters->order_list.first)
+ item_list.push_back(new Item_string("Using filesort",
+ 14, cs));
+ else
+ item_list.push_back(new Item_string("", 0, cs));
+
+ if (result->send_data(item_list))
+ return 1;
+ return 0;
+}
- if (result->send_data(item_list))
- join->error= 1;
+
+/*
+ Append MRR information from quick select to the given string
+*/
+
+void explain_append_mrr_info(QUICK_RANGE_SELECT *quick, String *res)
+{
+ char mrr_str_buf[128];
+ mrr_str_buf[0]=0;
+ int len;
+ handler *h= quick->head->file;
+ len= h->multi_range_read_explain_info(quick->mrr_flags, mrr_str_buf,
+ sizeof(mrr_str_buf));
+ if (len > 0)
+ {
+ //res->append(STRING_WITH_LEN("; "));
+ res->append(mrr_str_buf, len);
+ }
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// TODO: join with make_possible_keys_line ?
+void append_possible_keys(String *str, TABLE *table, key_map possible_keys)
+{
+ uint j;
+ for (j=0 ; j < table->s->keys ; j++)
+ {
+ if (possible_keys.is_set(j))
+ {
+ if (str->length())
+ str->append(',');
+ str->append(table->key_info[j].name,
+ strlen(table->key_info[j].name),
+ system_charset_info);
+ }
+ }
+}
+
+
+/*
+ Save Query Plan Footprint
+
+ @note
+ Currently, this function may be called multiple times
+*/
+
+int JOIN::save_explain_data_intern(Explain_query *output, bool need_tmp_table,
+ bool need_order, bool distinct,
+ const char *message)
+{
+ Explain_node *explain_node;
+ 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;
+ DBUG_ENTER("JOIN::save_explain_data_intern");
+ DBUG_PRINT("info", ("Select 0x%lx, type %s, message %s",
+ (ulong)join->select_lex, join->select_lex->type,
+ message ? message : "NULL"));
+ DBUG_ASSERT(have_query_plan == QEP_AVAILABLE);
+ /* Don't log this into the slow query log */
+
+ if (message)
+ {
+ Explain_select *xpl_sel;
+ explain_node= xpl_sel= new (output->mem_root) Explain_select;
+ join->select_lex->set_explain_type(true);
+
+ xpl_sel->select_id= join->select_lex->select_number;
+ xpl_sel->select_type= join->select_lex->type;
+ xpl_sel->message= message;
+ /* Setting xpl_sel->message means that all other members are invalid */
+ output->add_node(xpl_sel);
+ }
+ else if (join->select_lex == join->unit->fake_select_lex)
+ {
+ /* Do nothing, Explain_union will create and print fake_select_lex */
}
else if (!join->select_lex->master_unit()->derived ||
join->select_lex->master_unit()->derived->is_materialized_derived())
{
+ Explain_select *xpl_sel;
+ explain_node= xpl_sel= new (output->mem_root) Explain_select;
table_map used_tables=0;
- bool printing_materialize_nest= FALSE;
- uint select_id= join->select_lex->select_number;
+ join->select_lex->set_explain_type(true);
+ xpl_sel->select_id= join->select_lex->select_number;
+ xpl_sel->select_type= join->select_lex->type;
+
+ 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))
{
+ 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();
- printing_materialize_nest= TRUE;
}
-
+ else
+ select_id= join->select_lex->select_number;
+
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];
+ char buff4[512];
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)
@@ -22330,27 +23135,27 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
continue;
}
- 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));
-
- if ((tab->type == JT_ALL || tab->type == JT_HASH) &&
- tab->select && tab->select->quick)
+ if (join->table_access_tabs == join->join_tab &&
+ tab == (first_top_tab + join->const_tables) && pre_sort_join_tab)
{
- 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;
+ saved_join_tab= tab;
+ tab= pre_sort_join_tab;
}
+ Explain_table_access *eta= new (output->mem_root) Explain_table_access;
+ xpl_sel->add_table(eta);
+ eta->key.set(thd->mem_root, NULL, (uint)-1);
+ eta->quick_info= NULL;
+
+ /* id */
+ 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)
{
@@ -22358,7 +23163,7 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
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));
+ eta->table_name.copy(table_name_buffer, len, cs);
}
else if (tab->bush_children)
{
@@ -22368,60 +23173,54 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
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));
+ eta->table_name.copy(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));
+ eta->table_name.copy(real_table->alias, strlen(real_table->alias), cs);
}
+
/* "partitions" column */
- if (join->thd->lex->describe & 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);
+ make_used_partitions_str(part_info, &eta->used_partitions);
+ eta->used_partitions_set= true;
}
else
- item_list.push_back(item_null);
+ eta->used_partitions_set= false;
#else
/* just produce empty column if partitioning is not compiled in */
- item_list.push_back(item_null);
+ eta->used_partitions_set= false;
#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);
- }
- }
+ 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;
}
- if (tmp1.length())
- item_list.push_back(new Item_string(tmp1.ptr(),tmp1.length(),cs));
- else
- item_list.push_back(item_null);
+ eta->type= tab_type;
- /* Build "key", "key_len", and "ref" values and add them to item_list */
- if (tab->type == JT_NEXT)
+ /* 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;
@@ -22431,56 +23230,55 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
key_info= tab->get_keyinfo_by_key_no(tab->ref.key);
key_len= tab->ref.key_length;
}
- if (key_info)
+
+ /*
+ 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)
{
- 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)
+ 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)
{
- for (store_key **ref=tab->ref.key_copy ; *ref ; ref++)
+ store_key **ref=tab->ref.key_copy;
+ for (uint kp= 0; kp < tab->ref.key_parts; kp++)
{
if (tmp4.length())
tmp4.append(',');
- tmp4.append((*ref)->name(), strlen((*ref)->name()), cs);
+
+ 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)
+
+ if (tab_type == JT_HASH_NEXT) /* full index scan + hash join */
{
- tmp2.append(':');
- tmp3.append(':');
+ eta->hash_next_key.set(thd->mem_root,
+ table->key_info[tab->index].name,
+ table->key_info[tab->index].key_length);
}
- if (tab->type == JT_HASH_NEXT)
+
+ if (key_info)
{
- 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 && tab->select->quick)
- tab->select->quick->add_keys_and_lengths(&tmp2, &tmp3);
- if (key_info || (tab->select && 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));
+ if (key_info && tab_type != JT_NEXT)
+ {
+ eta->ref.copy(tmp4);
+ eta->ref_set= true;
+ }
else
- item_list.push_back(item_null);
+ eta->ref_set= false;
}
else
{
@@ -22490,122 +23288,112 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
{
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;
- tmp2.append(tmp_buff, strlen(tmp_buff), cs);
+ key_name_buf.append(tmp_buff, strlen(tmp_buff), cs);
}
if (table_list->has_table_lookup_value)
{
if (table_list->has_db_lookup_value)
- tmp2.append(',');
+ key_name_buf.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);
+ key_name_buf.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);
+
+ if (key_name_buf.length())
+ eta->key.set(thd->mem_root, key_name_buf.c_ptr_safe(), -1);
}
- else
- item_list.push_back(item_null);
- item_list.push_back(item_null);
- item_list.push_back(item_null);
+ eta->ref_set= false;
}
- /* Add "rows" field to item_list. */
+ /* "rows" */
if (table_list /* SJM bushes don't have table_list */ &&
table_list->schema_table)
{
- /* in_rows */
- if (join->thd->lex->describe & DESCRIBE_EXTENDED)
- item_list.push_back(item_null);
- /* rows */
- item_list.push_back(item_null);
+ /* I_S tables have rows=extra=NULL */
+ eta->rows_set= false;
+ eta->filtered_set= false;
}
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));
+ eta->rows_set= true;
+ eta->rows= examined_rows;
- /* Add "filtered" field to item_list. */
- if (join->thd->lex->describe & DESCRIBE_EXTENDED)
+ /* "filtered" */
+ float f= 0.0;
+ if (examined_rows)
{
- 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);
- set_if_smaller(f, 100.0);
- item_list.push_back(new Item_float(f, 2));
+ 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 add it to item_list. */
+ /* Build "Extra" field and save it */
key_read=table->key_read;
- if ((tab->type == JT_NEXT || tab->type == JT_CONST) &&
+ if ((tab_type == JT_NEXT || tab_type == JT_CONST) &&
table->covering_keys.is_set(tab->index))
key_read=1;
if (quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT &&
- !((QUICK_ROR_INTERSECT_SELECT*)tab->select->quick)->need_to_fetch_row)
+ !((QUICK_ROR_INTERSECT_SELECT*)quick)->need_to_fetch_row)
key_read=1;
if (tab->info)
- item_list.push_back(new Item_string(tab->info,strlen(tab->info),cs));
+ {
+ eta->push_extra(tab->info);
+ }
else if (tab->packed_info & TAB_INFO_HAVE_VALUE)
{
if (tab->packed_info & TAB_INFO_USING_INDEX)
- extra.append(STRING_WITH_LEN("; Using index"));
+ eta->push_extra(ET_USING_INDEX);
if (tab->packed_info & TAB_INFO_USING_WHERE)
- extra.append(STRING_WITH_LEN("; Using where"));
+ eta->push_extra(ET_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));
+ 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 && tab->select->quick)
- keyno = tab->select->quick->index;
+ 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"));
+ eta->push_extra(ET_USING_INDEX_CONDITION);
else if (tab->cache_idx_cond)
- extra.append(STRING_WITH_LEN("; Using index condition(BKA)"));
+ 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)
{
- extra.append(STRING_WITH_LEN("; Using "));
- tab->select->quick->add_info_string(&extra);
+ eta->push_extra(ET_USING);
}
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(')');
+ eta->push_extra(ET_RANGE_CHECKED_FOR_EACH_RECORD);
+ eta->range_checked_map= tab->keys;
}
- else if (tab->select->cond)
+ else if (tab->select->cond ||
+ (tab->cache_select && tab->cache_select->cond))
{
const COND *pushed_cond= tab->table->file->pushed_cond;
@@ -22615,16 +23403,19 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
HA_MUST_USE_TABLE_CONDITION_PUSHDOWN)) &&
pushed_cond)
{
- extra.append(STRING_WITH_LEN("; Using where with pushed "
- "condition"));
- if (thd->lex->describe & DESCRIBE_EXTENDED)
+ 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
- extra.append(STRING_WITH_LEN("; Using where"));
+ eta->push_extra(ET_USING_WHERE);
}
}
if (table_list /* SJM bushes don't have table_list */ &&
@@ -22632,19 +23423,20 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
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"));
+ eta->push_extra(ET_SKIP_OPEN_TABLE);
else if (table_list->table_open_method == OPEN_FRM_ONLY)
- extra.append(STRING_WITH_LEN("; Open_frm_only"));
+ eta->push_extra(ET_OPEN_FRM_ONLY);
else
- extra.append(STRING_WITH_LEN("; Open_full_table"));
+ 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)
- extra.append(STRING_WITH_LEN("; Scanned 0 databases"));
+ eta->push_extra(ET_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"));
+ eta->push_extra(ET_SCANNED_1_DATABASE);
else
- extra.append(STRING_WITH_LEN("; Scanned all databases"));
+ eta->push_extra(ET_SCANNED_ALL_DATABASES);
}
if (key_read)
{
@@ -22652,69 +23444,52 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
{
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);
+ eta->push_extra(ET_USING_INDEX_FOR_GROUP_BY);
+ eta->loose_scan_is_scanning= qgs->loose_scan_is_scanning();
}
else
- extra.append(STRING_WITH_LEN("; Using index"));
+ eta->push_extra(ET_USING_INDEX);
}
if (table->reginfo.not_exists_optimize)
- extra.append(STRING_WITH_LEN("; Not exists"));
+ eta->push_extra(ET_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)
{
- char mrr_str_buf[128];
- mrr_str_buf[0]=0;
- int len;
- uint mrr_flags=
- ((QUICK_RANGE_SELECT*)(tab->select->quick))->mrr_flags;
- len= table->file->multi_range_read_explain_info(mrr_flags,
- mrr_str_buf,
- sizeof(mrr_str_buf));
- if (len > 0)
- {
- extra.append(STRING_WITH_LEN("; "));
- extra.append(mrr_str_buf, len);
- }
+ 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;
- extra.append(STRING_WITH_LEN("; Using temporary"));
+ xpl_sel->using_temporary= true;
}
if (need_order)
{
need_order=0;
- extra.append(STRING_WITH_LEN("; Using filesort"));
+ xpl_sel->using_filesort= true;
}
if (distinct & test_all_bits(used_tables,
join->select_list_used_tables))
- extra.append(STRING_WITH_LEN("; Distinct"));
+ eta->push_extra(ET_DISTINCT);
if (tab->loosescan_match_tab)
{
- extra.append(STRING_WITH_LEN("; LooseScan"));
+ eta->push_extra(ET_LOOSESCAN);
}
if (tab->first_weedout_table)
- extra.append(STRING_WITH_LEN("; Start temporary"));
+ eta->push_extra(ET_START_TEMPORARY);
if (tab->check_weed_out_table)
- extra.append(STRING_WITH_LEN("; End temporary"));
+ eta->push_extra(ET_END_TEMPORARY);
else if (tab->do_firstmatch)
{
- if (tab->do_firstmatch == join->join_tab - 1)
- extra.append(STRING_WITH_LEN("; FirstMatch"));
+ if (tab->do_firstmatch == /*join->join_tab*/ first_top_tab - 1)
+ eta->push_extra(ET_FIRST_MATCH);
else
{
- extra.append(STRING_WITH_LEN("; FirstMatch("));
+ eta->push_extra(ET_FIRST_MATCH);
TABLE *prev_table=tab->do_firstmatch->table;
if (prev_table->derived_select_number)
{
@@ -22723,11 +23498,10 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
int len= my_snprintf(namebuf, sizeof(namebuf)-1,
"<derived%u>",
prev_table->derived_select_number);
- extra.append(namebuf, len);
+ eta->firstmatch_table_name.append(namebuf, len);
}
else
- extra.append(prev_table->pos_in_table_list->alias);
- extra.append(STRING_WITH_LEN(")"));
+ eta->firstmatch_table_name.append(prev_table->pos_in_table_list->alias);
}
}
@@ -22735,34 +23509,84 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
{
if (tab->ref.cond_guards[part])
{
- extra.append(STRING_WITH_LEN("; Full scan on NULL key"));
+ eta->push_extra(ET_FULL_SCAN_ON_NULL_KEY);
break;
}
}
if (tab->cache)
{
- extra.append(STRING_WITH_LEN("; Using join buffer"));
- tab->cache->print_explain_comment(&extra);
+ eta->push_extra(ET_USING_JOIN_BUFFER);
+ tab->cache->save_explain_data(&eta->bka_type);
}
-
- /* 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))
- join->error= 1;
}
+ output->add_node(xpl_sel);
+ }
+
+ for (SELECT_LEX_UNIT *unit= join->select_lex->first_inner_unit();
+ unit;
+ unit= unit->next_unit())
+ {
+ /*
+ Display subqueries only if
+ (1) they are not parts of ON clauses that were eliminated by table
+ elimination.
+ (2) they are not merged derived tables
+ */
+ if (!(unit->item && unit->item->eliminated) && // (1)
+ (!unit->derived || unit->derived->is_materialized_derived())) // (2)
+ {
+ explain_node->add_child(unit->first_select()->select_number);
+ }
+ }
+
+ if (!error && select_lex->is_top_level_node())
+ output->query_plan_ready();
+
+
+ DBUG_RETURN(error);
+}
+
+
+/*
+ This function serves as "shortcut point" for EXPLAIN queries.
+
+ The EXPLAIN statement executes just like its SELECT counterpart would
+ execute, except that JOIN::exec() will call select_describe() instead of
+ actually executing the query.
+
+ Inside select_describe():
+ - Query plan is updated with latest QEP choices made at the start of
+ JOIN::exec().
+ - the proces of "almost execution" is invoked for the children subqueries.
+
+ Overall, select_describe() is a legacy of old EXPLAIN implementation and
+ should be removed.
+*/
+
+static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
+ bool distinct,const char *message)
+{
+ THD *thd=join->thd;
+ select_result *result=join->result;
+ DBUG_ENTER("select_describe");
+
+ /* Update the QPF with latest values of using_temporary, using_filesort */
+ Explain_select *explain_sel;
+ uint select_nr= join->select_lex->select_number;
+ if ((explain_sel= thd->lex->explain->get_select(select_nr)))
+ {
+ explain_sel->using_temporary= need_tmp_table;
+ explain_sel->using_filesort= need_order;
}
+
for (SELECT_LEX_UNIT *unit= join->select_lex->first_inner_unit();
unit;
unit= unit->next_unit())
@@ -22805,13 +23629,13 @@ bool mysql_explain_union(THD *thd, SELECT_LEX_UNIT *unit, select_result *result)
for (SELECT_LEX *sl= first; sl; sl= sl->next_select())
{
- sl->set_explain_type();
+ sl->set_explain_type(FALSE);
sl->options|= SELECT_DESCRIBE;
}
if (unit->is_union())
{
- unit->fake_select_lex->select_number= UINT_MAX; // jost for initialization
+ unit->fake_select_lex->select_number= FAKE_SELECT_LEX_ID; // jost for initialization
unit->fake_select_lex->type= "UNION RESULT";
unit->fake_select_lex->options|= SELECT_DESCRIBE;
if (!(res= unit->prepare(thd, result, SELECT_NO_UNLOCK | SELECT_DESCRIBE)))
@@ -22921,6 +23745,7 @@ static void print_join(THD *thd,
List_iterator_fast<TABLE_LIST> ti(*tables);
TABLE_LIST **table;
uint non_const_tables= 0;
+ DBUG_ENTER("print_join");
for (TABLE_LIST *t= ti++; t ; t= ti++)
{
@@ -22934,13 +23759,13 @@ static void print_join(THD *thd,
if (!non_const_tables)
{
str->append(STRING_WITH_LEN("dual"));
- return; // all tables were optimized away
+ DBUG_VOID_RETURN; // all tables were optimized away
}
ti.rewind();
if (!(table= (TABLE_LIST **)thd->alloc(sizeof(TABLE_LIST*) *
non_const_tables)))
- return; // out of memory
+ DBUG_VOID_RETURN; // out of memory
TABLE_LIST *tmp, **t= table + (non_const_tables - 1);
while ((tmp= ti++))
@@ -22979,6 +23804,7 @@ static void print_join(THD *thd,
}
print_table_array(thd, eliminated_tables, str, table,
table + non_const_tables, query_type);
+ DBUG_VOID_RETURN;
}
/**
@@ -23487,7 +24313,8 @@ JOIN::reoptimize(Item *added_where, table_map join_tables,
reset_query_plan();
if (!keyuse.buffer &&
- my_init_dynamic_array(&keyuse, sizeof(KEYUSE), 20, 64))
+ my_init_dynamic_array(&keyuse, sizeof(KEYUSE), 20, 64,
+ MYF(MY_THREAD_SPECIFIC)))
{
delete_dynamic(&added_keyuse);
return REOPT_ERROR;
@@ -23620,7 +24447,7 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table,
int best_key= -1;
bool is_best_covering= FALSE;
double fanout= 1;
- ha_rows table_records= table->file->stats.records;
+ ha_rows table_records= table->stat_records();
bool group= join && join->group && order == join->group_list;
ha_rows ref_key_quick_rows= HA_POS_ERROR;
const bool has_limit= (select_limit_arg != HA_POS_ERROR);
@@ -23712,7 +24539,7 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table,
if (used_key_parts > used_index_parts)
used_pk_parts= used_key_parts-used_index_parts;
rec_per_key= used_key_parts ?
- keyinfo->rec_per_key[used_key_parts-1] : 1;
+ keyinfo->actual_rec_per_key(used_key_parts-1) : 1;
/* Take into account the selectivity of the used pk prefix */
if (used_pk_parts)
{
@@ -23727,14 +24554,14 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table,
rec_per_key= 1;
if (rec_per_key > 1)
{
- rec_per_key*= pkinfo->rec_per_key[used_pk_parts-1];
- rec_per_key/= pkinfo->rec_per_key[0];
+ rec_per_key*= pkinfo->actual_rec_per_key(used_pk_parts-1);
+ rec_per_key/= pkinfo->actual_rec_per_key(0);
/*
The value of rec_per_key for the extended key has
to be adjusted accordingly if some components of
the secondary key are included in the primary key.
*/
- for(uint i= 0; i < used_pk_parts; i++)
+ for(uint i= 1; i < used_pk_parts; i++)
{
if (pkinfo->key_part[i].field->key_start.is_set(nr))
{
@@ -23742,10 +24569,9 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table,
We presume here that for any index rec_per_key[i] != 0
if rec_per_key[0] != 0.
*/
- DBUG_ASSERT(pkinfo->rec_per_key[i]);
- DBUG_ASSERT(i > 0);
- rec_per_key*= pkinfo->rec_per_key[i-1];
- rec_per_key/= pkinfo->rec_per_key[i];
+ DBUG_ASSERT(pkinfo->actual_rec_per_key(i));
+ rec_per_key*= pkinfo->actual_rec_per_key(i-1);
+ rec_per_key/= pkinfo->actual_rec_per_key(i);
}
}
}
@@ -23790,7 +24616,7 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table,
select_limit= (ha_rows) (select_limit *
(double) table_records /
table->quick_condition_rows);
- rec_per_key= keyinfo->rec_per_key[keyinfo->key_parts-1];
+ rec_per_key= keyinfo->actual_rec_per_key(keyinfo->key_parts-1);
set_if_bigger(rec_per_key, 1);
/*
Here we take into account the fact that rows are
@@ -23854,6 +24680,8 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table,
@param table Table to find a key
@param select Pointer to access/update select->quick (if any)
@param limit LIMIT clause parameter
+ @param [out] scanned_limit How many records we expect to scan
+ Valid if *need_sort=FALSE.
@param [out] need_sort TRUE if filesort needed
@param [out] reverse
TRUE if the key is reversed again given ORDER (undefined if key == MAX_KEY)
@@ -23871,7 +24699,8 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table,
*/
uint get_index_for_order(ORDER *order, TABLE *table, SQL_SELECT *select,
- ha_rows limit, bool *need_sort, bool *reverse)
+ ha_rows limit, ha_rows *scanned_limit,
+ bool *need_sort, bool *reverse)
{
if (!order)
{
@@ -23913,6 +24742,7 @@ uint get_index_for_order(ORDER *order, TABLE *table, SQL_SELECT *select,
{
select->set_quick(reverse_quick);
*need_sort= FALSE;
+ *scanned_limit= select->quick->records;
return select->quick->index;
}
else
@@ -23931,7 +24761,7 @@ uint get_index_for_order(ORDER *order, TABLE *table, SQL_SELECT *select,
Update quick_condition_rows since single table UPDATE/DELETE procedures
don't call make_join_statistics() and leave this variable uninitialized.
*/
- table->quick_condition_rows= table->file->stats.records;
+ table->quick_condition_rows= table->stat_records();
int key, direction;
if (test_if_cheaper_ordering(NULL, order, table,
@@ -23941,6 +24771,7 @@ uint get_index_for_order(ORDER *order, TABLE *table, SQL_SELECT *select,
!is_key_used(table, key, table->write_set))
{
*need_sort= FALSE;
+ *scanned_limit= limit;
*reverse= (direction < 0);
return key;
}
@@ -23949,6 +24780,78 @@ uint get_index_for_order(ORDER *order, TABLE *table, SQL_SELECT *select,
return MAX_KEY;
}
+/*
+ Count how much times conditions are true for several first rows of the table
+
+ @param thd thread handle
+ @param rows_to_read how much rows to check
+ @param table table which should be checked
+ @conds conds list of conditions and countars for them
+
+ @return number of really checked rows or 0 in case of error or empty table
+*/
+
+ulong check_selectivity(THD *thd,
+ ulong rows_to_read,
+ TABLE *table,
+ List<COND_STATISTIC> *conds)
+{
+ ulong count= 0;
+ COND_STATISTIC *cond;
+ List_iterator_fast<COND_STATISTIC> it(*conds);
+ handler *file= table->file;
+ uchar *record= table->record[0];
+ int error= 0;
+ DBUG_ENTER("check_selectivity");
+
+ DBUG_ASSERT(rows_to_read > 0);
+ while ((cond= it++))
+ {
+ DBUG_ASSERT(cond->cond);
+ DBUG_ASSERT(cond->cond->used_tables() == table->map);
+ cond->positive= 0;
+ }
+ it.rewind();
+
+ if (file->ha_rnd_init_with_error(1))
+ DBUG_RETURN(0);
+ do
+ {
+ error= file->ha_rnd_next(record);
+
+ if (thd->killed)
+ {
+ thd->send_kill_message();
+ count= 0;
+ goto err;
+ }
+ if (error)
+ {
+ if (error == HA_ERR_RECORD_DELETED)
+ continue;
+ if (error == HA_ERR_END_OF_FILE)
+ break;
+ goto err;
+ }
+
+ count++;
+ while ((cond= it++))
+ {
+ if (cond->cond->val_bool())
+ cond->positive++;
+ }
+ it.rewind();
+
+ } while (count < rows_to_read);
+
+ file->ha_rnd_end();
+ DBUG_RETURN(count);
+
+err:
+ DBUG_PRINT("error", ("error %d", error));
+ file->ha_rnd_end();
+ DBUG_RETURN(0);
+}
/**
@} (end of group Query_Optimizer)