Commit 78ddd9ff authored by unknown's avatar unknown

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

Step1 in the separation of the creation of IN->EXISTS equi-join conditions from
their injection. The goal of this separation is to make it possible that the
IN->EXISTS conditions can be used for cost estimation without actually modifying
the subquery.

This patch separates single_value_in_to_exists_transformer() into two methods:
- create_single_value_in_to_exists_cond(), and
- inject_single_value_in_to_exists_cond()
The patch performs minimal refactoring of the code so that it is easier to solve
problems resulting from the separation. There is a lot to be simplified in this
code, but this will be done separately.
parent 0d35bddf
...@@ -1521,16 +1521,35 @@ Item_in_subselect::single_value_transformer(JOIN *join, ...@@ -1521,16 +1521,35 @@ Item_in_subselect::single_value_transformer(JOIN *join,
*/ */
Item_subselect::trans_res Item_subselect::trans_res
Item_in_subselect::single_value_in_to_exists_transformer(JOIN * join, Comp_creator *func) Item_in_subselect::single_value_in_to_exists_transformer(JOIN * join,
Comp_creator *func)
{
Item *where_term;
Item *having_term;
Item_subselect::trans_res res;
res= create_single_value_in_to_exists_cond(join, func,
&where_term, &having_term);
if (res != RES_OK)
return res;
res= inject_single_value_in_to_exists_cond(join, func,
where_term, having_term);
return res;
}
Item_subselect::trans_res
Item_in_subselect::create_single_value_in_to_exists_cond(JOIN * join,
Comp_creator *func,
Item **where_term,
Item **having_term)
{ {
SELECT_LEX *select_lex= join->select_lex; SELECT_LEX *select_lex= join->select_lex;
DBUG_ENTER("Item_in_subselect::single_value_in_to_exists_transformer"); DBUG_ENTER("Item_in_subselect::create_single_value_in_to_exists_cond");
select_lex->uncacheable|= UNCACHEABLE_DEPENDENT;
if (join->having || select_lex->with_sum_func || if (join->having || select_lex->with_sum_func ||
select_lex->group_list.elements) select_lex->group_list.elements)
{ {
bool tmp;
Item *item= func->create(expr, Item *item= func->create(expr,
new Item_ref_null_helper(&select_lex->context, new Item_ref_null_helper(&select_lex->context,
this, this,
...@@ -1546,132 +1565,199 @@ Item_in_subselect::single_value_in_to_exists_transformer(JOIN * join, Comp_creat ...@@ -1546,132 +1565,199 @@ Item_in_subselect::single_value_in_to_exists_transformer(JOIN * join, Comp_creat
*/ */
item= new Item_func_trig_cond(item, get_cond_guard(0)); item= new Item_func_trig_cond(item, get_cond_guard(0));
} }
if (item->fix_fields(thd, 0))
DBUG_RETURN(RES_ERROR);
*having_term= item;
*where_term= NULL;
}
else
{
Item *item= (Item*) select_lex->item_list.head();
if (select_lex->table_list.elements)
{
Item *having= item;
Item *orig_item= item;
item= func->create(expr, item);
if (!abort_on_null && orig_item->maybe_null)
{
having= new Item_is_not_null_test(this, having);
if (left_expr->maybe_null)
{
if (!(having= new Item_func_trig_cond(having,
get_cond_guard(0))))
DBUG_RETURN(RES_ERROR);
}
if (having->fix_fields(thd, 0))
DBUG_RETURN(RES_ERROR);
*having_term= having;
item= new Item_cond_or(item,
new Item_func_isnull(orig_item));
}
/*
If we may encounter NULL IN (SELECT ...) and care whether subquery
result is NULL or FALSE, wrap condition in a trig_cond.
*/
if (!abort_on_null && left_expr->maybe_null)
{
if (!(item= new Item_func_trig_cond(item, get_cond_guard(0))))
DBUG_RETURN(RES_ERROR);
}
if (item->fix_fields(thd, 0))
DBUG_RETURN(RES_ERROR);
*where_term= item;
}
else
{
if (select_lex->master_unit()->is_union())
{
/*
comparison functions can't be changed during fix_fields()
we can assign select_lex->having here, and pass 0 as last
argument (reference) to fix_fields()
*/
Item *new_having=
func->create(expr,
new Item_ref_null_helper(&select_lex->context, this,
select_lex->ref_pointer_array,
(char *)"<no matter>",
(char *)"<result>"));
if (!abort_on_null && left_expr->maybe_null)
{
if (!(new_having= new Item_func_trig_cond(new_having,
get_cond_guard(0))))
DBUG_RETURN(RES_ERROR);
}
if (new_having->fix_fields(thd, 0))
DBUG_RETURN(RES_ERROR);
*having_term= new_having;
*where_term= NULL;
}
else
{
*having_term= NULL;
*where_term= (Item*) select_lex->item_list.head();
}
}
}
DBUG_RETURN(RES_OK);
}
Item_subselect::trans_res
Item_in_subselect::inject_single_value_in_to_exists_cond(JOIN * join,
Comp_creator *func,
Item *where_term,
Item *having_term)
{
SELECT_LEX *select_lex= join->select_lex;
bool fix_res;
DBUG_ENTER("Item_in_subselect::single_value_in_to_exists_transformer");
select_lex->uncacheable|= UNCACHEABLE_DEPENDENT;
if (join->having || select_lex->with_sum_func ||
select_lex->group_list.elements)
{
/* /*
AND and comparison functions can't be changed during fix_fields() AND and comparison functions can't be changed during fix_fields()
we can assign select_lex->having here, and pass 0 as last we can assign select_lex->having here, and pass 0 as last
argument (reference) to fix_fields() argument (reference) to fix_fields()
*/ */
select_lex->having= join->having= and_items(join->having, item); select_lex->having= join->having= and_items(join->having, having_term);
if (join->having == item) if (join->having == having_term)
item->name= (char*)in_having_cond; having_term->name= (char*)in_having_cond;
select_lex->having_fix_field= 1; select_lex->having_fix_field= 1;
/* /*
we do not check join->having->fixed, because Item_and (from and_items) we do not check join->having->fixed, because Item_and (from and_items)
or comparison function (from func->create) can't be fixed after creation or comparison function (from func->create) can't be fixed after creation
*/ */
tmp= join->having->fix_fields(thd, 0); if (!join->having->fixed)
fix_res= join->having->fix_fields(thd, 0);
select_lex->having_fix_field= 0; select_lex->having_fix_field= 0;
if (tmp) if (fix_res)
DBUG_RETURN(RES_ERROR); DBUG_RETURN(RES_ERROR);
} }
else else
{ {
Item *item= (Item*) select_lex->item_list.head();
if (select_lex->table_list.elements) if (select_lex->table_list.elements)
{ {
bool tmp; Item *orig_item= (Item*) select_lex->item_list.head();
Item *having= item, *orig_item= item;
select_lex->item_list.empty(); select_lex->item_list.empty();
select_lex->item_list.push_back(new Item_int("Not_used", select_lex->item_list.push_back(new Item_int("Not_used",
(longlong) 1, (longlong) 1,
MY_INT64_NUM_DECIMAL_DIGITS)); MY_INT64_NUM_DECIMAL_DIGITS));
select_lex->ref_pointer_array[0]= select_lex->item_list.head(); select_lex->ref_pointer_array[0]= select_lex->item_list.head();
item= func->create(expr, item);
if (!abort_on_null && orig_item->maybe_null) if (!abort_on_null && orig_item->maybe_null)
{ {
having= new Item_is_not_null_test(this, having);
if (left_expr->maybe_null)
{
if (!(having= new Item_func_trig_cond(having,
get_cond_guard(0))))
DBUG_RETURN(RES_ERROR);
}
/* /*
Item_is_not_null_test can't be changed during fix_fields() Item_is_not_null_test can't be changed during fix_fields()
we can assign select_lex->having here, and pass 0 as last we can assign select_lex->having here, and pass 0 as last
argument (reference) to fix_fields() argument (reference) to fix_fields()
*/ */
having->name= (char*)in_having_cond; having_term->name= (char*)in_having_cond;
select_lex->having= join->having= having; select_lex->having= join->having= having_term;
select_lex->having_fix_field= 1; select_lex->having_fix_field= 1;
/* /*
we do not check join->having->fixed, because Item_and (from we do not check join->having->fixed, because Item_and (from
and_items) or comparison function (from func->create) can't be and_items) or comparison function (from func->create) can't be
fixed after creation fixed after creation
*/ */
tmp= join->having->fix_fields(thd, 0); if (!join->having->fixed)
fix_res= join->having->fix_fields(thd, 0);
select_lex->having_fix_field= 0; select_lex->having_fix_field= 0;
if (tmp) if (fix_res)
DBUG_RETURN(RES_ERROR); DBUG_RETURN(RES_ERROR);
item= new Item_cond_or(item,
new Item_func_isnull(orig_item));
}
/*
If we may encounter NULL IN (SELECT ...) and care whether subquery
result is NULL or FALSE, wrap condition in a trig_cond.
*/
if (!abort_on_null && left_expr->maybe_null)
{
if (!(item= new Item_func_trig_cond(item, get_cond_guard(0))))
DBUG_RETURN(RES_ERROR);
} }
/* /*
TODO: figure out why the following is done here in TODO: figure out why the following is done here in
single_value_transformer but there is no corresponding action in single_value_transformer but there is no corresponding action in
row_value_transformer? row_value_transformer?
*/ */
item->name= (char *)in_additional_cond; where_term->name= (char *)in_additional_cond;
/* /*
AND can't be changed during fix_fields() AND can't be changed during fix_fields()
we can assign select_lex->having here, and pass 0 as last we can assign select_lex->having here, and pass 0 as last
argument (reference) to fix_fields() argument (reference) to fix_fields()
*/ */
select_lex->where= join->conds= and_items(join->conds, item); select_lex->where= join->conds= and_items(join->conds, where_term);
select_lex->where->top_level_item(); select_lex->where->top_level_item();
/* /*
we do not check join->conds->fixed, because Item_and can't be fixed we do not check join->conds->fixed, because Item_and can't be fixed
after creation after creation
*/ */
if (join->conds->fix_fields(thd, 0)) if (!join->conds->fixed && join->conds->fix_fields(thd, 0))
DBUG_RETURN(RES_ERROR); DBUG_RETURN(RES_ERROR);
} }
else else
{ {
bool tmp;
if (select_lex->master_unit()->is_union()) if (select_lex->master_unit()->is_union())
{ {
/* having_term->name= (char*)in_having_cond;
comparison functions can't be changed during fix_fields() select_lex->having= join->having= having_term;
we can assign select_lex->having here, and pass 0 as last
argument (reference) to fix_fields()
*/
Item *new_having=
func->create(expr,
new Item_ref_null_helper(&select_lex->context, this,
select_lex->ref_pointer_array,
(char *)"<no matter>",
(char *)"<result>"));
if (!abort_on_null && left_expr->maybe_null)
{
if (!(new_having= new Item_func_trig_cond(new_having,
get_cond_guard(0))))
DBUG_RETURN(RES_ERROR);
}
new_having->name= (char*)in_having_cond;
select_lex->having= join->having= new_having;
select_lex->having_fix_field= 1; select_lex->having_fix_field= 1;
/* /*
we do not check join->having->fixed, because comparison function we do not check join->having->fixed, because comparison function
(from func->create) can't be fixed after creation (from func->create) can't be fixed after creation
*/ */
tmp= join->having->fix_fields(thd, 0); if (!join->having->fixed)
fix_res= join->having->fix_fields(thd, 0);
select_lex->having_fix_field= 0; select_lex->having_fix_field= 0;
if (tmp) if (fix_res)
DBUG_RETURN(RES_ERROR); DBUG_RETURN(RES_ERROR);
} }
else else
...@@ -1679,11 +1765,11 @@ Item_in_subselect::single_value_in_to_exists_transformer(JOIN * join, Comp_creat ...@@ -1679,11 +1765,11 @@ Item_in_subselect::single_value_in_to_exists_transformer(JOIN * join, Comp_creat
// it is single select without tables => possible optimization // it is single select without tables => possible optimization
// remove the dependence mark since the item is moved to upper // remove the dependence mark since the item is moved to upper
// select and is not outer anymore. // select and is not outer anymore.
item->walk(&Item::remove_dependence_processor, 0, where_term->walk(&Item::remove_dependence_processor, 0,
(uchar *) select_lex->outer_select()); (uchar *) select_lex->outer_select());
item= func->create(left_expr, item); where_term= func->create(left_expr, where_term);
// fix_field of item will be done in time of substituting // fix_field of item will be done in time of substituting
substitution= item; substitution= where_term;
have_to_be_excluded= 1; have_to_be_excluded= 1;
if (thd->lex->describe) if (thd->lex->describe)
{ {
......
...@@ -425,8 +425,18 @@ class Item_in_subselect :public Item_exists_subselect ...@@ -425,8 +425,18 @@ class Item_in_subselect :public Item_exists_subselect
trans_res select_in_like_transformer(JOIN *join, Comp_creator *func); trans_res select_in_like_transformer(JOIN *join, Comp_creator *func);
trans_res single_value_transformer(JOIN *join, Comp_creator *func); trans_res single_value_transformer(JOIN *join, Comp_creator *func);
trans_res row_value_transformer(JOIN * join); trans_res row_value_transformer(JOIN * join);
trans_res single_value_in_to_exists_transformer(JOIN * join, trans_res single_value_in_to_exists_transformer(JOIN * join,
Comp_creator *func); Comp_creator *func);
trans_res create_single_value_in_to_exists_cond(JOIN * join,
Comp_creator *func,
Item **where_term,
Item **having_term);
trans_res inject_single_value_in_to_exists_cond(JOIN * join,
Comp_creator *func,
Item *where_term,
Item *having_term);
trans_res row_value_in_to_exists_transformer(JOIN * join); trans_res row_value_in_to_exists_transformer(JOIN * join);
virtual bool exec(); virtual bool exec();
longlong val_int(); longlong val_int();
......
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