Commit 0bee625f authored by unknown's avatar unknown

MWL#89

Post-review fixes. Intermediate commit to address
review points 1.1, 1.2, 1.3, 1.4, 1.5, and 3.1, 3.2, 3.3.
parent fd24b33b
...@@ -386,6 +386,8 @@ Item::Item(): ...@@ -386,6 +386,8 @@ Item::Item():
decimals= 0; max_length= 0; decimals= 0; max_length= 0;
with_subselect= 0; with_subselect= 0;
cmp_context= IMPOSSIBLE_RESULT; cmp_context= IMPOSSIBLE_RESULT;
/* Initially this item is not attached to any JOIN_TAB. */
join_tab_idx= MAX_TABLES;
/* Put item in free list so that we can free all items at end */ /* Put item in free list so that we can free all items at end */
THD *thd= current_thd; THD *thd= current_thd;
...@@ -414,6 +416,7 @@ Item::Item(): ...@@ -414,6 +416,7 @@ Item::Item():
tables. tables.
*/ */
Item::Item(THD *thd, Item *item): Item::Item(THD *thd, Item *item):
join_tab_idx(item->join_tab_idx),
is_expensive_cache(-1), is_expensive_cache(-1),
rsize(0), rsize(0),
str_value(item->str_value), str_value(item->str_value),
...@@ -470,6 +473,7 @@ void Item::cleanup() ...@@ -470,6 +473,7 @@ void Item::cleanup()
DBUG_ENTER("Item::cleanup"); DBUG_ENTER("Item::cleanup");
fixed=0; fixed=0;
marker= 0; marker= 0;
join_tab_idx= MAX_TABLES;
if (orig_name) if (orig_name)
name= orig_name; name= orig_name;
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
......
...@@ -491,6 +491,17 @@ typedef void (*Cond_traverser) (const Item *item, void *arg); ...@@ -491,6 +491,17 @@ typedef void (*Cond_traverser) (const Item *item, void *arg);
class Item { class Item {
Item(const Item &); /* Prevent use of these */ Item(const Item &); /* Prevent use of these */
void operator=(Item &); void operator=(Item &);
/**
The index in the JOIN::join_tab array of the JOIN_TAB this Item is attached
to. Items are attached (or 'pushed') to JOIN_TABs during optimization by the
make_cond_for_table procedure. During query execution, this item is
evaluated when the join loop reaches the corresponding JOIN_TAB.
If the value of join_tab_idx >= MAX_TABLES, this means that there is no
corresponding JOIN_TAB.
*/
uint join_tab_idx;
public: public:
static void *operator new(size_t size) throw () static void *operator new(size_t size) throw ()
{ return sql_alloc(size); } { return sql_alloc(size); }
...@@ -1179,6 +1190,16 @@ class Item { ...@@ -1179,6 +1190,16 @@ class Item {
Item* set_expr_cache(THD *thd, List<Item*> &depends_on); Item* set_expr_cache(THD *thd, List<Item*> &depends_on);
virtual Item *get_cached_item() { return NULL; } virtual Item *get_cached_item() { return NULL; }
/**
Set the join tab index to the minimal (left-most) JOIN_TAB to which this
Item is attached.
*/
virtual void set_join_tab_idx(uint join_tab_idx_arg)
{
if (join_tab_idx_arg < join_tab_idx)
join_tab_idx= join_tab_idx_arg;
}
virtual uint get_join_tab_idx() { return join_tab_idx; }
}; };
......
...@@ -268,6 +268,8 @@ class Item_in_optimizer: public Item_bool_func ...@@ -268,6 +268,8 @@ class Item_in_optimizer: public Item_bool_func
virtual Item *expr_cache_insert_transformer(uchar *thd_arg); virtual Item *expr_cache_insert_transformer(uchar *thd_arg);
bool is_expensive_processor(uchar *arg); bool is_expensive_processor(uchar *arg);
bool is_expensive(); bool is_expensive();
void set_join_tab_idx(uint join_tab_idx_arg)
{ args[1]->set_join_tab_idx(join_tab_idx_arg); }
}; };
class Comp_creator class Comp_creator
......
...@@ -172,11 +172,11 @@ Item_subselect::~Item_subselect() ...@@ -172,11 +172,11 @@ Item_subselect::~Item_subselect()
engine= NULL; engine= NULL;
} }
Item_subselect::trans_res bool
Item_subselect::select_transformer(JOIN *join) Item_subselect::select_transformer(JOIN *join)
{ {
DBUG_ENTER("Item_subselect::select_transformer"); DBUG_ENTER("Item_subselect::select_transformer");
DBUG_RETURN(RES_OK); DBUG_RETURN(false);
} }
...@@ -809,13 +809,17 @@ void Item_singlerow_subselect::reset() ...@@ -809,13 +809,17 @@ void Item_singlerow_subselect::reset()
- switch off this optimization for prepare statement, - switch off this optimization for prepare statement,
because we do not rollback this changes. because we do not rollback this changes.
Make rollback for it, or special name resolving mode in 5.0. Make rollback for it, or special name resolving mode in 5.0.
@param join Join object of the subquery (i.e. 'child' join).
@retval false The subquery was transformed
*/ */
Item_subselect::trans_res bool
Item_singlerow_subselect::select_transformer(JOIN *join) Item_singlerow_subselect::select_transformer(JOIN *join)
{ {
DBUG_ENTER("Item_singlerow_subselect::select_transformer"); DBUG_ENTER("Item_singlerow_subselect::select_transformer");
if (changed) if (changed)
DBUG_RETURN(RES_OK); DBUG_RETURN(false);
SELECT_LEX *select_lex= join->select_lex; SELECT_LEX *select_lex= join->select_lex;
Query_arena *arena= thd->stmt_arena; Query_arena *arena= thd->stmt_arena;
...@@ -842,7 +846,6 @@ Item_singlerow_subselect::select_transformer(JOIN *join) ...@@ -842,7 +846,6 @@ Item_singlerow_subselect::select_transformer(JOIN *join)
!arena->is_stmt_prepare_or_first_sp_execute() !arena->is_stmt_prepare_or_first_sp_execute()
) )
{ {
have_to_be_excluded= 1; have_to_be_excluded= 1;
if (thd->lex->describe) if (thd->lex->describe)
{ {
...@@ -858,9 +861,8 @@ Item_singlerow_subselect::select_transformer(JOIN *join) ...@@ -858,9 +861,8 @@ Item_singlerow_subselect::select_transformer(JOIN *join)
*/ */
substitution->walk(&Item::remove_dependence_processor, 0, substitution->walk(&Item::remove_dependence_processor, 0,
(uchar *) select_lex->outer_select()); (uchar *) select_lex->outer_select());
DBUG_RETURN(RES_REDUCE);
} }
DBUG_RETURN(RES_OK); DBUG_RETURN(false);
} }
...@@ -1368,11 +1370,11 @@ my_decimal *Item_in_subselect::val_decimal(my_decimal *decimal_value) ...@@ -1368,11 +1370,11 @@ my_decimal *Item_in_subselect::val_decimal(my_decimal *decimal_value)
@param join Join object of the subquery (i.e. 'child' join). @param join Join object of the subquery (i.e. 'child' join).
@details @details
Rewrite a single-column subquery using rule-based approach. The subquery Rewrite a single-column subquery using rule-based approach. Given the subquery
oe $cmp$ (SELECT ie FROM ... WHERE subq_where ... HAVING subq_having) oe $cmp$ (SELECT ie FROM ... WHERE subq_where ... HAVING subq_having)
First, try to convert the subquery to scalar-result subquery in one of First, try to convert the subquery to a scalar-result subquery in one of
the forms: the forms:
- oe $cmp$ (SELECT MAX(...) ) // handled by Item_singlerow_subselect - oe $cmp$ (SELECT MAX(...) ) // handled by Item_singlerow_subselect
...@@ -1387,11 +1389,11 @@ my_decimal *Item_in_subselect::val_decimal(my_decimal *decimal_value) ...@@ -1387,11 +1389,11 @@ my_decimal *Item_in_subselect::val_decimal(my_decimal *decimal_value)
EXISTS by injecting additional predicates, or will be executed via subquery EXISTS by injecting additional predicates, or will be executed via subquery
materialization in its unmodified form. materialization in its unmodified form.
@retval RES_OK The subquery was transformed @retval false The subquery was transformed
@retval RES_ERROR Error @retval true Error
*/ */
Item_subselect::trans_res bool
Item_in_subselect::single_value_transformer(JOIN *join) Item_in_subselect::single_value_transformer(JOIN *join)
{ {
SELECT_LEX *select_lex= join->select_lex; SELECT_LEX *select_lex= join->select_lex;
...@@ -1405,7 +1407,7 @@ Item_in_subselect::single_value_transformer(JOIN *join) ...@@ -1405,7 +1407,7 @@ Item_in_subselect::single_value_transformer(JOIN *join)
if (select_lex->item_list.elements > 1) if (select_lex->item_list.elements > 1)
{ {
my_error(ER_OPERAND_COLUMNS, MYF(0), 1); my_error(ER_OPERAND_COLUMNS, MYF(0), 1);
DBUG_RETURN(RES_ERROR); DBUG_RETURN(true);
} }
/* /*
...@@ -1425,7 +1427,7 @@ Item_in_subselect::single_value_transformer(JOIN *join) ...@@ -1425,7 +1427,7 @@ Item_in_subselect::single_value_transformer(JOIN *join)
if (substitution) if (substitution)
{ {
/* It is second (third, ...) SELECT of UNION => All is done */ /* It is second (third, ...) SELECT of UNION => All is done */
DBUG_RETURN(RES_OK); DBUG_RETURN(false);
} }
Item *subs; Item *subs;
...@@ -1470,7 +1472,7 @@ Item_in_subselect::single_value_transformer(JOIN *join) ...@@ -1470,7 +1472,7 @@ Item_in_subselect::single_value_transformer(JOIN *join)
we do not check item->fixed we do not check item->fixed
*/ */
if (item->fix_fields(thd, 0)) if (item->fix_fields(thd, 0))
DBUG_RETURN(RES_ERROR); DBUG_RETURN(true);
thd->lex->allow_sum_func= save_allow_sum_func; thd->lex->allow_sum_func= save_allow_sum_func;
/* we added aggregate function => we have to change statistic */ /* we added aggregate function => we have to change statistic */
count_field_types(select_lex, &join->tmp_table_param, join->all_fields, count_field_types(select_lex, &join->tmp_table_param, join->all_fields,
...@@ -1487,7 +1489,7 @@ Item_in_subselect::single_value_transformer(JOIN *join) ...@@ -1487,7 +1489,7 @@ Item_in_subselect::single_value_transformer(JOIN *join)
} }
/* fix fields is already called for left expression */ /* fix fields is already called for left expression */
substitution= func->create(left_expr, subs); substitution= func->create(left_expr, subs);
DBUG_RETURN(RES_OK); DBUG_RETURN(false);
} }
Item* join_having= join->having ? join->having : join->tmp_having; Item* join_having= join->having ? join->having : join->tmp_having;
...@@ -1513,7 +1515,7 @@ Item_in_subselect::single_value_transformer(JOIN *join) ...@@ -1513,7 +1515,7 @@ Item_in_subselect::single_value_transformer(JOIN *join)
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
ER_SELECT_REDUCED, warn_buff); ER_SELECT_REDUCED, warn_buff);
} }
DBUG_RETURN(RES_OK); DBUG_RETURN(false);
} }
/* /*
...@@ -1533,7 +1535,7 @@ Item_in_subselect::single_value_transformer(JOIN *join) ...@@ -1533,7 +1535,7 @@ Item_in_subselect::single_value_transformer(JOIN *join)
if (!optimizer || optimizer->fix_left(thd, 0)) if (!optimizer || optimizer->fix_left(thd, 0))
{ {
thd->lex->current_select= current; thd->lex->current_select= current;
DBUG_RETURN(RES_ERROR); DBUG_RETURN(true);
} }
thd->lex->current_select= current; thd->lex->current_select= current;
...@@ -1554,7 +1556,7 @@ Item_in_subselect::single_value_transformer(JOIN *join) ...@@ -1554,7 +1556,7 @@ Item_in_subselect::single_value_transformer(JOIN *join)
// select_lex->uncacheable|= UNCACHEABLE_DEPENDENT; // select_lex->uncacheable|= UNCACHEABLE_DEPENDENT;
} }
DBUG_RETURN(RES_OK); DBUG_RETURN(false);
} }
...@@ -1603,11 +1605,11 @@ bool Item_in_subselect::fix_having(Item *having, SELECT_LEX *select_lex) ...@@ -1603,11 +1605,11 @@ bool Item_in_subselect::fix_having(Item *having, SELECT_LEX *select_lex)
WHERE subq_where AND trigcond((oe $cmp$ ie) OR (ie IS NULL)) WHERE subq_where AND trigcond((oe $cmp$ ie) OR (ie IS NULL))
HAVING trigcond(<is_not_null_test>(ie)) HAVING trigcond(<is_not_null_test>(ie))
@retval RES_OK If the new conditions were created successfully @retval false If the new conditions were created successfully
@retval RES_ERROR Error @retval true Error
*/ */
Item_subselect::trans_res bool
Item_in_subselect::create_single_in_to_exists_cond(JOIN * join, Item_in_subselect::create_single_in_to_exists_cond(JOIN * join,
Item **where_item, Item **where_item,
Item **having_item) Item **having_item)
...@@ -1646,7 +1648,7 @@ Item_in_subselect::create_single_in_to_exists_cond(JOIN * join, ...@@ -1646,7 +1648,7 @@ Item_in_subselect::create_single_in_to_exists_cond(JOIN * join,
if (!join_having) if (!join_having)
item->name= (char*) in_having_cond; item->name= (char*) in_having_cond;
if (fix_having(item, select_lex)) if (fix_having(item, select_lex))
DBUG_RETURN(RES_ERROR); DBUG_RETURN(true);
*having_item= item; *having_item= item;
} }
else else
...@@ -1666,11 +1668,11 @@ Item_in_subselect::create_single_in_to_exists_cond(JOIN * join, ...@@ -1666,11 +1668,11 @@ Item_in_subselect::create_single_in_to_exists_cond(JOIN * join,
{ {
if (!(having= new Item_func_trig_cond(having, if (!(having= new Item_func_trig_cond(having,
get_cond_guard(0)))) get_cond_guard(0))))
DBUG_RETURN(RES_ERROR); DBUG_RETURN(true);
} }
having->name= (char*) in_having_cond; having->name= (char*) in_having_cond;
if (fix_having(having, select_lex)) if (fix_having(having, select_lex))
DBUG_RETURN(RES_ERROR); DBUG_RETURN(true);
*having_item= having; *having_item= having;
item= new Item_cond_or(item, item= new Item_cond_or(item,
...@@ -1683,7 +1685,7 @@ Item_in_subselect::create_single_in_to_exists_cond(JOIN * join, ...@@ -1683,7 +1685,7 @@ Item_in_subselect::create_single_in_to_exists_cond(JOIN * join,
if (!abort_on_null && left_expr->maybe_null) if (!abort_on_null && left_expr->maybe_null)
{ {
if (!(item= new Item_func_trig_cond(item, get_cond_guard(0)))) if (!(item= new Item_func_trig_cond(item, get_cond_guard(0))))
DBUG_RETURN(RES_ERROR); DBUG_RETURN(true);
} }
/* /*
...@@ -1693,7 +1695,7 @@ Item_in_subselect::create_single_in_to_exists_cond(JOIN * join, ...@@ -1693,7 +1695,7 @@ Item_in_subselect::create_single_in_to_exists_cond(JOIN * join,
*/ */
item->name= (char *) in_additional_cond; item->name= (char *) in_additional_cond;
if (!item->fixed && item->fix_fields(thd, 0)) if (!item->fixed && item->fix_fields(thd, 0))
DBUG_RETURN(RES_ERROR); DBUG_RETURN(true);
*where_item= item; *where_item= item;
} }
else else
...@@ -1710,12 +1712,12 @@ Item_in_subselect::create_single_in_to_exists_cond(JOIN * join, ...@@ -1710,12 +1712,12 @@ Item_in_subselect::create_single_in_to_exists_cond(JOIN * join,
{ {
if (!(new_having= new Item_func_trig_cond(new_having, if (!(new_having= new Item_func_trig_cond(new_having,
get_cond_guard(0)))) get_cond_guard(0))))
DBUG_RETURN(RES_ERROR); DBUG_RETURN(true);
} }
new_having->name= (char*) in_having_cond; new_having->name= (char*) in_having_cond;
if (fix_having(new_having, select_lex)) if (fix_having(new_having, select_lex))
DBUG_RETURN(RES_ERROR); DBUG_RETURN(true);
*having_item= new_having; *having_item= new_having;
} }
else else
...@@ -1723,7 +1725,7 @@ Item_in_subselect::create_single_in_to_exists_cond(JOIN * join, ...@@ -1723,7 +1725,7 @@ Item_in_subselect::create_single_in_to_exists_cond(JOIN * join,
} }
} }
DBUG_RETURN(RES_OK); DBUG_RETURN(false);
} }
...@@ -1739,11 +1741,11 @@ Item_in_subselect::create_single_in_to_exists_cond(JOIN * join, ...@@ -1739,11 +1741,11 @@ Item_in_subselect::create_single_in_to_exists_cond(JOIN * join,
additional predicates, or will be executed via subquery materialization in its additional predicates, or will be executed via subquery materialization in its
unmodified form. unmodified form.
@retval RES_OK The subquery was transformed @retval false The subquery was transformed
@retval RES_ERROR Error @retval true Error
*/ */
Item_subselect::trans_res bool
Item_in_subselect::row_value_transformer(JOIN *join) Item_in_subselect::row_value_transformer(JOIN *join)
{ {
SELECT_LEX *select_lex= join->select_lex; SELECT_LEX *select_lex= join->select_lex;
...@@ -1755,7 +1757,7 @@ Item_in_subselect::row_value_transformer(JOIN *join) ...@@ -1755,7 +1757,7 @@ Item_in_subselect::row_value_transformer(JOIN *join)
if (select_lex->item_list.elements != cols_num) if (select_lex->item_list.elements != cols_num)
{ {
my_error(ER_OPERAND_COLUMNS, MYF(0), cols_num); my_error(ER_OPERAND_COLUMNS, MYF(0), cols_num);
DBUG_RETURN(RES_ERROR); DBUG_RETURN(true);
} }
/* /*
...@@ -1774,7 +1776,7 @@ Item_in_subselect::row_value_transformer(JOIN *join) ...@@ -1774,7 +1776,7 @@ Item_in_subselect::row_value_transformer(JOIN *join)
if (!optimizer || optimizer->fix_left(thd, 0)) if (!optimizer || optimizer->fix_left(thd, 0))
{ {
thd->lex->current_select= current; thd->lex->current_select= current;
DBUG_RETURN(RES_ERROR); DBUG_RETURN(true);
} }
// we will refer to upper level cache array => we have to save it in PS // we will refer to upper level cache array => we have to save it in PS
...@@ -1786,7 +1788,7 @@ Item_in_subselect::row_value_transformer(JOIN *join) ...@@ -1786,7 +1788,7 @@ Item_in_subselect::row_value_transformer(JOIN *join)
//select_lex->uncacheable|= UNCACHEABLE_DEPENDENT; //select_lex->uncacheable|= UNCACHEABLE_DEPENDENT;
} }
DBUG_RETURN(RES_OK); DBUG_RETURN(false);
} }
...@@ -1795,18 +1797,51 @@ Item_in_subselect::row_value_transformer(JOIN *join) ...@@ -1795,18 +1797,51 @@ Item_in_subselect::row_value_transformer(JOIN *join)
subselect into a correlated EXISTS via predicate injection. subselect into a correlated EXISTS via predicate injection.
@details @details
There are two cases - either the subquery has aggregates, GROUP BY, The correlated predicates are created as follows:
or HAVING, or not. Both cases are described inline in the code.
- If the subquery has aggregates, GROUP BY, or HAVING, convert to
(l1, l2, l3) IN (SELECT v1, v2, v3 ... HAVING having)
=>
EXISTS (SELECT ... HAVING having and
(l1 = v1 or is null v1) and
(l2 = v2 or is null v2) and
(l3 = v3 or is null v3) and
is_not_null_test(v1) and
is_not_null_test(v2) and
is_not_null_test(v3))
where is_not_null_test used to register nulls in case if we have
not found matching to return correct NULL value.
- Otherwise (no aggregates/GROUP BY/HAVING) convert the subquery as follows:
(l1, l2, l3) IN (SELECT v1, v2, v3 ... WHERE where)
=>
EXISTS (SELECT ... WHERE where and
(l1 = v1 or is null v1) and
(l2 = v2 or is null v2) and
(l3 = v3 or is null v3)
HAVING is_not_null_test(v1) and
is_not_null_test(v2) and
is_not_null_test(v3))
where is_not_null_test registers NULLs values but reject rows.
in case when we do not need correct NULL, we have simplier construction:
EXISTS (SELECT ... WHERE where and
(l1 = v1) and
(l2 = v2) and
(l3 = v3)
@param join[in] Join object of the subquery (i.e. 'child' join). @param join[in] Join object of the subquery (i.e. 'child' join).
@param where_item[out] the in-to-exists addition to the where clause @param where_item[out] the in-to-exists addition to the where clause
@param having_item[out] the in-to-exists addition to the having clause @param having_item[out] the in-to-exists addition to the having clause
@retval RES_OK If the new conditions were created successfully @retval false If the new conditions were created successfully
@retval RES_ERROR Error @retval true Error
*/ */
Item_subselect::trans_res bool
Item_in_subselect::create_row_in_to_exists_cond(JOIN * join, Item_in_subselect::create_row_in_to_exists_cond(JOIN * join,
Item **where_item, Item **where_item,
Item **having_item) Item **having_item)
...@@ -1829,19 +1864,7 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join, ...@@ -1829,19 +1864,7 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join,
if (is_having_used) if (is_having_used)
{ {
/* /* TODO: say here explicitly if the order of AND parts matters or not. */
(l1, l2, l3) IN (SELECT v1, v2, v3 ... HAVING having) =>
EXISTS (SELECT ... HAVING having and
(l1 = v1 or is null v1) and
(l2 = v2 or is null v2) and
(l3 = v3 or is null v3) and
is_not_null_test(v1) and
is_not_null_test(v2) and
is_not_null_test(v3))
where is_not_null_test used to register nulls in case if we have
not found matching to return correct NULL value
TODO: say here explicitly if the order of AND parts matters or not.
*/
Item *item_having_part2= 0; Item *item_having_part2= 0;
for (uint i= 0; i < cols_num; i++) for (uint i= 0; i < cols_num; i++)
{ {
...@@ -1853,7 +1876,7 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join, ...@@ -1853,7 +1876,7 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join,
Item_ref::OUTER_REF)); Item_ref::OUTER_REF));
if (select_lex->ref_pointer_array[i]-> if (select_lex->ref_pointer_array[i]->
check_cols(left_expr->element_index(i)->cols())) check_cols(left_expr->element_index(i)->cols()))
DBUG_RETURN(RES_ERROR); DBUG_RETURN(true);
Item *item_eq= Item *item_eq=
new Item_func_eq(new new Item_func_eq(new
Item_ref(&select_lex->context, Item_ref(&select_lex->context,
...@@ -1865,20 +1888,18 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join, ...@@ -1865,20 +1888,18 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join,
Item_ref(&select_lex->context, Item_ref(&select_lex->context,
select_lex->ref_pointer_array + i, select_lex->ref_pointer_array + i,
(char *)"<no matter>", (char *)"<no matter>",
(char *)"<list ref>") (char *)"<list ref>"));
);
Item *item_isnull= Item *item_isnull=
new Item_func_isnull(new new Item_func_isnull(new
Item_ref(&select_lex->context, Item_ref(&select_lex->context,
select_lex->ref_pointer_array+i, select_lex->ref_pointer_array+i,
(char *)"<no matter>", (char *)"<no matter>",
(char *)"<list ref>") (char *)"<list ref>"));
);
Item *col_item= new Item_cond_or(item_eq, item_isnull); Item *col_item= new Item_cond_or(item_eq, item_isnull);
if (!abort_on_null && left_expr->element_index(i)->maybe_null) if (!abort_on_null && left_expr->element_index(i)->maybe_null)
{ {
if (!(col_item= new Item_func_trig_cond(col_item, get_cond_guard(i)))) if (!(col_item= new Item_func_trig_cond(col_item, get_cond_guard(i))))
DBUG_RETURN(RES_ERROR); DBUG_RETURN(true);
} }
*having_item= and_items(*having_item, col_item); *having_item= and_items(*having_item, col_item);
...@@ -1893,7 +1914,7 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join, ...@@ -1893,7 +1914,7 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join,
{ {
if (!(item_nnull_test= if (!(item_nnull_test=
new Item_func_trig_cond(item_nnull_test, get_cond_guard(i)))) new Item_func_trig_cond(item_nnull_test, get_cond_guard(i))))
DBUG_RETURN(RES_ERROR); DBUG_RETURN(true);
} }
item_having_part2= and_items(item_having_part2, item_nnull_test); item_having_part2= and_items(item_having_part2, item_nnull_test);
item_having_part2->top_level_item(); item_having_part2->top_level_item();
...@@ -1902,23 +1923,6 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join, ...@@ -1902,23 +1923,6 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join,
} }
else else
{ {
/*
(l1, l2, l3) IN (SELECT v1, v2, v3 ... WHERE where) =>
EXISTS (SELECT ... WHERE where and
(l1 = v1 or is null v1) and
(l2 = v2 or is null v2) and
(l3 = v3 or is null v3)
HAVING is_not_null_test(v1) and
is_not_null_test(v2) and
is_not_null_test(v3))
where is_not_null_test register NULLs values but reject rows
in case when we do not need correct NULL, we have simplier construction:
EXISTS (SELECT ... WHERE where and
(l1 = v1) and
(l2 = v2) and
(l3 = v3)
*/
for (uint i= 0; i < cols_num; i++) for (uint i= 0; i < cols_num; i++)
{ {
Item *item, *item_isnull; Item *item, *item_isnull;
...@@ -1929,7 +1933,7 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join, ...@@ -1929,7 +1933,7 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join,
Item_ref::OUTER_REF)); Item_ref::OUTER_REF));
if (select_lex->ref_pointer_array[i]-> if (select_lex->ref_pointer_array[i]->
check_cols(left_expr->element_index(i)->cols())) check_cols(left_expr->element_index(i)->cols()))
DBUG_RETURN(RES_ERROR); DBUG_RETURN(true);
item= item=
new Item_func_eq(new new Item_func_eq(new
Item_direct_ref(&select_lex->context, Item_direct_ref(&select_lex->context,
...@@ -1942,8 +1946,7 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join, ...@@ -1942,8 +1946,7 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join,
select_lex-> select_lex->
ref_pointer_array+i, ref_pointer_array+i,
(char *)"<no matter>", (char *)"<no matter>",
(char *)"<list ref>") (char *)"<list ref>"));
);
if (!abort_on_null) if (!abort_on_null)
{ {
Item *having_col_item= Item *having_col_item=
...@@ -1961,8 +1964,7 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join, ...@@ -1961,8 +1964,7 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join,
select_lex-> select_lex->
ref_pointer_array+i, ref_pointer_array+i,
(char *)"<no matter>", (char *)"<no matter>",
(char *)"<list ref>") (char *)"<list ref>"));
);
item= new Item_cond_or(item, item_isnull); item= new Item_cond_or(item, item_isnull);
/* /*
TODO: why we create the above for cases where the right part TODO: why we create the above for cases where the right part
...@@ -1971,10 +1973,10 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join, ...@@ -1971,10 +1973,10 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join,
if (left_expr->element_index(i)->maybe_null) if (left_expr->element_index(i)->maybe_null)
{ {
if (!(item= new Item_func_trig_cond(item, get_cond_guard(i)))) if (!(item= new Item_func_trig_cond(item, get_cond_guard(i))))
DBUG_RETURN(RES_ERROR); DBUG_RETURN(true);
if (!(having_col_item= if (!(having_col_item=
new Item_func_trig_cond(having_col_item, get_cond_guard(i)))) new Item_func_trig_cond(having_col_item, get_cond_guard(i))))
DBUG_RETURN(RES_ERROR); DBUG_RETURN(true);
} }
*having_item= and_items(*having_item, having_col_item); *having_item= and_items(*having_item, having_col_item);
} }
...@@ -1985,7 +1987,7 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join, ...@@ -1985,7 +1987,7 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join,
if (*where_item) if (*where_item)
{ {
if (!(*where_item)->fixed && (*where_item)->fix_fields(thd, 0)) if (!(*where_item)->fixed && (*where_item)->fix_fields(thd, 0))
DBUG_RETURN(RES_ERROR); DBUG_RETURN(true);
(*where_item)->top_level_item(); (*where_item)->top_level_item();
} }
...@@ -1994,15 +1996,15 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join, ...@@ -1994,15 +1996,15 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join,
if (!join_having) if (!join_having)
(*having_item)->name= (char*) in_having_cond; (*having_item)->name= (char*) in_having_cond;
if (fix_having(*having_item, select_lex)) if (fix_having(*having_item, select_lex))
DBUG_RETURN(RES_ERROR); DBUG_RETURN(true);
(*having_item)->top_level_item(); (*having_item)->top_level_item();
} }
DBUG_RETURN(RES_OK); DBUG_RETURN(false);
} }
Item_subselect::trans_res bool
Item_in_subselect::select_transformer(JOIN *join) Item_in_subselect::select_transformer(JOIN *join)
{ {
return select_in_like_transformer(join); return select_in_like_transformer(join);
...@@ -2021,7 +2023,7 @@ Item_in_subselect::select_transformer(JOIN *join) ...@@ -2021,7 +2023,7 @@ Item_in_subselect::select_transformer(JOIN *join)
bool Item_in_subselect::create_in_to_exists_cond(JOIN *join_arg) bool Item_in_subselect::create_in_to_exists_cond(JOIN *join_arg)
{ {
Item_subselect::trans_res res; bool res;
DBUG_ASSERT(engine->engine_type() == subselect_engine::SINGLE_SELECT_ENGINE || DBUG_ASSERT(engine->engine_type() == subselect_engine::SINGLE_SELECT_ENGINE ||
engine->engine_type() == subselect_engine::UNION_ENGINE); engine->engine_type() == subselect_engine::UNION_ENGINE);
...@@ -2042,7 +2044,7 @@ bool Item_in_subselect::create_in_to_exists_cond(JOIN *join_arg) ...@@ -2042,7 +2044,7 @@ bool Item_in_subselect::create_in_to_exists_cond(JOIN *join_arg)
res= create_row_in_to_exists_cond(join_arg, res= create_row_in_to_exists_cond(join_arg,
&(join_arg->in_to_exists_where), &(join_arg->in_to_exists_where),
&(join_arg->in_to_exists_having)); &(join_arg->in_to_exists_having));
return (res != RES_OK); return (res);
} }
...@@ -2092,28 +2094,28 @@ bool Item_in_subselect::inject_in_to_exists_cond(JOIN *join_arg) ...@@ -2092,28 +2094,28 @@ bool Item_in_subselect::inject_in_to_exists_cond(JOIN *join_arg)
/** /**
Prepare IN/ALL/ANY/SOME subquery transformation and call appropriate Prepare IN/ALL/ANY/SOME subquery transformation and call the appropriate
transformation function. transformation function.
@param join JOIN object of transforming subquery @param join JOIN object of transforming subquery
@notes @notes
To decide which transformation procedure (scalar or row) applicable here To decide which transformation procedure (scalar or row) applicable here
we have to call fix_fields() for left expression to be able to call we have to call fix_fields() for the left expression to be able to call
cols() method on it. Also this method make arena management for cols() method on it. Also this method makes arena management for
underlying transformation methods. underlying transformation methods.
@retval RES_OK OK @retval false OK
@retval RES_ERROR Error @retval true Error
*/ */
Item_subselect::trans_res bool
Item_in_subselect::select_in_like_transformer(JOIN *join) Item_in_subselect::select_in_like_transformer(JOIN *join)
{ {
Query_arena *arena, backup; Query_arena *arena, backup;
SELECT_LEX *current= thd->lex->current_select; SELECT_LEX *current= thd->lex->current_select;
const char *save_where= thd->where; const char *save_where= thd->where;
Item_subselect::trans_res res= RES_ERROR; bool trans_res= true;
bool result; bool result;
DBUG_ENTER("Item_in_subselect::select_in_like_transformer"); DBUG_ENTER("Item_in_subselect::select_in_like_transformer");
...@@ -2132,7 +2134,7 @@ Item_in_subselect::select_in_like_transformer(JOIN *join) ...@@ -2132,7 +2134,7 @@ Item_in_subselect::select_in_like_transformer(JOIN *join)
} }
if (changed) if (changed)
DBUG_RETURN(RES_OK); DBUG_RETURN(false);
thd->where= "IN/ALL/ANY subquery"; thd->where= "IN/ALL/ANY subquery";
...@@ -2172,7 +2174,7 @@ Item_in_subselect::select_in_like_transformer(JOIN *join) ...@@ -2172,7 +2174,7 @@ Item_in_subselect::select_in_like_transformer(JOIN *join)
*/ */
arena= thd->activate_stmt_arena_if_needed(&backup); arena= thd->activate_stmt_arena_if_needed(&backup);
if (left_expr->cols() == 1) if (left_expr->cols() == 1)
res= single_value_transformer(join); trans_res= single_value_transformer(join);
else else
{ {
/* we do not support row operation for ALL/ANY/SOME */ /* we do not support row operation for ALL/ANY/SOME */
...@@ -2181,15 +2183,15 @@ Item_in_subselect::select_in_like_transformer(JOIN *join) ...@@ -2181,15 +2183,15 @@ Item_in_subselect::select_in_like_transformer(JOIN *join)
if (arena) if (arena)
thd->restore_active_arena(arena, &backup); thd->restore_active_arena(arena, &backup);
my_error(ER_OPERAND_COLUMNS, MYF(0), 1); my_error(ER_OPERAND_COLUMNS, MYF(0), 1);
DBUG_RETURN(RES_ERROR); DBUG_RETURN(true);
} }
res= row_value_transformer(join); trans_res= row_value_transformer(join);
} }
if (arena) if (arena)
thd->restore_active_arena(arena, &backup); thd->restore_active_arena(arena, &backup);
err: err:
thd->where= save_where; thd->where= save_where;
DBUG_RETURN(res); DBUG_RETURN(trans_res);
} }
...@@ -2381,7 +2383,7 @@ bool Item_in_subselect::init_cond_guards() ...@@ -2381,7 +2383,7 @@ bool Item_in_subselect::init_cond_guards()
} }
Item_subselect::trans_res bool
Item_allany_subselect::select_transformer(JOIN *join) Item_allany_subselect::select_transformer(JOIN *join)
{ {
DBUG_ENTER("Item_allany_subselect::select_transformer"); DBUG_ENTER("Item_allany_subselect::select_transformer");
......
...@@ -119,7 +119,6 @@ class Item_subselect :public Item_result_field ...@@ -119,7 +119,6 @@ class Item_subselect :public Item_result_field
/* TRUE <=> The underlying SELECT is correlated w.r.t some ancestor select */ /* TRUE <=> The underlying SELECT is correlated w.r.t some ancestor select */
bool is_correlated; bool is_correlated;
enum trans_res {RES_OK, RES_REDUCE, RES_ERROR};
enum subs_type {UNKNOWN_SUBS, SINGLEROW_SUBS, enum subs_type {UNKNOWN_SUBS, SINGLEROW_SUBS,
EXISTS_SUBS, IN_SUBS, ALL_SUBS, ANY_SUBS}; EXISTS_SUBS, IN_SUBS, ALL_SUBS, ANY_SUBS};
...@@ -148,7 +147,7 @@ class Item_subselect :public Item_result_field ...@@ -148,7 +147,7 @@ class Item_subselect :public Item_result_field
eliminated= FALSE; eliminated= FALSE;
null_value= 1; null_value= 1;
} }
virtual trans_res select_transformer(JOIN *join); virtual bool select_transformer(JOIN *join);
bool assigned() { return value_assigned; } bool assigned() { return value_assigned; }
void assigned(bool a) { value_assigned= a; } void assigned(bool a) { value_assigned= a; }
enum Type type() const; enum Type type() const;
...@@ -259,7 +258,7 @@ class Item_singlerow_subselect :public Item_subselect ...@@ -259,7 +258,7 @@ class Item_singlerow_subselect :public Item_subselect
subs_type substype() { return SINGLEROW_SUBS; } subs_type substype() { return SINGLEROW_SUBS; }
void reset(); void reset();
trans_res select_transformer(JOIN *join); bool select_transformer(JOIN *join);
void store(uint i, Item* item); void store(uint i, Item* item);
double val_real(); double val_real();
longlong val_int (); longlong val_int ();
...@@ -399,14 +398,14 @@ class Item_in_subselect :public Item_exists_subselect ...@@ -399,14 +398,14 @@ class Item_in_subselect :public Item_exists_subselect
protected: protected:
bool init_cond_guards(); bool init_cond_guards();
trans_res select_in_like_transformer(JOIN *join); bool select_in_like_transformer(JOIN *join);
trans_res single_value_transformer(JOIN *join); bool single_value_transformer(JOIN *join);
trans_res row_value_transformer(JOIN * join); bool row_value_transformer(JOIN * join);
bool fix_having(Item *having, st_select_lex *select_lex); bool fix_having(Item *having, st_select_lex *select_lex);
trans_res create_single_in_to_exists_cond(JOIN * join, bool create_single_in_to_exists_cond(JOIN * join,
Item **where_item, Item **where_item,
Item **having_item); Item **having_item);
trans_res create_row_in_to_exists_cond(JOIN * join, bool create_row_in_to_exists_cond(JOIN * join,
Item **where_item, Item **where_item,
Item **having_item); Item **having_item);
public: public:
...@@ -473,7 +472,7 @@ class Item_in_subselect :public Item_exists_subselect ...@@ -473,7 +472,7 @@ class Item_in_subselect :public Item_exists_subselect
null_value= 0; null_value= 0;
was_null= 0; was_null= 0;
} }
trans_res select_transformer(JOIN *join); bool select_transformer(JOIN *join);
bool create_in_to_exists_cond(JOIN *join_arg); bool create_in_to_exists_cond(JOIN *join_arg);
bool inject_in_to_exists_cond(JOIN *join_arg); bool inject_in_to_exists_cond(JOIN *join_arg);
...@@ -523,7 +522,7 @@ class Item_allany_subselect :public Item_in_subselect ...@@ -523,7 +522,7 @@ class Item_allany_subselect :public Item_in_subselect
// only ALL subquery has upper not // only ALL subquery has upper not
subs_type substype() { return all?ALL_SUBS:ANY_SUBS; } subs_type substype() { return all?ALL_SUBS:ANY_SUBS; }
trans_res select_transformer(JOIN *join); bool select_transformer(JOIN *join);
void create_comp_func(bool invert) { func= func_creator(invert); } void create_comp_func(bool invert) { func= func_creator(invert); }
virtual void print(String *str, enum_query_type query_type); virtual void print(String *str, enum_query_type query_type);
}; };
......
...@@ -29,6 +29,10 @@ static TABLE_LIST *alloc_join_nest(THD *thd); ...@@ -29,6 +29,10 @@ static TABLE_LIST *alloc_join_nest(THD *thd);
static static
void fix_list_after_tbl_changes(SELECT_LEX *new_parent, List<TABLE_LIST> *tlist); void fix_list_after_tbl_changes(SELECT_LEX *new_parent, List<TABLE_LIST> *tlist);
static uint get_tmp_table_rec_length(List<Item> &items); static uint get_tmp_table_rec_length(List<Item> &items);
static double get_tmp_table_lookup_cost(THD *thd, ha_rows row_count,
uint row_size);
static double get_tmp_table_write_cost(THD *thd, ha_rows row_count,
uint row_size);
bool find_eq_ref_candidate(TABLE *table, table_map sj_inner_tables); bool find_eq_ref_candidate(TABLE *table, table_map sj_inner_tables);
static SJ_MATERIALIZATION_INFO * static SJ_MATERIALIZATION_INFO *
at_sjmat_pos(const JOIN *join, table_map remaining_tables, const JOIN_TAB *tab, at_sjmat_pos(const JOIN *join, table_map remaining_tables, const JOIN_TAB *tab,
...@@ -257,10 +261,8 @@ int check_and_do_in_subquery_rewrites(JOIN *join) ...@@ -257,10 +261,8 @@ int check_and_do_in_subquery_rewrites(JOIN *join)
Transform each subquery predicate according to its overloaded Transform each subquery predicate according to its overloaded
transformer. transformer.
*/ */
Item_subselect::trans_res trans_res; if (subselect->select_transformer(join))
if ((trans_res= subselect->select_transformer(join)) != DBUG_RETURN(-11);
Item_subselect::RES_OK)
DBUG_RETURN((trans_res == Item_subselect::RES_ERROR));
} }
} }
DBUG_RETURN(0); DBUG_RETURN(0);
...@@ -502,18 +504,17 @@ bool convert_join_subqueries_to_semijoins(JOIN *join) ...@@ -502,18 +504,17 @@ bool convert_join_subqueries_to_semijoins(JOIN *join)
for (; in_subq!= in_subq_end; in_subq++) for (; in_subq!= in_subq_end; in_subq++)
{ {
JOIN *child_join= (*in_subq)->unit->first_select()->join; JOIN *child_join= (*in_subq)->unit->first_select()->join;
Item_subselect::trans_res res;
(*in_subq)->changed= 0; (*in_subq)->changed= 0;
(*in_subq)->fixed= 0; (*in_subq)->fixed= 0;
SELECT_LEX *save_select_lex= thd->lex->current_select; SELECT_LEX *save_select_lex= thd->lex->current_select;
thd->lex->current_select= (*in_subq)->unit->first_select(); thd->lex->current_select= (*in_subq)->unit->first_select();
res= (*in_subq)->select_transformer(child_join); bool res= (*in_subq)->select_transformer(child_join);
thd->lex->current_select= save_select_lex; thd->lex->current_select= save_select_lex;
if (res == Item_subselect::RES_ERROR) if (res)
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
(*in_subq)->changed= 1; (*in_subq)->changed= 1;
...@@ -1253,17 +1254,16 @@ bool optimize_semijoin_nests(JOIN *join, table_map all_table_map) ...@@ -1253,17 +1254,16 @@ bool optimize_semijoin_nests(JOIN *join, table_map all_table_map)
Calculate temporary table parameters and usage costs Calculate temporary table parameters and usage costs
*/ */
uint rowlen= get_tmp_table_rec_length(right_expr_list); uint rowlen= get_tmp_table_rec_length(right_expr_list);
double lookup_cost; double lookup_cost= get_tmp_table_lookup_cost(join->thd,
if (rowlen * subjoin_out_rows< join->thd->variables.max_heap_table_size) subjoin_out_rows, rowlen);
lookup_cost= HEAP_TEMPTABLE_LOOKUP_COST; double write_cost= get_tmp_table_write_cost(join->thd,
else subjoin_out_rows, rowlen);
lookup_cost= DISK_TEMPTABLE_LOOKUP_COST;
/* /*
Let materialization cost include the cost to write the data into the Let materialization cost include the cost to write the data into the
temporary table: temporary table:
*/ */
sjm->materialization_cost.add_io(subjoin_out_rows, lookup_cost); sjm->materialization_cost.add_io(subjoin_out_rows, write_cost);
/* /*
Set the cost to do a full scan of the temptable (will need this to Set the cost to do a full scan of the temptable (will need this to
...@@ -1338,6 +1338,49 @@ static uint get_tmp_table_rec_length(List<Item> &items) ...@@ -1338,6 +1338,49 @@ static uint get_tmp_table_rec_length(List<Item> &items)
return len; return len;
} }
/**
The cost of a lookup into a unique hash/btree index on a temporary table
with 'row_count' rows each of size 'row_size'.
@param thd current query context
@param row_count number of rows in the temp table
@param row_size average size in bytes of the rows
@return the cost of one lookup
*/
static double get_tmp_table_lookup_cost(THD *thd, ha_rows row_count, uint row_size)
{
if (row_count * row_size > thd->variables.max_heap_table_size)
return (double) DISK_TEMPTABLE_LOOKUP_COST;
else
return (double) HEAP_TEMPTABLE_LOOKUP_COST;
}
/**
The cost of writing a row into a temporary table with 'row_count' unique
rows each of size 'row_size'.
@param thd current query context
@param row_count number of rows in the temp table
@param row_size average size in bytes of the rows
@return the cost of writing one row
*/
static double get_tmp_table_write_cost(THD *thd, ha_rows row_count, uint row_size)
{
double lookup_cost= get_tmp_table_lookup_cost(thd, row_count, row_size);
/*
TODO:
This is an optimistic estimate. Add additional costs resulting from
actually writing the row to memory/disk and possible index reorganization.
*/
return lookup_cost;
}
//psergey-todo: is the below a kind of table elimination?? //psergey-todo: is the below a kind of table elimination??
/* /*
Check if table's KEYUSE elements have an eq_ref(outer_tables) candidate Check if table's KEYUSE elements have an eq_ref(outer_tables) candidate
...@@ -1867,15 +1910,15 @@ void advance_sj_state(JOIN *join, table_map remaining_tables, ...@@ -1867,15 +1910,15 @@ void advance_sj_state(JOIN *join, table_map remaining_tables,
- sj_inner_fanout*sj_outer_fanout lookups. - sj_inner_fanout*sj_outer_fanout lookups.
*/ */
double one_lookup_cost; double one_lookup_cost= get_tmp_table_lookup_cost(join->thd,
if (sj_outer_fanout*temptable_rec_size > sj_outer_fanout,
join->thd->variables.max_heap_table_size) temptable_rec_size);
one_lookup_cost= DISK_TEMPTABLE_LOOKUP_COST; double one_write_cost= get_tmp_table_write_cost(join->thd,
else sj_outer_fanout,
one_lookup_cost= HEAP_TEMPTABLE_LOOKUP_COST; temptable_rec_size);
double write_cost= join->positions[first_tab].prefix_record_count* double write_cost= join->positions[first_tab].prefix_record_count*
sj_outer_fanout * one_lookup_cost; sj_outer_fanout * one_write_cost;
double full_lookup_cost= join->positions[first_tab].prefix_record_count* double full_lookup_cost= join->positions[first_tab].prefix_record_count*
sj_outer_fanout* sj_inner_fanout * sj_outer_fanout* sj_inner_fanout *
one_lookup_cost; one_lookup_cost;
...@@ -3611,12 +3654,7 @@ bool JOIN::optimize_unflattened_subqueries() ...@@ -3611,12 +3654,7 @@ bool JOIN::optimize_unflattened_subqueries()
bool JOIN::choose_subquery_plan(table_map join_tables) bool JOIN::choose_subquery_plan(table_map join_tables)
{ /* The original QEP of the subquery. */ { /* The original QEP of the subquery. */
DYNAMIC_ARRAY save_keyuse; /* Copy of the JOIN::keyuse array. */ Query_plan_state save_qep;
POSITION save_best_positions[MAX_TABLES+1]; /* Copy of JOIN::best_positions */
/* Copies of the JOIN_TAB::keyuse pointers for each JOIN_TAB. */
KEYUSE *save_join_tab_keyuse[MAX_TABLES];
/* Copies of JOIN_TAB::checked_keys for each JOIN_TAB. */
key_map save_join_tab_checked_keys[MAX_TABLES];
enum_reopt_result reopt_result= REOPT_NONE; enum_reopt_result reopt_result= REOPT_NONE;
Item_in_subselect *in_subs; Item_in_subselect *in_subs;
...@@ -3635,9 +3673,6 @@ bool JOIN::choose_subquery_plan(table_map join_tables) ...@@ -3635,9 +3673,6 @@ bool JOIN::choose_subquery_plan(table_map join_tables)
DBUG_ASSERT(!in_to_exists_where || in_to_exists_where->fixed); DBUG_ASSERT(!in_to_exists_where || in_to_exists_where->fixed);
DBUG_ASSERT(!in_to_exists_having || in_to_exists_having->fixed); DBUG_ASSERT(!in_to_exists_having || in_to_exists_having->fixed);
save_keyuse.elements= 0;
save_keyuse.buffer= NULL;
/* /*
Compute and compare the costs of materialization and in-exists if both Compute and compare the costs of materialization and in-exists if both
strategies are possible and allowed by the user (checked during the prepare strategies are possible and allowed by the user (checked during the prepare
...@@ -3660,8 +3695,19 @@ bool JOIN::choose_subquery_plan(table_map join_tables) ...@@ -3660,8 +3695,19 @@ bool JOIN::choose_subquery_plan(table_map join_tables)
double in_exists_strategy_cost; double in_exists_strategy_cost;
if (outer_join) if (outer_join)
outer_join->get_partial_join_cost(outer_join->tables, {
/*
Make_cond_for_table is called for predicates only in the WHERE/ON
clauses. In all other cases, predicates are not pushed to any
JOIN_TAB, and their joi_tab_idx remains MAX_TABLES. Such predicates
are evaluated for each complete row.
*/
uint partial_plan_len= (in_subs->get_join_tab_idx() == MAX_TABLES) ?
outer_join->tables :
in_subs->get_join_tab_idx() + 1;
outer_join->get_partial_join_cost(partial_plan_len,
&outer_read_time, &outer_record_count); &outer_read_time, &outer_record_count);
}
else else
{ {
/* /*
...@@ -3673,7 +3719,10 @@ bool JOIN::choose_subquery_plan(table_map join_tables) ...@@ -3673,7 +3719,10 @@ bool JOIN::choose_subquery_plan(table_map join_tables)
} }
inner_join->get_partial_join_cost(inner_join->tables, inner_join->get_partial_join_cost(inner_join->tables,
&inner_read_time_1, &inner_record_count_1); &inner_read_time_1,
&inner_record_count_1);
/* inner_read_time_1 above is a dummy, get the correct total join cost. */
inner_read_time_1= inner_join->best_read;
if (in_to_exists_where && const_tables != tables) if (in_to_exists_where && const_tables != tables)
{ {
...@@ -3681,18 +3730,16 @@ bool JOIN::choose_subquery_plan(table_map join_tables) ...@@ -3681,18 +3730,16 @@ bool JOIN::choose_subquery_plan(table_map join_tables)
Re-optimize and cost the subquery taking into account the IN-EXISTS Re-optimize and cost the subquery taking into account the IN-EXISTS
conditions. conditions.
*/ */
if (save_query_plan(&save_keyuse, save_best_positions, reopt_result= reoptimize(in_to_exists_where, join_tables, &save_qep);
save_join_tab_keyuse, save_join_tab_checked_keys)) if (reopt_result == REOPT_ERROR)
return TRUE;
reopt_result= reoptimize(in_to_exists_where, join_tables);
if (reopt_result == REOPT_OLD_PLAN)
restore_query_plan(&save_keyuse, save_best_positions,
save_join_tab_keyuse, save_join_tab_checked_keys);
else if (reopt_result == REOPT_ERROR)
return TRUE; return TRUE;
inner_join->get_partial_join_cost(inner_join->tables, inner_join->get_partial_join_cost(inner_join->tables,
&inner_read_time_2, &inner_record_count_2); &inner_read_time_2,
&inner_record_count_2);
/* inner_read_time_2 above is a dummy, get the correct total join cost. */
inner_read_time_2= inner_join->best_read;
} }
else else
{ {
...@@ -3705,24 +3752,20 @@ bool JOIN::choose_subquery_plan(table_map join_tables) ...@@ -3705,24 +3752,20 @@ bool JOIN::choose_subquery_plan(table_map join_tables)
/* /*
1. Compute the cost of the materialization strategy. 1. Compute the cost of the materialization strategy.
*/ */
double materialization_cost; /* The cost of executing the subquery and */ uint rowlen= get_tmp_table_rec_length(unit->first_select()->item_list);
/* storing its result in an indexed temp table.*/ /* The cost of writing one row into the temporary table. */
double write_cost= get_tmp_table_write_cost(thd, inner_record_count_1,
rowlen);
/* The cost of a lookup into the unique index of the materialized table. */ /* The cost of a lookup into the unique index of the materialized table. */
double lookup_cost; double lookup_cost= get_tmp_table_lookup_cost(thd, inner_record_count_1,
double write_row_cost= 1; /* TODO: what is the real cost to write a row? */ rowlen);
materialization_cost= inner_read_time_1 +
inner_record_count_1 * write_row_cost;
/* /*
The cost of a hash/btree lookup into a unique index of a materialized The cost of executing the subquery and storing its result in an indexed
subquery. temporary table.
TIMOUR: TODO: the block of code below is exact copy/paste from
opt_subselect.cc:optimize_semi_join_nests() - refactor it.
*/ */
uint rowlen= get_tmp_table_rec_length(unit->first_select()->item_list); double materialization_cost= inner_read_time_1 +
if (rowlen * inner_record_count_1 < thd->variables.max_heap_table_size) write_cost * inner_record_count_1;
lookup_cost= HEAP_TEMPTABLE_LOOKUP_COST;
else
lookup_cost= DISK_TEMPTABLE_LOOKUP_COST;
materialize_strategy_cost= materialization_cost + materialize_strategy_cost= materialization_cost +
outer_record_count * lookup_cost; outer_record_count * lookup_cost;
...@@ -3764,8 +3807,7 @@ bool JOIN::choose_subquery_plan(table_map join_tables) ...@@ -3764,8 +3807,7 @@ bool JOIN::choose_subquery_plan(table_map join_tables)
{ {
/* Restore the original query plan used for materialization. */ /* Restore the original query plan used for materialization. */
if (reopt_result == REOPT_NEW_PLAN) if (reopt_result == REOPT_NEW_PLAN)
restore_query_plan(&save_keyuse, save_best_positions, restore_query_plan(&save_qep);
save_join_tab_keyuse, save_join_tab_checked_keys);
/* TODO: should we set/unset this flag for both select_lex and its unit? */ /* TODO: should we set/unset this flag for both select_lex and its unit? */
in_subs->unit->uncacheable&= ~UNCACHEABLE_DEPENDENT; in_subs->unit->uncacheable&= ~UNCACHEABLE_DEPENDENT;
...@@ -3789,11 +3831,8 @@ bool JOIN::choose_subquery_plan(table_map join_tables) ...@@ -3789,11 +3831,8 @@ bool JOIN::choose_subquery_plan(table_map join_tables)
} }
else if (in_subs->in_strategy & SUBS_IN_TO_EXISTS) else if (in_subs->in_strategy & SUBS_IN_TO_EXISTS)
{ {
/* Keep the new query plan with injected conditions, delete the old plan. */ if (reopt_result == REOPT_NONE && in_to_exists_where &&
if (reopt_result == REOPT_NEW_PLAN) const_tables != tables)
delete_dynamic(&save_keyuse);
if (reopt_result == REOPT_NONE && in_to_exists_where && const_tables != tables)
{ {
/* /*
The subquery was not reoptimized either because the user allowed only the The subquery was not reoptimized either because the user allowed only the
...@@ -3805,7 +3844,7 @@ bool JOIN::choose_subquery_plan(table_map join_tables) ...@@ -3805,7 +3844,7 @@ bool JOIN::choose_subquery_plan(table_map join_tables)
join_tab[i].keyuse= NULL; join_tab[i].keyuse= NULL;
join_tab[i].checked_keys.clear_all(); join_tab[i].checked_keys.clear_all();
} }
if ((reopt_result= reoptimize(in_to_exists_where, join_tables)) == if ((reopt_result= reoptimize(in_to_exists_where, join_tables, NULL)) ==
REOPT_ERROR) REOPT_ERROR)
return TRUE; return TRUE;
} }
......
...@@ -179,10 +179,12 @@ int join_read_always_key_or_null(JOIN_TAB *tab); ...@@ -179,10 +179,12 @@ int join_read_always_key_or_null(JOIN_TAB *tab);
int join_read_next_same_or_null(READ_RECORD *info); int join_read_next_same_or_null(READ_RECORD *info);
static COND *make_cond_for_table(Item *cond,table_map table, static COND *make_cond_for_table(Item *cond,table_map table,
table_map used_table, table_map used_table,
uint join_tab_idx_arg,
bool exclude_expensive_cond); bool exclude_expensive_cond);
static COND *make_cond_for_table_from_pred(Item *root_cond, Item *cond, static COND *make_cond_for_table_from_pred(Item *root_cond, Item *cond,
table_map tables, table_map tables,
table_map used_table, table_map used_table,
uint join_tab_idx_arg,
bool exclude_expensive_cond); bool exclude_expensive_cond);
static Item* part_of_refkey(TABLE *form,Field *field); static Item* part_of_refkey(TABLE *form,Field *field);
...@@ -919,7 +921,7 @@ JOIN::optimize() ...@@ -919,7 +921,7 @@ JOIN::optimize()
if (conds && !(thd->lex->describe & DESCRIBE_EXTENDED)) if (conds && !(thd->lex->describe & DESCRIBE_EXTENDED))
{ {
COND *table_independent_conds= COND *table_independent_conds=
make_cond_for_table(conds, PSEUDO_TABLE_BITS, 0, FALSE); make_cond_for_table(conds, PSEUDO_TABLE_BITS, 0, MAX_TABLES, FALSE);
DBUG_EXECUTE("where", DBUG_EXECUTE("where",
print_where(table_independent_conds, print_where(table_independent_conds,
"where after opt_sum_query()", "where after opt_sum_query()",
...@@ -2235,7 +2237,8 @@ JOIN::exec() ...@@ -2235,7 +2237,8 @@ JOIN::exec()
Item* sort_table_cond= make_cond_for_table(curr_join->tmp_having, Item* sort_table_cond= make_cond_for_table(curr_join->tmp_having,
used_tables, used_tables,
(table_map)0, FALSE); (table_map)0,
MAX_TABLES, FALSE);
if (sort_table_cond) if (sort_table_cond)
{ {
if (!curr_table->select) if (!curr_table->select)
...@@ -2266,7 +2269,8 @@ JOIN::exec() ...@@ -2266,7 +2269,8 @@ JOIN::exec()
QT_ORDINARY);); QT_ORDINARY););
curr_join->tmp_having= make_cond_for_table(curr_join->tmp_having, curr_join->tmp_having= make_cond_for_table(curr_join->tmp_having,
~ (table_map) 0, ~ (table_map) 0,
~used_tables, FALSE); ~used_tables,
MAX_TABLES, FALSE);
DBUG_EXECUTE("where",print_where(curr_join->tmp_having, DBUG_EXECUTE("where",print_where(curr_join->tmp_having,
"having after sort", "having after sort",
QT_ORDINARY);); QT_ORDINARY););
...@@ -5483,6 +5487,8 @@ void JOIN::get_partial_join_cost(uint n_tables, ...@@ -5483,6 +5487,8 @@ void JOIN::get_partial_join_cost(uint n_tables,
double record_count= 1; double record_count= 1;
double read_time= 0.0; double read_time= 0.0;
DBUG_ASSERT(n_tables <= tables && n_tables > 0);
for (uint i= const_tables; i < n_tables; i++) for (uint i= const_tables; i < n_tables; i++)
{ {
if (best_positions[i].records_read) if (best_positions[i].records_read)
...@@ -6674,7 +6680,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) ...@@ -6674,7 +6680,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
join->exec_const_cond= join->exec_const_cond=
make_cond_for_table(cond, make_cond_for_table(cond,
join->const_table_map, join->const_table_map,
(table_map) 0, FALSE); (table_map) 0, MAX_TABLES, FALSE);
DBUG_EXECUTE("where",print_where(join->exec_const_cond, "constants", DBUG_EXECUTE("where",print_where(join->exec_const_cond, "constants",
QT_ORDINARY);); QT_ORDINARY););
for (JOIN_TAB *tab= join->join_tab+join->const_tables; for (JOIN_TAB *tab= join->join_tab+join->const_tables;
...@@ -6685,7 +6691,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) ...@@ -6685,7 +6691,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
JOIN_TAB *cond_tab= tab->first_inner; JOIN_TAB *cond_tab= tab->first_inner;
COND *tmp= make_cond_for_table(*tab->on_expr_ref, COND *tmp= make_cond_for_table(*tab->on_expr_ref,
join->const_table_map, join->const_table_map,
(table_map) 0, FALSE); (table_map) 0, MAX_TABLES, FALSE);
if (!tmp) if (!tmp)
continue; continue;
tmp= new Item_func_trig_cond(tmp, &cond_tab->not_null_compl); tmp= new Item_func_trig_cond(tmp, &cond_tab->not_null_compl);
...@@ -6776,7 +6782,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) ...@@ -6776,7 +6782,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
tmp= NULL; tmp= NULL;
if (cond) if (cond)
tmp= make_cond_for_table(cond, used_tables, current_map, FALSE); tmp= make_cond_for_table(cond, used_tables, current_map, i, FALSE);
if (cond && !tmp && tab->quick) if (cond && !tmp && tab->quick)
{ // Outer join { // Outer join
if (tab->type != JT_ALL) if (tab->type != JT_ALL)
...@@ -6834,7 +6840,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) ...@@ -6834,7 +6840,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
if (thd->variables.engine_condition_pushdown && !first_inner_tab) if (thd->variables.engine_condition_pushdown && !first_inner_tab)
{ {
COND *push_cond= COND *push_cond=
make_cond_for_table(tmp, current_map, current_map, FALSE); make_cond_for_table(tmp, current_map, current_map, MAX_TABLES, FALSE);
if (push_cond) if (push_cond)
{ {
/* Push condition to handler */ /* Push condition to handler */
...@@ -6965,7 +6971,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) ...@@ -6965,7 +6971,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
(tmp=make_cond_for_table(cond, (tmp=make_cond_for_table(cond,
join->const_table_map | join->const_table_map |
current_map, current_map,
current_map, FALSE))) current_map, MAX_TABLES, FALSE)))
{ {
DBUG_EXECUTE("where",print_where(tmp,"cache", QT_ORDINARY);); DBUG_EXECUTE("where",print_where(tmp,"cache", QT_ORDINARY););
tab->cache_select=(SQL_SELECT*) tab->cache_select=(SQL_SELECT*)
...@@ -6995,7 +7001,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) ...@@ -6995,7 +7001,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
JOIN_TAB *cond_tab= join_tab->first_inner; JOIN_TAB *cond_tab= join_tab->first_inner;
COND *tmp= make_cond_for_table(*join_tab->on_expr_ref, COND *tmp= make_cond_for_table(*join_tab->on_expr_ref,
join->const_table_map, join->const_table_map,
(table_map) 0, FALSE); (table_map) 0, MAX_TABLES, FALSE);
if (!tmp) if (!tmp)
continue; continue;
tmp= new Item_func_trig_cond(tmp, &cond_tab->not_null_compl); tmp= new Item_func_trig_cond(tmp, &cond_tab->not_null_compl);
...@@ -7014,6 +7020,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) ...@@ -7014,6 +7020,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
} }
/* Push down non-constant conditions from on expressions */ /* Push down non-constant conditions from on expressions */
JOIN_TAB *first_tab= join->join_tab+join->const_tables;
JOIN_TAB *last_tab= tab; JOIN_TAB *last_tab= tab;
while (first_inner_tab && first_inner_tab->last_inner == last_tab) while (first_inner_tab && first_inner_tab->last_inner == last_tab)
{ {
...@@ -7025,12 +7032,13 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) ...@@ -7025,12 +7032,13 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
table_map used_tables2= (join->const_table_map | table_map used_tables2= (join->const_table_map |
OUTER_REF_TABLE_BIT | RAND_TABLE_BIT); OUTER_REF_TABLE_BIT | RAND_TABLE_BIT);
for (tab= join->join_tab+join->const_tables; tab <= last_tab ; tab++) for (tab= first_tab; tab <= last_tab ; tab++)
{ {
current_map= tab->table->map; current_map= tab->table->map;
used_tables2|= current_map; used_tables2|= current_map;
COND *tmp_cond= make_cond_for_table(on_expr, used_tables2, COND *tmp_cond= make_cond_for_table(on_expr, used_tables2,
current_map, FALSE); current_map,
(tab - first_tab), FALSE);
if (tmp_cond) if (tmp_cond)
{ {
JOIN_TAB *cond_tab= tab < first_inner_tab ? first_inner_tab : tab; JOIN_TAB *cond_tab= tab < first_inner_tab ? first_inner_tab : tab;
...@@ -14682,6 +14690,8 @@ bool test_if_ref(Item *root_cond, Item_field *left_item,Item *right_item) ...@@ -14682,6 +14690,8 @@ bool test_if_ref(Item *root_cond, Item_field *left_item,Item *right_item)
tables Tables for which "current field values" are available tables Tables for which "current field values" are available
used_table Table that we're extracting the condition for (may used_table Table that we're extracting the condition for (may
also include PSEUDO_TABLE_BITS also include PSEUDO_TABLE_BITS
join_tab_idx_arg The index of the JOIN_TAB this Item is being extracted
for. MAX_TABLES if there is no corresponding JOIN_TAB.
exclude_expensive_cond Do not push expensive conditions exclude_expensive_cond Do not push expensive conditions
DESCRIPTION DESCRIPTION
...@@ -14708,15 +14718,18 @@ bool test_if_ref(Item *root_cond, Item_field *left_item,Item *right_item) ...@@ -14708,15 +14718,18 @@ bool test_if_ref(Item *root_cond, Item_field *left_item,Item *right_item)
static Item * static Item *
make_cond_for_table(Item *cond, table_map tables, table_map used_table, make_cond_for_table(Item *cond, table_map tables, table_map used_table,
uint join_tab_idx_arg,
bool exclude_expensive_cond __attribute__((unused))) bool exclude_expensive_cond __attribute__((unused)))
{ {
return make_cond_for_table_from_pred(cond, cond, tables, used_table, return make_cond_for_table_from_pred(cond, cond, tables, used_table,
join_tab_idx_arg,
exclude_expensive_cond); exclude_expensive_cond);
} }
static Item * static Item *
make_cond_for_table_from_pred(Item *root_cond, Item *cond, make_cond_for_table_from_pred(Item *root_cond, Item *cond,
table_map tables, table_map used_table, table_map tables, table_map used_table,
uint join_tab_idx_arg,
bool exclude_expensive_cond __attribute__((unused))) bool exclude_expensive_cond __attribute__((unused)))
{ {
if (cond->type() == Item::COND_ITEM) if (cond->type() == Item::COND_ITEM)
...@@ -14733,6 +14746,7 @@ make_cond_for_table_from_pred(Item *root_cond, Item *cond, ...@@ -14733,6 +14746,7 @@ make_cond_for_table_from_pred(Item *root_cond, Item *cond,
{ {
Item *fix=make_cond_for_table_from_pred(root_cond, item, Item *fix=make_cond_for_table_from_pred(root_cond, item,
tables, used_table, tables, used_table,
join_tab_idx_arg,
exclude_expensive_cond); exclude_expensive_cond);
if (fix) if (fix)
new_cond->argument_list()->push_back(fix); new_cond->argument_list()->push_back(fix);
...@@ -14765,6 +14779,7 @@ make_cond_for_table_from_pred(Item *root_cond, Item *cond, ...@@ -14765,6 +14779,7 @@ make_cond_for_table_from_pred(Item *root_cond, Item *cond,
{ {
Item *fix=make_cond_for_table_from_pred(root_cond, item, Item *fix=make_cond_for_table_from_pred(root_cond, item,
tables, 0L, tables, 0L,
join_tab_idx_arg,
exclude_expensive_cond); exclude_expensive_cond);
if (!fix) if (!fix)
return (COND*) 0; // Always true return (COND*) 0; // Always true
...@@ -14789,7 +14804,10 @@ make_cond_for_table_from_pred(Item *root_cond, Item *cond, ...@@ -14789,7 +14804,10 @@ make_cond_for_table_from_pred(Item *root_cond, Item *cond,
if (cond->marker == 3 || (cond->used_tables() & ~tables)) if (cond->marker == 3 || (cond->used_tables() & ~tables))
return (COND*) 0; // Can't check this yet return (COND*) 0; // Can't check this yet
if (cond->marker == 2 || cond->eq_cmp_result() == Item::COND_OK) if (cond->marker == 2 || cond->eq_cmp_result() == Item::COND_OK)
{
cond->set_join_tab_idx(join_tab_idx_arg);
return cond; // Not boolean op return cond; // Not boolean op
}
if (cond->type() == Item::FUNC_ITEM && if (cond->type() == Item::FUNC_ITEM &&
((Item_func*) cond)->functype() == Item_func::EQ_FUNC) ((Item_func*) cond)->functype() == Item_func::EQ_FUNC)
...@@ -14810,6 +14828,7 @@ make_cond_for_table_from_pred(Item *root_cond, Item *cond, ...@@ -14810,6 +14828,7 @@ make_cond_for_table_from_pred(Item *root_cond, Item *cond,
} }
} }
cond->marker=2; cond->marker=2;
cond->set_join_tab_idx(join_tab_idx_arg);
return cond; return cond;
} }
...@@ -15986,7 +16005,7 @@ static bool fix_having(JOIN *join, Item **having) ...@@ -15986,7 +16005,7 @@ static bool fix_having(JOIN *join, Item **having)
DBUG_EXECUTE("where",print_where(*having,"having", QT_ORDINARY);); DBUG_EXECUTE("where",print_where(*having,"having", QT_ORDINARY););
Item* sort_table_cond=make_cond_for_table(*having, used_tables, used_tables, Item* sort_table_cond=make_cond_for_table(*having, used_tables, used_tables,
FALSE); MAX_TABLES, FALSE);
if (sort_table_cond) if (sort_table_cond)
{ {
if (!table->select) if (!table->select)
...@@ -16004,7 +16023,8 @@ static bool fix_having(JOIN *join, Item **having) ...@@ -16004,7 +16023,8 @@ static bool fix_having(JOIN *join, Item **having)
DBUG_EXECUTE("where",print_where(table->select_cond, DBUG_EXECUTE("where",print_where(table->select_cond,
"select and having", "select and having",
QT_ORDINARY);); QT_ORDINARY););
*having= make_cond_for_table(*having,~ (table_map) 0,~used_tables, FALSE); *having= make_cond_for_table(*having,~ (table_map) 0,~used_tables,
MAX_TABLES, FALSE);
DBUG_EXECUTE("where", DBUG_EXECUTE("where",
print_where(*having,"having after make_cond", QT_ORDINARY);); print_where(*having,"having after make_cond", QT_ORDINARY););
} }
...@@ -19193,7 +19213,7 @@ void TABLE_LIST::print(THD *thd, table_map eliminated_tables, String *str, ...@@ -19193,7 +19213,7 @@ void TABLE_LIST::print(THD *thd, table_map eliminated_tables, String *str,
void st_select_lex::print(THD *thd, String *str, enum_query_type query_type) void st_select_lex::print(THD *thd, String *str, enum_query_type query_type)
{ {
/* TODO: thd may not be set for sub queries, but this should be fixed */ /* TODO: thd may not be set for sub queries, but this should be fixed */
DBUG_ASSERT(thd); // DBUG_ASSERT(thd);
if (!thd) if (!thd)
thd= current_thd; thd= current_thd;
...@@ -19359,78 +19379,59 @@ bool JOIN::change_result(select_result *res) ...@@ -19359,78 +19379,59 @@ bool JOIN::change_result(select_result *res)
Save a query execution plan so that the caller can revert to it if needed, Save a query execution plan so that the caller can revert to it if needed,
and reset the current query plan so that it can be reoptimized. and reset the current query plan so that it can be reoptimized.
@param save_keyuse[out] a KEYUSE array to save JOIN::keyuse @param save_to The object into which the current query plan state is saved
@param save_best_positions[out] array to save JOIN::best_positions
@param save_join_tab_keyuse[out] array of KEYUSE pointers to save each
JOIN_TAB::keyuse pointer
@param save_join_tab_checked_keys[out] an array of bitmaps to save
each JOIN_TAB::checked_keys
@retval 0 OK
@retval 1 memory allocation error
*/ */
int JOIN::save_query_plan(DYNAMIC_ARRAY *save_keyuse,
POSITION *save_best_positions, void JOIN::save_query_plan(Query_plan_state *save_to)
KEYUSE **save_join_tab_keyuse,
key_map *save_join_tab_checked_keys)
{ {
if (keyuse.elements) if (keyuse.elements)
{ {
DYNAMIC_ARRAY tmp_keyuse; DYNAMIC_ARRAY tmp_keyuse;
if (my_init_dynamic_array(save_keyuse, sizeof(KEYUSE), 20, 64)) // TODO: isn't this allocated by update_ref_and_keys
return 1; //if (my_init_dynamic_array(save_keyuse, sizeof(KEYUSE), 20, 64))
// return 1;
/* Swap the current and the backup keyuse arrays. */ /* Swap the current and the backup keyuse arrays. */
tmp_keyuse= keyuse; tmp_keyuse= keyuse;
keyuse= (*save_keyuse); keyuse= save_to->keyuse;
(*save_keyuse)= tmp_keyuse; save_to->keyuse= tmp_keyuse;
for (uint i= 0; i < tables; i++) for (uint i= 0; i < tables; i++)
{ {
save_join_tab_keyuse[i]= join_tab[i].keyuse; save_to->join_tab_keyuse[i]= join_tab[i].keyuse;
join_tab[i].keyuse= NULL; join_tab[i].keyuse= NULL;
save_join_tab_checked_keys[i]= join_tab[i].checked_keys; save_to->join_tab_checked_keys[i]= join_tab[i].checked_keys;
join_tab[i].checked_keys.clear_all(); join_tab[i].checked_keys.clear_all();
} }
} }
memcpy((uchar*) save_best_positions, (uchar*) best_positions, memcpy((uchar*) save_to->best_positions, (uchar*) best_positions,
sizeof(POSITION) * (tables + 1)); sizeof(POSITION) * (tables + 1));
memset(best_positions, 0, sizeof(POSITION) * (tables + 1)); memset(best_positions, 0, sizeof(POSITION) * (tables + 1));
return 0;
} }
/** /**
Restore a query plan previously saved by the caller. Restore a query execution plan previously saved by the caller.
@param save_keyuse a KEYUSE array to restore into JOIN::keyuse @param The object from which the current query plan state is restored.
@param save_best_positions array to restore into JOIN::best_positions
@param save_join_tab_keyuse array of KEYUSE pointers to restore each
JOIN_TAB::keyuse pointer
@param save_join_tab_checked_keys an array of bitmaps to restore
each JOIN_TAB::checked_keys
*/ */
void JOIN::restore_query_plan(DYNAMIC_ARRAY *save_keyuse, void JOIN::restore_query_plan(Query_plan_state *restore_from)
POSITION *save_best_positions,
KEYUSE **save_join_tab_keyuse,
key_map *save_join_tab_checked_keys)
{ {
if (save_keyuse->elements) if (restore_from->keyuse.elements)
{ {
DYNAMIC_ARRAY tmp_keyuse; DYNAMIC_ARRAY tmp_keyuse;
tmp_keyuse= keyuse; tmp_keyuse= keyuse;
keyuse= (*save_keyuse); keyuse= restore_from->keyuse;
(*save_keyuse)= tmp_keyuse; restore_from->keyuse= tmp_keyuse;
delete_dynamic(save_keyuse);
for (uint i= 0; i < tables; i++) for (uint i= 0; i < tables; i++)
{ {
join_tab[i].keyuse= save_join_tab_keyuse[i]; join_tab[i].keyuse= restore_from->join_tab_keyuse[i];
join_tab[i].checked_keys= save_join_tab_checked_keys[i]; join_tab[i].checked_keys= restore_from->join_tab_checked_keys[i];
} }
} }
memcpy((uchar*) best_positions, (uchar*) save_best_positions, memcpy((uchar*) best_positions, (uchar*) restore_from->best_positions,
sizeof(POSITION) * (tables + 1)); sizeof(POSITION) * (tables + 1));
} }
...@@ -19441,8 +19442,7 @@ void JOIN::restore_query_plan(DYNAMIC_ARRAY *save_keyuse, ...@@ -19441,8 +19442,7 @@ void JOIN::restore_query_plan(DYNAMIC_ARRAY *save_keyuse,
@param added_where An extra conjunct to the WHERE clause to reoptimize with @param added_where An extra conjunct to the WHERE clause to reoptimize with
@param join_tables The set of tables to reoptimize @param join_tables The set of tables to reoptimize
@param save_best_positions The join order of the original plan to restore to @param save_to If != NULL, save here the state of the current query plan
if needed.
@notes @notes
Given a query plan that already optimized taking into account some WHERE clause Given a query plan that already optimized taking into account some WHERE clause
...@@ -19462,7 +19462,9 @@ void JOIN::restore_query_plan(DYNAMIC_ARRAY *save_keyuse, ...@@ -19462,7 +19462,9 @@ void JOIN::restore_query_plan(DYNAMIC_ARRAY *save_keyuse,
@retval REOPT_ERROR an irrecovarable error occured during reoptimization. @retval REOPT_ERROR an irrecovarable error occured during reoptimization.
*/ */
JOIN::enum_reopt_result JOIN::reoptimize(Item *added_where, table_map join_tables) JOIN::enum_reopt_result
JOIN::reoptimize(Item *added_where, table_map join_tables,
Query_plan_state *save_to)
{ {
DYNAMIC_ARRAY added_keyuse; DYNAMIC_ARRAY added_keyuse;
SARGABLE_PARAM *sargables= 0; /* Used only as a dummy parameter. */ SARGABLE_PARAM *sargables= 0; /* Used only as a dummy parameter. */
...@@ -19478,6 +19480,9 @@ JOIN::enum_reopt_result JOIN::reoptimize(Item *added_where, table_map join_table ...@@ -19478,6 +19480,9 @@ JOIN::enum_reopt_result JOIN::reoptimize(Item *added_where, table_map join_table
if (!added_keyuse.elements) if (!added_keyuse.elements)
return REOPT_OLD_PLAN; return REOPT_OLD_PLAN;
if (save_to)
save_query_plan(save_to);
/* Add the new access methods to the keyuse array. */ /* Add the new access methods to the keyuse array. */
if (!keyuse.buffer && if (!keyuse.buffer &&
my_init_dynamic_array(&keyuse, sizeof(KEYUSE), 20, 64)) my_init_dynamic_array(&keyuse, sizeof(KEYUSE), 20, 64))
......
...@@ -1374,6 +1374,31 @@ class JOIN :public Sql_alloc ...@@ -1374,6 +1374,31 @@ class JOIN :public Sql_alloc
JOIN& operator=(const JOIN &rhs); /**< not implemented */ JOIN& operator=(const JOIN &rhs); /**< not implemented */
protected: protected:
/**
???
*/
class Query_plan_state {
public:
DYNAMIC_ARRAY keyuse; /* Copy of the JOIN::keyuse array. */
POSITION best_positions[MAX_TABLES+1]; /* Copy of JOIN::best_positions */
/* Copies of the JOIN_TAB::keyuse pointers for each JOIN_TAB. */
KEYUSE *join_tab_keyuse[MAX_TABLES];
/* Copies of JOIN_TAB::checked_keys for each JOIN_TAB. */
key_map join_tab_checked_keys[MAX_TABLES];
public:
Query_plan_state()
{
keyuse.elements= 0;
keyuse.buffer= NULL;
}
Query_plan_state(JOIN *join);
~Query_plan_state()
{
delete_dynamic(&keyuse);
}
};
/* Results of reoptimizing a JOIN via JOIN::reoptimize(). */ /* Results of reoptimizing a JOIN via JOIN::reoptimize(). */
enum enum_reopt_result { enum enum_reopt_result {
REOPT_NEW_PLAN, /* there is a new reoptimized plan */ REOPT_NEW_PLAN, /* there is a new reoptimized plan */
...@@ -1383,13 +1408,10 @@ class JOIN :public Sql_alloc ...@@ -1383,13 +1408,10 @@ class JOIN :public Sql_alloc
}; };
/* Support for plan reoptimization with rewritten conditions. */ /* Support for plan reoptimization with rewritten conditions. */
enum_reopt_result reoptimize(Item *added_where, table_map join_tables); enum_reopt_result reoptimize(Item *added_where, table_map join_tables,
int save_query_plan(DYNAMIC_ARRAY *save_keyuse, POSITION *save_positions, Query_plan_state *save_to);
KEYUSE **save_join_tab_keyuse, void save_query_plan(Query_plan_state *save_to);
key_map *save_join_tab_checked_keys); void restore_query_plan(Query_plan_state *restore_from);
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. */ /* Choose a subquery plan for a table-less subquery. */
bool choose_tableless_subquery_plan(); bool choose_tableless_subquery_plan();
......
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