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:
@ -1242,25 +1242,61 @@ bool mysql_derived_reinit(THD *thd, LEX *lex, TABLE_LIST *derived)
|
||||
|
||||
/**
|
||||
@brief
|
||||
Extract the condition depended on derived table/view and pushed it there
|
||||
Extract condition that can be pushed into a derived table/view
|
||||
|
||||
@param thd The thread handle
|
||||
@param cond The condition from which to extract the pushed condition
|
||||
@param derived The reference to the derived table/view
|
||||
@param thd the thread handle
|
||||
@param cond current condition
|
||||
@param derived the reference to the derived table/view
|
||||
|
||||
@details
|
||||
This functiom builds the most restrictive condition depending only on
|
||||
the derived table/view that can be extracted from the condition cond.
|
||||
The built condition is pushed into the having clauses of the
|
||||
selects contained in the query specifying the derived table/view.
|
||||
The function also checks for each select whether any condition depending
|
||||
only on grouping fields can be extracted from the pushed condition.
|
||||
If so, it pushes the condition over grouping fields into the where
|
||||
clause of the select.
|
||||
|
||||
@retval
|
||||
true if an error is reported
|
||||
false otherwise
|
||||
This function builds the most restrictive condition depending only on
|
||||
the derived table/view (directly or indirectly through equality) that
|
||||
can be extracted from the given condition cond and pushes it into the
|
||||
derived table/view.
|
||||
|
||||
Example of the transformation:
|
||||
|
||||
SELECT *
|
||||
FROM t1,
|
||||
(
|
||||
SELECT x,MAX(y) AS max_y
|
||||
FROM t2
|
||||
GROUP BY x
|
||||
) AS d_tab
|
||||
WHERE d_tab.x>1 AND d_tab.max_y<30;
|
||||
|
||||
=>
|
||||
|
||||
SELECT *
|
||||
FROM t1,
|
||||
(
|
||||
SELECT x,z,MAX(y) AS max_y
|
||||
FROM t2
|
||||
WHERE x>1
|
||||
HAVING max_y<30
|
||||
GROUP BY x
|
||||
) AS d_tab
|
||||
WHERE d_tab.x>1 AND d_tab.max_y<30;
|
||||
|
||||
In details:
|
||||
1. Check what pushable formula can be extracted from cond
|
||||
2. Build a clone PC of the formula that can be extracted
|
||||
(the clone is built only if the extracted formula is a AND subformula
|
||||
of cond or conjunction of such subformulas)
|
||||
Do for every select specifying derived table/view:
|
||||
3. If there is no HAVING clause prepare PC to be conjuncted with
|
||||
WHERE clause of the select. Otherwise do 4-7.
|
||||
4. Check what formula PC_where can be extracted from PC to be pushed
|
||||
into the WHERE clause of the select
|
||||
5. Build PC_where and if PC_where is a conjunct(s) of PC remove it from PC
|
||||
getting PC_having
|
||||
6. Prepare PC_where to be conjuncted with the WHERE clause of the select
|
||||
7. Prepare PC_having to be conjuncted with the HAVING clause of the select
|
||||
@note
|
||||
This method is similar to pushdown_cond_for_in_subquery()
|
||||
|
||||
@retval TRUE if an error occurs
|
||||
@retval FALSE otherwise
|
||||
*/
|
||||
|
||||
bool pushdown_cond_for_derived(THD *thd, Item *cond, TABLE_LIST *derived)
|
||||
@ -1300,63 +1336,25 @@ bool pushdown_cond_for_derived(THD *thd, Item *cond, TABLE_LIST *derived)
|
||||
if (!some_select_allows_cond_pushdown)
|
||||
DBUG_RETURN(false);
|
||||
|
||||
/*
|
||||
Build the most restrictive condition extractable from 'cond'
|
||||
that can be pushed into the derived table 'derived'.
|
||||
All subexpressions of this condition are cloned from the
|
||||
subexpressions of 'cond'.
|
||||
This condition has to be fixed yet.
|
||||
*/
|
||||
/* 1. Check what pushable formula can be extracted from cond */
|
||||
Item *extracted_cond;
|
||||
derived->check_pushable_cond_for_table(cond);
|
||||
extracted_cond= derived->build_pushable_cond_for_table(thd, cond);
|
||||
cond->check_pushable_cond(&Item::pushable_cond_checker_for_derived,
|
||||
(uchar *)(&derived->table->map));
|
||||
/* 2. Build a clone PC of the formula that can be extracted */
|
||||
extracted_cond=
|
||||
cond->build_pushable_cond(thd,
|
||||
&Item::pushable_equality_checker_for_derived,
|
||||
((uchar *)&derived->table->map));
|
||||
if (!extracted_cond)
|
||||
{
|
||||
/* Nothing can be pushed into the derived table */
|
||||
DBUG_RETURN(false);
|
||||
}
|
||||
/* Push extracted_cond into every select of the unit specifying 'derived' */
|
||||
|
||||
st_select_lex *save_curr_select= thd->lex->current_select;
|
||||
for (; sl; sl= sl->next_select())
|
||||
{
|
||||
Item *extracted_cond_copy;
|
||||
if (!sl->cond_pushdown_is_allowed())
|
||||
continue;
|
||||
thd->lex->current_select= sl;
|
||||
if (sl->have_window_funcs())
|
||||
{
|
||||
if (sl->join->group_list || sl->join->implicit_grouping)
|
||||
continue;
|
||||
ORDER *common_partition_fields=
|
||||
sl->find_common_window_func_partition_fields(thd);
|
||||
if (!common_partition_fields)
|
||||
continue;
|
||||
extracted_cond_copy= !sl->next_select() ?
|
||||
extracted_cond :
|
||||
extracted_cond->build_clone(thd);
|
||||
if (!extracted_cond_copy)
|
||||
continue;
|
||||
|
||||
Item *cond_over_partition_fields;;
|
||||
sl->collect_grouping_fields(thd, common_partition_fields);
|
||||
sl->check_cond_extraction_for_grouping_fields(extracted_cond_copy,
|
||||
derived);
|
||||
cond_over_partition_fields=
|
||||
sl->build_cond_for_grouping_fields(thd, extracted_cond_copy, true);
|
||||
if (cond_over_partition_fields)
|
||||
cond_over_partition_fields= cond_over_partition_fields->transform(thd,
|
||||
&Item::derived_grouping_field_transformer_for_where,
|
||||
(uchar*) sl);
|
||||
if (cond_over_partition_fields)
|
||||
{
|
||||
cond_over_partition_fields->walk(
|
||||
&Item::cleanup_excluding_const_fields_processor, 0, 0);
|
||||
sl->cond_pushed_into_where= cond_over_partition_fields;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
For each select of the unit except the last one
|
||||
create a clone of extracted_cond
|
||||
@ -1367,73 +1365,44 @@ bool pushdown_cond_for_derived(THD *thd, Item *cond, TABLE_LIST *derived)
|
||||
if (!extracted_cond_copy)
|
||||
continue;
|
||||
|
||||
if (!sl->join->group_list && !sl->with_sum_func)
|
||||
/* Collect fields that are used in the GROUP BY of sl */
|
||||
if (sl->have_window_funcs())
|
||||
{
|
||||
/* extracted_cond_copy is pushed into where of sl */
|
||||
extracted_cond_copy= extracted_cond_copy->transform(thd,
|
||||
&Item::derived_field_transformer_for_where,
|
||||
(uchar*) sl);
|
||||
if (extracted_cond_copy)
|
||||
{
|
||||
extracted_cond_copy->walk(
|
||||
&Item::cleanup_excluding_const_fields_processor, 0, 0);
|
||||
sl->cond_pushed_into_where= extracted_cond_copy;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
Figure out what can be extracted from the pushed condition
|
||||
that could be pushed into the where clause of sl
|
||||
*/
|
||||
Item *cond_over_grouping_fields;
|
||||
sl->collect_grouping_fields(thd, sl->join->group_list);
|
||||
sl->check_cond_extraction_for_grouping_fields(extracted_cond_copy,
|
||||
derived);
|
||||
cond_over_grouping_fields=
|
||||
sl->build_cond_for_grouping_fields(thd, extracted_cond_copy, true);
|
||||
|
||||
/*
|
||||
Transform the references to the 'derived' columns from the condition
|
||||
pushed into the where clause of sl to make them usable in the new context
|
||||
*/
|
||||
if (cond_over_grouping_fields)
|
||||
cond_over_grouping_fields= cond_over_grouping_fields->transform(thd,
|
||||
&Item::derived_grouping_field_transformer_for_where,
|
||||
(uchar*) sl);
|
||||
|
||||
if (cond_over_grouping_fields)
|
||||
{
|
||||
/*
|
||||
In extracted_cond_copy remove top conjuncts that
|
||||
has been pushed into the where clause of sl
|
||||
*/
|
||||
extracted_cond_copy= remove_pushed_top_conjuncts(thd, extracted_cond_copy);
|
||||
|
||||
cond_over_grouping_fields->walk(
|
||||
&Item::cleanup_excluding_const_fields_processor, 0, 0);
|
||||
sl->cond_pushed_into_where= cond_over_grouping_fields;
|
||||
|
||||
if (!extracted_cond_copy)
|
||||
if (sl->group_list.first || sl->join->implicit_grouping)
|
||||
continue;
|
||||
ORDER *common_partition_fields=
|
||||
sl->find_common_window_func_partition_fields(thd);
|
||||
if (!common_partition_fields)
|
||||
continue;
|
||||
sl->collect_grouping_fields(thd, common_partition_fields);
|
||||
}
|
||||
else
|
||||
sl->collect_grouping_fields(thd, sl->group_list.first);
|
||||
|
||||
Item *remaining_cond= NULL;
|
||||
/* Do 4-6 */
|
||||
sl->pushdown_cond_into_where_clause(thd, extracted_cond_copy,
|
||||
&remaining_cond,
|
||||
&Item::derived_field_transformer_for_where,
|
||||
(uchar *) sl);
|
||||
|
||||
if (!remaining_cond)
|
||||
continue;
|
||||
/*
|
||||
Transform the references to the 'derived' columns from the condition
|
||||
pushed into the having clause of sl to make them usable in the new context
|
||||
7. Prepare PC_having to be conjuncted with the HAVING clause of
|
||||
the select
|
||||
*/
|
||||
extracted_cond_copy= extracted_cond_copy->transform(thd,
|
||||
&Item::derived_field_transformer_for_having,
|
||||
(uchar*) sl);
|
||||
if (!extracted_cond_copy)
|
||||
remaining_cond=
|
||||
remaining_cond->transform(thd,
|
||||
&Item::derived_field_transformer_for_having,
|
||||
(uchar *) sl);
|
||||
if (!remaining_cond)
|
||||
continue;
|
||||
|
||||
extracted_cond_copy->walk(&Item::cleanup_excluding_const_fields_processor,
|
||||
0, 0);
|
||||
sl->cond_pushed_into_having= extracted_cond_copy;
|
||||
remaining_cond->walk(&Item::cleanup_excluding_const_fields_processor,
|
||||
0, 0);
|
||||
sl->cond_pushed_into_having= remaining_cond;
|
||||
}
|
||||
thd->lex->current_select= save_curr_select;
|
||||
DBUG_RETURN(false);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user