diff --git a/sql/item.h b/sql/item.h index b627729374d..313c7e4d8fd 100644 --- a/sql/item.h +++ b/sql/item.h @@ -947,6 +947,8 @@ public: virtual bool register_field_in_read_map(uchar *arg) { return 0; } virtual bool enumerate_field_refs_processor(uchar *arg) { return 0; } virtual bool mark_as_eliminated_processor(uchar *arg) { return 0; } + virtual bool eliminate_subselect_processor(uchar *arg) { return 0; } + virtual bool set_fake_select_as_master_processor(uchar *arg) { return 0; } /* The next function differs from the previous one that a bitmap to be updated is passed as uchar *arg. diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index d1ef5fb5264..a3fa1ecf748 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -1964,6 +1964,18 @@ Item *Item_in_optimizer::transform(Item_transformer transformer, uchar *argument } +bool Item_in_optimizer::is_expensive_processor(uchar *arg) +{ + return args[1]->is_expensive_processor(arg); +} + + +bool Item_in_optimizer::is_expensive() +{ + return args[1]->is_expensive(); +} + + longlong Item_func_eq::val_int() { DBUG_ASSERT(fixed == 1); @@ -5342,7 +5354,7 @@ Item *Item_func_nop_all::neg_transformer(THD *thd) /* "NOT (e $cmp$ ANY (SELECT ...)) -> e $rev_cmp$" ALL (SELECT ...) */ Item_func_not_all *new_item= new Item_func_not_all(args[0]); Item_allany_subselect *allany= (Item_allany_subselect*)args[0]; - allany->func= allany->func_creator(FALSE); + allany->create_comp_func(FALSE); allany->all= !allany->all; allany->upper_item= new_item; return new_item; @@ -5354,7 +5366,7 @@ Item *Item_func_not_all::neg_transformer(THD *thd) Item_func_nop_all *new_item= new Item_func_nop_all(args[0]); Item_allany_subselect *allany= (Item_allany_subselect*)args[0]; allany->all= !allany->all; - allany->func= allany->func_creator(TRUE); + allany->create_comp_func(TRUE); allany->upper_item= new_item; return new_item; } diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 053a54349c2..e161c1baa84 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -265,6 +265,8 @@ public: void keep_top_level_cache(); Item *transform(Item_transformer transformer, uchar *arg); virtual Item *expr_cache_insert_transformer(uchar *thd_arg); + bool is_expensive_processor(uchar *arg); + bool is_expensive(); }; class Comp_creator diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 2c00137d571..aced2faaed5 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -61,7 +61,8 @@ void Item_subselect::init(st_select_lex *select_lex, DBUG_ENTER("Item_subselect::init"); DBUG_PRINT("enter", ("select_lex: 0x%lx", (long) select_lex)); unit= select_lex->master_unit(); - + thd= unit->thd; + is_min_max_optimized= FALSE; if (unit->item) { /* @@ -159,6 +160,9 @@ void Item_in_subselect::cleanup() } first_execution= TRUE; is_constant= FALSE; + if (exec_method == MATERIALIZATION) + exec_method= NOT_TRANSFORMED; + pushed_cond_guards= NULL; Item_subselect::cleanup(); DBUG_VOID_RETURN; } @@ -184,7 +188,8 @@ bool Item_subselect::fix_fields(THD *thd_param, Item **ref) bool res; DBUG_ASSERT(fixed == 0); - engine->set_thd((thd= thd_param)); + DBUG_ASSERT(thd == thd_param); + engine->set_thd(thd); if (!done_first_fix_fields) { done_first_fix_fields= TRUE; @@ -207,11 +212,7 @@ bool Item_subselect::fix_fields(THD *thd_param, Item **ref) { // all transformation is done (used by prepared statements) changed= 1; - inside_first_fix_fields= FALSE; - - - // all transformation is done (used by prepared statements) - changed= 1; + inside_first_fix_fields= FALSE; /* Substitute the current item with an Item_in_optimizer that was @@ -221,11 +222,14 @@ bool Item_subselect::fix_fields(THD *thd_param, Item **ref) */ if (substitution) { - // did we changed top item of WHERE condition + /* + If the top item of the WHERE/HAVING condition changed, + set correct WHERE/HAVING for PS. + */ if (unit->outer_select()->where == (*ref)) - unit->outer_select()->where= substitution; // correct WHERE for PS + thd->change_item_tree(&(unit->outer_select()->where), substitution); else if (unit->outer_select()->having == (*ref)) - unit->outer_select()->having= substitution; // correct HAVING for PS + thd->change_item_tree(&(unit->outer_select()->having), substitution); (*ref)= substitution; substitution->name= name; @@ -285,6 +289,61 @@ bool Item_subselect::mark_as_eliminated_processor(uchar *arg) } +bool Item_subselect::eliminate_subselect_processor(uchar *arg) +{ + unit->item= NULL; + unit->exclude_from_tree(); + eliminated= TRUE; + return FALSE; +} + + +/* + 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. +*/ + +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 + 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 + some_select_lex->master == some_select_lex. + */ + unit->set_master(fake_select); + /* Adjust the name resolution context hierarchy accordingly. */ + 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. + */ + unit->item= this; + eliminated= FALSE; + } + return FALSE; // return TRUE ? because we need to stop processing down +} + + bool Item_subselect::mark_as_dependent(THD *thd, st_select_lex *select, Item *item) { @@ -1017,6 +1076,7 @@ Item_in_subselect::Item_in_subselect(Item * left_exp, { DBUG_ENTER("Item_in_subselect::Item_in_subselect"); left_expr= left_exp; + func= &eq_creator; init(select_lex, new select_exists_subselect(this)); max_columns= UINT_MAX; maybe_null= 1; @@ -1333,8 +1393,7 @@ my_decimal *Item_in_subselect::val_decimal(my_decimal *decimal_value) */ Item_subselect::trans_res -Item_in_subselect::single_value_transformer(JOIN *join, - Comp_creator *func) +Item_in_subselect::single_value_transformer(JOIN *join) { SELECT_LEX *select_lex= join->select_lex; DBUG_ENTER("Item_in_subselect::single_value_transformer"); @@ -1429,6 +1488,33 @@ Item_in_subselect::single_value_transformer(JOIN *join, } /* fix fields is already called for left expression */ substitution= func->create(left_expr, subs); + is_min_max_optimized= TRUE; + DBUG_RETURN(RES_OK); + } + + Item* join_having= join->having ? join->having : join->tmp_having; + if (!(join_having || select_lex->with_sum_func || + select_lex->group_list.elements) && + select_lex->table_list.elements == 0 && + !select_lex->master_unit()->is_union()) + { + Item *where_item= (Item*) select_lex->item_list.head(); + /* + it is single select without tables => possible optimization + remove the dependence mark since the item is moved to upper + select and is not outer anymore. + */ + where_item->walk(&Item::remove_dependence_processor, 0, + (uchar *) select_lex->outer_select()); + substitution= func->create(left_expr, where_item); + have_to_be_excluded= 1; + if (thd->lex->describe) + { + char warn_buff[MYSQL_ERRMSG_SIZE]; + sprintf(warn_buff, ER(ER_SELECT_REDUCED), select_lex->select_number); + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + ER_SELECT_REDUCED, warn_buff); + } DBUG_RETURN(RES_OK); } @@ -1462,25 +1548,10 @@ Item_in_subselect::single_value_transformer(JOIN *join, (char *)in_left_expr_name); master_unit->uncacheable|= UNCACHEABLE_DEPENDENT; - //psergey: placed then removed: select_lex->uncacheable|= UNCACHEABLE_DEPENDENT; + //select_lex->uncacheable|= UNCACHEABLE_DEPENDENT; } - if (!abort_on_null && left_expr->maybe_null && !pushed_cond_guards) - { - if (!(pushed_cond_guards= (bool*)join->thd->alloc(sizeof(bool)))) - DBUG_RETURN(RES_ERROR); - pushed_cond_guards[0]= TRUE; - } - - /* - If this IN predicate can be computed via materialization, do not - perform the IN -> EXISTS transformation. - */ - if (exec_method == MATERIALIZATION) - DBUG_RETURN(RES_OK); - - /* Perform the IN=>EXISTS transformation. */ - DBUG_RETURN(single_value_in_to_exists_transformer(join, func)); + DBUG_RETURN(RES_OK); } @@ -1521,33 +1592,22 @@ Item_in_subselect::single_value_transformer(JOIN *join, */ Item_subselect::trans_res -Item_in_subselect::single_value_in_to_exists_transformer(JOIN * join, - Comp_creator *func) -{ - Item *where_item; - Item *having_item; - Item_subselect::trans_res res; - - res= create_single_value_in_to_exists_cond(join, func, - &where_item, &having_item); - if (res != RES_OK) - return res; - res= inject_single_value_in_to_exists_cond(join, func, - where_item, having_item); - return res; -} - - -Item_subselect::trans_res -Item_in_subselect::create_single_value_in_to_exists_cond(JOIN * join, - Comp_creator *func, - Item **where_item, - Item **having_item) +Item_in_subselect::create_single_in_to_exists_cond(JOIN * join, + Item **where_item, + Item **having_item) { SELECT_LEX *select_lex= join->select_lex; - DBUG_ENTER("Item_in_subselect::create_single_value_in_to_exists_cond"); + /* + The non-transformed HAVING clause of 'join' may be stored differently in + JOIN::optimize: + this->tmp_having= this->having + this->having= 0; + */ + Item* join_having= join->having ? join->having : join->tmp_having; - if (join->having || select_lex->with_sum_func || + DBUG_ENTER("Item_in_subselect::create_single_in_to_exists_cond"); + + if (join_having || select_lex->with_sum_func || select_lex->group_list.elements) { Item *item= func->create(expr, @@ -1645,6 +1705,8 @@ Item_in_subselect::create_single_value_in_to_exists_cond(JOIN * join, } else { + DBUG_ASSERT(FALSE); + /* TIMOUR TODO */ *having_item= NULL; *where_item= (Item*) select_lex->item_list.head(); } @@ -1657,17 +1719,22 @@ Item_in_subselect::create_single_value_in_to_exists_cond(JOIN * join, Item_subselect::trans_res -Item_in_subselect::inject_single_value_in_to_exists_cond(JOIN * join, - Comp_creator *func, - Item *where_item, - Item *having_item) +Item_in_subselect::inject_single_in_to_exists_cond(JOIN * join, + Item *where_item, + Item *having_item) { 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; + */ + Item* join_having= join->having ? join->having : join->tmp_having; bool fix_res; - DBUG_ENTER("Item_in_subselect::single_value_in_to_exists_transformer"); + DBUG_ENTER("Item_in_subselect::inject_single_in_to_exists_cond"); - select_lex->uncacheable|= UNCACHEABLE_DEPENDENT; - if (join->having || select_lex->with_sum_func || + if (join_having || select_lex->with_sum_func || select_lex->group_list.elements) { /* @@ -1675,7 +1742,8 @@ Item_in_subselect::inject_single_value_in_to_exists_cond(JOIN * join, we can assign select_lex->having here, and pass 0 as last argument (reference) to fix_fields() */ - select_lex->having= join->having= and_items(join->having, having_item); + thd->change_item_tree(&select_lex->having, and_items(join_having, having_item)); + join->having= select_lex->having; if (join->having == having_item) having_item->name= (char*)in_having_cond; select_lex->having_fix_field= 1; @@ -1693,14 +1761,7 @@ Item_in_subselect::inject_single_value_in_to_exists_cond(JOIN * join, { if (select_lex->table_list.elements) { - Item *orig_item= (Item*) select_lex->item_list.head(); - select_lex->item_list.empty(); - select_lex->item_list.push_back(new Item_int("Not_used", - (longlong) 1, - MY_INT64_NUM_DECIMAL_DIGITS)); - select_lex->ref_pointer_array[0]= select_lex->item_list.head(); - - if (!abort_on_null && orig_item->maybe_null) + if (!abort_on_null && select_lex->item_list.head()->maybe_null) { /* Item_is_not_null_test can't be changed during fix_fields() @@ -1708,7 +1769,8 @@ Item_in_subselect::inject_single_value_in_to_exists_cond(JOIN * join, argument (reference) to fix_fields() */ having_item->name= (char*)in_having_cond; - select_lex->having= join->having= having_item; + thd->change_item_tree(&select_lex->having, having_item); + join->having= select_lex->having; select_lex->having_fix_field= 1; /* we do not check join->having->fixed, because Item_and (from @@ -1733,7 +1795,8 @@ Item_in_subselect::inject_single_value_in_to_exists_cond(JOIN * join, we can assign select_lex->having here, and pass 0 as last argument (reference) to fix_fields() */ - select_lex->where= join->conds= and_items(join->conds, where_item); + thd->change_item_tree(&select_lex->where, and_items(join->conds, where_item)); + join->conds= select_lex->where; select_lex->where->top_level_item(); /* we do not check join->conds->fixed, because Item_and can't be fixed @@ -1747,7 +1810,8 @@ Item_in_subselect::inject_single_value_in_to_exists_cond(JOIN * join, if (select_lex->master_unit()->is_union()) { having_item->name= (char*)in_having_cond; - select_lex->having= join->having= having_item; + thd->change_item_tree(&select_lex->having, having_item); + join->having= select_lex->having; select_lex->having_fix_field= 1; /* @@ -1761,25 +1825,7 @@ Item_in_subselect::inject_single_value_in_to_exists_cond(JOIN * join, DBUG_RETURN(RES_ERROR); } else - { - // it is single select without tables => possible optimization - // remove the dependence mark since the item is moved to upper - // select and is not outer anymore. - where_item->walk(&Item::remove_dependence_processor, 0, - (uchar *) select_lex->outer_select()); - where_item= func->create(left_expr, where_item); - // fix_field of item will be done in time of substituting - substitution= where_item; - have_to_be_excluded= 1; - if (thd->lex->describe) - { - char warn_buff[MYSQL_ERRMSG_SIZE]; - sprintf(warn_buff, ER(ER_SELECT_REDUCED), select_lex->select_number); - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, - ER_SELECT_REDUCED, warn_buff); - } - DBUG_RETURN(RES_REDUCE); - } + DBUG_ASSERT(FALSE); } } @@ -1826,26 +1872,10 @@ Item_in_subselect::row_value_transformer(JOIN *join) thd->lex->current_select= current; master_unit->uncacheable|= UNCACHEABLE_DEPENDENT; - - if (!abort_on_null && left_expr->maybe_null && !pushed_cond_guards) - { - if (!(pushed_cond_guards= (bool*)join->thd->alloc(sizeof(bool) * - left_expr->cols()))) - DBUG_RETURN(RES_ERROR); - for (uint i= 0; i < cols_num; i++) - pushed_cond_guards[i]= TRUE; - } + //select_lex->uncacheable|= UNCACHEABLE_DEPENDENT; } - /* - If this IN predicate can be computed via materialization, do not - perform the IN -> EXISTS transformation. - */ - if (exec_method == MATERIALIZATION) - DBUG_RETURN(RES_OK); - - /* Perform the IN=>EXISTS transformation. */ - DBUG_RETURN(row_value_in_to_exists_transformer(join)); + DBUG_RETURN(RES_OK); } @@ -1866,33 +1896,26 @@ Item_in_subselect::row_value_transformer(JOIN *join) add the equi-join and the "is null" to WHERE add the is_not_null_test to HAVING */ -Item_subselect::trans_res -Item_in_subselect::row_value_in_to_exists_transformer(JOIN * join) -{ - Item *where_item; - Item *having_item; - Item_subselect::trans_res res; - - res= create_row_value_in_to_exists_cond(join, &where_item, &having_item); - if (res != RES_OK) - return res; - res= inject_row_value_in_to_exists_cond(join, where_item, having_item); - return res; -} - Item_subselect::trans_res -Item_in_subselect::create_row_value_in_to_exists_cond(JOIN * join, - Item **where_item, - Item **having_item) +Item_in_subselect::create_row_in_to_exists_cond(JOIN * join, + Item **where_item, + Item **having_item) { SELECT_LEX *select_lex= join->select_lex; uint cols_num= left_expr->cols(); - bool is_having_used= (join->having || select_lex->with_sum_func || + /* + The non-transformed HAVING clause of 'join' may be stored differently in + 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 || select_lex->group_list.first || !select_lex->table_list.elements); - DBUG_ENTER("Item_in_subselect::create_row_value_in_to_exists_cond"); + DBUG_ENTER("Item_in_subselect::create_row_in_to_exists_cond"); *where_item= NULL; *having_item= NULL; @@ -2059,18 +2082,23 @@ Item_in_subselect::create_row_value_in_to_exists_cond(JOIN * join, Item_subselect::trans_res -Item_in_subselect::inject_row_value_in_to_exists_cond(JOIN * join, - Item *where_item, - Item *having_item) +Item_in_subselect::inject_row_in_to_exists_cond(JOIN * join, + Item *where_item, + Item *having_item) { SELECT_LEX *select_lex= join->select_lex; - bool is_having_used= (join->having || select_lex->with_sum_func || + /* + The non-transformed HAVING clause of 'join' may be stored differently in + 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 || select_lex->group_list.first || !select_lex->table_list.elements); - DBUG_ENTER("Item_in_subselect::inject_row_value_in_to_exists_cond"); - - select_lex->uncacheable|= UNCACHEABLE_DEPENDENT; + DBUG_ENTER("Item_in_subselect::inject_row_in_to_exists_cond"); if (!is_having_used) { @@ -2079,7 +2107,8 @@ Item_in_subselect::inject_row_value_in_to_exists_cond(JOIN * join, we can assign select_lex->where here, and pass 0 as last argument (reference) to fix_fields() */ - select_lex->where= join->conds= and_items(join->conds, where_item); + thd->change_item_tree(&select_lex->where, and_items(join->conds, where_item)); + join->conds= select_lex->where; select_lex->where->top_level_item(); if (!join->conds->fixed && join->conds->fix_fields(thd, 0)) DBUG_RETURN(RES_ERROR); @@ -2088,7 +2117,9 @@ Item_in_subselect::inject_row_value_in_to_exists_cond(JOIN * join, if (having_item) { bool res; - select_lex->having= join->having= and_items(join->having, having_item); + thd->change_item_tree(&select_lex->having, + and_items(join_having, having_item)); + join->having= select_lex->having; if (having_item == select_lex->having) having_item->name= (char*)in_having_cond; select_lex->having->top_level_item(); @@ -2112,7 +2143,52 @@ Item_in_subselect::inject_row_value_in_to_exists_cond(JOIN * join, Item_subselect::trans_res Item_in_subselect::select_transformer(JOIN *join) { - return select_in_like_transformer(join, &eq_creator); + return select_in_like_transformer(join); +} + + +bool Item_in_subselect::create_in_to_exists_cond(JOIN *join_arg) +{ + Item_subselect::trans_res res; + + DBUG_ASSERT(engine->engine_type() == subselect_engine::SINGLE_SELECT_ENGINE || + engine->engine_type() == subselect_engine::UNION_ENGINE); + /* + TIMOUR 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. + */ + init_cond_guards(); + join_arg->select_lex->uncacheable|= UNCACHEABLE_DEPENDENT; + if (left_expr->cols() == 1) + res= create_single_in_to_exists_cond(join_arg, + &(join_arg->in_to_exists_where), + &(join_arg->in_to_exists_having)); + else + res= create_row_in_to_exists_cond(join_arg, + &(join_arg->in_to_exists_where), + &(join_arg->in_to_exists_having)); + return (res != RES_OK); +} + + +bool Item_in_subselect::inject_in_to_exists_cond(JOIN * join_arg) +{ + Item_subselect::trans_res res; + + if (left_expr->cols() == 1) + res= inject_single_in_to_exists_cond(join_arg, + join_arg->in_to_exists_where, + join_arg->in_to_exists_having); + else + res= inject_row_in_to_exists_cond(join_arg, + join_arg->in_to_exists_where, + join_arg->in_to_exists_having); + + return (res != RES_OK && res != RES_REDUCE); } @@ -2138,7 +2214,7 @@ Item_in_subselect::select_transformer(JOIN *join) */ Item_subselect::trans_res -Item_in_subselect::select_in_like_transformer(JOIN *join, Comp_creator *func) +Item_in_subselect::select_in_like_transformer(JOIN *join) { Query_arena *arena, backup; SELECT_LEX *current= thd->lex->current_select; @@ -2193,14 +2269,6 @@ Item_in_subselect::select_in_like_transformer(JOIN *join, Comp_creator *func) if (result) goto err; - /* - If we didn't choose an execution method up to this point, we choose - the IN=>EXISTS transformation. - */ - if (exec_method == NOT_TRANSFORMED) - exec_method= IN_TO_EXISTS; - arena= thd->activate_stmt_arena_if_needed(&backup); - /* Both transformers call fix_fields() only for Items created inside them, and all that items do not make permanent changes in current item arena @@ -2208,8 +2276,9 @@ Item_in_subselect::select_in_like_transformer(JOIN *join, Comp_creator *func) of Item, we have to call fix_fields() for it only with original arena to avoid memory leack) */ + arena= thd->activate_stmt_arena_if_needed(&backup); if (left_expr->cols() == 1) - res= single_value_transformer(join, func); + res= single_value_transformer(join); else { /* we do not support row operation for ALL/ANY/SOME */ @@ -2345,20 +2414,12 @@ void Item_in_subselect::update_used_tables() @retval FALSE an execution method was chosen successfully */ -bool Item_in_subselect::setup_engine() +bool Item_in_subselect::setup_mat_engine() { subselect_hash_sj_engine *mat_engine= NULL; subselect_single_select_engine *select_engine; - DBUG_ENTER("Item_in_subselect::setup_engine"); - - - SELECT_LEX *save_select= thd->lex->current_select; - thd->lex->current_select= get_select_lex(); - int res= thd->lex->current_select->join->optimize(); - thd->lex->current_select= save_select; - if (res) - DBUG_RETURN(TRUE); + DBUG_ENTER("Item_in_subselect::setup_mat_engine"); /* The select_engine (that executes transformed IN=>EXISTS subselects) is @@ -2373,34 +2434,7 @@ bool Item_in_subselect::setup_engine() DBUG_RETURN(TRUE); if (mat_engine->init(&select_engine->join->fields_list)) - { - Item_subselect::trans_res trans_res; - /* - If for some reason we cannot use materialization for this IN predicate, - delete all materialization-related objects, and apply the IN=>EXISTS - transformation. - */ - delete mat_engine; - mat_engine= NULL; - exec_method= NOT_TRANSFORMED; - - if (left_expr->cols() == 1) - trans_res= single_value_in_to_exists_transformer(select_engine->join, - &eq_creator); - else - trans_res= row_value_in_to_exists_transformer(select_engine->join); - - /* - The IN=>EXISTS transformation above injects new predicates into the - WHERE and HAVING clauses. Since the subquery was already optimized, - below we force its reoptimization with the new injected conditions - by the first call to subselect_single_select_engine::exec(). - This is the only case of lazy subquery optimization in the server. - */ - DBUG_ASSERT(select_engine->join->optimized); - select_engine->join->optimized= false; - DBUG_RETURN(trans_res != Item_subselect::RES_OK); - } + DBUG_RETURN(TRUE); /* Reset the "LIMIT 1" set in Item_exists_subselect::fix_length_and_dec. @@ -2456,6 +2490,20 @@ bool Item_in_subselect::init_left_expr_cache() } +bool Item_in_subselect::init_cond_guards() +{ + uint cols_num= left_expr->cols(); + if (!abort_on_null && left_expr->maybe_null && !pushed_cond_guards) + { + if (!(pushed_cond_guards= (bool*)thd->alloc(sizeof(bool) * cols_num))) + return TRUE; + for (uint i= 0; i < cols_num; i++) + pushed_cond_guards[i]= TRUE; + } + return FALSE; +} + + /* Callback to test if an IN predicate is expensive. @@ -2471,7 +2519,7 @@ bool Item_in_subselect::init_left_expr_cache() bool Item_in_subselect::is_expensive_processor(uchar *arg) { - return exec_method == MATERIALIZATION; + return TRUE; // exec_method == MATERIALIZATION; } @@ -2482,7 +2530,7 @@ Item_allany_subselect::select_transformer(JOIN *join) exec_method= IN_TO_EXISTS; if (upper_item) upper_item->show= 1; - DBUG_RETURN(select_in_like_transformer(join, func)); + DBUG_RETURN(select_in_like_transformer(join)); } diff --git a/sql/item_subselect.h b/sql/item_subselect.h index d6cca97c351..d35d036e9ea 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -108,6 +108,9 @@ public: /* subquery is transformed */ bool changed; + /* TIMOUR: this is temporary, remove it. */ + bool is_min_max_optimized; + /* TRUE <=> The underlying SELECT is correlated w.r.t some ancestor select */ bool is_correlated; @@ -180,6 +183,8 @@ public: enum_parsing_place place() { return parsing_place; } bool walk(Item_processor processor, bool walk_subquery, uchar *arg); bool mark_as_eliminated_processor(uchar *arg); + bool eliminate_subselect_processor(uchar *arg); + bool set_fake_select_as_master_processor(uchar *arg); bool enumerate_field_refs_processor(uchar *arg); bool check_vcol_func_processor(uchar *int_arg) { @@ -326,8 +331,6 @@ public: class Item_in_subselect :public Item_exists_subselect { -public: - Item *left_expr; protected: /* Cache of the left operand of the subquery predicate. Allocated in the @@ -350,10 +353,30 @@ protected: Item_in_optimizer *optimizer; bool was_null; bool abort_on_null; -public: /* Used to trigger on/off conditions that were pushed down to subselect */ bool *pushed_cond_guards; - + Comp_creator *func; + +protected: + bool init_cond_guards(); + trans_res select_in_like_transformer(JOIN *join); + trans_res single_value_transformer(JOIN *join); + trans_res row_value_transformer(JOIN * join); + trans_res create_single_in_to_exists_cond(JOIN * join, + Item **where_item, + Item **having_item); + trans_res inject_single_in_to_exists_cond(JOIN * join, + Item *where_item, + Item *having_item); + + trans_res create_row_in_to_exists_cond(JOIN * join, + Item **where_item, + Item **having_item); + trans_res inject_row_in_to_exists_cond(JOIN * join, + Item *where_item, + Item *having_item); +public: + Item *left_expr; /* Priority of this predicate in the convert-to-semi-join-nest process. */ int sj_convert_priority; /* @@ -410,8 +433,9 @@ public: Item_in_subselect() :Item_exists_subselect(), left_expr_cache(0), first_execution(TRUE), is_constant(FALSE), optimizer(0), abort_on_null(0), - pushed_cond_guards(NULL), exec_method(NOT_TRANSFORMED), upper_item(0) - {} + pushed_cond_guards(NULL), func(NULL), exec_method(NOT_TRANSFORMED), + upper_item(0) + {} void cleanup(); subs_type substype() { return IN_SUBS; } void reset() @@ -422,28 +446,8 @@ public: was_null= 0; } trans_res select_transformer(JOIN *join); - trans_res select_in_like_transformer(JOIN *join, Comp_creator *func); - trans_res single_value_transformer(JOIN *join, Comp_creator *func); - trans_res row_value_transformer(JOIN * join); - - trans_res single_value_in_to_exists_transformer(JOIN * join, - Comp_creator *func); - trans_res create_single_value_in_to_exists_cond(JOIN * join, - Comp_creator *func, - Item **where_term, - Item **having_term); - trans_res inject_single_value_in_to_exists_cond(JOIN * join, - Comp_creator *func, - Item *where_term, - Item *having_term); - - trans_res row_value_in_to_exists_transformer(JOIN * join); - trans_res create_row_value_in_to_exists_cond(JOIN * join, - Item **where_term, - Item **having_term); - trans_res inject_row_value_in_to_exists_cond(JOIN * join, - Item *where_term, - Item *having_term); + bool create_in_to_exists_cond(JOIN * join_arg); + bool inject_in_to_exists_cond(JOIN * join_arg); virtual bool exec(); longlong val_int(); @@ -459,11 +463,12 @@ public: bool fix_fields(THD *thd, Item **ref); void fix_after_pullout(st_select_lex *new_parent, Item **ref); void update_used_tables(); - bool setup_engine(); + bool setup_mat_engine(); bool init_left_expr_cache(); /* Inform 'this' that it was computed, and contains a valid result. */ void set_first_execution() { if (first_execution) first_execution= FALSE; } bool is_expensive_processor(uchar *arg); + bool is_expensive() { return TRUE; } bool expr_cache_is_needed(THD *thd); /* @@ -485,7 +490,6 @@ class Item_allany_subselect :public Item_in_subselect { public: chooser_compare_func_creator func_creator; - Comp_creator *func; bool all; Item_allany_subselect(Item * left_expr, chooser_compare_func_creator fc, @@ -494,6 +498,7 @@ public: // only ALL subquery has upper not subs_type substype() { return all?ALL_SUBS:ANY_SUBS; } trans_res select_transformer(JOIN *join); + void create_comp_func(bool invert) { func= func_creator(invert); } virtual void print(String *str, enum_query_type query_type); }; diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc index 8567d291341..13b8bb40ea7 100644 --- a/sql/opt_subselect.cc +++ b/sql/opt_subselect.cc @@ -67,12 +67,15 @@ int check_and_do_in_subquery_rewrites(JOIN *join) { THD *thd=join->thd; st_select_lex *select_lex= join->select_lex; + st_select_lex_unit* parent_unit= select_lex->master_unit(); DBUG_ENTER("check_and_do_in_subquery_rewrites"); /* If 1) this join is inside a subquery (of any type except FROM-clause subquery) and 2) we aren't just normalizing a VIEW + 3) The join and its select_lex object do not represent the 'fake' + select used to compute the result of a UNION. Then perform early unconditional subquery transformations: - Convert subquery predicate into semi-join, or @@ -84,8 +87,9 @@ int check_and_do_in_subquery_rewrites(JOIN *join) TODO: for PS, make the whole block execute only on the first execution */ Item_subselect *subselect; - if (!thd->lex->view_prepare_mode && // (1) - (subselect= select_lex->master_unit()->item)) // (2) + if (!thd->lex->view_prepare_mode && // (1) + (subselect= parent_unit->item))// && // (2) +// select_lex == parent_unit->fake_select_lex) // (3) { Item_in_subselect *in_subs= NULL; if (subselect->substype() == Item_subselect::IN_SUBS) @@ -129,6 +133,9 @@ int check_and_do_in_subquery_rewrites(JOIN *join) if (failure) DBUG_RETURN(-1); /* purecov: deadcode */ } + if (select_lex == parent_unit->fake_select_lex) + DBUG_RETURN(0); + DBUG_PRINT("info", ("Checking if subq can be converted to semi-join")); /* Check if we're in subquery that is a candidate for flattening into a @@ -154,7 +161,7 @@ int check_and_do_in_subquery_rewrites(JOIN *join) !join->having && !select_lex->with_sum_func && // 4 thd->thd_marker.emb_on_expr_nest && // 5 select_lex->outer_select()->join && // 6 - select_lex->master_unit()->first_select()->leaf_tables && // 7 + parent_unit->first_select()->leaf_tables && // 7 in_subs->exec_method == Item_in_subselect::NOT_TRANSFORMED && // 8 select_lex->outer_select()->leaf_tables && // 9 !((join->select_options | // 10 @@ -212,7 +219,7 @@ int check_and_do_in_subquery_rewrites(JOIN *join) if (optimizer_flag(thd, OPTIMIZER_SWITCH_MATERIALIZATION) && in_subs && // 1 !select_lex->is_part_of_union() && // 2 - select_lex->master_unit()->first_select()->leaf_tables && // 3 + parent_unit->first_select()->leaf_tables && // 3 thd->lex->sql_command == SQLCOM_SELECT && // * select_lex->outer_select()->leaf_tables && // 3A subquery_types_allow_materialization(in_subs) && @@ -223,15 +230,27 @@ int check_and_do_in_subquery_rewrites(JOIN *join) !in_subs->is_correlated && // 5 in_subs->exec_method == Item_in_subselect::NOT_TRANSFORMED) // 6 { - in_subs->exec_method= Item_in_subselect::MATERIALIZATION; + /* + Materialization is possible, later the optimize phase of each + subquery will choose either materialization or in-to-exists based + on cost. + */ + in_subs->exec_method= Item_in_subselect::MATERIALIZATION; + } + else if (in_subs) + { + /* Materialization is not possible at all. */ + in_subs->exec_method= Item_in_subselect::IN_TO_EXISTS; } + /* + Transform each subquery predicate according to its overloaded + transformer. + */ Item_subselect::trans_res trans_res; if ((trans_res= subselect->select_transformer(join)) != Item_subselect::RES_OK) - { DBUG_RETURN((trans_res == Item_subselect::RES_ERROR)); - } } } DBUG_RETURN(0); @@ -3505,3 +3524,56 @@ static void remove_subq_pushed_predicates(JOIN *join, Item **where) } +bool JOIN::choose_subquery_plan() +{ + double mat_strategy_cost; /* The cost to compute IN via materialization. */ + double in_exists_strategy_cost; /* The cost of the IN->EXISTS strategy. */ + bool res; + + DBUG_ASSERT(in_to_exists_where || in_to_exists_having); + DBUG_ASSERT(select_lex->master_unit()->item && + (select_lex->master_unit()->item->substype() == + Item_subselect::IN_SUBS || + select_lex->master_unit()->item->substype() == + Item_subselect::ALL_SUBS || + select_lex->master_unit()->item->substype() == + Item_subselect::ANY_SUBS)); + + Item_in_subselect *in_subs= (Item_in_subselect*) + select_lex->master_unit()->item; + + /* Always revert to IN->EXISTS. */ + mat_strategy_cost= 1; + in_exists_strategy_cost= 0; + + if (mat_strategy_cost < in_exists_strategy_cost) + { + in_subs->exec_method = Item_in_subselect::MATERIALIZATION; + if (in_subs->setup_mat_engine()) + { + /* + In some cases it is not possible to create usable indexes for the + materialization strategy, so fall back to IN->EXISTS. + */ + in_subs->exec_method= Item_in_subselect::IN_TO_EXISTS; + } + } + else + in_subs->exec_method= Item_in_subselect::IN_TO_EXISTS; + + if (in_subs->exec_method == Item_in_subselect::MATERIALIZATION) + { + + // TODO: should we unset the UNCACHEABLE_DEPENDENT flag fro + // select_lex->uncacheable; ? + // This affects how we execute JOIN::join_free - full or not. + // inner_join->restore_plan (keyuse, best_positions, best_read) + ; + } + else if (in_subs->exec_method == Item_in_subselect::IN_TO_EXISTS) + res= in_subs->inject_in_to_exists_cond(this); + else + DBUG_ASSERT(FALSE); + + return res; +} diff --git a/sql/sql_class.cc b/sql/sql_class.cc index a4e8c84401b..930cf2393f7 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -2630,6 +2630,7 @@ void Query_arena::free_items() for (; free_list; free_list= next) { next= free_list->next; + DBUG_ASSERT(free_list != next); free_list->delete_self(); } /* Postcondition: free_list is 0 */ diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index ddb6af97865..862d3326c2a 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -92,6 +92,10 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, } } + /* Apply the IN=>EXISTS transformation to all subqueries and optimize them. */ + if (select_lex->optimize_unflattened_subqueries()) + DBUG_RETURN(TRUE); + const_cond= (!conds || conds->const_item()); safe_update=test(thd->options & OPTION_SAFE_UPDATES); if (safe_update && const_cond) diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 7b19b4874b1..69a0e9c6e14 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -1734,17 +1734,29 @@ void st_select_lex_node::fast_exclude() } + /* - excluding select_lex structure (except first (first select can't be + Exclude a node from the tree lex structure, but leave it in the global + list of nodes. +*/ + +void st_select_lex_node::exclude_from_tree() +{ + if ((*prev= next)) + next->prev= prev; +} + + +/* + Exclude select_lex structure (except first (first select can't be deleted, because it is most upper select)) */ void st_select_lex_node::exclude() { - //exclude from global list + /* exclude from global list */ fast_exclude(); - //exclude from other structures - if ((*prev= next)) - next->prev= prev; + /* exclude from other structures */ + exclude_from_tree(); /* We do not need following statements, because prev pointer of first list element point to master->slave @@ -3076,6 +3088,46 @@ bool st_select_lex::add_index_hint (THD *thd, char *str, uint length) str, length)); } + +bool st_select_lex::optimize_unflattened_subqueries() +{ + for (SELECT_LEX_UNIT *un= first_inner_unit(); un; un= un->next_unit()) + { + Item_subselect *subquery_predicate= un->item; + if (subquery_predicate) + { + Item_in_subselect *item_in= NULL; + if (subquery_predicate->substype() == Item_subselect::IN_SUBS || + subquery_predicate->substype() == Item_subselect::ALL_SUBS || + subquery_predicate->substype() == Item_subselect::ANY_SUBS) + item_in= (Item_in_subselect*) subquery_predicate; + for (SELECT_LEX *sl= un->first_select(); sl; sl= sl->next_select()) + { + JOIN *inner_join= sl->join; + SELECT_LEX *save_select= un->thd->lex->current_select; + int res; + + /* + Make sure that we do not create IN->EXISTS conditions for + subquery predicates that were substituted by Item_maxmin_subselect + or by Item_singlerow_subselect. + */ + DBUG_ASSERT(!item_in || (item_in && !item_in->is_min_max_optimized)); + if (item_in && item_in->create_in_to_exists_cond(inner_join)) + return TRUE; + un->set_limit(un->global_parameters); + un->thd->lex->current_select= sl; + res= inner_join->optimize(); + un->thd->lex->current_select= save_select; + if (res) + return TRUE; + } + } + } + return FALSE; +} + + /** A routine used by the parser to decide whether we are specifying a full partitioning or if only partitions to add or to split. @@ -3093,4 +3145,3 @@ bool st_lex::is_partition_management() const (alter_info.flags == ALTER_ADD_PARTITION || alter_info.flags == ALTER_REORGANIZE_PARTITION)); } - diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 335aa7f4ed4..d106bfd55fd 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -439,6 +439,7 @@ public: st_select_lex_node(): linkage(UNSPECIFIED_TYPE) {} virtual ~st_select_lex_node() {} inline st_select_lex_node* get_master() { return master; } + inline void set_master(st_select_lex_node* master_arg) { master= master_arg; } virtual void init_query(); virtual void init_select(); void include_down(st_select_lex_node *upper); @@ -446,6 +447,7 @@ public: void include_standalone(st_select_lex_node *sel, st_select_lex_node **ref); void include_global(st_select_lex_node **plink); void exclude(); + void exclude_from_tree(); virtual st_select_lex_unit* master_unit()= 0; virtual st_select_lex* outer_select()= 0; @@ -839,6 +841,12 @@ public: void clear_index_hints(void) { index_hints= NULL; } bool is_part_of_union() { return master_unit()->is_union(); } + /* + Optimize all subqueries that have not been flattened into semi-joins. + This functionality is a method of SELECT_LEX instead of JOIN because + some SQL statements as DELETE do not have a corresponding JOIN object. + */ + bool optimize_unflattened_subqueries(); private: /* current index hint kind. used in filling up index_hints */ enum index_hint_type current_index_hint_type; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index a5baa60fe32..574353897de 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -926,9 +926,27 @@ JOIN::optimize() { DBUG_PRINT("info",("No tables")); error= 0; - /* Create all structures needed for materialized subquery execution. */ - if (setup_subquery_materialization()) + if (optimize_unflattened_subqueries()) DBUG_RETURN(1); + if (in_to_exists_where || in_to_exists_having) + { + /* + TIMOUR: TODO: refactor this block and JOIN::choose_subquery_plan + */ + Item_in_subselect *in_subs= (Item_in_subselect*) + select_lex->master_unit()->item; + + if (in_subs->exec_method == Item_in_subselect::MATERIALIZATION) + ; // setup materialized execution structures + else if (in_subs->exec_method == Item_in_subselect::IN_TO_EXISTS) + { + if (in_subs->inject_in_to_exists_cond(this)) + DBUG_RETURN(1); + tmp_having= having; + } + else + DBUG_ASSERT(FALSE); + } DBUG_RETURN(0); } error= -1; // Error is sent to client @@ -1286,7 +1304,7 @@ JOIN::optimize() init_ftfuncs(thd, select_lex, test(order)); /* Create all structures needed for materialized subquery execution. */ - if (setup_subquery_materialization()) + if (optimize_unflattened_subqueries()) DBUG_RETURN(1); int res; @@ -1381,6 +1399,34 @@ JOIN::optimize() if (join_tab->is_using_loose_index_scan()) tmp_table_param.precomputed_group_by= TRUE; + error= 0; + DBUG_RETURN(0); + +setup_subq_exit: + /* + Even with zero matching rows, subqueries in the HAVING clause may + need to be evaluated if there are aggregate functions in the query. + If we planned to materialize the subquery, we need to set it up + properly before prematurely leaving optimize(). + */ + if (optimize_unflattened_subqueries()) + DBUG_RETURN(1); + error= 0; + DBUG_RETURN(0); +} + + +/** + Create and initialize objects neeed for the execution of a query plan. +*/ + +int JOIN::init_execution() +{ + DBUG_ENTER("JOIN::init_execution"); + + DBUG_ASSERT(optimized); + initialized= true; + /* Create a tmp table if distinct or if the sort is too complicated */ if (need_tmp) { @@ -1413,7 +1459,7 @@ JOIN::optimize() select_options, tmp_rows_limit, (char *) ""))) - { + { DBUG_RETURN(1); } @@ -1499,19 +1545,6 @@ JOIN::optimize() DBUG_RETURN(-1); /* purecov: inspected */ } - error= 0; - DBUG_RETURN(0); - -setup_subq_exit: - /* - Even with zero matching rows, subqueries in the HAVING clause may - need to be evaluated if there are aggregate functions in the - query. If we have planned to materialize the subquery, we need to - set it up properly before prematurely leaving optimize(). - */ - if (setup_subquery_materialization()) - DBUG_RETURN(1); - error= 0; DBUG_RETURN(0); } @@ -1775,6 +1808,9 @@ JOIN::exec() int tmp_error; DBUG_ENTER("JOIN::exec"); + if (!initialized && init_execution()) + DBUG_VOID_RETURN; + thd_proc_info(thd, "executing"); error= 0; if (procedure) @@ -2604,25 +2640,9 @@ err: @retval TRUE error occurred. */ -bool JOIN::setup_subquery_materialization() +bool JOIN::optimize_unflattened_subqueries() { - for (SELECT_LEX_UNIT *un= select_lex->first_inner_unit(); un; - un= un->next_unit()) - { - for (SELECT_LEX *sl= un->first_select(); sl; sl= sl->next_select()) - { - Item_subselect *subquery_predicate= sl->master_unit()->item; - if (subquery_predicate && - subquery_predicate->substype() == Item_subselect::IN_SUBS) - { - Item_in_subselect *in_subs= (Item_in_subselect*) subquery_predicate; - if (in_subs->exec_method == Item_in_subselect::MATERIALIZATION && - in_subs->setup_engine()) - return TRUE; - } - } - } - return FALSE; + return select_lex->optimize_unflattened_subqueries(); } @@ -3143,6 +3163,10 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, COND *conds, sizeof(POSITION)*join->const_tables); join->best_read=1.0; } + if ((join->in_to_exists_where || join->in_to_exists_having) + && join->choose_subquery_plan()) + goto error; + /* Generate an execution plan from the found optimal join order. */ DBUG_RETURN(join->thd->killed || get_best_combination(join)); diff --git a/sql/sql_select.h b/sql/sql_select.h index 2b7fc8fd47c..c87147f6106 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -1573,8 +1573,15 @@ public: bool union_part; ///< this subselect is part of union bool optimized; ///< flag to avoid double optimization in EXPLAIN + bool initialized; ///< flag to avoid double init_execution calls Array sj_subselects; + /* + Additional WHERE and HAVING predicates to be considered for IN=>EXISTS + subquery transformation of a JOIN object. + */ + Item *in_to_exists_where; + Item *in_to_exists_having; /* Temporary tables used to weed-out semi-join duplicates */ List sj_tmp_tables; @@ -1649,6 +1656,7 @@ public: ref_pointer_array_size= 0; zero_result_cause= 0; optimized= 0; + initialized= 0; cond_equal= 0; group_optimized_away= 0; @@ -1662,6 +1670,8 @@ public: no_const_tables= FALSE; first_select= sub_select; + in_to_exists_where= NULL; + in_to_exists_having= NULL; } int prepare(Item ***rref_pointer_array, TABLE_LIST *tables, uint wind_num, @@ -1670,12 +1680,13 @@ public: SELECT_LEX_UNIT *unit); int optimize(); int reinit(); + int init_execution(); void exec(); int destroy(); void restore_tmp(); bool alloc_func_list(); bool flatten_subqueries(); - bool setup_subquery_materialization(); + bool optimize_unflattened_subqueries(); bool make_sum_func_list(List &all_fields, List &send_fields, bool before_group_by, bool recompute= FALSE); @@ -1735,6 +1746,7 @@ public: NULL : join_tab+const_tables; } bool setup_subquery_caches(); + bool choose_subquery_plan(); private: /** TRUE if the query contains an aggregate function but has no GROUP diff --git a/sql/sql_union.cc b/sql/sql_union.cc index 374e92c6a52..d91bdc069e8 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -186,6 +186,8 @@ st_select_lex_unit::init_prepare_fake_select_lex(THD *thd_arg) { (*order->item)->walk(&Item::change_context_processor, 0, (uchar*) &fake_select_lex->context); + (*order->item)->walk(&Item::set_fake_select_as_master_processor, 0, + (uchar*) fake_select_lex); } } @@ -271,6 +273,18 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, can_skip_order_by= is_union_select && !(sl->braces && sl->explicit_limit); + /* + Remove all references from the select_lex_units to the subqueries that + are inside the ORDER BY clause. + */ + if (can_skip_order_by) + { + for (ORDER *ord= (ORDER *)sl->order_list.first; ord; ord= ord->next) + { + (*ord->item)->walk(&Item::eliminate_subselect_processor, FALSE, NULL); + } + } + saved_error= join->prepare(&sl->ref_pointer_array, (TABLE_LIST*) sl->table_list.first, sl->with_wild, diff --git a/sql/sql_update.cc b/sql/sql_update.cc index a7d12ce6efd..da744bf83a4 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -290,6 +290,10 @@ int mysql_update(THD *thd, DBUG_RETURN(1); /* purecov: inspected */ } + /* Apply the IN=>EXISTS transformation to all subqueries and optimize them. */ + if (select_lex->optimize_unflattened_subqueries()) + DBUG_RETURN(TRUE); + if (select_lex->inner_refs_list.elements && fix_inner_refs(thd, all_fields, select_lex, select_lex->ref_pointer_array)) DBUG_RETURN(1);