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.cc274
1 files changed, 219 insertions, 55 deletions
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 0058ce668b1..5850dc612ee 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -125,7 +125,8 @@ best_extension_by_limited_search(JOIN *join,
uint idx, double record_count,
double read_time, uint depth,
uint prune_level,
- uint use_cond_selectivity);
+ uint use_cond_selectivity,
+ table_map *processed_eq_ref_tables);
static uint determine_search_depth(JOIN* join);
C_MODE_START
static int join_tab_cmp(const void *dummy, const void* ptr1, const void* ptr2);
@@ -5468,7 +5469,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
join->unit->item->get_IN_subquery()->test_strategy(SUBS_IN_TO_EXISTS));
if (keyuse_array->elements &&
- sort_and_filter_keyuse(thd, keyuse_array,
+ sort_and_filter_keyuse(join, keyuse_array,
skip_unprefixed_keyparts))
goto error;
DBUG_EXECUTE("opt", print_keyuse_array(keyuse_array););
@@ -7266,6 +7267,34 @@ update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab,
DBUG_RETURN(FALSE);
}
+/*
+ check if key could be used with eq_ref
+
+ The assumption is that all previous key parts where used
+*/
+
+static void remember_if_eq_ref_key(JOIN *join, KEYUSE *use)
+{
+ if (use->keypart == FT_KEYPART || use->key == MAX_KEY)
+ return;
+
+ TABLE *table= use->table;
+ KEY *key= table->key_info+use->key;
+ ulong key_flags= table->actual_key_flags(key);
+
+ /*
+ Check if possible eq_ref key
+ This may include keys that does not have HA_NULL_PART_KEY
+ set, but this is ok as best_access_path will resolve this.
+ */
+ if ((key_flags & (HA_NOSAME | HA_EXT_NOSAME)))
+ {
+ uint key_parts= table->actual_n_key_parts(key);
+ if (use->keypart+1 == key_parts)
+ join->eq_ref_tables|= table->map;
+ }
+}
+
/**
Sort the array of possible keys and remove the following key parts:
@@ -7276,14 +7305,19 @@ update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab,
(e.g. if there is a key(a,b,c) but only b < 5 (or a=2 and c < 3) is
used in the query, we drop the partial key parts from consideration).
Special treatment for ft-keys.
+ Update join->eq_ref_tables with a bitmap of all tables that can possible
+ have a EQ_REF key.
*/
-bool sort_and_filter_keyuse(THD *thd, DYNAMIC_ARRAY *keyuse,
+bool sort_and_filter_keyuse(JOIN *join, DYNAMIC_ARRAY *keyuse,
bool skip_unprefixed_keyparts)
{
+ THD *thd= join->thd;
KEYUSE key_end, *prev, *save_pos, *use;
uint found_eq_constant, i;
+ bool found_unprefixed_key_part= 0;
+ join->eq_ref_tables= 0;
DBUG_ASSERT(keyuse->elements);
my_qsort(keyuse->buffer, keyuse->elements, sizeof(KEYUSE),
@@ -7311,18 +7345,45 @@ bool sort_and_filter_keyuse(THD *thd, DYNAMIC_ARRAY *keyuse,
{
if (use->key == prev->key && use->table == prev->table)
{
- if ((prev->keypart+1 < use->keypart && skip_unprefixed_keyparts) ||
- (prev->keypart == use->keypart && found_eq_constant))
- continue; /* remove */
+ if (prev->keypart == use->keypart && found_eq_constant)
+ continue;
+ if (prev->keypart+1 < use->keypart)
+ {
+ found_unprefixed_key_part= 1;
+ if (skip_unprefixed_keyparts)
+ continue; /* remove */
+ }
+ }
+ else
+ {
+ /* Key changed, check if previous key was a primary/unique key lookup */
+ if (prev != &key_end && !found_unprefixed_key_part)
+ remember_if_eq_ref_key(join, prev);
+ found_unprefixed_key_part= 0;
+ if (use->keypart != 0)
+ {
+ found_unprefixed_key_part= 1;
+ if (skip_unprefixed_keyparts)
+ continue; /* remove - first found key part must be 0 */
+ }
}
- else if (use->keypart != 0 && skip_unprefixed_keyparts)
- continue; /* remove - first found must be 0 */
}
-
+ else /* FT_KEY_PART */
+ {
+ if (prev != &key_end && !found_unprefixed_key_part)
+ remember_if_eq_ref_key(join, prev);
+ found_unprefixed_key_part= 1; // This key cannot be EQ_REF
+ }
prev= use;
found_eq_constant= !use->used_tables;
use->table->reginfo.join_tab->checked_keys.set_bit(use->key);
}
+ else
+ {
+ if (prev != &key_end && !found_unprefixed_key_part)
+ remember_if_eq_ref_key(join, prev);
+ prev= &key_end;
+ }
/*
Old gcc used a memcpy(), which is undefined if save_pos==use:
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=19410
@@ -7336,6 +7397,8 @@ bool sort_and_filter_keyuse(THD *thd, DYNAMIC_ARRAY *keyuse,
use->table->reginfo.join_tab->keyuse= save_pos;
save_pos++;
}
+ if (prev != &key_end && !found_unprefixed_key_part)
+ remember_if_eq_ref_key(join, prev);
i= (uint) (save_pos-(KEYUSE*) keyuse->buffer);
(void) set_dynamic(keyuse,(uchar*) &key_end,i);
keyuse->elements= i;
@@ -7782,6 +7845,8 @@ best_access_path(JOIN *join,
Json_writer_object trace_wrapper(thd, "best_access_path");
+ trace_wrapper.add_table_name(s);
+
bitmap_clear_all(eq_join_set);
loose_scan_opt.init(join, s, remaining_tables);
@@ -7947,7 +8012,8 @@ best_access_path(JOIN *join,
(!(key_flags & HA_NULL_PART_KEY) || // (2)
all_key_parts == notnull_part)) // (3)
{
-
+ /* Check that eq_ref_tables are correctly updated */
+ DBUG_ASSERT(join->eq_ref_tables & table->map);
/* TODO: Adjust cost for covering and clustering key */
type= JT_EQ_REF;
trace_access_idx.add("access_type", join_type_str[type])
@@ -9057,10 +9123,7 @@ optimize_straight_join(JOIN *join, table_map remaining_tables)
POSITION *position= join->positions + idx;
Json_writer_object trace_one_table(thd);
if (unlikely(thd->trace_started()))
- {
trace_plan_prefix(join, idx, remaining_tables);
- trace_one_table.add_table_name(s);
- }
/* Find the best access method from 's' to the current partial plan */
best_access_path(join, s, remaining_tables, join->positions, idx,
disable_jbuf, record_count,
@@ -9193,7 +9256,7 @@ greedy_search(JOIN *join,
uint idx= join->const_tables; // index into 'join->best_ref'
uint best_idx;
uint size_remain; // cardinality of remaining_tables
- table_map usable_tables;
+ table_map usable_tables, eq_ref_tables;
POSITION best_pos;
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
@@ -9220,7 +9283,8 @@ greedy_search(JOIN *join,
record_count,
read_time, search_depth,
prune_level,
- use_cond_selectivity) <
+ use_cond_selectivity,
+ &eq_ref_tables) <
(int) SEARCH_OK)
DBUG_RETURN(TRUE);
/*
@@ -9915,6 +9979,101 @@ sort_positions(SORT_POSITION *a, SORT_POSITION *b)
}
+/*
+ Call best_access_path() for a set of tables and collect results
+
+ @param join JOIN object
+ @param trace_one_table Current optimizer_trace
+ @param pos Pointer to remanining tables
+ @param allowed_tables bitmap of allowed tables. On return set to
+ the collected tables.
+ @param store_poisition Points to where to store next found SORT_POSITION.
+ Will be updated to next free position.
+ @param stop_on_eq_ref Stop searching for more tables if we found an EQ_REF
+ table.
+
+ @return
+ 0 Normal
+ 1 Eq_ref table found (only if stop_on_eq_ref is used)
+
+ join->next_sort_position will be update to next free position.
+*/
+
+static bool
+get_costs_for_tables(JOIN *join, table_map remaining_tables, uint idx,
+ double record_count,
+ Json_writer_object *trace_one_table,
+ JOIN_TAB **pos, SORT_POSITION **store_position,
+ table_map *allowed_tables,
+ bool stop_on_eq_ref)
+{
+ THD *thd= join->thd;
+ POSITION *sort_position= join->next_sort_position;
+ SORT_POSITION *sort_end= *store_position;
+ JOIN_TAB *s;
+ table_map found_tables= 0;
+ bool found_eq_ref= 0;
+ bool disable_jbuf= join->thd->variables.join_cache_level == 0;
+ DBUG_ENTER("get_plans_for_tables");
+
+ s= *pos;
+ do
+ {
+ table_map real_table_bit= s->table->map;
+ if ((*allowed_tables & real_table_bit) &&
+ !(remaining_tables & s->dependent))
+ {
+#ifdef DBUG_ASSERT_EXISTS
+ DBUG_ASSERT(!check_interleaving_with_nj(s));
+ restore_prev_nj_state(s); // Revert effect of check_... call
+#endif
+ sort_end->join_tab= pos;
+ sort_end->position= sort_position;
+
+
+ Json_writer_object wrapper(thd);
+ /* Find the best access method from 's' to the current partial plan */
+ best_access_path(join, s, remaining_tables, join->positions, idx,
+ disable_jbuf, record_count,
+ sort_position, sort_position + 1);
+ found_tables|= s->table->map;
+ sort_end++;
+ sort_position+= 2;
+ if (unlikely(stop_on_eq_ref) && sort_position[-2].type == JT_EQ_REF)
+ {
+ /* Found an eq_ref tables. Use this, ignoring the other tables */
+ found_eq_ref= 1;
+ if (found_tables == s->table->map)
+ break; // First table
+
+ /* Store the found eq_ref table first in store_position */
+ sort_position-= 2;
+ *allowed_tables= s->table->map;
+ (*store_position)->join_tab= pos;
+ (*store_position)->position= sort_position;
+ (*store_position)++;
+ join->next_sort_position[0]= sort_position[0];
+ join->next_sort_position[1]= sort_position[1];
+ join->next_sort_position+= 2;
+ DBUG_RETURN(1);
+ }
+ }
+ else
+ {
+ /* Verify that 'allowed_current_tables' was calculated correctly */
+ DBUG_ASSERT((remaining_tables & s->dependent) ||
+ !(remaining_tables & real_table_bit) ||
+ !(*allowed_tables & real_table_bit) ||
+ check_interleaving_with_nj(s));
+ }
+ } while ((s= *++pos));
+
+ *allowed_tables= found_tables;
+ *store_position= sort_end;
+ join->next_sort_position= sort_position;
+ DBUG_RETURN(found_eq_ref);
+}
+
/**
Find a good, possibly optimal, query execution plan (QEP) by a possibly
exhaustive search.
@@ -10047,7 +10206,8 @@ best_extension_by_limited_search(JOIN *join,
double read_time,
uint search_depth,
uint prune_level,
- uint use_cond_selectivity)
+ uint use_cond_selectivity,
+ table_map *processed_eq_ref_tables)
{
THD *thd= join->thd;
/*
@@ -10057,12 +10217,11 @@ best_extension_by_limited_search(JOIN *join,
JOIN_TAB *s;
double best_record_count= DBL_MAX;
double best_read_time= DBL_MAX;
- bool disable_jbuf= join->thd->variables.join_cache_level == 0;
enum_best_search best_res;
uint tables_left= join->table_count - idx, found_tables;
uint accepted_tables __attribute__((unused));
+ table_map found_eq_ref_tables= 0, used_eq_ref_table= 0;
table_map allowed_tables, allowed_current_tables;
- POSITION *sort_position= join->next_sort_position;
SORT_POSITION *sort= (SORT_POSITION*) alloca(sizeof(SORT_POSITION)*tables_left);
SORT_POSITION *sort_end;
DBUG_ENTER("best_extension_by_limited_search");
@@ -10104,54 +10263,53 @@ best_extension_by_limited_search(JOIN *join,
}
DBUG_ASSERT(allowed_tables & remaining_tables);
+ sort_end= sort;
{
Json_writer_object trace_one_table(thd);
+ JOIN_TAB **best_ref= join->best_ref + idx;
if (unlikely(thd->trace_started()))
trace_plan_prefix(join, idx, remaining_tables);
- /*
- Sort tables in ascending order of generated row combinations
- */
- sort_end= sort;
- for (JOIN_TAB **pos= join->best_ref + idx ; (s= *pos) ; pos++)
- {
- table_map real_table_bit= s->table->map;
- if ((allowed_current_tables & real_table_bit) &&
- !(remaining_tables & s->dependent))
- {
-#ifdef DBUG_ASSERT_EXISTS
- DBUG_ASSERT(!check_interleaving_with_nj(s));
- restore_prev_nj_state(s); // Revert effect of check_... call
-#endif
- sort_end->join_tab= pos;
- sort_end->position= sort_position;
+ Json_writer_array arr(thd, "get_costs_for_tables");
- if (unlikely(thd->trace_started()))
- trace_one_table.add_table_name(s);
-
- /* Find the best access method from 's' to the current partial plan */
- best_access_path(join, s, remaining_tables, join->positions, idx,
- disable_jbuf, record_count,
- sort_position, sort_position + 1);
- sort_end++;
- sort_position+= 2;
- }
+ if (idx > join->const_tables && prune_level >= 2 &&
+ join->positions[idx-1].type == JT_EQ_REF &&
+ (join->eq_ref_tables & allowed_current_tables))
+ {
+ /* Previous table was an EQ REF table, only add other possible EQ_REF
+ tables to the chain, stop after first one is found.
+ */
+ table_map table_map= join->eq_ref_tables & allowed_current_tables;
+ if (get_costs_for_tables(join, remaining_tables, idx, record_count,
+ &trace_one_table, best_ref, &sort_end,
+ &table_map, 1))
+ used_eq_ref_table= (*sort->join_tab)->table->map;
else
{
- /* Verify that 'allowed_current_tables' was calculated correctly */
- DBUG_ASSERT((remaining_tables & s->dependent) ||
- !(remaining_tables & real_table_bit) ||
- !(allowed_tables & real_table_bit) ||
- check_interleaving_with_nj(s));
+ /* We didn't find another EQ_REF table, add remaining tables */
+ if ((table_map= allowed_current_tables & ~table_map))
+ get_costs_for_tables(join, remaining_tables, idx, record_count,
+ &trace_one_table, best_ref, &sort_end, &table_map,
+ 0);
}
}
- found_tables= sort_end - sort;
+ else
+ {
+ table_map table_map= allowed_current_tables;
+ get_costs_for_tables(join, remaining_tables, idx, record_count,
+ &trace_one_table, best_ref, &sort_end, &table_map,
+ 0);
+ }
+ found_tables= (uint) (sort_end - sort);
DBUG_ASSERT(found_tables > 0);
+
+ /*
+ Sort tables in ascending order of generated row combinations
+ */
if (found_tables > 1)
my_qsort(sort, found_tables, sizeof(SORT_POSITION),
(qsort_cmp) sort_positions);
}
- join->next_sort_position+= found_tables*2;
DBUG_ASSERT(join->next_sort_position <=
join->sort_positions + join->sort_space);
@@ -10159,7 +10317,8 @@ best_extension_by_limited_search(JOIN *join,
for (SORT_POSITION *pos= sort ; pos < sort_end ; pos++)
{
s= *pos->join_tab;
- if (!check_interleaving_with_nj(s))
+ if (!(found_eq_ref_tables & s->table->map) &&
+ !check_interleaving_with_nj(s))
{
table_map real_table_bit= s->table->map;
double current_record_count, current_read_time;
@@ -10214,7 +10373,7 @@ best_extension_by_limited_search(JOIN *join,
Prune some less promising partial plans. This heuristic may miss
the optimal QEPs, thus it results in a non-exhaustive search.
*/
- if (prune_level == 1)
+ if (prune_level >= 1)
{
if (best_record_count > current_record_count ||
best_read_time > current_read_time ||
@@ -10288,7 +10447,8 @@ best_extension_by_limited_search(JOIN *join,
current_read_time,
search_depth - 1,
prune_level,
- use_cond_selectivity);
+ use_cond_selectivity,
+ &found_eq_ref_tables);
swap_variables(JOIN_TAB*, join->best_ref[idx], *pos->join_tab);
if ((int) best_res < (int) SEARCH_OK)
@@ -10346,6 +10506,10 @@ best_extension_by_limited_search(JOIN *join,
end:
join->next_sort_position-= found_tables*2;
+ if (used_eq_ref_table)
+ *processed_eq_ref_tables|= used_eq_ref_table | found_eq_ref_tables;
+ else
+ *processed_eq_ref_tables= 0;
DBUG_RETURN(best_res);
}
@@ -29048,7 +29212,7 @@ JOIN::reoptimize(Item *added_where, table_map join_tables,
/* added_keyuse contents is copied, and it is no longer needed. */
delete_dynamic(&added_keyuse);
- if (sort_and_filter_keyuse(thd, &keyuse, true))
+ if (sort_and_filter_keyuse(this, &keyuse, true))
return REOPT_ERROR;
optimize_keyuse(this, &keyuse);