1
0
mirror of https://github.com/MariaDB/server.git synced 2025-07-30 16:24:05 +03:00

MDEV-12387 Push conditions into materialized subqueries

The logic and the implementation scheme are similar with the
MDEV-9197 Pushdown conditions into non-mergeable views/derived tables

How the push down is made on the example:

select * from t1
where a>3 and b>10 and
 (a,b) in (select x,max(y) from t2 group by x);

-->

select * from t1
where a>3 and b>10 and
  (a,b) in (select x,max(y)
            from t2
            where x>3
            group by x
            having max(y)>10);

The implementation scheme:

1. Search for the condition cond that depends only on the fields
   from the left part of the IN subquery (left_part)
2. Find fields F_group in the select of the right part of the
   IN subquery (right_part) that are used in the GROUP BY
3. Extract from the cond condition cond_where that depends only on the
   fields from the left_part that stay at the same places in the left_part
   (have the same indexes) as the F_group fields in the projection of the
   right_part
4. Transform cond_where so it can be pushed into the WHERE clause of the
   right_part and delete cond_where from the cond
5. Transform cond so it can be pushed into the HAVING clause of the right_part

The optimization is made in the
Item_in_subselect::pushdown_cond_for_in_subquery() and is controlled by the
variable condition_pushdown_for_subquery.

New test file in_subq_cond_pushdown.test is created.

There are also some changes made for setup_jtbm_semi_joins().
Now it is decomposed into the 2 procedures: setup_degenerate_jtbm_semi_joins()
that is called before optimize_cond() for cond and setup_jtbm_semi_joins()
that is called after optimize_cond().
New setup_jtbm_semi_joins() is made in the way so that the result of its work is
the same as if it was called before optimize_cond().

The code that is common for pushdown into materialized derived and into materialized
IN subqueries is factored out into pushdown_cond_for_derived(),
Item_in_subselect::pushdown_cond_for_in_subquery() and
st_select_lex::pushdown_cond_into_where_clause().
This commit is contained in:
Galina Shalygina
2018-05-15 23:45:59 +02:00
parent 569e3ad1ea
commit d3ff133390
30 changed files with 6115 additions and 504 deletions

View File

@ -7245,9 +7245,9 @@ void st_select_lex::collect_grouping_fields(THD *thd,
{
if ((*ord->item)->eq((Item*)item, 0))
{
Grouping_tmp_field *grouping_tmp_field=
new Grouping_tmp_field(master_unit()->derived->table->field[i], item);
grouping_tmp_fields.push_back(grouping_tmp_field);
Field_pair *grouping_tmp_field=
new Field_pair(master_unit()->derived->table->field[i], item);
grouping_tmp_fields.push_back(grouping_tmp_field);
}
}
}
@ -7277,8 +7277,7 @@ void st_select_lex::collect_grouping_fields(THD *thd,
*/
void
st_select_lex::check_cond_extraction_for_grouping_fields(Item *cond,
TABLE_LIST *derived)
st_select_lex::check_cond_extraction_for_grouping_fields(Item *cond)
{
cond->clear_extraction_flag();
if (cond->type() == Item::COND_ITEM)
@ -7291,7 +7290,7 @@ st_select_lex::check_cond_extraction_for_grouping_fields(Item *cond,
Item *item;
while ((item=li++))
{
check_cond_extraction_for_grouping_fields(item, derived);
check_cond_extraction_for_grouping_fields(item);
if (item->get_extraction_flag() != NO_EXTRACTION_FL)
{
count++;
@ -7340,7 +7339,7 @@ st_select_lex::check_cond_extraction_for_grouping_fields(Item *cond,
to figure out whether a subformula depends only on these fields or not.
@note
The built condition C is always implied by the condition cond
(cond => C). The method tries to build the most restictive such
(cond => C). The method tries to build the least restictive such
condition (i.e. for any other condition C' such that cond => C'
we have C => C').
@note
@ -7353,7 +7352,7 @@ st_select_lex::check_cond_extraction_for_grouping_fields(Item *cond,
*/
Item *st_select_lex::build_cond_for_grouping_fields(THD *thd, Item *cond,
bool no_top_clones)
bool no_top_clones)
{
if (cond->get_extraction_flag() == FULL_EXTRACTION_FL)
{
@ -7381,17 +7380,17 @@ Item *st_select_lex::build_cond_for_grouping_fields(THD *thd, Item *cond,
{
if (item->get_extraction_flag() == NO_EXTRACTION_FL)
{
DBUG_ASSERT(cond_and);
item->clear_extraction_flag();
continue;
DBUG_ASSERT(cond_and);
item->clear_extraction_flag();
continue;
}
Item *fix= build_cond_for_grouping_fields(thd, item,
no_top_clones & cond_and);
no_top_clones & cond_and);
if (!fix)
{
if (cond_and)
continue;
break;
if (cond_and)
continue;
break;
}
new_cond->argument_list()->push_back(fix, thd->mem_root);
}
@ -7399,7 +7398,7 @@ Item *st_select_lex::build_cond_for_grouping_fields(THD *thd, Item *cond,
if (!cond_and && item)
{
while((item= li++))
item->clear_extraction_flag();
item->clear_extraction_flag();
return 0;
}
switch (new_cond->argument_list()->elements)
@ -7757,3 +7756,128 @@ Item *Lex_trim_st::make_item_func_trim(THD *thd) const
make_item_func_trim_oracle(thd) :
make_item_func_trim_std(thd);
}
/**
@brief
Extract from given item a condition pushable into WHERE clause
@param thd the thread handle
@param cond the item to extract a condition to be pushed
into WHERE
@param remaining_cond the condition that will remain of cond after
the pushdown of its parts into the WHERE clause
@param transformer the transformer callback function to be
applied to the condition so it can be pushed
down into the WHERE clause of this select
@param arg parameter to be passed to the transformer
@details
This method checks if cond entirely or its parts can be
pushed into the WHERE clause of this select and prepares it for pushing.
First it checks wherever this select doesn't have any aggregation function
in its projection and GROUP BY clause. If so cond can be entirely
pushed into the WHERE clause of this select but before its fields should
be transformed with transformer_for_where to make it pushable.
Otherwise the method checks wherever any condition depending only on
grouping fields can be extracted from cond. If there is any it prepares it
for pushing using grouping_field_transformer_for_where and if it happens to
be a conjunct of cond it removes it from cond. It saves the result of
removal in remaining_cond.
The extracted condition is saved in cond_pushed_into_where of this select.
@note
When looking for pushable condition the method considers only the grouping
fields from the list grouping_tmp_fields whose elements are of the type
Field_pair. This list must be prepared before the call of the
function.
@note
This method is called for pushdown conditions into materialized
derived tables/views optimization.
Item::derived_field_transformer_for_where is passed as the actual
callback function.
Also it is called for pushdown conditions into materialized IN subqueries.
Item::in_subq_field_transformer_for_where is passed as the actual
callback function.
*/
void st_select_lex::pushdown_cond_into_where_clause(THD *thd, Item *cond,
Item **remaining_cond,
Item_transformer transformer,
uchar *arg)
{
if (!cond_pushdown_is_allowed())
return;
thd->lex->current_select= this;
if (have_window_funcs())
{
Item *cond_over_partition_fields;
check_cond_extraction_for_grouping_fields(cond);
cond_over_partition_fields=
build_cond_for_grouping_fields(thd, cond, true);
if (cond_over_partition_fields)
cond_over_partition_fields= cond_over_partition_fields->transform(thd,
&Item::grouping_field_transformer_for_where,
(uchar*) this);
if (cond_over_partition_fields)
{
cond_over_partition_fields->walk(
&Item::cleanup_excluding_const_fields_processor, 0, 0);
cond_pushed_into_where= cond_over_partition_fields;
}
return;
}
if (!join->group_list && !with_sum_func)
{
cond=
cond->transform(thd, transformer, arg);
if (cond)
{
cond->walk(
&Item::cleanup_excluding_const_fields_processor, 0, 0);
cond_pushed_into_where= cond;
}
return;
}
/*
Figure out what can be extracted from cond
that could be pushed into the WHERE clause of this select
*/
Item *cond_over_grouping_fields;
check_cond_extraction_for_grouping_fields(cond);
cond_over_grouping_fields=
build_cond_for_grouping_fields(thd, cond, true);
/*
Transform the references to the columns from the cond
pushed into the WHERE clause of this select to make them usable in
the new context
*/
if (cond_over_grouping_fields)
cond_over_grouping_fields= cond_over_grouping_fields->transform(thd,
&Item::grouping_field_transformer_for_where,
(uchar*) this);
if (cond_over_grouping_fields)
{
/*
In cond remove top conjuncts that has been pushed into the WHERE
clause of this select
*/
cond= remove_pushed_top_conjuncts(thd, cond);
cond_over_grouping_fields->walk(
&Item::cleanup_excluding_const_fields_processor, 0, 0);
cond_pushed_into_where= cond_over_grouping_fields;
}
*remaining_cond= cond;
}