Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
M
MariaDB
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
nexedi
MariaDB
Commits
4a3f135a
Commit
4a3f135a
authored
Oct 05, 2010
by
unknown
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
MWL#89: Cost-based choice between Materialization and IN->EXISTS transformation
Added/corrected/improved comments.
parent
77c03bcf
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
243 additions
and
150 deletions
+243
-150
mysql-test/r/subselect_mat.result
mysql-test/r/subselect_mat.result
+2
-2
sql/item_subselect.cc
sql/item_subselect.cc
+145
-129
sql/item_subselect.h
sql/item_subselect.h
+1
-1
sql/opt_subselect.cc
sql/opt_subselect.cc
+52
-12
sql/sql_select.cc
sql/sql_select.cc
+43
-6
No files found.
mysql-test/r/subselect_mat.result
View file @
4a3f135a
...
...
@@ -1139,7 +1139,7 @@ insert into t1 values (5);
explain select min(a1) from t1 where 7 in (select b1 from t2 group by b1);
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY NULL NULL NULL NULL NULL NULL NULL Select tables optimized away
2 SUBQUERY
NULL NULL NULL NULL NULL NULL NULL no matching row in const table
2 SUBQUERY
t2 system NULL NULL NULL NULL 0 const row not found
select min(a1) from t1 where 7 in (select b1 from t2 group by b1);
min(a1)
set @@optimizer_switch='default,materialization=off';
...
...
@@ -1153,7 +1153,7 @@ set @@optimizer_switch='default,semijoin=off';
explain select min(a1) from t1 where 7 in (select b1 from t2);
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY NULL NULL NULL NULL NULL NULL NULL Select tables optimized away
2 SUBQUERY
NULL NULL NULL NULL NULL NULL NULL no matching row in const table
2 SUBQUERY
t2 system NULL NULL NULL NULL 0 const row not found
select min(a1) from t1 where 7 in (select b1 from t2);
min(a1)
set @@optimizer_switch='default,materialization=off';
...
...
sql/item_subselect.cc
View file @
4a3f135a
...
...
@@ -187,7 +187,7 @@ bool Item_subselect::fix_fields(THD *thd_param, Item **ref)
bool
res
;
DBUG_ASSERT
(
fixed
==
0
);
DBUG_ASSERT
(
thd
==
thd_param
);
DBUG_ASSERT
(
thd
==
thd_param
);
/* thd can't change during execution. */
engine
->
set_thd
(
thd
);
if
(
!
done_first_fix_fields
)
{
...
...
@@ -288,6 +288,16 @@ bool Item_subselect::mark_as_eliminated_processor(uchar *arg)
}
/**
Remove a subselect item from its unit so that the unit no longer
represents a subquery.
@param arg unused parameter
@return
FALSE to force the evaluation of the processor for the subsequent items.
*/
bool
Item_subselect
::
eliminate_subselect_processor
(
uchar
*
arg
)
{
unit
->
item
=
NULL
;
...
...
@@ -297,34 +307,37 @@ bool Item_subselect::eliminate_subselect_processor(uchar *arg)
}
/*
/*
*
Adjust the master select of the subquery to be the fake_select which
represents the whole UNION right above the subquery, instead of the
last query of the UNION.
@param arg pointer to the fake select
@return
FALSE to force the evaluation of the processor for the subsequent items.
*/
bool
Item_subselect
::
set_fake_select_as_master_processor
(
uchar
*
arg
)
{
SELECT_LEX
*
fake_select
=
(
SELECT_LEX
*
)
arg
;
/*
Apply the substitution only for immediate child subqueries of a
Move the st_select_lex_unit of a subquery from a global ORDER BY clause to
become a direct child of the fake_select of a UNION. In this way the
ORDER BY is applied to the temporary table that contains the result of the
whole UNION, and all columns in the subquery are resolved against this table.
Apply the transformation only for immediate child subqueries of a
UNION query.
*/
if
(
unit
->
outer_select
()
->
master_unit
()
->
fake_select_lex
==
fake_select
)
{
/*
Include the st_select_lex_unit of a subquery from a global ORDER BY
clause as a direct child of the fake_select of a UNION. In this way
the ORDER BY is applied to the temporary table that contains the
result of the whole UNION, and all columns in the subquery are
resolved against this table.
*/
/*
Set the master of the subquery to be the fake select (i.e. the whole
UNION, instead of the last query in the UNION.
TODO: this is a hack, instead we should call:
unit->include_down(fake_select);
however, this call results in an infinite loop where
Set the master of the subquery to be the fake select (i.e. the whole UNION),
instead of the last query in the UNION.
TODO:
This is a hack, instead we should call: unit->include_down(fake_select);
However, this call results in an infinite loop where
some_select_lex->master == some_select_lex.
*/
unit
->
set_master
(
fake_select
);
...
...
@@ -332,14 +345,13 @@ bool Item_subselect::set_fake_select_as_master_processor(uchar *arg)
for
(
SELECT_LEX
*
sl
=
unit
->
first_select
();
sl
;
sl
=
sl
->
next_select
())
sl
->
context
.
outer_context
=
&
(
fake_select
->
context
);
/*
Undo Item_subselect::eliminate_subselect_processor because at that
phase we don't know yet (or don't know how to figure it out) that
the ORDER clause will be moved to the fake select.
Undo Item_subselect::eliminate_subselect_processor because at that phase
we don't know yet that the ORDER clause will be moved to the fake select.
*/
unit
->
item
=
this
;
eliminated
=
FALSE
;
}
return
FALSE
;
// return TRUE ? because we need to stop processing down
return
FALSE
;
}
...
...
@@ -1341,54 +1353,33 @@ my_decimal *Item_in_subselect::val_decimal(my_decimal *decimal_value)
}
/*
Rewrite a single-column IN/ALL/ANY subselect
SYNOPSIS
Item_in_subselect::single_value_transformer()
join Join object of the subquery (i.e. 'child' join).
func Subquery comparison creator
DESCRIPTION
Rewrite a single-column subquery using rule-based approach. The subquery
oe $cmp$ (SELECT ie FROM ... WHERE subq_where ... HAVING subq_having)
First, try to convert the subquery to scalar-result subquery in one of
the forms:
- oe $cmp$ (SELECT MAX(...) ) // handled by Item_singlerow_subselect
- oe $cmp$ <max>(SELECT ...) // handled by Item_maxmin_subselect
If that fails, the subquery will be handled with class Item_in_optimizer,
Inject the predicates into subquery, i.e. convert it to:
- If the subquery has aggregates, GROUP BY, or HAVING, convert to
SELECT ie FROM ... HAVING subq_having AND
trigcond(oe $cmp$ ref_or_null_helper<ie>)
the addition is wrapped into trigger only when we want to distinguish
between NULL and FALSE results.
/**
Rewrite a single-column IN/ALL/ANY subselect.
- Otherwise (no aggregates/GROUP BY/HAVING) convert it to one of the
following:
@param join Join object of the subquery (i.e. 'child' join).
= If we don't need to distinguish between NULL and FALSE subquery:
SELECT 1 FROM ... WHERE (oe $cmp$ ie) AND subq_where
@details
Rewrite a single-column subquery using rule-based approach. The subquery
= If we need to distinguish between those:
oe $cmp$ (SELECT ie FROM ... WHERE subq_where ... HAVING subq_having)
SELECT 1 FROM ...
WHERE subq_where AND trigcond((oe $cmp$ ie) OR (ie IS NULL))
HAVING trigcond(<is_not_null_test>(ie))
First, try to convert the subquery to scalar-result subquery in one of
the forms:
- oe $cmp$ (SELECT MAX(...) ) // handled by Item_singlerow_subselect
- oe $cmp$ <max>(SELECT ...) // handled by Item_maxmin_subselect
RETURN
RES_OK Either subquery was transformed, or appopriate
predicates where injected into it.
RES_REDUCE The subquery was reduced to non-subquery
RES_ERROR Error
If that fails, check if the subquery is a single select without tables,
and substitute the subquery predicate with "oe $cmp$ ie".
If that fails, the subquery predicate is wrapped into an Item_in_optimizer.
Later the query optimization phase chooses whether the subquery under the
Item_in_optimizer will be further transformed into an equivalent correlated
EXISTS by injecting additional predicates, or will be executed via subquery
materialization in its unmodified form.
@retval RES_OK The subquery was transformed
@retval RES_ERROR Error
*/
Item_subselect
::
trans_res
...
...
@@ -1424,7 +1415,7 @@ Item_in_subselect::single_value_transformer(JOIN *join)
{
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
);
}
...
...
@@ -1516,6 +1507,10 @@ Item_in_subselect::single_value_transformer(JOIN *join)
DBUG_RETURN
(
RES_OK
);
}
/*
Wrap the current IN predicate in an Item_in_optimizer. The actual
substitution in the Item tree takes place in Item_subselect::fix_fields.
*/
if
(
!
substitution
)
{
/* We're invoked for the 1st (or the only) SELECT in the subquery UNION */
...
...
@@ -1546,7 +1541,8 @@ Item_in_subselect::single_value_transformer(JOIN *join)
(
char
*
)
in_left_expr_name
);
master_unit
->
uncacheable
|=
UNCACHEABLE_DEPENDENT
;
//select_lex->uncacheable|= UNCACHEABLE_DEPENDENT;
// TODO: do we need to set both?
// select_lex->uncacheable|= UNCACHEABLE_DEPENDENT;
}
DBUG_RETURN
(
RES_OK
);
...
...
@@ -1567,10 +1563,15 @@ bool Item_in_subselect::fix_having(Item *having, SELECT_LEX *select_lex)
/**
Transform an IN predicate into EXISTS via predicate injection.
Create the predicates needed to transform a single-column IN/ALL/ANY
subselect into a correlated EXISTS via predicate injection.
@details The transformation injects additional predicates into the subquery
(and makes the subquery correlated) as follows.
@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 having_item[out] the in-to-exists addition to the having clause
@details
The correlated predicates are created as follows:
- If the subquery has aggregates, GROUP BY, or HAVING, convert to
...
...
@@ -1585,21 +1586,16 @@ bool Item_in_subselect::fix_having(Item *having, SELECT_LEX *select_lex)
= If we don't need to distinguish between NULL and FALSE subquery:
SELECT
1 FROM ... WHERE (oe $cmp$ ie) AND subq_where
SELECT
ie FROM ... WHERE subq_where AND (oe $cmp$ ie)
= If we need to distinguish between those:
SELECT
1
FROM ...
SELECT
ie
FROM ...
WHERE subq_where AND trigcond((oe $cmp$ ie) OR (ie IS NULL))
HAVING trigcond(<is_not_null_test>(ie))
@param join Join object of the subquery (i.e. 'child' join).
@param func Subquery comparison creator
@retval RES_OK Either subquery was transformed, or appopriate
predicates where injected into it.
@retval RES_REDUCE The subquery was reduced to non-subquery
@retval RES_ERROR Error
@retval RES_OK If the new conditions were created successfully
@retval RES_ERROR Error
*/
Item_subselect
::
trans_res
...
...
@@ -1609,10 +1605,8 @@ Item_in_subselect::create_single_in_to_exists_cond(JOIN * join,
{
SELECT_LEX
*
select_lex
=
join
->
select_lex
;
/*
The non-transformed HAVING clause of 'join' may be stored differently in
JOIN::optimize:
this->tmp_having= this->having
this->having= 0;
The non-transformed HAVING clause of 'join' may be stored in two ways
during JOIN::optimize: this->tmp_having= this->having; this->having= 0;
*/
Item
*
join_having
=
join
->
having
?
join
->
having
:
join
->
tmp_having
;
...
...
@@ -1724,6 +1718,22 @@ Item_in_subselect::create_single_in_to_exists_cond(JOIN * join,
}
/**
Wrap a multi-column IN/ALL/ANY subselect into an Item_in_optimizer.
@param join Join object of the subquery (i.e. 'child' join).
@details
The subquery predicate is wrapped into an Item_in_optimizer. Later the query
optimization phase chooses whether the subquery under the Item_in_optimizer
will be further transformed into an equivalent correlated EXISTS by injecting
additional predicates, or will be executed via subquery materialization in its
unmodified form.
@retval RES_OK The subquery was transformed
@retval RES_ERROR Error
*/
Item_subselect
::
trans_res
Item_in_subselect
::
row_value_transformer
(
JOIN
*
join
)
{
...
...
@@ -1763,6 +1773,7 @@ Item_in_subselect::row_value_transformer(JOIN *join)
thd
->
lex
->
current_select
=
current
;
master_unit
->
uncacheable
|=
UNCACHEABLE_DEPENDENT
;
// TODO: do we need to set both?
//select_lex->uncacheable|= UNCACHEABLE_DEPENDENT;
}
...
...
@@ -1771,21 +1782,19 @@ Item_in_subselect::row_value_transformer(JOIN *join)
/**
Tranform a (possibly non-correlated) IN subquery into a correlated EXISTS.
Create the predicates needed to transform a multi-column IN/ALL/ANY
subselect into a correlated EXISTS via predicate injection.
@todo
The IF-ELSE below can be refactored so that there is no duplication of the
statements that create the new conditions. For this we have to invert the IF
and the FOR statements as this:
for (each left operand)
create the equi-join condition
if (is_having_used || !abort_on_null)
create the "is null" and is_not_null_test items
if (is_having_used)
add the equi-join and the null tests to HAVING
else
add the equi-join and the "is null" to WHERE
add the is_not_null_test to HAVING
@details
There are two cases - either the subquery has aggregates, GROUP BY,
or HAVING, or not. Both cases are described inline in the code.
@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 having_item[out] the in-to-exists addition to the having clause
@retval RES_OK If the new conditions were created successfully
@retval RES_ERROR Error
*/
Item_subselect
::
trans_res
...
...
@@ -1796,10 +1805,8 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join,
SELECT_LEX
*
select_lex
=
join
->
select_lex
;
uint
cols_num
=
left_expr
->
cols
();
/*
The non-transformed HAVING clause of 'join' may be stored differently in
JOIN::optimize:
this->tmp_having= this->having
this->having= 0;
The non-transformed HAVING clause of 'join' may be stored in two ways
during JOIN::optimize: this->tmp_having= this->having; this->having= 0;
*/
Item
*
join_having
=
join
->
having
?
join
->
having
:
join
->
tmp_having
;
bool
is_having_used
=
(
join_having
||
select_lex
->
with_sum_func
||
...
...
@@ -1993,6 +2000,16 @@ Item_in_subselect::select_transformer(JOIN *join)
}
/**
Create the predicates needed to transform an IN/ALL/ANY subselect into a
correlated EXISTS via predicate injection.
@param join_arg Join object of the subquery.
@retval FALSE ok
@retval TRUE error
*/
bool
Item_in_subselect
::
create_in_to_exists_cond
(
JOIN
*
join_arg
)
{
Item_subselect
::
trans_res
res
;
...
...
@@ -2000,12 +2017,11 @@ bool Item_in_subselect::create_in_to_exists_cond(JOIN *join_arg)
DBUG_ASSERT
(
engine
->
engine_type
()
==
subselect_engine
::
SINGLE_SELECT_ENGINE
||
engine
->
engine_type
()
==
subselect_engine
::
UNION_ENGINE
);
/*
T
IMOUR T
ODO: the call to init_cond_guards allocates and initializes an
TODO: the call to init_cond_guards allocates and initializes an
array of booleans that may not be used later because we may choose
materialization.
The two calls below to create_XYZ_cond depend on this boolean array.
This dependency can be easily removed, and the call moved to a later
phase.
If the dependency is removed, the call can be moved to a later phase.
*/
init_cond_guards
();
join_arg
->
select_lex
->
uncacheable
|=
UNCACHEABLE_DEPENDENT
;
...
...
@@ -2021,6 +2037,16 @@ bool Item_in_subselect::create_in_to_exists_cond(JOIN *join_arg)
}
/**
Transform an IN/ALL/ANY subselect into a correlated EXISTS via injecting
correlated in-to-exists predicates.
@param join_arg Join object of the subquery.
@retval FALSE ok
@retval TRUE error
*/
bool
Item_in_subselect
::
inject_in_to_exists_cond
(
JOIN
*
join_arg
)
{
SELECT_LEX
*
select_lex
=
join_arg
->
select_lex
;
...
...
@@ -2034,6 +2060,7 @@ bool Item_in_subselect::inject_in_to_exists_cond(JOIN *join_arg)
where_item
=
and_items
(
join_arg
->
conds
,
where_item
);
if
(
!
where_item
->
fixed
&&
where_item
->
fix_fields
(
thd
,
0
))
DBUG_RETURN
(
true
);
// TIMOUR TODO: call optimize_cond() for the new where clause
thd
->
change_item_tree
(
&
select_lex
->
where
,
where_item
);
select_lex
->
where
->
top_level_item
();
join_arg
->
conds
=
select_lex
->
where
;
...
...
@@ -2045,6 +2072,7 @@ bool Item_in_subselect::inject_in_to_exists_cond(JOIN *join_arg)
having_item
=
and_items
(
join_having
,
having_item
);
if
(
fix_having
(
having_item
,
select_lex
))
DBUG_RETURN
(
true
);
// TIMOUR TODO: call optimize_cond() for the new having clause
thd
->
change_item_tree
(
&
select_lex
->
having
,
having_item
);
select_lex
->
having
->
top_level_item
();
join_arg
->
having
=
select_lex
->
having
;
...
...
@@ -2058,21 +2086,16 @@ bool Item_in_subselect::inject_in_to_exists_cond(JOIN *join_arg)
Prepare IN/ALL/ANY/SOME subquery transformation and call appropriate
transformation function.
To decide which transformation procedure (scalar or row) applicable here
we have to call fix_fields() for left expression to be able to call
cols() method on it. Also this method make arena management for
underlying transformation methods.
@param join JOIN object of transforming subquery
@param func creator of condition function of subquery
@retval
RES_OK OK
@retval
RES_REDUCE OK, and current subquery was reduced during
transformation
@retval
RES_ERROR Error
@notes
To decide which transformation procedure (scalar or row) applicable here
we have to call fix_fields() for left expression to be able to call
cols() method on it. Also this method make arena management for
underlying transformation methods.
@retval RES_OK OK
@retval RES_ERROR Error
*/
Item_subselect
::
trans_res
...
...
@@ -2252,24 +2275,17 @@ void Item_in_subselect::update_used_tables()
used_tables_cache
|=
left_expr
->
used_tables
();
}
/**
Try to create an
engine to compute the subselect via materialization,
and if this fails, revert to execution via the IN=>EXISTS transform
ation.
Try to create an
d initialize an engine to compute a subselect via
materializ
ation.
@details
The purpose of this method is to hide the implementation details
of this Item's execution. The method creates a new engine for
materialized execution, and initializes the engine.
If this initialization fails
- either because it wasn't possible to create the needed temporary table
and its index,
- or because of a memory allocation error,
then we revert back to execution via the IN=>EXISTS tranformation.
The initialization of the new engine is divided in two parts - a permanent
one that lives across prepared statements, and one that is repeated for each
execution.
The method creates a new engine for materialized execution, and initializes
the engine. The initialization may fail
- either because it wasn't possible to create the needed temporary table
and its index,
- or because of a memory allocation error,
@returns
@retval TRUE memory allocation error occurred
...
...
sql/item_subselect.h
View file @
4a3f135a
...
...
@@ -319,7 +319,7 @@ class Item_exists_subselect :public Item_subselect
/*
Possible methods to execute an IN predicate. These are set by the optimizer
based on user-set optimizer switches, s
yntac
tic analysis and cost comparison.
based on user-set optimizer switches, s
eman
tic analysis and cost comparison.
*/
#define SUBS_NOT_TRANSFORMED 0
/* No execution method was chosen for this IN. */
#define SUBS_SEMI_JOIN 1
/* IN was converted to semi-join. */
...
...
sql/opt_subselect.cc
View file @
4a3f135a
...
...
@@ -185,6 +185,7 @@ int check_and_do_in_subquery_rewrites(JOIN *join)
else
{
DBUG_PRINT
(
"info"
,
(
"Subquery can't be converted to semi-join"
));
/* Test if the user has set a legal combination of optimizer switches. */
if
(
!
optimizer_flag
(
thd
,
OPTIMIZER_SWITCH_IN_TO_EXISTS
)
&&
!
optimizer_flag
(
thd
,
OPTIMIZER_SWITCH_MATERIALIZATION
))
my_error
(
ER_ILLEGAL_SUBQUERY_OPTIMIZER_SWITCHES
,
MYF
(
0
));
...
...
@@ -3543,16 +3544,10 @@ static void remove_subq_pushed_predicates(JOIN *join, Item **where)
/**
Setup for execution all subqueries of a query, for which the optimizer
chose hash semi-join.
Optimize all subqueries of a query that have were flattened into a semijoin.
@details Iterate over all immediate child subqueries of the query, and if
they are under an IN predicate, and the optimizer chose to compute it via
materialization:
- optimize each subquery,
- choose an optimial execution strategy for the IN predicate - either
materialization, or an IN=>EXISTS transformation with an approriate
engine.
@details
Optimize all immediate children subqueries of a query.
This phase must be called after substitute_for_best_equal_field() because
that function may replace items with other items from a multiple equality,
...
...
@@ -3570,6 +3565,42 @@ bool JOIN::optimize_unflattened_subqueries()
}
/**
Choose an optimal strategy to execute an IN/ALL/ANY subquery predicate
based on cost.
@param join_tables the set of tables joined in the subquery
@notes
The method chooses between the materialization and IN=>EXISTS rewrite
strategies for the execution of a non-flattened subquery IN predicate.
The cost-based decision is made as follows:
1. compute materialize_strategy_cost based on the unmodified subquery
2. reoptimize the subquery taking into account the IN-EXISTS predicates
3. compute in_exists_strategy_cost based on the reoptimized plan
4. compare and set the cheaper strategy
if (materialize_strategy_cost >= in_exists_strategy_cost)
in_strategy = MATERIALIZATION
else
in_strategy = IN_TO_EXISTS
5. if in_strategy = MATERIALIZATION and it is not possible to initialize it
revert to IN_TO_EXISTS
6. if (in_strategy == MATERIALIZATION)
revert the subquery plan to the original one before reoptimizing
else
inject the IN=>EXISTS predicates into the new EXISTS subquery plan
The implementation itself is a bit more complicated because it takes into
account two more factors:
- whether the user allowed both strategies through an optimizer_switch, and
- if materialization was the cheaper strategy, whether it can be executed
or not.
@retval FALSE success.
@retval TRUE error occurred.
*/
bool
JOIN
::
choose_subquery_plan
(
table_map
join_tables
)
{
/* The original QEP of the subquery. */
...
...
@@ -3627,7 +3658,10 @@ bool JOIN::choose_subquery_plan(table_map join_tables)
&
outer_read_time
,
&
outer_record_count
);
else
{
/* TODO: outer_join can be NULL for DELETE statements. */
/*
TODO: outer_join can be NULL for DELETE statements.
How to compute its cost?
*/
outer_read_time
=
1
;
/* TODO */
outer_record_count
=
1
;
/* TODO */
}
...
...
@@ -3694,13 +3728,14 @@ bool JOIN::choose_subquery_plan(table_map join_tables)
}
/*
If (1) materialization is a possible strategy based on s
ta
tic analysis
If (1) materialization is a possible strategy based on s
eman
tic analysis
during the prepare phase, then if
(2) it is more expensive than the IN->EXISTS transformation, and
(3) it is not possible to create usable indexes for the materialization
strategy,
fall back to IN->EXISTS.
otherwise use materialization.
otherwise
use materialization.
*/
if
(
in_subs
->
in_strategy
&
SUBS_MATERIALIZATION
&&
in_subs
->
setup_mat_engine
())
...
...
@@ -3752,6 +3787,11 @@ bool JOIN::choose_subquery_plan(table_map join_tables)
if
(
!
in_exists_reoptimized
&&
in_to_exists_where
&&
const_tables
!=
tables
)
{
/*
The subquery was not reoptimized either because the user allowed only the
IN-EXISTS strategy, or because materialization was not possible based on
semantic analysis. Clenup the original plan and reoptimize.
*/
for
(
uint
i
=
0
;
i
<
tables
;
i
++
)
{
join_tab
[
i
].
keyuse
=
NULL
;
...
...
sql/sql_select.cc
View file @
4a3f135a
...
...
@@ -19264,8 +19264,18 @@ bool JOIN::change_result(select_result *res)
/**
Save the original 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.
@param save_keyuse[out] a KEYUSE array to save JOIN::keyuse
@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
,
...
...
@@ -19298,8 +19308,14 @@ int JOIN::save_query_plan(DYNAMIC_ARRAY *save_keyuse,
/**
Restore the query plan saved before reoptimization with additional
conditions.
Restore a query plan previously saved by the caller.
@param save_keyuse a KEYUSE array to restore into JOIN::keyuse
@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
,
...
...
@@ -19328,8 +19344,29 @@ void JOIN::restore_query_plan(DYNAMIC_ARRAY *save_keyuse,
/**
Reoptimize a query plan taking into account an additional conjunct to the
WHERE clause.
Reoptimize a query plan taking into account an additional conjunct to the
WHERE clause.
@param added_where An extra conjunct to the WHERE clause to reoptimize with
@param join_tables The set of tables to reoptimize
@param save_best_positions The join order of the original plan to restore to
if needed.
@notes
Given a query plan that already optimized taking into account some WHERE clause
'C', reoptimize this plan with a new WHERE clause 'C AND added_where'. The
reoptimization works as follows:
1. Call update_ref_and_keys *only* for the new conditions 'added_where'
that are about to be injected into the query.
2. Expand if necessary the original KEYUSE array JOIN::keyuse to
accommodate the new REF accesses computed for the 'added_where' condition.
3. Add the new KEYUSEs into JOIN::keyuse.
4. Re-sort and re-filter the JOIN::keyuse array with the newly added
KEYUSE elements.
@retval 0 OK
@retval 1 memory allocation error
*/
int
JOIN
::
reoptimize
(
Item
*
added_where
,
table_map
join_tables
,
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment