Post review changes for the optimization of IN predicates into IN subqueries.

This commit is contained in:
Galina Shalygina 2017-09-02 23:19:20 +02:00
parent d76f74d46c
commit 6bce8e1422
10 changed files with 310 additions and 151 deletions

View File

@ -6,29 +6,26 @@ create table t2 (a int, b int, c int);
insert into t2
values (1,2,3), (5,1,2), (4,3,7),
(8,9,0), (10,7,1), (5,5,1);
create table t3 (a int, b varchar(16), index idx(a));
insert into t3 values
(1, "abc"), (3, "egh"), (8, "axxx"), (10, "abc"),
(2, "ccw"), (8, "wqqe"), (7, "au"), (9, "waa"),
(3, "rass"), (9, "ert"), (9, "lok"), (8, "aww"),
(1, "todd"), (3, "rew"), (8, "aww"), (3, "sw"),
(11, "llk"), (7, "rbw"), (1, "sm"), (2, "jyp"),
(4, "yq"), (5, "pled"), (12, "ligin"), (12, "toww"),
(6, "mxm"), (15, "wanone"), (9, "sunqq"), (2, "abe");
# optimization is not used
select * from t1 where a in (1,2);
a b
1 2
1 1
2 5
explain select * from t1 where a in (1,2);
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 6 Using where
explain format=json select * from t1 where a in (1,2);
EXPLAIN
{
"query_block": {
"select_id": 1,
"table": {
"table_name": "t1",
"access_type": "ALL",
"rows": 6,
"filtered": 100,
"attached_condition": "t1.a in (1,2)"
}
}
}
explain extended select * from t1 where a in (1,2);
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 6 100.00 Using where
Warnings:
Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` where `test`.`t1`.`a` in (1,2)
# set minimum number of values in VALUEs list when optimization works to 2
set @@in_subquery_conversion_threshold= 2;
# single IN-predicate in WHERE-part
@ -481,5 +478,68 @@ a b
1 1
2 5
deallocate prepare stmt;
drop table t1, t2;
# use inside out access from tvc rows
set @@in_subquery_conversion_threshold= default;
select * from t3 where a in (1,4,10);
a b
1 abc
1 todd
1 sm
4 yq
10 abc
explain extended select * from t3 where a in (1,4,10);
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE t3 range idx idx 5 NULL 5 100.00 Using index condition
Warnings:
Note 1003 select `test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b` from `test`.`t3` where `test`.`t3`.`a` in (1,4,10)
set @@in_subquery_conversion_threshold= 2;
select * from t3 where a in (1,4,10);
a b
1 abc
1 todd
1 sm
4 yq
10 abc
explain extended select * from t3 where a in (1,4,10);
id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY <subquery2> ALL distinct_key NULL NULL NULL 2 100.00
1 PRIMARY t3 ref idx idx 5 tvc_0.1 3 100.00
2 MATERIALIZED <derived3> ALL NULL NULL NULL NULL 2 100.00
3 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used
Warnings:
Note 1003 /* select#1 */ select `test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b` from `test`.`t3` semi join ((values (1),(4),(10)) `tvc_0`) where `test`.`t3`.`a` = `tvc_0`.`1`
# use vectors in IN predeicate
set @@in_subquery_conversion_threshold= 4;
select * from t1 where (a,b) in ((1,2),(3,4));
a b
1 2
explain extended select * from t1 where (a,b) in ((1,2),(3,4));
id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY <subquery2> ALL distinct_key NULL NULL NULL 2 100.00
1 PRIMARY t1 ALL NULL NULL NULL NULL 6 100.00 Using where; Using join buffer (flat, BNL join)
2 MATERIALIZED <derived3> ALL NULL NULL NULL NULL 2 100.00
3 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used
Warnings:
Note 1003 /* select#1 */ select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` semi join ((values (1,2),(3,4)) `tvc_0`) where `test`.`t1`.`a` = `tvc_0`.`1` and `test`.`t1`.`b` = `tvc_0`.`2`
set @@in_subquery_conversion_threshold= 2;
# trasformation works for the one IN predicate and doesn't work for the other
set @@in_subquery_conversion_threshold= 5;
select * from t2
where (a,b) in ((1,2),(8,9)) and
(a,c) in ((1,3),(8,0),(5,1));
a b c
1 2 3
8 9 0
explain extended select * from t2
where (a,b) in ((1,2),(8,9)) and
(a,c) in ((1,3),(8,0),(5,1));
id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY <subquery2> ALL distinct_key NULL NULL NULL 2 100.00
1 PRIMARY t2 ALL NULL NULL NULL NULL 6 100.00 Using where; Using join buffer (flat, BNL join)
2 MATERIALIZED <derived3> ALL NULL NULL NULL NULL 2 100.00
3 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used
Warnings:
Note 1003 /* select#1 */ select `test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t2`.`c` AS `c` from `test`.`t2` semi join ((values (1,3),(8,0),(5,1)) `tvc_0`) where `test`.`t2`.`a` = `tvc_0`.`1` and `test`.`t2`.`c` = `tvc_0`.`3` and (`tvc_0`.`1`,`test`.`t2`.`b`) in (<cache>((1,2)),<cache>((8,9)))
set @@in_subquery_conversion_threshold= 2;
drop table t1, t2, t3;
set @@in_subquery_conversion_threshold= default;

View File

@ -10,12 +10,21 @@ insert into t2
values (1,2,3), (5,1,2), (4,3,7),
(8,9,0), (10,7,1), (5,5,1);
create table t3 (a int, b varchar(16), index idx(a));
insert into t3 values
(1, "abc"), (3, "egh"), (8, "axxx"), (10, "abc"),
(2, "ccw"), (8, "wqqe"), (7, "au"), (9, "waa"),
(3, "rass"), (9, "ert"), (9, "lok"), (8, "aww"),
(1, "todd"), (3, "rew"), (8, "aww"), (3, "sw"),
(11, "llk"), (7, "rbw"), (1, "sm"), (2, "jyp"),
(4, "yq"), (5, "pled"), (12, "ligin"), (12, "toww"),
(6, "mxm"), (15, "wanone"), (9, "sunqq"), (2, "abe");
--echo # optimization is not used
let $query= select * from t1 where a in (1,2);
eval $query;
eval explain $query;
eval explain format=json $query;
eval explain extended $query;
--echo # set minimum number of values in VALUEs list when optimization works to 2
@ -239,5 +248,39 @@ execute stmt;
execute stmt;
deallocate prepare stmt;
drop table t1, t2;
--echo # use inside out access from tvc rows
let $query= select * from t3 where a in (1,4,10);
set @@in_subquery_conversion_threshold= default;
eval $query;
eval explain extended $query;
set @@in_subquery_conversion_threshold= 2;
eval $query;
eval explain extended $query;
--echo # use vectors in IN predeicate
set @@in_subquery_conversion_threshold= 4;
let $query=
select * from t1 where (a,b) in ((1,2),(3,4));
eval $query;
eval explain extended $query;
set @@in_subquery_conversion_threshold= 2;
--echo # trasformation works for the one IN predicate and doesn't work for the other
set @@in_subquery_conversion_threshold= 5;
let $query=
select * from t2
where (a,b) in ((1,2),(8,9)) and
(a,c) in ((1,3),(8,0),(5,1));
eval $query;
eval explain extended $query;
set @@in_subquery_conversion_threshold= 2;
drop table t1, t2, t3;
set @@in_subquery_conversion_threshold= default;

View File

@ -4334,8 +4334,11 @@ void Item_func_in::mark_as_condition_AND_part(TABLE_LIST *embedding)
Query_arena *arena, backup;
arena= thd->activate_stmt_arena_if_needed(&backup);
if (can_be_transformed_in_tvc(thd))
if (to_be_transformed_into_in_subq(thd))
{
transform_into_subq= true;
thd->lex->current_select->in_funcs.push_back(this, thd->mem_root);
}
if (arena)
thd->restore_active_arena(arena, &backup);

View File

@ -2128,6 +2128,7 @@ class Item_func_in :public Item_func_opt_neg,
protected:
SEL_TREE *get_func_mm_tree(RANGE_OPT_PARAM *param,
Field *field, Item *value);
bool transform_into_subq;
public:
/// An array of values, created when the bisection lookup method is used
in_vector *array;
@ -2244,7 +2245,8 @@ public:
return clone;
}
void mark_as_condition_AND_part(TABLE_LIST *embedding);
bool can_be_transformed_in_tvc(THD *thd);
bool to_be_transformed_into_in_subq(THD *thd);
bool create_value_list_for_tvc(THD *thd, List< List<Item> > *values);
Item *in_predicate_to_in_subs_transformer(THD *thd, uchar *arg);
};

View File

@ -1047,7 +1047,7 @@ bool convert_join_subqueries_to_semijoins(JOIN *join)
DBUG_RETURN(1);
if (subq_sel->handle_derived(thd->lex, DT_MERGE))
DBUG_RETURN(TRUE);
if (subq_sel->join->transform_in_predicate_into_tvc(thd))
if (subq_sel->join->transform_in_predicates_into_in_subq(thd))
DBUG_RETURN(TRUE);
subq_sel->update_used_tables();
}

View File

@ -2265,7 +2265,7 @@ void st_select_lex::init_select()
lock_type= TL_READ_DEFAULT;
tvc= 0;
in_funcs.empty();
cur_tvc= 0;
curr_tvc_name= 0;
}
/*

View File

@ -874,7 +874,7 @@ public:
transformation of IN-predicate into IN-subquery for this
st_select_lex.
*/
uint cur_tvc;
uint curr_tvc_name;
/*
Needed to correctly generate 'PRIMARY' or 'SIMPLE' for select_type column

View File

@ -1189,7 +1189,7 @@ JOIN::optimize_inner()
}
if (select_lex->first_cond_optimization &&
transform_in_predicate_into_tvc(thd))
transform_in_predicates_into_in_subq(thd))
DBUG_RETURN(1);
// Update used tables after all handling derived table procedures

View File

@ -1648,7 +1648,7 @@ public:
bool need_order, bool distinct,
const char *message);
JOIN_TAB *first_breadth_first_tab() { return join_tab; }
bool transform_in_predicate_into_tvc(THD *thd_arg);
bool transform_in_predicates_into_in_subq(THD *thd);
private:
/**
Create a temporary table to be used for processing DISTINCT/ORDER

View File

@ -374,164 +374,211 @@ void table_value_constr::print(THD *thd_arg, String *str,
/**
@brief
Transforms IN-predicate in IN-subselect
Create list of lists for TVC from the list of this IN predicate
@param thd_arg The context of the statement
@param arg Argument is 0 in this context
@param thd The context of the statement
@param values TVC list of values
@details
The method creates this SELECT statement:
The method uses the list of values of this IN predicate to build
an equivalent list of values that can be used in TVC.
SELECT * FROM (VALUES values) AS new_tvc
E.g.:
If during creation of SELECT statement some action is
unsuccesfull backup is made to the state in which system
was at the beginning of the procedure.
<value_list> = 5,2,7
<transformed_value_list> = (5),(2),(7)
<value_list> = (5,2),(7,1)
<transformed_value_list> = (5,2),(7,1)
@retval
pointer to the created SELECT statement
NULL - if creation was unsuccesfull
false if the method succeeds
true otherwise
*/
bool Item_func_in::create_value_list_for_tvc(THD *thd,
List< List<Item> > *values)
{
bool is_list_of_rows= args[1]->type() == Item::ROW_ITEM;
for (uint i=1; i < arg_count; i++)
{
List<Item> *tvc_value;
if (!(tvc_value= new (thd->mem_root) List<Item>()))
return true;
if (is_list_of_rows)
{
Item_row *row_list= (Item_row *)(args[i]);
for (uint j=0; j < row_list->cols(); j++)
{
if (tvc_value->push_back(row_list->element_index(j),
thd->mem_root))
return true;
}
}
else if (tvc_value->push_back(args[i]))
return true;
if (values->push_back(tvc_value, thd->mem_root))
return true;
}
return false;
}
static bool create_tvc_name(THD *thd, st_select_lex *parent_select,
LEX_CSTRING *alias)
{
char buff[6];
alias->length= my_snprintf(buff, sizeof(buff),
"tvc_%u", parent_select->curr_tvc_name);
alias->str= thd->strmake(buff, alias->length);
if (!alias->str)
return true;
return false;
}
/**
@brief
Transform IN predicate into IN subquery
@param thd The context of the statement
@param arg Not used
@details
The method transforms this IN predicate into in equivalent IN subquery:
<left_expr> IN (<value_list>)
=>
<left_expr> IN (SELECT * FROM (VALUES <transformed_value_list>) AS tvc_#)
E.g.:
<value_list> = 5,2,7
<transformed_value_list> = (5),(2),(7)
<value_list> = (5,2),(7,1)
<transformed_value_list> = (5,2),(7,1)
If the transformation succeeds the method returns the result IN subquery,
otherwise this IN predicate is returned.
@retval
pointer to the result of transformation if succeeded
pointer to this IN predicate otherwise
*/
Item *Item_func_in::in_predicate_to_in_subs_transformer(THD *thd,
uchar *arg)
{
SELECT_LEX *old_select= thd->lex->current_select;
if (!transform_into_subq)
return this;
transform_into_subq= false;
List<List_item> values;
Item *item;
SELECT_LEX *sel;
SELECT_LEX_UNIT *unit;
TABLE_LIST *new_tab;
Table_ident *ti;
Item_in_subselect *in_subs;
LEX *lex= thd->lex;
/* SELECT_LEX object where the transformation is performed */
SELECT_LEX *parent_select= lex->current_select;
uint8 save_derived_tables= lex->derived_tables;
Query_arena backup;
Query_arena *arena= thd->activate_stmt_arena_if_needed(&backup);
LEX *lex= thd->lex;
char buff[6];
LEX_CSTRING alias;
/*
Creation of values list of lists
Create SELECT_LEX of the subquery SQ used in the result of transformation
*/
bool list_of_lists= false;
if (args[1]->type() == Item::ROW_ITEM)
list_of_lists= true;
for (uint i=1; i < arg_count; i++)
{
List<Item> *new_value= new (thd->mem_root) List<Item>();
if (list_of_lists)
{
Item_row *in_list= (Item_row *)(args[i]);
for (uint j=0; j < in_list->cols(); i++)
new_value->push_back(in_list->element_index(j), thd->mem_root);
}
else
new_value->push_back(args[i]);
values.push_back(new_value, thd->mem_root);
}
/*
Creation of TVC name
*/
alias.length= my_snprintf(buff, sizeof(buff),
"tvc_%u", old_select->cur_tvc);
alias.str= thd->strmake(buff, alias.length);
if (!alias.str)
goto err;
/*
Creation of SELECT statement: SELECT * FROM ...
*/
if (mysql_new_select(lex, 1, NULL))
goto err;
mysql_init_select(lex);
lex->current_select->parsing_place= SELECT_LIST;
item= new (thd->mem_root) Item_field(thd, &lex->current_select->context,
/* Create item list as '*' for the subquery SQ */
Item *item;
SELECT_LEX *sq_select; // select for IN subquery;
sq_select= lex->current_select;
sq_select->parsing_place= SELECT_LIST;
item= new (thd->mem_root) Item_field(thd, &sq_select->context,
NULL, NULL, &star_clex_str);
if (item == NULL)
if (item == NULL || add_item_to_list(thd, item))
goto err;
if (add_item_to_list(thd, item))
goto err;
(lex->current_select->with_wild)++;
(sq_select->with_wild)++;
/*
Creation of TVC as derived table
Create derived table DT that will wrap TVC in the result of transformation
*/
lex->derived_tables|= DERIVED_SUBQUERY;
SELECT_LEX *tvc_select; // select for tvc
SELECT_LEX_UNIT *derived_unit; // unit for tvc_select
if (mysql_new_select(lex, 1, NULL))
goto err;
mysql_init_select(lex);
tvc_select= lex->current_select;
derived_unit= tvc_select->master_unit();
tvc_select->linkage= DERIVED_TABLE_TYPE;
sel= lex->current_select;
unit= sel->master_unit();
sel->linkage= DERIVED_TABLE_TYPE;
if (!(sel->tvc=
/* Create TVC used in the transformation */
if (create_value_list_for_tvc(thd, &values))
goto err;
if (!(tvc_select->tvc=
new (thd->mem_root)
table_value_constr(values,
sel,
sel->options)))
tvc_select,
tvc_select->options)))
goto err;
lex->check_automatic_up(UNSPECIFIED_TYPE);
lex->current_select= sel= unit->outer_select();
lex->current_select= sq_select;
ti= new (thd->mem_root) Table_ident(unit);
if (ti == NULL)
/*
Create the name of the wrapping derived table and
add it to the FROM list of the subquery SQ
*/
Table_ident *ti;
LEX_CSTRING alias;
TABLE_LIST *derived_tab;
if (!(ti= new (thd->mem_root) Table_ident(derived_unit)) ||
create_tvc_name(thd, parent_select, &alias))
goto err;
if (!(new_tab= sel->add_table_to_list(thd,
ti, &alias, 0,
TL_READ, MDL_SHARED_READ)))
if (!(derived_tab=
sq_select->add_table_to_list(thd,
ti, &alias, 0,
TL_READ, MDL_SHARED_READ)))
goto err;
sq_select->add_joined_table(derived_tab);
sq_select->add_where_field(derived_unit->first_select());
sq_select->context.table_list= sq_select->table_list.first;
sq_select->context.first_name_resolution_table= sq_select->table_list.first;
sq_select->table_list.first->derived_type= DTYPE_TABLE | DTYPE_MATERIALIZE;
lex->derived_tables|= DERIVED_SUBQUERY;
sel->add_joined_table(new_tab);
sq_select->where= 0;
sq_select->set_braces(false);
derived_unit->set_with_clause(0);
new_tab->select_lex->add_where_field(new_tab->derived->first_select());
sel->context.table_list=
sel->context.first_name_resolution_table=
sel->table_list.first;
sel->where= 0;
sel->set_braces(false);
unit->with_clause= 0;
if (!sel)
/* Create IN subquery predicate */
sq_select->parsing_place= parent_select->parsing_place;
Item_in_subselect *in_subs;
if (!(in_subs=
new (thd->mem_root) Item_in_subselect(thd, args[0], sq_select)))
goto err;
sel->parsing_place= old_select->parsing_place;
sel->table_list.first->derived_type= 10;
in_subs= new (thd->mem_root) Item_in_subselect(thd, args[0], sel);
thd->lex->derived_tables |= DERIVED_SUBQUERY;
in_subs->emb_on_expr_nest= emb_on_expr_nest;
old_select->cur_tvc++;
thd->lex->current_select= old_select;
if (arena)
thd->restore_active_arena(arena, &backup);
thd->lex->current_select= parent_select;
in_subs->fix_fields(thd, (Item **)&in_subs);
if (in_subs->fix_fields(thd, (Item **)&in_subs))
goto err;
parent_select->curr_tvc_name++;
return in_subs;
err:
if (arena)
thd->restore_active_arena(arena, &backup);
lex->derived_tables= save_derived_tables;
thd->lex->current_select= parent_select;
return this;
}
@ -552,9 +599,9 @@ err:
false otherwise
*/
bool Item_func_in::can_be_transformed_in_tvc(THD *thd)
bool Item_func_in::to_be_transformed_into_in_subq(THD *thd)
{
uint opt_can_be_used= arg_count;
uint opt_can_be_used= arg_count-1;
if (args[1]->type() == Item::ROW_ITEM)
opt_can_be_used*= ((Item_row *)(args[1]))->cols();
@ -567,32 +614,35 @@ bool Item_func_in::can_be_transformed_in_tvc(THD *thd)
/**
@brief
Calls transformer that transforms IN-predicate into IN-subquery
for this select
Transform IN predicates into IN subqueries in WHERE and ON expressions
@param thd_arg The context of the statement
@param thd The context of the statement
@details
Calls in_predicate_to_in_subs_transformer
for WHERE-part and each table from join list of this SELECT
For each IN predicate from AND parts of the WHERE condition and/or
ON expressions of the SELECT for this join the method performs
the intransformation into an equivalent IN sunquery if it's needed.
@retval
false always
*/
bool JOIN::transform_in_predicate_into_tvc(THD *thd_arg)
bool JOIN::transform_in_predicates_into_in_subq(THD *thd)
{
if (!select_lex->in_funcs.elements)
return false;
SELECT_LEX *old_select= thd_arg->lex->current_select;
enum_parsing_place old_parsing_place= select_lex->parsing_place;
thd_arg->lex->current_select= select_lex;
SELECT_LEX *save_current_select= thd->lex->current_select;
enum_parsing_place save_parsing_place= select_lex->parsing_place;
thd->lex->current_select= select_lex;
if (conds)
{
select_lex->parsing_place= IN_WHERE;
conds=
conds->transform(thd_arg,
conds->transform(thd,
&Item::in_predicate_to_in_subs_transformer,
(uchar*) 0);
select_lex->prep_where= conds ? conds->copy_andor_structure(thd) : 0;
select_lex->where= conds;
}
@ -607,7 +657,7 @@ bool JOIN::transform_in_predicate_into_tvc(THD *thd_arg)
if (table->on_expr)
{
table->on_expr=
table->on_expr->transform(thd_arg,
table->on_expr->transform(thd,
&Item::in_predicate_to_in_subs_transformer,
(uchar*) 0);
table->prep_on_expr= table->on_expr ?
@ -615,8 +665,9 @@ bool JOIN::transform_in_predicate_into_tvc(THD *thd_arg)
}
}
}
select_lex->in_funcs.empty();
select_lex->parsing_place= old_parsing_place;
thd_arg->lex->current_select= old_select;
select_lex->parsing_place= save_parsing_place;
thd->lex->current_select= save_current_select;
return false;
}