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'
-- 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 when JOIN::optimize detects an empty subquery result.
#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);
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);
-- echo
#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');
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');
-- echo /* E.6 make_join_select detects impossible WHERE. *
......
This diff is collapsed.
......@@ -36,7 +36,7 @@ Item_subselect::Item_subselect():
Item_result_field(), value_assigned(0), thd(0), substitution(0),
engine(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),
inside_first_fix_fields(0), done_first_fix_fields(FALSE), forced_const(FALSE),
eliminated(FALSE),
engine_changed(0), changed(0), is_correlated(FALSE)
{
......@@ -121,6 +121,7 @@ void Item_subselect::cleanup()
depends_on.empty();
reset();
value_assigned= 0;
forced_const= FALSE;
DBUG_VOID_RETURN;
}
......@@ -158,7 +159,6 @@ void Item_in_subselect::cleanup()
left_expr_cache= NULL;
}
first_execution= TRUE;
is_constant= FALSE;
if (in_strategy & SUBS_MATERIALIZATION)
in_strategy= 0;
pushed_cond_guards= NULL;
......@@ -682,12 +682,15 @@ Item *Item_subselect::get_tmp_table_item(THD *thd_arg)
void Item_subselect::update_used_tables()
{
recalc_used_tables(parent_select, FALSE);
if (!engine->uncacheable())
if (!forced_const)
{
// did all used tables become static?
if (!(used_tables_cache & ~engine->upper_select_const_tables()))
const_item_cache= 1;
recalc_used_tables(parent_select, FALSE);
if (!engine->uncacheable())
{
// did all used tables become static?
if (!(used_tables_cache & ~engine->upper_select_const_tables()))
const_item_cache= 1;
}
}
}
......@@ -753,7 +756,7 @@ Item_maxmin_subselect::Item_maxmin_subselect(THD *thd_param,
of Items belonged to subquery, which will be not repeated
*/
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
......@@ -791,8 +794,7 @@ void Item_maxmin_subselect::print(String *str, enum_query_type query_type)
void Item_singlerow_subselect::reset()
{
eliminated= FALSE;
null_value= 1;
Item_subselect::reset();
if (value)
value->null_value= 1;
}
......@@ -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,
st_select_lex *select_lex):
Item_exists_subselect(), left_expr_cache(0), first_execution(TRUE),
is_constant(FALSE), optimizer(0), pushed_cond_guards(NULL),
in_strategy(0), upper_item(0)
optimizer(0), pushed_cond_guards(NULL), in_strategy(0), upper_item(0)
{
DBUG_ENTER("Item_in_subselect::Item_in_subselect");
left_expr= left_exp;
......@@ -1313,7 +1314,7 @@ bool Item_in_subselect::val_bool()
{
DBUG_ASSERT(fixed == 1);
null_value= 0;
if (is_constant)
if (forced_const)
return value;
if (exec())
{
......@@ -4147,11 +4148,10 @@ int subselect_hash_sj_engine::exec()
tmp_table->file->info(HA_STATUS_VARIABLE);
if (!tmp_table->file->stats.records)
{
item_in->value= FALSE;
/* 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();
/* TIMOUR: check if we need this: item_in->null_value= FALSE; */
DBUG_RETURN(FALSE);
}
......
......@@ -70,6 +70,13 @@ class Item_subselect :public Item_result_field
bool inside_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:
/* A reference from inside subquery predicate to somewhere outside of it */
class Ref_to_outside : public Sql_alloc
......@@ -154,12 +161,21 @@ class Item_subselect :public Item_result_field
void fix_after_pullout(st_select_lex *new_parent, Item **ref);
void recalc_used_tables(st_select_lex *new_parent, bool after_pullout);
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();
table_map used_tables() const;
table_map not_null_tables() const { return 0; }
bool const_item() const;
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);
void update_used_tables();
virtual void print(String *str, enum_query_type query_type);
......@@ -353,12 +369,6 @@ class Item_in_subselect :public Item_exists_subselect
*/
List<Cached_item> *left_expr_cache;
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
......@@ -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_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),
upper_item(0)
{}
......
......@@ -3823,3 +3823,65 @@ bool JOIN::choose_subquery_plan(table_map join_tables)
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()
"Impossible HAVING" : "Impossible WHERE";
tables= 0;
error= 0;
choose_tableless_subquery_plan();
goto setup_subq_exit;
}
}
......@@ -873,12 +874,13 @@ JOIN::optimize()
*/
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"));
zero_result_cause= "No matching min/max row";
tables= 0;
error=0;
choose_tableless_subquery_plan();
goto setup_subq_exit;
}
if (res > 1)
......@@ -887,14 +889,7 @@ JOIN::optimize()
DBUG_PRINT("error",("Error from opt_sum_query"));
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"));
zero_result_cause= "Select tables optimized away";
tables_list= 0; // All tables resolved
......@@ -919,40 +914,14 @@ JOIN::optimize()
QT_ORDINARY););
conds= table_independent_conds;
}
goto setup_subq_exit;
}
}
if (!tables_list)
{
DBUG_PRINT("info",("No tables"));
error= 0;
if (optimize_unflattened_subqueries())
DBUG_RETURN(1);
/*
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);
choose_tableless_subquery_plan();
goto setup_subq_exit;
}
error= -1; // Error is sent to client
sort_by_table= get_sort_by_table(order, group_list, select_lex->leaf_tables);
......
......@@ -1383,6 +1383,8 @@ class JOIN :public Sql_alloc
void restore_query_plan(DYNAMIC_ARRAY *save_keyuse, POSITION *save_positions,
KEYUSE **save_join_tab_keyuse,
key_map *save_join_tab_checked_keys);
/* Choose a subquery plan for a table-less subquery. */
bool choose_tableless_subquery_plan();
public:
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