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:
156
sql/sql_lex.cc
156
sql/sql_lex.cc
@ -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;
|
||||
}
|
||||
|
Reference in New Issue
Block a user