Commit f670b6d2 authored by unknown's avatar unknown

MWL#89: Cost-based choice between Materialization and IN->EXISTS transformation

Added missing logic to handle the case when subquery tables are optimized
away early during optimization.
parent e85a4cb6
...@@ -123,13 +123,13 @@ select a1 from t1 where a1 in (select b1 from t2 where b1 = b2 and b2 = '1 - 03' ...@@ -123,13 +123,13 @@ select a1 from t1 where a1 in (select b1 from t2 where b1 = b2 and b2 = '1 - 03'
-- echo /* E.5 opt_sum_query detects no matching min/max row or substitutes MIN/MAX with a const. */ -- echo /* E.5 opt_sum_query detects no matching min/max row or substitutes MIN/MAX with a const. */
-- echo TODO this test produces wrong result due to missing logic to handle the case -- echo TODO this test produces wrong result due to missing logic to handle the case
-- echo when JOIN::optimize detects an empty subquery result. -- echo when JOIN::optimize detects an empty subquery result.
#explain explain
#select a1 from t1 where a1 in (select max(b1) from t2); select a1 from t1 where a1 in (select max(b1) from t2);
#select a1 from t1 where a1 in (select max(b1) from t2); select a1 from t1 where a1 in (select max(b1) from t2);
-- echo -- echo
#explain explain
#select a1 from t1 where a1 in (select max(b1) from t2 where b1 = '7 - 02'); select a1 from t1 where a1 in (select max(b1) from t2 where b1 = '7 - 02');
#select a1 from t1 where a1 in (select max(b1) from t2 where b1 = '7 - 02'); select a1 from t1 where a1 in (select max(b1) from t2 where b1 = '7 - 02');
-- echo /* E.6 make_join_select detects impossible WHERE. * -- echo /* E.6 make_join_select detects impossible WHERE. *
......
This diff is collapsed.
...@@ -36,7 +36,7 @@ Item_subselect::Item_subselect(): ...@@ -36,7 +36,7 @@ Item_subselect::Item_subselect():
Item_result_field(), value_assigned(0), thd(0), substitution(0), Item_result_field(), value_assigned(0), thd(0), substitution(0),
engine(0), old_engine(0), used_tables_cache(0), have_to_be_excluded(0), engine(0), old_engine(0), used_tables_cache(0), have_to_be_excluded(0),
const_item_cache(1), const_item_cache(1),
inside_first_fix_fields(0), done_first_fix_fields(FALSE), inside_first_fix_fields(0), done_first_fix_fields(FALSE), forced_const(FALSE),
eliminated(FALSE), eliminated(FALSE),
engine_changed(0), changed(0), is_correlated(FALSE) engine_changed(0), changed(0), is_correlated(FALSE)
{ {
...@@ -121,6 +121,7 @@ void Item_subselect::cleanup() ...@@ -121,6 +121,7 @@ void Item_subselect::cleanup()
depends_on.empty(); depends_on.empty();
reset(); reset();
value_assigned= 0; value_assigned= 0;
forced_const= FALSE;
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
...@@ -158,7 +159,6 @@ void Item_in_subselect::cleanup() ...@@ -158,7 +159,6 @@ void Item_in_subselect::cleanup()
left_expr_cache= NULL; left_expr_cache= NULL;
} }
first_execution= TRUE; first_execution= TRUE;
is_constant= FALSE;
if (in_strategy & SUBS_MATERIALIZATION) if (in_strategy & SUBS_MATERIALIZATION)
in_strategy= 0; in_strategy= 0;
pushed_cond_guards= NULL; pushed_cond_guards= NULL;
...@@ -682,6 +682,8 @@ Item *Item_subselect::get_tmp_table_item(THD *thd_arg) ...@@ -682,6 +682,8 @@ Item *Item_subselect::get_tmp_table_item(THD *thd_arg)
void Item_subselect::update_used_tables() void Item_subselect::update_used_tables()
{ {
if (!forced_const)
{
recalc_used_tables(parent_select, FALSE); recalc_used_tables(parent_select, FALSE);
if (!engine->uncacheable()) if (!engine->uncacheable())
{ {
...@@ -689,6 +691,7 @@ void Item_subselect::update_used_tables() ...@@ -689,6 +691,7 @@ void Item_subselect::update_used_tables()
if (!(used_tables_cache & ~engine->upper_select_const_tables())) if (!(used_tables_cache & ~engine->upper_select_const_tables()))
const_item_cache= 1; const_item_cache= 1;
} }
}
} }
...@@ -753,7 +756,7 @@ Item_maxmin_subselect::Item_maxmin_subselect(THD *thd_param, ...@@ -753,7 +756,7 @@ Item_maxmin_subselect::Item_maxmin_subselect(THD *thd_param,
of Items belonged to subquery, which will be not repeated of Items belonged to subquery, which will be not repeated
*/ */
used_tables_cache= parent->get_used_tables_cache(); used_tables_cache= parent->get_used_tables_cache();
const_item_cache= parent->get_const_item_cache(); const_item_cache= parent->const_item();
/* /*
this subquery always creates during preparation, so we can assign this subquery always creates during preparation, so we can assign
...@@ -791,8 +794,7 @@ void Item_maxmin_subselect::print(String *str, enum_query_type query_type) ...@@ -791,8 +794,7 @@ void Item_maxmin_subselect::print(String *str, enum_query_type query_type)
void Item_singlerow_subselect::reset() void Item_singlerow_subselect::reset()
{ {
eliminated= FALSE; Item_subselect::reset();
null_value= 1;
if (value) if (value)
value->null_value= 1; value->null_value= 1;
} }
...@@ -1082,8 +1084,7 @@ bool Item_in_subselect::test_limit(st_select_lex_unit *unit_arg) ...@@ -1082,8 +1084,7 @@ bool Item_in_subselect::test_limit(st_select_lex_unit *unit_arg)
Item_in_subselect::Item_in_subselect(Item * left_exp, Item_in_subselect::Item_in_subselect(Item * left_exp,
st_select_lex *select_lex): st_select_lex *select_lex):
Item_exists_subselect(), left_expr_cache(0), first_execution(TRUE), Item_exists_subselect(), left_expr_cache(0), first_execution(TRUE),
is_constant(FALSE), optimizer(0), pushed_cond_guards(NULL), optimizer(0), pushed_cond_guards(NULL), in_strategy(0), upper_item(0)
in_strategy(0), upper_item(0)
{ {
DBUG_ENTER("Item_in_subselect::Item_in_subselect"); DBUG_ENTER("Item_in_subselect::Item_in_subselect");
left_expr= left_exp; left_expr= left_exp;
...@@ -1313,7 +1314,7 @@ bool Item_in_subselect::val_bool() ...@@ -1313,7 +1314,7 @@ bool Item_in_subselect::val_bool()
{ {
DBUG_ASSERT(fixed == 1); DBUG_ASSERT(fixed == 1);
null_value= 0; null_value= 0;
if (is_constant) if (forced_const)
return value; return value;
if (exec()) if (exec())
{ {
...@@ -4147,11 +4148,10 @@ int subselect_hash_sj_engine::exec() ...@@ -4147,11 +4148,10 @@ int subselect_hash_sj_engine::exec()
tmp_table->file->info(HA_STATUS_VARIABLE); tmp_table->file->info(HA_STATUS_VARIABLE);
if (!tmp_table->file->stats.records) if (!tmp_table->file->stats.records)
{ {
item_in->value= FALSE;
/* The value of IN will not change during this execution. */ /* The value of IN will not change during this execution. */
item_in->is_constant= TRUE; item_in->reset();
item_in->make_const();
item_in->set_first_execution(); item_in->set_first_execution();
/* TIMOUR: check if we need this: item_in->null_value= FALSE; */
DBUG_RETURN(FALSE); DBUG_RETURN(FALSE);
} }
......
...@@ -70,6 +70,13 @@ class Item_subselect :public Item_result_field ...@@ -70,6 +70,13 @@ class Item_subselect :public Item_result_field
bool inside_first_fix_fields; bool inside_first_fix_fields;
bool done_first_fix_fields; bool done_first_fix_fields;
/*
Set to TRUE if at optimization or execution time we determine that this
item's value is a constant. We need this member because it is not possible
to substitute 'this' with a constant item.
*/
bool forced_const;
public: public:
/* A reference from inside subquery predicate to somewhere outside of it */ /* A reference from inside subquery predicate to somewhere outside of it */
class Ref_to_outside : public Sql_alloc class Ref_to_outside : public Sql_alloc
...@@ -154,12 +161,21 @@ class Item_subselect :public Item_result_field ...@@ -154,12 +161,21 @@ class Item_subselect :public Item_result_field
void fix_after_pullout(st_select_lex *new_parent, Item **ref); void fix_after_pullout(st_select_lex *new_parent, Item **ref);
void recalc_used_tables(st_select_lex *new_parent, bool after_pullout); void recalc_used_tables(st_select_lex *new_parent, bool after_pullout);
virtual bool exec(); virtual bool exec();
/*
If subquery optimization or execution determines that the subquery has
an empty result, mark the subquery predicate as a constant value.
*/
void make_const()
{
used_tables_cache= 0;
const_item_cache= 0;
forced_const= TRUE;
}
virtual void fix_length_and_dec(); virtual void fix_length_and_dec();
table_map used_tables() const; table_map used_tables() const;
table_map not_null_tables() const { return 0; } table_map not_null_tables() const { return 0; }
bool const_item() const; bool const_item() const;
inline table_map get_used_tables_cache() { return used_tables_cache; } inline table_map get_used_tables_cache() { return used_tables_cache; }
inline bool get_const_item_cache() { return const_item_cache; }
Item *get_tmp_table_item(THD *thd); Item *get_tmp_table_item(THD *thd);
void update_used_tables(); void update_used_tables();
virtual void print(String *str, enum_query_type query_type); virtual void print(String *str, enum_query_type query_type);
...@@ -353,12 +369,6 @@ class Item_in_subselect :public Item_exists_subselect ...@@ -353,12 +369,6 @@ class Item_in_subselect :public Item_exists_subselect
*/ */
List<Cached_item> *left_expr_cache; List<Cached_item> *left_expr_cache;
bool first_execution; bool first_execution;
/*
Set to TRUE if at query execution time we determine that this item's
value is a constant during this execution. We need this member because
it is not possible to substitute 'this' with a constant item.
*/
bool is_constant;
/* /*
expr & optimizer used in subselect rewriting to store Item for expr & optimizer used in subselect rewriting to store Item for
...@@ -435,7 +445,7 @@ class Item_in_subselect :public Item_exists_subselect ...@@ -435,7 +445,7 @@ class Item_in_subselect :public Item_exists_subselect
Item_in_subselect(Item * left_expr, st_select_lex *select_lex); Item_in_subselect(Item * left_expr, st_select_lex *select_lex);
Item_in_subselect() Item_in_subselect()
:Item_exists_subselect(), left_expr_cache(0), first_execution(TRUE), :Item_exists_subselect(), left_expr_cache(0), first_execution(TRUE),
is_constant(FALSE), optimizer(0), abort_on_null(0), optimizer(0), abort_on_null(0),
pushed_cond_guards(NULL), func(NULL), in_strategy(0), pushed_cond_guards(NULL), func(NULL), in_strategy(0),
upper_item(0) upper_item(0)
{} {}
......
...@@ -3823,3 +3823,65 @@ bool JOIN::choose_subquery_plan(table_map join_tables) ...@@ -3823,3 +3823,65 @@ bool JOIN::choose_subquery_plan(table_map join_tables)
return FALSE; return FALSE;
} }
/**
Choose a query plan for a table-less subquery.
@notes
@retval FALSE success.
@retval TRUE error occurred.
*/
bool JOIN::choose_tableless_subquery_plan()
{
DBUG_ASSERT(!tables_list || !tables);
if (select_lex->master_unit()->item)
{
DBUG_ASSERT(select_lex->master_unit()->item->type() ==
Item::SUBSELECT_ITEM);
Item_subselect *subs_predicate= select_lex->master_unit()->item;
/*
If the optimizer determined that his query has an empty result,
in most cases the subquery predicate is a known constant value -
either FALSE or NULL. The implementation of Item_subselect::reset()
determines which one.
*/
if (zero_result_cause)
{
if (!implicit_grouping)
{
/*
Both group by queries and non-group by queries without aggregate
functions produce empty subquery result.
*/
subs_predicate->reset();
subs_predicate->make_const();
return FALSE;
}
/* TODO:
A further optimization is possible when a non-group query with
MIN/MAX/COUNT is optimized by opt_sum_query. Then, if there are
only MIN/MAX functions over an empty result set, the subquery
result is a NULL value/row, thus the value of subs_predicate is
NULL.
*/
}
if (subs_predicate->is_in_predicate())
{
Item_in_subselect *in_subs;
in_subs= (Item_in_subselect*) subs_predicate;
in_subs->in_strategy= SUBS_IN_TO_EXISTS;
if (in_subs->create_in_to_exists_cond(this) ||
in_subs->inject_in_to_exists_cond(this))
return TRUE;
tmp_having= having;
}
}
return FALSE;
}
...@@ -829,6 +829,7 @@ JOIN::optimize() ...@@ -829,6 +829,7 @@ JOIN::optimize()
"Impossible HAVING" : "Impossible WHERE"; "Impossible HAVING" : "Impossible WHERE";
tables= 0; tables= 0;
error= 0; error= 0;
choose_tableless_subquery_plan();
goto setup_subq_exit; goto setup_subq_exit;
} }
} }
...@@ -873,12 +874,13 @@ JOIN::optimize() ...@@ -873,12 +874,13 @@ JOIN::optimize()
*/ */
if ((res=opt_sum_query(select_lex->leaf_tables, all_fields, conds))) if ((res=opt_sum_query(select_lex->leaf_tables, all_fields, conds)))
{ {
if (res == HA_ERR_KEY_NOT_FOUND) if (res == HA_ERR_KEY_NOT_FOUND || res < 0)
{ {
DBUG_PRINT("info",("No matching min/max row")); DBUG_PRINT("info",("No matching min/max row"));
zero_result_cause= "No matching min/max row"; zero_result_cause= "No matching min/max row";
tables= 0; tables= 0;
error=0; error=0;
choose_tableless_subquery_plan();
goto setup_subq_exit; goto setup_subq_exit;
} }
if (res > 1) if (res > 1)
...@@ -887,14 +889,7 @@ JOIN::optimize() ...@@ -887,14 +889,7 @@ JOIN::optimize()
DBUG_PRINT("error",("Error from opt_sum_query")); DBUG_PRINT("error",("Error from opt_sum_query"));
DBUG_RETURN(1); DBUG_RETURN(1);
} }
if (res < 0)
{
DBUG_PRINT("info",("No matching min/max row"));
zero_result_cause= "No matching min/max row";
tables= 0;
error=0;
goto setup_subq_exit;
}
DBUG_PRINT("info",("Select tables optimized away")); DBUG_PRINT("info",("Select tables optimized away"));
zero_result_cause= "Select tables optimized away"; zero_result_cause= "Select tables optimized away";
tables_list= 0; // All tables resolved tables_list= 0; // All tables resolved
...@@ -919,40 +914,14 @@ JOIN::optimize() ...@@ -919,40 +914,14 @@ JOIN::optimize()
QT_ORDINARY);); QT_ORDINARY););
conds= table_independent_conds; conds= table_independent_conds;
} }
goto setup_subq_exit;
} }
} }
if (!tables_list) if (!tables_list)
{ {
DBUG_PRINT("info",("No tables")); DBUG_PRINT("info",("No tables"));
error= 0; error= 0;
if (optimize_unflattened_subqueries()) choose_tableless_subquery_plan();
DBUG_RETURN(1); goto setup_subq_exit;
/*
TIMOUR: TODO: consider do we need to optimize here at all and refactor
this block and JOIN::choose_subquery_plan.
if (choose_subquery_plan())
DBUG_RETURN(1);
*/
Item_in_subselect *in_subs;
if (select_lex->master_unit()->item &&
select_lex->master_unit()->item->is_in_predicate())
{
in_subs= (Item_in_subselect*) select_lex->master_unit()->item;
if (in_subs->in_strategy & SUBS_MATERIALIZATION &&
in_subs->setup_mat_engine())
in_subs->in_strategy= SUBS_IN_TO_EXISTS;
if (in_subs->in_strategy & SUBS_IN_TO_EXISTS)
{
if (in_subs->create_in_to_exists_cond(this))
DBUG_RETURN(1);
if (in_subs->inject_in_to_exists_cond(this))
DBUG_RETURN(1);
tmp_having= having;
}
}
DBUG_RETURN(0);
} }
error= -1; // Error is sent to client error= -1; // Error is sent to client
sort_by_table= get_sort_by_table(order, group_list, select_lex->leaf_tables); sort_by_table= get_sort_by_table(order, group_list, select_lex->leaf_tables);
......
...@@ -1383,6 +1383,8 @@ class JOIN :public Sql_alloc ...@@ -1383,6 +1383,8 @@ class JOIN :public Sql_alloc
void restore_query_plan(DYNAMIC_ARRAY *save_keyuse, POSITION *save_positions, void restore_query_plan(DYNAMIC_ARRAY *save_keyuse, POSITION *save_positions,
KEYUSE **save_join_tab_keyuse, KEYUSE **save_join_tab_keyuse,
key_map *save_join_tab_checked_keys); key_map *save_join_tab_checked_keys);
/* Choose a subquery plan for a table-less subquery. */
bool choose_tableless_subquery_plan();
public: public:
JOIN_TAB *join_tab,**best_ref; JOIN_TAB *join_tab,**best_ref;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment