summaryrefslogtreecommitdiff
path: root/sql/item_subselect.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/item_subselect.cc')
-rw-r--r--sql/item_subselect.cc377
1 files changed, 251 insertions, 126 deletions
diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc
index 32e94d3226f..6134903fee6 100644
--- a/sql/item_subselect.cc
+++ b/sql/item_subselect.cc
@@ -38,11 +38,14 @@ Item_subselect::Item_subselect():
Item_result_field(), value_assigned(0), own_engine(0), thd(0), old_engine(0),
used_tables_cache(0), have_to_be_excluded(0), const_item_cache(1),
inside_first_fix_fields(0), done_first_fix_fields(FALSE),
- substitution(0), expr_cache(0), engine(0), forced_const(FALSE), eliminated(FALSE),
+ expr_cache(0), forced_const(FALSE), substitution(0), engine(0), eliminated(FALSE),
engine_changed(0), changed(0), is_correlated(FALSE)
{
DBUG_ENTER("Item_subselect::Item_subselect");
DBUG_PRINT("enter", ("this: 0x%lx", (ulong) this));
+#ifndef DBUG_OFF
+ exec_counter= 0;
+#endif
with_subselect= 1;
reset();
/*
@@ -125,11 +128,14 @@ void Item_subselect::cleanup()
}
if (engine)
engine->cleanup();
- depends_on.empty();
reset();
value_assigned= 0;
expr_cache= 0;
forced_const= FALSE;
+ DBUG_PRINT("info", ("exec_counter: %d", exec_counter));
+#ifndef DBUG_OFF
+ exec_counter= 0;
+#endif
DBUG_VOID_RETURN;
}
@@ -152,14 +158,32 @@ void Item_in_subselect::cleanup()
delete left_expr_cache;
left_expr_cache= NULL;
}
+ /*
+ TODO: This breaks the commented assert in add_strategy().
+ in_strategy&= ~SUBS_STRATEGY_CHOSEN;
+ */
first_execution= TRUE;
- if (in_strategy & SUBS_MATERIALIZATION)
- in_strategy= 0;
pushed_cond_guards= NULL;
Item_subselect::cleanup();
DBUG_VOID_RETURN;
}
+
+void Item_allany_subselect::cleanup()
+{
+ /*
+ The MAX/MIN transformation through injection is reverted through the
+ change_item_tree() mechanism. Revert the select_lex object of the
+ query to its initial state.
+ */
+ for (SELECT_LEX *sl= unit->first_select();
+ sl; sl= sl->next_select())
+ if (test_strategy(SUBS_MAXMIN_INJECTED))
+ sl->with_sum_func= false;
+ Item_in_subselect::cleanup();
+}
+
+
Item_subselect::~Item_subselect()
{
DBUG_ENTER("Item_subselect::~Item_subselect");
@@ -206,7 +230,7 @@ bool Item_subselect::fix_fields(THD *thd_param, Item **ref)
if (check_stack_overrun(thd, STACK_MIN_SIZE, (uchar*)&res))
return TRUE;
-
+
if (!(res= engine->prepare()))
{
// all transformation is done (used by prepared statements)
@@ -469,12 +493,12 @@ void Item_subselect::recalc_used_tables(st_select_lex *new_parent,
upper->item->walk(&Item::enumerate_field_refs_processor, FALSE,
(uchar*)&fixer);
used_tables_cache |= fixer.used_tables;
- /*
+ upper->item->walk(&Item::update_table_bitmaps_processor, FALSE, NULL);
+/*
if (after_pullout)
upper->item->fix_after_pullout(new_parent, &(upper->item));
upper->item->update_used_tables();
- used_tables_cache |= upper->item->used_tables();
- */
+*/
}
}
}
@@ -492,6 +516,20 @@ void Item_subselect::recalc_used_tables(st_select_lex *new_parent,
bool Item_subselect::walk(Item_processor processor, bool walk_subquery,
uchar *argument)
{
+ if (!(unit->uncacheable & ~UNCACHEABLE_DEPENDENT) && engine->is_executed() &&
+ !unit->describe)
+ {
+ /*
+ The subquery has already been executed (for real, it wasn't EXPLAIN's
+ fake execution) so it should not matter what it has inside.
+
+ The actual reason for not walking inside is that parts of the subquery
+ (e.g. JTBM join nests and their IN-equality conditions may have been
+ invalidated by irreversible cleanups (those happen after an uncorrelated
+ subquery has been executed).
+ */
+ return FALSE;
+ }
if (walk_subquery)
{
@@ -548,7 +586,9 @@ bool Item_subselect::exec()
DBUG_EXECUTE_IF("subselect_exec_fail", return 1;);
res= engine->exec();
-
+#ifndef DBUG_OFF
+ ++exec_counter;
+#endif
if (engine_changed)
{
engine_changed= 0;
@@ -558,6 +598,14 @@ bool Item_subselect::exec()
}
+void Item_subselect::get_cache_parameters(List<Item> &parameters)
+{
+ Collect_deps_prm prm= {&parameters,
+ unit->first_select()->nest_level_base,
+ unit->first_select()->nest_level};
+ walk(&Item::collect_outer_ref_processor, TRUE, (uchar*)&prm);
+}
+
int Item_in_subselect::optimize(double *out_rows, double *cost)
{
int res;
@@ -622,7 +670,7 @@ int Item_in_subselect::optimize(double *out_rows, double *cost)
bool Item_subselect::expr_cache_is_needed(THD *thd)
{
- return (depends_on.elements &&
+ return ((engine->uncacheable() & UNCACHEABLE_DEPENDENT) &&
engine->cols() == 1 &&
optimizer_flag(thd, OPTIMIZER_SWITCH_SUBQUERY_CACHE) &&
!(engine->uncacheable() & (UNCACHEABLE_RAND |
@@ -650,8 +698,7 @@ bool Item_subselect::expr_cache_is_needed(THD *thd)
bool Item_in_subselect::expr_cache_is_needed(THD *thd)
{
- return (depends_on.elements &&
- optimizer_flag(thd, OPTIMIZER_SWITCH_SUBQUERY_CACHE) &&
+ return (optimizer_flag(thd, OPTIMIZER_SWITCH_SUBQUERY_CACHE) &&
!(engine->uncacheable() & (UNCACHEABLE_RAND |
UNCACHEABLE_SIDEEFFECT)));
}
@@ -676,7 +723,7 @@ bool Item_in_subselect::exec()
- on a cost-based basis, that takes into account the cost of a cache
lookup, the cache hit rate, and the savings per cache hit.
*/
- if (!left_expr_cache && (in_strategy & SUBS_MATERIALIZATION))
+ if (!left_expr_cache && (test_strategy(SUBS_MATERIALIZATION)))
init_left_expr_cache();
/*
@@ -793,7 +840,10 @@ Item_maxmin_subselect::Item_maxmin_subselect(THD *thd_param,
{
DBUG_ENTER("Item_maxmin_subselect::Item_maxmin_subselect");
max= max_arg;
- init(select_lex, new select_max_min_finder_subselect(this, max_arg));
+ init(select_lex,
+ new select_max_min_finder_subselect(this, max_arg,
+ parent->substype() ==
+ Item_subselect::ALL_SUBS));
max_columns= 1;
maybe_null= 1;
max_columns= 1;
@@ -984,7 +1034,7 @@ Item* Item_singlerow_subselect::expr_cache_insert_transformer(uchar *thd_arg)
DBUG_RETURN(expr_cache);
if (expr_cache_is_needed(thd) &&
- (expr_cache= set_expr_cache(thd, depends_on)))
+ (expr_cache= set_expr_cache(thd)))
DBUG_RETURN(expr_cache);
DBUG_RETURN(this);
}
@@ -1136,9 +1186,11 @@ bool Item_in_subselect::test_limit(st_select_lex_unit *unit_arg)
Item_in_subselect::Item_in_subselect(Item * left_exp,
st_select_lex *select_lex):
- Item_exists_subselect(), left_expr_cache(0), first_execution(TRUE),
- optimizer(0), pushed_cond_guards(NULL), in_strategy(0),
- is_jtbm_merged(FALSE), is_flattenable_semijoin(FALSE),
+ Item_exists_subselect(),
+ left_expr_cache(0), first_execution(TRUE), in_strategy(SUBS_NOT_TRANSFORMED),
+ optimizer(0), pushed_cond_guards(NULL), emb_on_expr_nest(NULL),
+ is_jtbm_merged(FALSE), is_flattenable_semijoin(FALSE),
+ is_registered_semijoin(FALSE),
upper_item(0)
{
DBUG_ENTER("Item_in_subselect::Item_in_subselect");
@@ -1244,7 +1296,7 @@ Item* Item_exists_subselect::expr_cache_insert_transformer(uchar *thd_arg)
DBUG_RETURN(expr_cache);
if (substype() == EXISTS_SUBS && expr_cache_is_needed(thd) &&
- (expr_cache= set_expr_cache(thd, depends_on)))
+ (expr_cache= set_expr_cache(thd)))
DBUG_RETURN(expr_cache);
DBUG_RETURN(this);
}
@@ -1398,9 +1450,9 @@ String *Item_in_subselect::val_str(String *str)
bool Item_in_subselect::val_bool()
{
DBUG_ASSERT(fixed == 1);
- null_value= was_null= FALSE;
if (forced_const)
return value;
+ null_value= was_null= FALSE;
if (exec())
{
reset();
@@ -1557,7 +1609,7 @@ Item_in_subselect::single_value_transformer(JOIN *join)
bool Item_allany_subselect::transform_into_max_min(JOIN *join)
{
DBUG_ENTER("Item_allany_subselect::transform_into_max_min");
- if (!(in_strategy & SUBS_MAXMIN))
+ if (!test_strategy(SUBS_MAXMIN_INJECTED | SUBS_MAXMIN_ENGINE))
DBUG_RETURN(false);
Item **place= optimizer->arguments() + 1;
THD *thd= join->thd;
@@ -1568,11 +1620,20 @@ bool Item_allany_subselect::transform_into_max_min(JOIN *join)
*/
DBUG_ASSERT(!substitution);
- if (!select_lex->group_list.elements &&
- !select_lex->having &&
- !select_lex->with_sum_func &&
- !(select_lex->next_select()) &&
- select_lex->table_list.elements)
+ /*
+ Check if optimization with aggregate min/max possible
+ 1 There is no aggregate in the subquery
+ 2 It is not UNION
+ 3 There is tables
+ 4 It is not ALL subquery with possible NULLs in the SELECT list
+ */
+ if (!select_lex->group_list.elements && /*1*/
+ !select_lex->having && /*1*/
+ !select_lex->with_sum_func && /*1*/
+ !(select_lex->next_select()) && /*2*/
+ select_lex->table_list.elements && /*3*/
+ (!select_lex->ref_pointer_array[0]->maybe_null || /*4*/
+ substype() != Item_subselect::ALL_SUBS)) /*4*/
{
Item_sum_hybrid *item;
nesting_map save_allow_sum_func;
@@ -1617,6 +1678,12 @@ bool Item_allany_subselect::transform_into_max_min(JOIN *join)
if (join->prepare_stage2())
DBUG_RETURN(true);
subs= new Item_singlerow_subselect(select_lex);
+
+ /*
+ Remove other strategies if any (we already changed the query and
+ can't apply other strategy).
+ */
+ set_strategy(SUBS_MAXMIN_INJECTED);
}
else
{
@@ -1624,9 +1691,17 @@ bool Item_allany_subselect::transform_into_max_min(JOIN *join)
subs= item= new Item_maxmin_subselect(thd, this, select_lex, func->l_op());
if (upper_item)
upper_item->set_sub_test(item);
+ /*
+ Remove other strategies if any (we already changed the query and
+ can't apply other strategy).
+ */
+ set_strategy(SUBS_MAXMIN_ENGINE);
}
- /* fix fields is already called for left expression */
- subs= func->create(left_expr, subs);
+ /*
+ The swap is needed for expressions of type 'f1 < ALL ( SELECT ....)'
+ where we want to evaluate the sub query even if f1 would be null.
+ */
+ subs= func->create_swap(*(optimizer->get_cache()), subs);
thd->change_item_tree(place, subs);
if (subs->fix_fields(thd, &subs))
DBUG_RETURN(true);
@@ -1634,11 +1709,6 @@ bool Item_allany_subselect::transform_into_max_min(JOIN *join)
select_lex->master_unit()->uncacheable&= ~UNCACHEABLE_DEPENDENT_INJECTED;
select_lex->uncacheable&= ~UNCACHEABLE_DEPENDENT_INJECTED;
- /*
- Remove other strategies if there was (we already changed the query and
- can't apply other strategy).
- */
- in_strategy= SUBS_MAXMIN;
DBUG_RETURN(false);
}
@@ -1662,7 +1732,7 @@ bool Item_allany_subselect::is_maxmin_applicable(JOIN *join)
Check if max/min optimization applicable: It is top item of
WHERE condition.
*/
- return (abort_on_null || (upper_item && upper_item->top_level())) &&
+ return (abort_on_null || (upper_item && upper_item->is_top_level_item())) &&
!join->select_lex->master_unit()->uncacheable && !func->eqne_op();
}
@@ -2212,7 +2282,12 @@ bool Item_in_subselect::inject_in_to_exists_cond(JOIN *join_arg)
{
/* The argument list of the top-level AND may change after fix fields. */
and_args= ((Item_cond*) join_arg->conds)->argument_list();
- and_args->concat((List<Item> *) &join_arg->cond_equal->current_level);
+ List_iterator<Item_equal> li(join_arg->cond_equal->current_level);
+ Item_equal *elem;
+ while ((elem= li++))
+ {
+ and_args->push_back(elem);
+ }
}
}
@@ -2340,7 +2415,7 @@ err:
void Item_in_subselect::print(String *str, enum_query_type query_type)
{
- if (in_strategy & SUBS_IN_TO_EXISTS)
+ if (test_strategy(SUBS_IN_TO_EXISTS))
str->append(STRING_WITH_LEN("<exists>"));
else
{
@@ -2356,7 +2431,7 @@ bool Item_in_subselect::fix_fields(THD *thd_arg, Item **ref)
uint outer_cols_num;
List<Item> *inner_cols;
- if (in_strategy & SUBS_SEMI_JOIN)
+ if (test_strategy(SUBS_SEMI_JOIN))
return !( (*ref)= new Item_int(1));
/*
@@ -2410,7 +2485,6 @@ bool Item_in_subselect::fix_fields(THD *thd_arg, Item **ref)
return TRUE;
if (Item_subselect::fix_fields(thd_arg, ref))
return TRUE;
-
fixed= TRUE;
return FALSE;
}
@@ -2420,6 +2494,7 @@ void Item_in_subselect::fix_after_pullout(st_select_lex *new_parent, Item **ref)
{
left_expr->fix_after_pullout(new_parent, &left_expr);
Item_subselect::fix_after_pullout(new_parent, ref);
+ used_tables_cache |= left_expr->used_tables();
}
void Item_in_subselect::update_used_tables()
@@ -2531,8 +2606,8 @@ bool
Item_allany_subselect::select_transformer(JOIN *join)
{
DBUG_ENTER("Item_allany_subselect::select_transformer");
- DBUG_ASSERT((in_strategy & ~(SUBS_MAXMIN | SUBS_IN_TO_EXISTS)) == 0);
- in_strategy|= SUBS_IN_TO_EXISTS;
+ DBUG_ASSERT((in_strategy & ~(SUBS_MAXMIN_INJECTED | SUBS_MAXMIN_ENGINE |
+ SUBS_IN_TO_EXISTS | SUBS_STRATEGY_CHOSEN)) == 0);
if (upper_item)
upper_item->show= 1;
DBUG_RETURN(select_in_like_transformer(join));
@@ -2541,7 +2616,7 @@ Item_allany_subselect::select_transformer(JOIN *join)
void Item_allany_subselect::print(String *str, enum_query_type query_type)
{
- if (in_strategy & SUBS_IN_TO_EXISTS)
+ if (test_strategy(SUBS_IN_TO_EXISTS))
str->append(STRING_WITH_LEN("<exists>"));
else
{
@@ -2920,7 +2995,7 @@ int subselect_single_select_engine::exec()
executed= 1;
thd->where= save_where;
thd->lex->current_select= save_select;
- DBUG_RETURN(join->error||thd->is_fatal_error);
+ DBUG_RETURN(join->error || thd->is_fatal_error || thd->is_error());
}
thd->where= save_where;
thd->lex->current_select= save_select;
@@ -3856,9 +3931,10 @@ subselect_hash_sj_engine::get_strategy_using_data()
bitmap_set_bit(&non_null_key_parts, i);
--count_partial_match_columns;
}
- if (result_sink->get_null_count_of_col(i) ==
- tmp_table->file->stats.records)
+ if (result_sink->get_null_count_of_col(i) == tmp_table->file->stats.records)
++count_null_only_columns;
+ if (result_sink->get_null_count_of_col(i))
+ ++count_columns_with_nulls;
}
/* If no column contains NULLs use regular hash index lookups. */
@@ -4334,7 +4410,13 @@ double get_fanout_with_deps(JOIN *join, table_map tset)
for (JOIN_TAB *tab= first_top_level_tab(join, WITHOUT_CONST_TABLES); tab;
tab= next_top_level_tab(join, tab))
{
- if ((tab->table->map & checked_deps) && !tab->emb_sj_nest &&
+ /*
+ Ignore SJM nests. They have tab->table==NULL. There is no point to walk
+ inside them, because GROUP BY clause cannot refer to tables from within
+ subquery.
+ */
+ if (!tab->is_sjm_nest() && (tab->table->map & checked_deps) &&
+ !tab->emb_sj_nest &&
tab->records_read != 0)
{
fanout *= rows2double(tab->records_read);
@@ -4524,7 +4606,8 @@ int subselect_hash_sj_engine::exec()
/* The subquery should be optimized, and materialized only once. */
DBUG_ASSERT(materialize_join->optimized && !is_materialized);
materialize_join->exec();
- if ((res= test(materialize_join->error || thd->is_fatal_error)))
+ if ((res= test(materialize_join->error || thd->is_fatal_error ||
+ thd->is_error())))
goto err;
/*
@@ -4565,29 +4648,50 @@ int subselect_hash_sj_engine::exec()
if (strategy == PARTIAL_MATCH)
{
uint count_pm_keys; /* Total number of keys needed for partial matching. */
- MY_BITMAP *nn_key_parts; /* The key parts of the only non-NULL index. */
- uint covering_null_row_width;
+ MY_BITMAP *nn_key_parts= NULL; /* Key parts of the only non-NULL index. */
+ uint count_non_null_columns= 0; /* Number of columns in nn_key_parts. */
+ bool has_covering_null_row;
+ bool has_covering_null_columns;
select_materialize_with_stats *result_sink=
(select_materialize_with_stats *) result;
+ uint field_count= tmp_table->s->fields;
- nn_key_parts= (count_partial_match_columns < tmp_table->s->fields) ?
- &non_null_key_parts : NULL;
+ if (count_partial_match_columns < field_count)
+ {
+ nn_key_parts= &non_null_key_parts;
+ count_non_null_columns= bitmap_bits_set(nn_key_parts);
+ }
+ has_covering_null_row= (result_sink->get_max_nulls_in_row() == field_count);
+ has_covering_null_columns= (count_non_null_columns +
+ count_null_only_columns == field_count);
- if (result_sink->get_max_nulls_in_row() ==
- tmp_table->s->fields -
- (nn_key_parts ? bitmap_bits_set(nn_key_parts) : 0))
- covering_null_row_width= result_sink->get_max_nulls_in_row();
- else
- covering_null_row_width= 0;
+ if (has_covering_null_row && has_covering_null_columns)
+ {
+ /*
+ The whole table consist of only NULL values. The result of IN is
+ a constant UNKNOWN.
+ */
+ DBUG_ASSERT(tmp_table->file->stats.records == 1);
+ item_in->value= 0;
+ item_in->null_value= 1;
+ item_in->make_const();
+ item_in->set_first_execution();
+ DBUG_RETURN(FALSE);
+ }
- if (covering_null_row_width)
- count_pm_keys= nn_key_parts ? 1 : 0;
+ if (has_covering_null_row)
+ {
+ DBUG_ASSERT(count_partial_match_columns = field_count);
+ count_pm_keys= 0;
+ }
+ else if (has_covering_null_columns)
+ count_pm_keys= 1;
else
count_pm_keys= count_partial_match_columns - count_null_only_columns +
- (nn_key_parts ? 1 : 0);
+ (nn_key_parts ? 1 : 0);
choose_partial_match_strategy(test(nn_key_parts),
- test(covering_null_row_width),
+ has_covering_null_row,
&partial_match_key_parts);
DBUG_ASSERT(strategy == PARTIAL_MATCH_MERGE ||
strategy == PARTIAL_MATCH_SCAN);
@@ -4597,7 +4701,9 @@ int subselect_hash_sj_engine::exec()
new subselect_rowid_merge_engine(thd, (subselect_uniquesubquery_engine*)
lookup_engine, tmp_table,
count_pm_keys,
- covering_null_row_width,
+ has_covering_null_row,
+ has_covering_null_columns,
+ count_columns_with_nulls,
item, result,
semi_join_conds->argument_list());
if (!pm_engine ||
@@ -4622,7 +4728,9 @@ int subselect_hash_sj_engine::exec()
lookup_engine, tmp_table,
item, result,
semi_join_conds->argument_list(),
- covering_null_row_width)))
+ has_covering_null_row,
+ has_covering_null_columns,
+ count_columns_with_nulls)))
{
/* This is an irrecoverable error. */
res= 1;
@@ -5058,49 +5166,56 @@ subselect_partial_match_engine::subselect_partial_match_engine(
TABLE *tmp_table_arg, Item_subselect *item_arg,
select_result_interceptor *result_arg,
List<Item> *equi_join_conds_arg,
- uint covering_null_row_width_arg)
+ bool has_covering_null_row_arg,
+ bool has_covering_null_columns_arg,
+ uint count_columns_with_nulls_arg)
:subselect_engine(thd_arg, item_arg, result_arg),
tmp_table(tmp_table_arg), lookup_engine(engine_arg),
equi_join_conds(equi_join_conds_arg),
- covering_null_row_width(covering_null_row_width_arg)
+ has_covering_null_row(has_covering_null_row_arg),
+ has_covering_null_columns(has_covering_null_columns_arg),
+ count_columns_with_nulls(count_columns_with_nulls_arg)
{}
int subselect_partial_match_engine::exec()
{
Item_in_subselect *item_in= (Item_in_subselect *) item;
- int res;
+ int copy_res, lookup_res;
/* Try to find a matching row by index lookup. */
- res= lookup_engine->copy_ref_key_simple();
- if (res == -1)
+ copy_res= lookup_engine->copy_ref_key_simple();
+ if (copy_res == -1)
{
/* The result is FALSE based on the outer reference. */
item_in->value= 0;
item_in->null_value= 0;
return 0;
}
- else if (res == 0)
+ else if (copy_res == 0)
{
/* Search for a complete match. */
- if ((res= lookup_engine->index_lookup()))
+ if ((lookup_res= lookup_engine->index_lookup()))
{
/* An error occured during lookup(). */
item_in->value= 0;
item_in->null_value= 0;
- return res;
+ return lookup_res;
}
- else if (item_in->value)
+ else if (item_in->value || !count_columns_with_nulls)
{
/*
A complete match was found, the result of IN is TRUE.
+ If no match was found, and there are no NULLs in the materialized
+ subquery, then the result is guaranteed to be false because this
+ branch is executed when the outer reference has no NULLs as well.
Notice: (this->item == lookup_engine->item)
*/
return 0;
}
}
- if (covering_null_row_width == tmp_table->s->fields)
+ if (has_covering_null_row)
{
/*
If there is a NULL-only row that coveres all columns the result of IN
@@ -5164,7 +5279,6 @@ void subselect_partial_match_engine::print(String *str,
/*
@param non_null_key_parts
@param partial_match_key_parts A union of all single-column NULL key parts.
- @param count_partial_match_columns Number of NULL keyparts (set bits above).
@retval FALSE the engine was initialized successfully
@retval TRUE there was some (memory allocation) error during initialization,
@@ -5185,20 +5299,26 @@ subselect_rowid_merge_engine::init(MY_BITMAP *non_null_key_parts,
Item_in_subselect *item_in= (Item_in_subselect*) item;
int error;
- if (keys_count == 0)
+ if (merge_keys_count == 0)
{
+ DBUG_ASSERT(bitmap_bits_set(partial_match_key_parts) == 0 ||
+ has_covering_null_row);
/* There is nothing to initialize, we will only do regular lookups. */
return FALSE;
}
- DBUG_ASSERT(!covering_null_row_width || (covering_null_row_width &&
- keys_count == 1 &&
- non_null_key_parts));
+ /*
+ If all nullable columns contain only NULLs, there must be one index
+ over all non-null columns.
+ */
+ DBUG_ASSERT(!has_covering_null_columns ||
+ (has_covering_null_columns &&
+ merge_keys_count == 1 && non_null_key_parts));
/*
Allocate buffers to hold the merged keys and the mapping between rowids and
row numbers.
*/
- if (!(merge_keys= (Ordered_key**) thd->alloc(keys_count *
+ if (!(merge_keys= (Ordered_key**) thd->alloc(merge_keys_count *
sizeof(Ordered_key*))) ||
!(row_num_to_rowid= (uchar*) my_malloc((size_t)(row_count * rowid_length),
MYF(MY_WME))))
@@ -5217,15 +5337,13 @@ subselect_rowid_merge_engine::init(MY_BITMAP *non_null_key_parts,
}
/*
- If there is a covering NULL row, the only key that is needed is the
- only non-NULL key that is already created above. We create keys on
- NULL-able columns only if there is no covering NULL row.
+ If all nullable columns contain NULLs, the only key that is needed is the
+ only non-NULL key that is already created above.
*/
- if (!covering_null_row_width)
+ if (!has_covering_null_columns)
{
- if (bitmap_init_memroot(&matching_keys, keys_count, thd->mem_root) ||
- bitmap_init_memroot(&matching_outer_cols, keys_count, thd->mem_root) ||
- bitmap_init_memroot(&null_only_columns, keys_count, thd->mem_root))
+ if (bitmap_init_memroot(&matching_keys, merge_keys_count, thd->mem_root) ||
+ bitmap_init_memroot(&matching_outer_cols, merge_keys_count, thd->mem_root))
return TRUE;
/*
@@ -5234,31 +5352,25 @@ subselect_rowid_merge_engine::init(MY_BITMAP *non_null_key_parts,
*/
for (uint i= 0; i < partial_match_key_parts->n_bits; i++)
{
- if (!bitmap_is_set(partial_match_key_parts, i))
+ /* Skip columns that have no NULLs, or contain only NULLs. */
+ if (!bitmap_is_set(partial_match_key_parts, i) ||
+ result_sink->get_null_count_of_col(i) == row_count)
continue;
- if (result_sink->get_null_count_of_col(i) == row_count)
- {
- bitmap_set_bit(&null_only_columns, cur_keyid);
- continue;
- }
- else
- {
- merge_keys[cur_keyid]= new Ordered_key(
+ merge_keys[cur_keyid]= new Ordered_key(
cur_keyid, tmp_table,
item_in->left_expr->element_index(i),
result_sink->get_null_count_of_col(i),
result_sink->get_min_null_of_col(i),
result_sink->get_max_null_of_col(i),
row_num_to_rowid);
- if (merge_keys[cur_keyid]->init(i))
- return TRUE;
- merge_keys[cur_keyid]->first();
- }
+ if (merge_keys[cur_keyid]->init(i))
+ return TRUE;
+ merge_keys[cur_keyid]->first();
++cur_keyid;
}
}
- DBUG_ASSERT(cur_keyid == keys_count);
+ DBUG_ASSERT(cur_keyid == merge_keys_count);
/* Populate the indexes with data from the temporary table. */
if (tmp_table->file->ha_rnd_init_with_error(1))
@@ -5299,7 +5411,7 @@ subselect_rowid_merge_engine::init(MY_BITMAP *non_null_key_parts,
non_null_key->add_key(cur_rownum);
}
- for (uint i= (non_null_key ? 1 : 0); i < keys_count; i++)
+ for (uint i= (non_null_key ? 1 : 0); i < merge_keys_count; i++)
{
/*
Check if the first and only indexed column contains NULL in the curent
@@ -5316,14 +5428,14 @@ subselect_rowid_merge_engine::init(MY_BITMAP *non_null_key_parts,
tmp_table->file->ha_rnd_end();
/* Sort all the keys by their NULL selectivity. */
- my_qsort(merge_keys, keys_count, sizeof(Ordered_key*),
+ my_qsort(merge_keys, merge_keys_count, sizeof(Ordered_key*),
(qsort_cmp) cmp_keys_by_null_selectivity);
/* Sort the keys in each of the indexes. */
- for (uint i= 0; i < keys_count; i++)
+ for (uint i= 0; i < merge_keys_count; i++)
merge_keys[i]->sort_keys();
- if (init_queue(&pq, keys_count, 0, FALSE,
+ if (init_queue(&pq, merge_keys_count, 0, FALSE,
subselect_rowid_merge_engine::cmp_keys_by_cur_rownum, NULL,
0, 0))
return TRUE;
@@ -5335,10 +5447,10 @@ subselect_rowid_merge_engine::init(MY_BITMAP *non_null_key_parts,
subselect_rowid_merge_engine::~subselect_rowid_merge_engine()
{
/* None of the resources below is allocated if there are no ordered keys. */
- if (keys_count)
+ if (merge_keys_count)
{
my_free((char*) row_num_to_rowid, MYF(0));
- for (uint i= 0; i < keys_count; i++)
+ for (uint i= 0; i < merge_keys_count; i++)
delete merge_keys[i];
delete_queue(&pq);
if (tmp_table->file->inited == handler::RND)
@@ -5396,6 +5508,10 @@ subselect_rowid_merge_engine::cmp_keys_by_cur_rownum(void *arg,
Check if certain table row contains a NULL in all columns for which there is
no match in the corresponding value index.
+ @note
+ There is no need to check the columns that contain only NULLs, because
+ those are guaranteed to match.
+
@retval TRUE if a NULL row exists
@retval FALSE otherwise
*/
@@ -5403,16 +5519,14 @@ subselect_rowid_merge_engine::cmp_keys_by_cur_rownum(void *arg,
bool subselect_rowid_merge_engine::test_null_row(rownum_t row_num)
{
Ordered_key *cur_key;
- uint cur_id;
- for (uint i = 0; i < keys_count; i++)
+ for (uint i = 0; i < merge_keys_count; i++)
{
cur_key= merge_keys[i];
- cur_id= cur_key->get_keyid();
- if (bitmap_is_set(&matching_keys, cur_id))
+ if (bitmap_is_set(&matching_keys, cur_key->get_keyid()))
{
/*
- The key 'i' (with id 'cur_keyid') already matches a value in row 'row_num',
- thus we skip it as it can't possibly match a NULL.
+ The key 'i' (with id 'cur_keyid') already matches a value in row
+ 'row_num', thus we skip it as it can't possibly match a NULL.
*/
continue;
}
@@ -5435,6 +5549,8 @@ bool subselect_rowid_merge_engine::partial_match()
Ordered_key *cur_key;
rownum_t cur_row_num;
uint count_nulls_in_search_key= 0;
+ uint max_covering_null_row_len=
+ ((select_materialize_with_stats *) result)->get_max_nulls_in_row();
bool res= FALSE;
/* If there is a non-NULL key, it must be the first key in the keys array. */
@@ -5457,11 +5573,10 @@ bool subselect_rowid_merge_engine::partial_match()
}
/*
- If there is a NULL (sub)row that covers all NULL-able columns,
- then there is a guranteed partial match, and we don't need to search
- for the matching row.
- */
- if (covering_null_row_width)
+ If all nullable columns contain only NULLs, then there is a guranteed
+ partial match, and we don't need to search for a matching row.
+ */
+ if (has_covering_null_columns)
{
res= TRUE;
goto end;
@@ -5473,7 +5588,7 @@ bool subselect_rowid_merge_engine::partial_match()
Do not add the non_null_key, since it was already processed above.
*/
bitmap_clear_all(&matching_outer_cols);
- for (uint i= test(non_null_key); i < keys_count; i++)
+ for (uint i= test(non_null_key); i < merge_keys_count; i++)
{
DBUG_ASSERT(merge_keys[i]->get_column_count() == 1);
if (merge_keys[i]->get_search_key(0)->null_value)
@@ -5501,8 +5616,16 @@ bool subselect_rowid_merge_engine::partial_match()
If there is no NULL (sub)row that covers all NULL columns, and there is no
single match for any of the NULL columns, the result is FALSE.
*/
- if (pq.elements - test(non_null_key) == 0)
+ if ((pq.elements == 1 && non_null_key &&
+ max_covering_null_row_len < merge_keys_count - 1) ||
+ pq.elements == 0)
{
+ if (pq.elements == 0)
+ {
+ DBUG_ASSERT(!non_null_key); /* Must follow from the logic of this method */
+ /* This case must be handled by subselect_partial_match_engine::exec() */
+ DBUG_ASSERT(max_covering_null_row_len != tmp_table->s->fields);
+ }
res= FALSE;
goto end;
}
@@ -5511,7 +5634,6 @@ bool subselect_rowid_merge_engine::partial_match()
min_key= (Ordered_key*) queue_remove_top(&pq);
min_row_num= min_key->current();
- bitmap_copy(&matching_keys, &null_only_columns);
bitmap_set_bit(&matching_keys, min_key->get_keyid());
bitmap_union(&matching_keys, &matching_outer_cols);
if (min_key->next_same())
@@ -5547,7 +5669,7 @@ bool subselect_rowid_merge_engine::partial_match()
{
min_key= cur_key;
min_row_num= cur_row_num;
- bitmap_copy(&matching_keys, &null_only_columns);
+ bitmap_clear_all(&matching_keys);
bitmap_set_bit(&matching_keys, min_key->get_keyid());
bitmap_union(&matching_keys, &matching_outer_cols);
}
@@ -5568,6 +5690,8 @@ bool subselect_rowid_merge_engine::partial_match()
DBUG_ASSERT(FALSE);
end:
+ if (!has_covering_null_columns)
+ bitmap_clear_all(&matching_keys);
queue_remove_all(&pq);
tmp_table->file->ha_rnd_end();
return res;
@@ -5580,10 +5704,14 @@ subselect_table_scan_engine::subselect_table_scan_engine(
Item_subselect *item_arg,
select_result_interceptor *result_arg,
List<Item> *equi_join_conds_arg,
- uint covering_null_row_width_arg)
+ bool has_covering_null_row_arg,
+ bool has_covering_null_columns_arg,
+ uint count_columns_with_nulls_arg)
:subselect_partial_match_engine(thd_arg, engine_arg, tmp_table_arg, item_arg,
result_arg, equi_join_conds_arg,
- covering_null_row_width_arg)
+ has_covering_null_row_arg,
+ has_covering_null_columns_arg,
+ count_columns_with_nulls_arg)
{}
@@ -5622,10 +5750,6 @@ bool subselect_table_scan_engine::partial_match()
tmp_table->file->extra_opt(HA_EXTRA_CACHE,
current_thd->variables.read_buff_size);
- /*
- TIMOUR:
- scan_table() also calls "table->null_row= 0;", why, do we need it?
- */
for (;;)
{
error= tmp_table->file->ha_rnd_next(tmp_table->record[0]);
@@ -5674,3 +5798,4 @@ end:
void subselect_table_scan_engine::cleanup()
{
}
+