diff --git a/sql/item.cc b/sql/item.cc index 7b6f6e89a1f..a0409037f5c 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -386,6 +386,8 @@ Item::Item(): decimals= 0; max_length= 0; with_subselect= 0; cmp_context= IMPOSSIBLE_RESULT; + /* Initially this item is not attached to any JOIN_TAB. */ + join_tab_idx= MAX_TABLES; /* Put item in free list so that we can free all items at end */ THD *thd= current_thd; @@ -414,6 +416,7 @@ Item::Item(): tables. */ Item::Item(THD *thd, Item *item): + join_tab_idx(item->join_tab_idx), is_expensive_cache(-1), rsize(0), str_value(item->str_value), @@ -470,6 +473,7 @@ void Item::cleanup() DBUG_ENTER("Item::cleanup"); fixed=0; marker= 0; + join_tab_idx= MAX_TABLES; if (orig_name) name= orig_name; DBUG_VOID_RETURN; diff --git a/sql/item.h b/sql/item.h index 9adea4ec726..a212736f168 100644 --- a/sql/item.h +++ b/sql/item.h @@ -491,6 +491,17 @@ typedef void (*Cond_traverser) (const Item *item, void *arg); class Item { Item(const Item &); /* Prevent use of these */ void operator=(Item &); + /** + The index in the JOIN::join_tab array of the JOIN_TAB this Item is attached + to. Items are attached (or 'pushed') to JOIN_TABs during optimization by the + make_cond_for_table procedure. During query execution, this item is + evaluated when the join loop reaches the corresponding JOIN_TAB. + + If the value of join_tab_idx >= MAX_TABLES, this means that there is no + corresponding JOIN_TAB. + */ + uint join_tab_idx; + public: static void *operator new(size_t size) throw () { return sql_alloc(size); } @@ -1179,6 +1190,16 @@ public: Item* set_expr_cache(THD *thd, List &depends_on); virtual Item *get_cached_item() { return NULL; } + /** + Set the join tab index to the minimal (left-most) JOIN_TAB to which this + Item is attached. + */ + virtual void set_join_tab_idx(uint join_tab_idx_arg) + { + if (join_tab_idx_arg < join_tab_idx) + join_tab_idx= join_tab_idx_arg; + } + virtual uint get_join_tab_idx() { return join_tab_idx; } }; diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index a60a3b00d8b..0f197cc6880 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -268,6 +268,8 @@ public: virtual Item *expr_cache_insert_transformer(uchar *thd_arg); bool is_expensive_processor(uchar *arg); bool is_expensive(); + void set_join_tab_idx(uint join_tab_idx_arg) + { args[1]->set_join_tab_idx(join_tab_idx_arg); } }; class Comp_creator diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index a052b9139b4..c1bd04f5ef1 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -172,11 +172,11 @@ Item_subselect::~Item_subselect() engine= NULL; } -Item_subselect::trans_res +bool Item_subselect::select_transformer(JOIN *join) { DBUG_ENTER("Item_subselect::select_transformer"); - DBUG_RETURN(RES_OK); + DBUG_RETURN(false); } @@ -809,13 +809,17 @@ void Item_singlerow_subselect::reset() - switch off this optimization for prepare statement, because we do not rollback this changes. Make rollback for it, or special name resolving mode in 5.0. + + @param join Join object of the subquery (i.e. 'child' join). + + @retval false The subquery was transformed */ -Item_subselect::trans_res +bool Item_singlerow_subselect::select_transformer(JOIN *join) { DBUG_ENTER("Item_singlerow_subselect::select_transformer"); if (changed) - DBUG_RETURN(RES_OK); + DBUG_RETURN(false); SELECT_LEX *select_lex= join->select_lex; Query_arena *arena= thd->stmt_arena; @@ -842,7 +846,6 @@ Item_singlerow_subselect::select_transformer(JOIN *join) !arena->is_stmt_prepare_or_first_sp_execute() ) { - have_to_be_excluded= 1; if (thd->lex->describe) { @@ -858,9 +861,8 @@ Item_singlerow_subselect::select_transformer(JOIN *join) */ substitution->walk(&Item::remove_dependence_processor, 0, (uchar *) select_lex->outer_select()); - DBUG_RETURN(RES_REDUCE); } - DBUG_RETURN(RES_OK); + DBUG_RETURN(false); } @@ -1368,11 +1370,11 @@ my_decimal *Item_in_subselect::val_decimal(my_decimal *decimal_value) @param join Join object of the subquery (i.e. 'child' join). @details - Rewrite a single-column subquery using rule-based approach. The subquery + Rewrite a single-column subquery using rule-based approach. Given 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 + First, try to convert the subquery to a scalar-result subquery in one of the forms: - oe $cmp$ (SELECT MAX(...) ) // handled by Item_singlerow_subselect @@ -1387,11 +1389,11 @@ my_decimal *Item_in_subselect::val_decimal(my_decimal *decimal_value) 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 + @retval false The subquery was transformed + @retval true Error */ -Item_subselect::trans_res +bool Item_in_subselect::single_value_transformer(JOIN *join) { SELECT_LEX *select_lex= join->select_lex; @@ -1405,7 +1407,7 @@ Item_in_subselect::single_value_transformer(JOIN *join) if (select_lex->item_list.elements > 1) { my_error(ER_OPERAND_COLUMNS, MYF(0), 1); - DBUG_RETURN(RES_ERROR); + DBUG_RETURN(true); } /* @@ -1425,7 +1427,7 @@ Item_in_subselect::single_value_transformer(JOIN *join) if (substitution) { /* It is second (third, ...) SELECT of UNION => All is done */ - DBUG_RETURN(RES_OK); + DBUG_RETURN(false); } Item *subs; @@ -1470,7 +1472,7 @@ Item_in_subselect::single_value_transformer(JOIN *join) we do not check item->fixed */ if (item->fix_fields(thd, 0)) - DBUG_RETURN(RES_ERROR); + DBUG_RETURN(true); thd->lex->allow_sum_func= save_allow_sum_func; /* we added aggregate function => we have to change statistic */ count_field_types(select_lex, &join->tmp_table_param, join->all_fields, @@ -1487,7 +1489,7 @@ Item_in_subselect::single_value_transformer(JOIN *join) } /* fix fields is already called for left expression */ substitution= func->create(left_expr, subs); - DBUG_RETURN(RES_OK); + DBUG_RETURN(false); } Item* join_having= join->having ? join->having : join->tmp_having; @@ -1513,7 +1515,7 @@ Item_in_subselect::single_value_transformer(JOIN *join) push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, ER_SELECT_REDUCED, warn_buff); } - DBUG_RETURN(RES_OK); + DBUG_RETURN(false); } /* @@ -1533,7 +1535,7 @@ Item_in_subselect::single_value_transformer(JOIN *join) if (!optimizer || optimizer->fix_left(thd, 0)) { thd->lex->current_select= current; - DBUG_RETURN(RES_ERROR); + DBUG_RETURN(true); } thd->lex->current_select= current; @@ -1554,7 +1556,7 @@ Item_in_subselect::single_value_transformer(JOIN *join) // select_lex->uncacheable|= UNCACHEABLE_DEPENDENT; } - DBUG_RETURN(RES_OK); + DBUG_RETURN(false); } @@ -1603,11 +1605,11 @@ bool Item_in_subselect::fix_having(Item *having, SELECT_LEX *select_lex) WHERE subq_where AND trigcond((oe $cmp$ ie) OR (ie IS NULL)) HAVING trigcond((ie)) - @retval RES_OK If the new conditions were created successfully - @retval RES_ERROR Error + @retval false If the new conditions were created successfully + @retval true Error */ -Item_subselect::trans_res +bool Item_in_subselect::create_single_in_to_exists_cond(JOIN * join, Item **where_item, Item **having_item) @@ -1646,7 +1648,7 @@ Item_in_subselect::create_single_in_to_exists_cond(JOIN * join, if (!join_having) item->name= (char*) in_having_cond; if (fix_having(item, select_lex)) - DBUG_RETURN(RES_ERROR); + DBUG_RETURN(true); *having_item= item; } else @@ -1666,11 +1668,11 @@ Item_in_subselect::create_single_in_to_exists_cond(JOIN * join, { if (!(having= new Item_func_trig_cond(having, get_cond_guard(0)))) - DBUG_RETURN(RES_ERROR); + DBUG_RETURN(true); } having->name= (char*) in_having_cond; if (fix_having(having, select_lex)) - DBUG_RETURN(RES_ERROR); + DBUG_RETURN(true); *having_item= having; item= new Item_cond_or(item, @@ -1683,7 +1685,7 @@ Item_in_subselect::create_single_in_to_exists_cond(JOIN * join, if (!abort_on_null && left_expr->maybe_null) { if (!(item= new Item_func_trig_cond(item, get_cond_guard(0)))) - DBUG_RETURN(RES_ERROR); + DBUG_RETURN(true); } /* @@ -1693,7 +1695,7 @@ Item_in_subselect::create_single_in_to_exists_cond(JOIN * join, */ item->name= (char *) in_additional_cond; if (!item->fixed && item->fix_fields(thd, 0)) - DBUG_RETURN(RES_ERROR); + DBUG_RETURN(true); *where_item= item; } else @@ -1710,12 +1712,12 @@ Item_in_subselect::create_single_in_to_exists_cond(JOIN * join, { if (!(new_having= new Item_func_trig_cond(new_having, get_cond_guard(0)))) - DBUG_RETURN(RES_ERROR); + DBUG_RETURN(true); } new_having->name= (char*) in_having_cond; if (fix_having(new_having, select_lex)) - DBUG_RETURN(RES_ERROR); + DBUG_RETURN(true); *having_item= new_having; } else @@ -1723,7 +1725,7 @@ Item_in_subselect::create_single_in_to_exists_cond(JOIN * join, } } - DBUG_RETURN(RES_OK); + DBUG_RETURN(false); } @@ -1739,11 +1741,11 @@ Item_in_subselect::create_single_in_to_exists_cond(JOIN * join, additional predicates, or will be executed via subquery materialization in its unmodified form. - @retval RES_OK The subquery was transformed - @retval RES_ERROR Error + @retval false The subquery was transformed + @retval true Error */ -Item_subselect::trans_res +bool Item_in_subselect::row_value_transformer(JOIN *join) { SELECT_LEX *select_lex= join->select_lex; @@ -1755,7 +1757,7 @@ Item_in_subselect::row_value_transformer(JOIN *join) if (select_lex->item_list.elements != cols_num) { my_error(ER_OPERAND_COLUMNS, MYF(0), cols_num); - DBUG_RETURN(RES_ERROR); + DBUG_RETURN(true); } /* @@ -1774,7 +1776,7 @@ Item_in_subselect::row_value_transformer(JOIN *join) if (!optimizer || optimizer->fix_left(thd, 0)) { thd->lex->current_select= current; - DBUG_RETURN(RES_ERROR); + DBUG_RETURN(true); } // we will refer to upper level cache array => we have to save it in PS @@ -1786,7 +1788,7 @@ Item_in_subselect::row_value_transformer(JOIN *join) //select_lex->uncacheable|= UNCACHEABLE_DEPENDENT; } - DBUG_RETURN(RES_OK); + DBUG_RETURN(false); } @@ -1795,18 +1797,51 @@ Item_in_subselect::row_value_transformer(JOIN *join) subselect into a correlated EXISTS via predicate injection. @details - There are two cases - either the subquery has aggregates, GROUP BY, - or HAVING, or not. Both cases are described inline in the code. + The correlated predicates are created as follows: + + - If the subquery has aggregates, GROUP BY, or HAVING, convert to + + (l1, l2, l3) IN (SELECT v1, v2, v3 ... HAVING having) + => + EXISTS (SELECT ... HAVING having and + (l1 = v1 or is null v1) and + (l2 = v2 or is null v2) and + (l3 = v3 or is null v3) and + is_not_null_test(v1) and + is_not_null_test(v2) and + is_not_null_test(v3)) + + where is_not_null_test used to register nulls in case if we have + not found matching to return correct NULL value. + + - Otherwise (no aggregates/GROUP BY/HAVING) convert the subquery as follows: + + (l1, l2, l3) IN (SELECT v1, v2, v3 ... WHERE where) + => + EXISTS (SELECT ... WHERE where and + (l1 = v1 or is null v1) and + (l2 = v2 or is null v2) and + (l3 = v3 or is null v3) + HAVING is_not_null_test(v1) and + is_not_null_test(v2) and + is_not_null_test(v3)) + where is_not_null_test registers NULLs values but reject rows. + + in case when we do not need correct NULL, we have simplier construction: + EXISTS (SELECT ... WHERE where and + (l1 = v1) and + (l2 = v2) and + (l3 = v3) @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 + @retval false If the new conditions were created successfully + @retval true Error */ -Item_subselect::trans_res +bool Item_in_subselect::create_row_in_to_exists_cond(JOIN * join, Item **where_item, Item **having_item) @@ -1829,19 +1864,7 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join, if (is_having_used) { - /* - (l1, l2, l3) IN (SELECT v1, v2, v3 ... HAVING having) => - EXISTS (SELECT ... HAVING having and - (l1 = v1 or is null v1) and - (l2 = v2 or is null v2) and - (l3 = v3 or is null v3) and - is_not_null_test(v1) and - is_not_null_test(v2) and - is_not_null_test(v3)) - where is_not_null_test used to register nulls in case if we have - not found matching to return correct NULL value - TODO: say here explicitly if the order of AND parts matters or not. - */ + /* TODO: say here explicitly if the order of AND parts matters or not. */ Item *item_having_part2= 0; for (uint i= 0; i < cols_num; i++) { @@ -1853,7 +1876,7 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join, Item_ref::OUTER_REF)); if (select_lex->ref_pointer_array[i]-> check_cols(left_expr->element_index(i)->cols())) - DBUG_RETURN(RES_ERROR); + DBUG_RETURN(true); Item *item_eq= new Item_func_eq(new Item_ref(&select_lex->context, @@ -1865,20 +1888,18 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join, Item_ref(&select_lex->context, select_lex->ref_pointer_array + i, (char *)"", - (char *)"") - ); + (char *)"")); Item *item_isnull= new Item_func_isnull(new Item_ref(&select_lex->context, select_lex->ref_pointer_array+i, (char *)"", - (char *)"") - ); + (char *)"")); Item *col_item= new Item_cond_or(item_eq, item_isnull); if (!abort_on_null && left_expr->element_index(i)->maybe_null) { if (!(col_item= new Item_func_trig_cond(col_item, get_cond_guard(i)))) - DBUG_RETURN(RES_ERROR); + DBUG_RETURN(true); } *having_item= and_items(*having_item, col_item); @@ -1893,7 +1914,7 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join, { if (!(item_nnull_test= new Item_func_trig_cond(item_nnull_test, get_cond_guard(i)))) - DBUG_RETURN(RES_ERROR); + DBUG_RETURN(true); } item_having_part2= and_items(item_having_part2, item_nnull_test); item_having_part2->top_level_item(); @@ -1902,23 +1923,6 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join, } else { - /* - (l1, l2, l3) IN (SELECT v1, v2, v3 ... WHERE where) => - EXISTS (SELECT ... WHERE where and - (l1 = v1 or is null v1) and - (l2 = v2 or is null v2) and - (l3 = v3 or is null v3) - HAVING is_not_null_test(v1) and - is_not_null_test(v2) and - is_not_null_test(v3)) - where is_not_null_test register NULLs values but reject rows - - in case when we do not need correct NULL, we have simplier construction: - EXISTS (SELECT ... WHERE where and - (l1 = v1) and - (l2 = v2) and - (l3 = v3) - */ for (uint i= 0; i < cols_num; i++) { Item *item, *item_isnull; @@ -1929,7 +1933,7 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join, Item_ref::OUTER_REF)); if (select_lex->ref_pointer_array[i]-> check_cols(left_expr->element_index(i)->cols())) - DBUG_RETURN(RES_ERROR); + DBUG_RETURN(true); item= new Item_func_eq(new Item_direct_ref(&select_lex->context, @@ -1942,8 +1946,7 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join, select_lex-> ref_pointer_array+i, (char *)"", - (char *)"") - ); + (char *)"")); if (!abort_on_null) { Item *having_col_item= @@ -1961,8 +1964,7 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join, select_lex-> ref_pointer_array+i, (char *)"", - (char *)"") - ); + (char *)"")); item= new Item_cond_or(item, item_isnull); /* TODO: why we create the above for cases where the right part @@ -1971,10 +1973,10 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join, if (left_expr->element_index(i)->maybe_null) { if (!(item= new Item_func_trig_cond(item, get_cond_guard(i)))) - DBUG_RETURN(RES_ERROR); + DBUG_RETURN(true); if (!(having_col_item= new Item_func_trig_cond(having_col_item, get_cond_guard(i)))) - DBUG_RETURN(RES_ERROR); + DBUG_RETURN(true); } *having_item= and_items(*having_item, having_col_item); } @@ -1985,7 +1987,7 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join, if (*where_item) { if (!(*where_item)->fixed && (*where_item)->fix_fields(thd, 0)) - DBUG_RETURN(RES_ERROR); + DBUG_RETURN(true); (*where_item)->top_level_item(); } @@ -1994,15 +1996,15 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join, if (!join_having) (*having_item)->name= (char*) in_having_cond; if (fix_having(*having_item, select_lex)) - DBUG_RETURN(RES_ERROR); + DBUG_RETURN(true); (*having_item)->top_level_item(); } - DBUG_RETURN(RES_OK); + DBUG_RETURN(false); } -Item_subselect::trans_res +bool Item_in_subselect::select_transformer(JOIN *join) { return select_in_like_transformer(join); @@ -2021,7 +2023,7 @@ Item_in_subselect::select_transformer(JOIN *join) bool Item_in_subselect::create_in_to_exists_cond(JOIN *join_arg) { - Item_subselect::trans_res res; + bool res; DBUG_ASSERT(engine->engine_type() == subselect_engine::SINGLE_SELECT_ENGINE || engine->engine_type() == subselect_engine::UNION_ENGINE); @@ -2042,7 +2044,7 @@ bool Item_in_subselect::create_in_to_exists_cond(JOIN *join_arg) 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); + return (res); } @@ -2092,28 +2094,28 @@ bool Item_in_subselect::inject_in_to_exists_cond(JOIN *join_arg) /** - Prepare IN/ALL/ANY/SOME subquery transformation and call appropriate + Prepare IN/ALL/ANY/SOME subquery transformation and call the appropriate transformation function. @param join JOIN object of transforming subquery @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 + we have to call fix_fields() for the left expression to be able to call + cols() method on it. Also this method makes arena management for underlying transformation methods. - @retval RES_OK OK - @retval RES_ERROR Error + @retval false OK + @retval true Error */ -Item_subselect::trans_res +bool Item_in_subselect::select_in_like_transformer(JOIN *join) { Query_arena *arena, backup; SELECT_LEX *current= thd->lex->current_select; const char *save_where= thd->where; - Item_subselect::trans_res res= RES_ERROR; + bool trans_res= true; bool result; DBUG_ENTER("Item_in_subselect::select_in_like_transformer"); @@ -2132,7 +2134,7 @@ Item_in_subselect::select_in_like_transformer(JOIN *join) } if (changed) - DBUG_RETURN(RES_OK); + DBUG_RETURN(false); thd->where= "IN/ALL/ANY subquery"; @@ -2172,7 +2174,7 @@ Item_in_subselect::select_in_like_transformer(JOIN *join) */ arena= thd->activate_stmt_arena_if_needed(&backup); if (left_expr->cols() == 1) - res= single_value_transformer(join); + trans_res= single_value_transformer(join); else { /* we do not support row operation for ALL/ANY/SOME */ @@ -2181,15 +2183,15 @@ Item_in_subselect::select_in_like_transformer(JOIN *join) if (arena) thd->restore_active_arena(arena, &backup); my_error(ER_OPERAND_COLUMNS, MYF(0), 1); - DBUG_RETURN(RES_ERROR); + DBUG_RETURN(true); } - res= row_value_transformer(join); + trans_res= row_value_transformer(join); } if (arena) thd->restore_active_arena(arena, &backup); err: thd->where= save_where; - DBUG_RETURN(res); + DBUG_RETURN(trans_res); } @@ -2381,7 +2383,7 @@ bool Item_in_subselect::init_cond_guards() } -Item_subselect::trans_res +bool Item_allany_subselect::select_transformer(JOIN *join) { DBUG_ENTER("Item_allany_subselect::select_transformer"); diff --git a/sql/item_subselect.h b/sql/item_subselect.h index fc278fe03ad..a6d591de103 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -119,7 +119,6 @@ public: /* TRUE <=> The underlying SELECT is correlated w.r.t some ancestor select */ bool is_correlated; - enum trans_res {RES_OK, RES_REDUCE, RES_ERROR}; enum subs_type {UNKNOWN_SUBS, SINGLEROW_SUBS, EXISTS_SUBS, IN_SUBS, ALL_SUBS, ANY_SUBS}; @@ -148,7 +147,7 @@ public: eliminated= FALSE; null_value= 1; } - virtual trans_res select_transformer(JOIN *join); + virtual bool select_transformer(JOIN *join); bool assigned() { return value_assigned; } void assigned(bool a) { value_assigned= a; } enum Type type() const; @@ -259,7 +258,7 @@ public: subs_type substype() { return SINGLEROW_SUBS; } void reset(); - trans_res select_transformer(JOIN *join); + bool select_transformer(JOIN *join); void store(uint i, Item* item); double val_real(); longlong val_int (); @@ -399,16 +398,16 @@ protected: 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); + bool select_in_like_transformer(JOIN *join); + bool single_value_transformer(JOIN *join); + bool row_value_transformer(JOIN * join); bool fix_having(Item *having, st_select_lex *select_lex); - trans_res create_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); + bool create_single_in_to_exists_cond(JOIN * join, + Item **where_item, + Item **having_item); + bool create_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. */ @@ -473,7 +472,7 @@ public: null_value= 0; was_null= 0; } - trans_res select_transformer(JOIN *join); + bool select_transformer(JOIN *join); bool create_in_to_exists_cond(JOIN *join_arg); bool inject_in_to_exists_cond(JOIN *join_arg); @@ -523,7 +522,7 @@ public: // only ALL subquery has upper not subs_type substype() { return all?ALL_SUBS:ANY_SUBS; } - trans_res select_transformer(JOIN *join); + bool 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 c00335d80e9..7b47bed1e09 100644 --- a/sql/opt_subselect.cc +++ b/sql/opt_subselect.cc @@ -29,6 +29,10 @@ static TABLE_LIST *alloc_join_nest(THD *thd); static void fix_list_after_tbl_changes(SELECT_LEX *new_parent, List *tlist); static uint get_tmp_table_rec_length(List &items); +static double get_tmp_table_lookup_cost(THD *thd, ha_rows row_count, + uint row_size); +static double get_tmp_table_write_cost(THD *thd, ha_rows row_count, + uint row_size); bool find_eq_ref_candidate(TABLE *table, table_map sj_inner_tables); static SJ_MATERIALIZATION_INFO * at_sjmat_pos(const JOIN *join, table_map remaining_tables, const JOIN_TAB *tab, @@ -257,10 +261,8 @@ int check_and_do_in_subquery_rewrites(JOIN *join) 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)); + if (subselect->select_transformer(join)) + DBUG_RETURN(-11); } } DBUG_RETURN(0); @@ -502,18 +504,17 @@ skip_conversion: for (; in_subq!= in_subq_end; in_subq++) { JOIN *child_join= (*in_subq)->unit->first_select()->join; - Item_subselect::trans_res res; (*in_subq)->changed= 0; (*in_subq)->fixed= 0; SELECT_LEX *save_select_lex= thd->lex->current_select; thd->lex->current_select= (*in_subq)->unit->first_select(); - res= (*in_subq)->select_transformer(child_join); + bool res= (*in_subq)->select_transformer(child_join); thd->lex->current_select= save_select_lex; - if (res == Item_subselect::RES_ERROR) + if (res) DBUG_RETURN(TRUE); (*in_subq)->changed= 1; @@ -1253,17 +1254,16 @@ bool optimize_semijoin_nests(JOIN *join, table_map all_table_map) Calculate temporary table parameters and usage costs */ uint rowlen= get_tmp_table_rec_length(right_expr_list); - double lookup_cost; - if (rowlen * subjoin_out_rows< join->thd->variables.max_heap_table_size) - lookup_cost= HEAP_TEMPTABLE_LOOKUP_COST; - else - lookup_cost= DISK_TEMPTABLE_LOOKUP_COST; + double lookup_cost= get_tmp_table_lookup_cost(join->thd, + subjoin_out_rows, rowlen); + double write_cost= get_tmp_table_write_cost(join->thd, + subjoin_out_rows, rowlen); /* Let materialization cost include the cost to write the data into the temporary table: */ - sjm->materialization_cost.add_io(subjoin_out_rows, lookup_cost); + sjm->materialization_cost.add_io(subjoin_out_rows, write_cost); /* Set the cost to do a full scan of the temptable (will need this to @@ -1338,6 +1338,49 @@ static uint get_tmp_table_rec_length(List &items) return len; } + +/** + The cost of a lookup into a unique hash/btree index on a temporary table + with 'row_count' rows each of size 'row_size'. + + @param thd current query context + @param row_count number of rows in the temp table + @param row_size average size in bytes of the rows + + @return the cost of one lookup +*/ + +static double get_tmp_table_lookup_cost(THD *thd, ha_rows row_count, uint row_size) +{ + if (row_count * row_size > thd->variables.max_heap_table_size) + return (double) DISK_TEMPTABLE_LOOKUP_COST; + else + return (double) HEAP_TEMPTABLE_LOOKUP_COST; +} + +/** + The cost of writing a row into a temporary table with 'row_count' unique + rows each of size 'row_size'. + + @param thd current query context + @param row_count number of rows in the temp table + @param row_size average size in bytes of the rows + + @return the cost of writing one row +*/ + +static double get_tmp_table_write_cost(THD *thd, ha_rows row_count, uint row_size) +{ + double lookup_cost= get_tmp_table_lookup_cost(thd, row_count, row_size); + /* + TODO: + This is an optimistic estimate. Add additional costs resulting from + actually writing the row to memory/disk and possible index reorganization. + */ + return lookup_cost; +} + + //psergey-todo: is the below a kind of table elimination?? /* Check if table's KEYUSE elements have an eq_ref(outer_tables) candidate @@ -1867,15 +1910,15 @@ void advance_sj_state(JOIN *join, table_map remaining_tables, - sj_inner_fanout*sj_outer_fanout lookups. */ - double one_lookup_cost; - if (sj_outer_fanout*temptable_rec_size > - join->thd->variables.max_heap_table_size) - one_lookup_cost= DISK_TEMPTABLE_LOOKUP_COST; - else - one_lookup_cost= HEAP_TEMPTABLE_LOOKUP_COST; + double one_lookup_cost= get_tmp_table_lookup_cost(join->thd, + sj_outer_fanout, + temptable_rec_size); + double one_write_cost= get_tmp_table_write_cost(join->thd, + sj_outer_fanout, + temptable_rec_size); double write_cost= join->positions[first_tab].prefix_record_count* - sj_outer_fanout * one_lookup_cost; + sj_outer_fanout * one_write_cost; double full_lookup_cost= join->positions[first_tab].prefix_record_count* sj_outer_fanout* sj_inner_fanout * one_lookup_cost; @@ -3611,12 +3654,7 @@ bool JOIN::optimize_unflattened_subqueries() bool JOIN::choose_subquery_plan(table_map join_tables) { /* The original QEP of the subquery. */ - DYNAMIC_ARRAY save_keyuse; /* Copy of the JOIN::keyuse array. */ - POSITION save_best_positions[MAX_TABLES+1]; /* Copy of JOIN::best_positions */ - /* Copies of the JOIN_TAB::keyuse pointers for each JOIN_TAB. */ - KEYUSE *save_join_tab_keyuse[MAX_TABLES]; - /* Copies of JOIN_TAB::checked_keys for each JOIN_TAB. */ - key_map save_join_tab_checked_keys[MAX_TABLES]; + Query_plan_state save_qep; enum_reopt_result reopt_result= REOPT_NONE; Item_in_subselect *in_subs; @@ -3635,9 +3673,6 @@ bool JOIN::choose_subquery_plan(table_map join_tables) DBUG_ASSERT(!in_to_exists_where || in_to_exists_where->fixed); DBUG_ASSERT(!in_to_exists_having || in_to_exists_having->fixed); - save_keyuse.elements= 0; - save_keyuse.buffer= NULL; - /* Compute and compare the costs of materialization and in-exists if both strategies are possible and allowed by the user (checked during the prepare @@ -3660,8 +3695,19 @@ bool JOIN::choose_subquery_plan(table_map join_tables) double in_exists_strategy_cost; if (outer_join) - outer_join->get_partial_join_cost(outer_join->tables, + { + /* + Make_cond_for_table is called for predicates only in the WHERE/ON + clauses. In all other cases, predicates are not pushed to any + JOIN_TAB, and their joi_tab_idx remains MAX_TABLES. Such predicates + are evaluated for each complete row. + */ + uint partial_plan_len= (in_subs->get_join_tab_idx() == MAX_TABLES) ? + outer_join->tables : + in_subs->get_join_tab_idx() + 1; + outer_join->get_partial_join_cost(partial_plan_len, &outer_read_time, &outer_record_count); + } else { /* @@ -3673,7 +3719,10 @@ bool JOIN::choose_subquery_plan(table_map join_tables) } inner_join->get_partial_join_cost(inner_join->tables, - &inner_read_time_1, &inner_record_count_1); + &inner_read_time_1, + &inner_record_count_1); + /* inner_read_time_1 above is a dummy, get the correct total join cost. */ + inner_read_time_1= inner_join->best_read; if (in_to_exists_where && const_tables != tables) { @@ -3681,18 +3730,16 @@ bool JOIN::choose_subquery_plan(table_map join_tables) Re-optimize and cost the subquery taking into account the IN-EXISTS conditions. */ - if (save_query_plan(&save_keyuse, save_best_positions, - save_join_tab_keyuse, save_join_tab_checked_keys)) - return TRUE; - reopt_result= reoptimize(in_to_exists_where, join_tables); - if (reopt_result == REOPT_OLD_PLAN) - restore_query_plan(&save_keyuse, save_best_positions, - save_join_tab_keyuse, save_join_tab_checked_keys); - else if (reopt_result == REOPT_ERROR) + reopt_result= reoptimize(in_to_exists_where, join_tables, &save_qep); + if (reopt_result == REOPT_ERROR) return TRUE; inner_join->get_partial_join_cost(inner_join->tables, - &inner_read_time_2, &inner_record_count_2); + &inner_read_time_2, + &inner_record_count_2); + /* inner_read_time_2 above is a dummy, get the correct total join cost. */ + inner_read_time_2= inner_join->best_read; + } else { @@ -3705,24 +3752,20 @@ bool JOIN::choose_subquery_plan(table_map join_tables) /* 1. Compute the cost of the materialization strategy. */ - double materialization_cost; /* The cost of executing the subquery and */ - /* storing its result in an indexed temp table.*/ - /* The cost of a lookup into the unique index of the materialized table. */ - double lookup_cost; - double write_row_cost= 1; /* TODO: what is the real cost to write a row? */ - materialization_cost= inner_read_time_1 + - inner_record_count_1 * write_row_cost; - /* - The cost of a hash/btree lookup into a unique index of a materialized - subquery. - TIMOUR: TODO: the block of code below is exact copy/paste from - opt_subselect.cc:optimize_semi_join_nests() - refactor it. - */ uint rowlen= get_tmp_table_rec_length(unit->first_select()->item_list); - if (rowlen * inner_record_count_1 < thd->variables.max_heap_table_size) - lookup_cost= HEAP_TEMPTABLE_LOOKUP_COST; - else - lookup_cost= DISK_TEMPTABLE_LOOKUP_COST; + /* The cost of writing one row into the temporary table. */ + double write_cost= get_tmp_table_write_cost(thd, inner_record_count_1, + rowlen); + /* The cost of a lookup into the unique index of the materialized table. */ + double lookup_cost= get_tmp_table_lookup_cost(thd, inner_record_count_1, + rowlen); + /* + The cost of executing the subquery and storing its result in an indexed + temporary table. + */ + double materialization_cost= inner_read_time_1 + + write_cost * inner_record_count_1; + materialize_strategy_cost= materialization_cost + outer_record_count * lookup_cost; @@ -3764,8 +3807,7 @@ bool JOIN::choose_subquery_plan(table_map join_tables) { /* Restore the original query plan used for materialization. */ if (reopt_result == REOPT_NEW_PLAN) - restore_query_plan(&save_keyuse, save_best_positions, - save_join_tab_keyuse, save_join_tab_checked_keys); + restore_query_plan(&save_qep); /* TODO: should we set/unset this flag for both select_lex and its unit? */ in_subs->unit->uncacheable&= ~UNCACHEABLE_DEPENDENT; @@ -3789,11 +3831,8 @@ bool JOIN::choose_subquery_plan(table_map join_tables) } else if (in_subs->in_strategy & SUBS_IN_TO_EXISTS) { - /* Keep the new query plan with injected conditions, delete the old plan. */ - if (reopt_result == REOPT_NEW_PLAN) - delete_dynamic(&save_keyuse); - - if (reopt_result == REOPT_NONE && in_to_exists_where && const_tables != tables) + if (reopt_result == REOPT_NONE && in_to_exists_where && + const_tables != tables) { /* The subquery was not reoptimized either because the user allowed only the @@ -3805,7 +3844,7 @@ bool JOIN::choose_subquery_plan(table_map join_tables) join_tab[i].keyuse= NULL; join_tab[i].checked_keys.clear_all(); } - if ((reopt_result= reoptimize(in_to_exists_where, join_tables)) == + if ((reopt_result= reoptimize(in_to_exists_where, join_tables, NULL)) == REOPT_ERROR) return TRUE; } diff --git a/sql/sql_select.cc b/sql/sql_select.cc index b5fdcb9b6b5..f23fda455c6 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -179,10 +179,12 @@ int join_read_always_key_or_null(JOIN_TAB *tab); int join_read_next_same_or_null(READ_RECORD *info); static COND *make_cond_for_table(Item *cond,table_map table, table_map used_table, + uint join_tab_idx_arg, bool exclude_expensive_cond); static COND *make_cond_for_table_from_pred(Item *root_cond, Item *cond, table_map tables, table_map used_table, + uint join_tab_idx_arg, bool exclude_expensive_cond); static Item* part_of_refkey(TABLE *form,Field *field); @@ -919,7 +921,7 @@ JOIN::optimize() if (conds && !(thd->lex->describe & DESCRIBE_EXTENDED)) { COND *table_independent_conds= - make_cond_for_table(conds, PSEUDO_TABLE_BITS, 0, FALSE); + make_cond_for_table(conds, PSEUDO_TABLE_BITS, 0, MAX_TABLES, FALSE); DBUG_EXECUTE("where", print_where(table_independent_conds, "where after opt_sum_query()", @@ -2235,7 +2237,8 @@ JOIN::exec() Item* sort_table_cond= make_cond_for_table(curr_join->tmp_having, used_tables, - (table_map)0, FALSE); + (table_map)0, + MAX_TABLES, FALSE); if (sort_table_cond) { if (!curr_table->select) @@ -2266,7 +2269,8 @@ JOIN::exec() QT_ORDINARY);); curr_join->tmp_having= make_cond_for_table(curr_join->tmp_having, ~ (table_map) 0, - ~used_tables, FALSE); + ~used_tables, + MAX_TABLES, FALSE); DBUG_EXECUTE("where",print_where(curr_join->tmp_having, "having after sort", QT_ORDINARY);); @@ -5483,6 +5487,8 @@ void JOIN::get_partial_join_cost(uint n_tables, double record_count= 1; double read_time= 0.0; + DBUG_ASSERT(n_tables <= tables && n_tables > 0); + for (uint i= const_tables; i < n_tables; i++) { if (best_positions[i].records_read) @@ -6674,7 +6680,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) join->exec_const_cond= make_cond_for_table(cond, join->const_table_map, - (table_map) 0, FALSE); + (table_map) 0, MAX_TABLES, FALSE); DBUG_EXECUTE("where",print_where(join->exec_const_cond, "constants", QT_ORDINARY);); for (JOIN_TAB *tab= join->join_tab+join->const_tables; @@ -6685,7 +6691,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) JOIN_TAB *cond_tab= tab->first_inner; COND *tmp= make_cond_for_table(*tab->on_expr_ref, join->const_table_map, - (table_map) 0, FALSE); + (table_map) 0, MAX_TABLES, FALSE); if (!tmp) continue; tmp= new Item_func_trig_cond(tmp, &cond_tab->not_null_compl); @@ -6776,7 +6782,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) tmp= NULL; if (cond) - tmp= make_cond_for_table(cond, used_tables, current_map, FALSE); + tmp= make_cond_for_table(cond, used_tables, current_map, i, FALSE); if (cond && !tmp && tab->quick) { // Outer join if (tab->type != JT_ALL) @@ -6834,7 +6840,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) if (thd->variables.engine_condition_pushdown && !first_inner_tab) { COND *push_cond= - make_cond_for_table(tmp, current_map, current_map, FALSE); + make_cond_for_table(tmp, current_map, current_map, MAX_TABLES, FALSE); if (push_cond) { /* Push condition to handler */ @@ -6965,7 +6971,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) (tmp=make_cond_for_table(cond, join->const_table_map | current_map, - current_map, FALSE))) + current_map, MAX_TABLES, FALSE))) { DBUG_EXECUTE("where",print_where(tmp,"cache", QT_ORDINARY);); tab->cache_select=(SQL_SELECT*) @@ -6995,7 +7001,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) JOIN_TAB *cond_tab= join_tab->first_inner; COND *tmp= make_cond_for_table(*join_tab->on_expr_ref, join->const_table_map, - (table_map) 0, FALSE); + (table_map) 0, MAX_TABLES, FALSE); if (!tmp) continue; tmp= new Item_func_trig_cond(tmp, &cond_tab->not_null_compl); @@ -7014,6 +7020,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) } /* Push down non-constant conditions from on expressions */ + JOIN_TAB *first_tab= join->join_tab+join->const_tables; JOIN_TAB *last_tab= tab; while (first_inner_tab && first_inner_tab->last_inner == last_tab) { @@ -7025,12 +7032,13 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) table_map used_tables2= (join->const_table_map | OUTER_REF_TABLE_BIT | RAND_TABLE_BIT); - for (tab= join->join_tab+join->const_tables; tab <= last_tab ; tab++) + for (tab= first_tab; tab <= last_tab ; tab++) { current_map= tab->table->map; used_tables2|= current_map; COND *tmp_cond= make_cond_for_table(on_expr, used_tables2, - current_map, FALSE); + current_map, + (tab - first_tab), FALSE); if (tmp_cond) { JOIN_TAB *cond_tab= tab < first_inner_tab ? first_inner_tab : tab; @@ -14682,6 +14690,8 @@ bool test_if_ref(Item *root_cond, Item_field *left_item,Item *right_item) tables Tables for which "current field values" are available used_table Table that we're extracting the condition for (may also include PSEUDO_TABLE_BITS + join_tab_idx_arg The index of the JOIN_TAB this Item is being extracted + for. MAX_TABLES if there is no corresponding JOIN_TAB. exclude_expensive_cond Do not push expensive conditions DESCRIPTION @@ -14708,15 +14718,18 @@ bool test_if_ref(Item *root_cond, Item_field *left_item,Item *right_item) static Item * make_cond_for_table(Item *cond, table_map tables, table_map used_table, + uint join_tab_idx_arg, bool exclude_expensive_cond __attribute__((unused))) { return make_cond_for_table_from_pred(cond, cond, tables, used_table, + join_tab_idx_arg, exclude_expensive_cond); } static Item * make_cond_for_table_from_pred(Item *root_cond, Item *cond, table_map tables, table_map used_table, + uint join_tab_idx_arg, bool exclude_expensive_cond __attribute__((unused))) { if (cond->type() == Item::COND_ITEM) @@ -14733,6 +14746,7 @@ make_cond_for_table_from_pred(Item *root_cond, Item *cond, { Item *fix=make_cond_for_table_from_pred(root_cond, item, tables, used_table, + join_tab_idx_arg, exclude_expensive_cond); if (fix) new_cond->argument_list()->push_back(fix); @@ -14765,6 +14779,7 @@ make_cond_for_table_from_pred(Item *root_cond, Item *cond, { Item *fix=make_cond_for_table_from_pred(root_cond, item, tables, 0L, + join_tab_idx_arg, exclude_expensive_cond); if (!fix) return (COND*) 0; // Always true @@ -14789,7 +14804,10 @@ make_cond_for_table_from_pred(Item *root_cond, Item *cond, if (cond->marker == 3 || (cond->used_tables() & ~tables)) return (COND*) 0; // Can't check this yet if (cond->marker == 2 || cond->eq_cmp_result() == Item::COND_OK) + { + cond->set_join_tab_idx(join_tab_idx_arg); return cond; // Not boolean op + } if (cond->type() == Item::FUNC_ITEM && ((Item_func*) cond)->functype() == Item_func::EQ_FUNC) @@ -14810,6 +14828,7 @@ make_cond_for_table_from_pred(Item *root_cond, Item *cond, } } cond->marker=2; + cond->set_join_tab_idx(join_tab_idx_arg); return cond; } @@ -15986,7 +16005,7 @@ static bool fix_having(JOIN *join, Item **having) DBUG_EXECUTE("where",print_where(*having,"having", QT_ORDINARY);); Item* sort_table_cond=make_cond_for_table(*having, used_tables, used_tables, - FALSE); + MAX_TABLES, FALSE); if (sort_table_cond) { if (!table->select) @@ -16004,7 +16023,8 @@ static bool fix_having(JOIN *join, Item **having) DBUG_EXECUTE("where",print_where(table->select_cond, "select and having", QT_ORDINARY);); - *having= make_cond_for_table(*having,~ (table_map) 0,~used_tables, FALSE); + *having= make_cond_for_table(*having,~ (table_map) 0,~used_tables, + MAX_TABLES, FALSE); DBUG_EXECUTE("where", print_where(*having,"having after make_cond", QT_ORDINARY);); } @@ -19193,7 +19213,7 @@ void TABLE_LIST::print(THD *thd, table_map eliminated_tables, String *str, void st_select_lex::print(THD *thd, String *str, enum_query_type query_type) { /* TODO: thd may not be set for sub queries, but this should be fixed */ - DBUG_ASSERT(thd); +// DBUG_ASSERT(thd); if (!thd) thd= current_thd; @@ -19359,78 +19379,59 @@ bool JOIN::change_result(select_result *res) 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 + @param save_to The object into which the current query plan state is saved */ -int JOIN::save_query_plan(DYNAMIC_ARRAY *save_keyuse, - POSITION *save_best_positions, - KEYUSE **save_join_tab_keyuse, - key_map *save_join_tab_checked_keys) + +void JOIN::save_query_plan(Query_plan_state *save_to) { if (keyuse.elements) { DYNAMIC_ARRAY tmp_keyuse; - if (my_init_dynamic_array(save_keyuse, sizeof(KEYUSE), 20, 64)) - return 1; + // TODO: isn't this allocated by update_ref_and_keys + //if (my_init_dynamic_array(save_keyuse, sizeof(KEYUSE), 20, 64)) + // return 1; /* Swap the current and the backup keyuse arrays. */ tmp_keyuse= keyuse; - keyuse= (*save_keyuse); - (*save_keyuse)= tmp_keyuse; + keyuse= save_to->keyuse; + save_to->keyuse= tmp_keyuse; for (uint i= 0; i < tables; i++) { - save_join_tab_keyuse[i]= join_tab[i].keyuse; + save_to->join_tab_keyuse[i]= join_tab[i].keyuse; join_tab[i].keyuse= NULL; - save_join_tab_checked_keys[i]= join_tab[i].checked_keys; + save_to->join_tab_checked_keys[i]= join_tab[i].checked_keys; join_tab[i].checked_keys.clear_all(); } } - memcpy((uchar*) save_best_positions, (uchar*) best_positions, + memcpy((uchar*) save_to->best_positions, (uchar*) best_positions, sizeof(POSITION) * (tables + 1)); memset(best_positions, 0, sizeof(POSITION) * (tables + 1)); - return 0; } /** - Restore a query plan previously saved by the caller. + Restore a query execution 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 + @param The object from which the current query plan state is restored. */ -void JOIN::restore_query_plan(DYNAMIC_ARRAY *save_keyuse, - POSITION *save_best_positions, - KEYUSE **save_join_tab_keyuse, - key_map *save_join_tab_checked_keys) +void JOIN::restore_query_plan(Query_plan_state *restore_from) { - if (save_keyuse->elements) + if (restore_from->keyuse.elements) { DYNAMIC_ARRAY tmp_keyuse; tmp_keyuse= keyuse; - keyuse= (*save_keyuse); - (*save_keyuse)= tmp_keyuse; - delete_dynamic(save_keyuse); + keyuse= restore_from->keyuse; + restore_from->keyuse= tmp_keyuse; for (uint i= 0; i < tables; i++) { - join_tab[i].keyuse= save_join_tab_keyuse[i]; - join_tab[i].checked_keys= save_join_tab_checked_keys[i]; + join_tab[i].keyuse= restore_from->join_tab_keyuse[i]; + join_tab[i].checked_keys= restore_from->join_tab_checked_keys[i]; } } - memcpy((uchar*) best_positions, (uchar*) save_best_positions, + memcpy((uchar*) best_positions, (uchar*) restore_from->best_positions, sizeof(POSITION) * (tables + 1)); } @@ -19441,8 +19442,7 @@ void JOIN::restore_query_plan(DYNAMIC_ARRAY *save_keyuse, @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. + @param save_to If != NULL, save here the state of the current query plan @notes Given a query plan that already optimized taking into account some WHERE clause @@ -19462,7 +19462,9 @@ void JOIN::restore_query_plan(DYNAMIC_ARRAY *save_keyuse, @retval REOPT_ERROR an irrecovarable error occured during reoptimization. */ -JOIN::enum_reopt_result JOIN::reoptimize(Item *added_where, table_map join_tables) +JOIN::enum_reopt_result +JOIN::reoptimize(Item *added_where, table_map join_tables, + Query_plan_state *save_to) { DYNAMIC_ARRAY added_keyuse; SARGABLE_PARAM *sargables= 0; /* Used only as a dummy parameter. */ @@ -19478,6 +19480,9 @@ JOIN::enum_reopt_result JOIN::reoptimize(Item *added_where, table_map join_table if (!added_keyuse.elements) return REOPT_OLD_PLAN; + if (save_to) + save_query_plan(save_to); + /* Add the new access methods to the keyuse array. */ if (!keyuse.buffer && my_init_dynamic_array(&keyuse, sizeof(KEYUSE), 20, 64)) diff --git a/sql/sql_select.h b/sql/sql_select.h index 9be3b7c3ffe..6541dd58383 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -1374,6 +1374,31 @@ private: JOIN& operator=(const JOIN &rhs); /**< not implemented */ protected: + + /** + ??? + */ + class Query_plan_state { + public: + DYNAMIC_ARRAY keyuse; /* Copy of the JOIN::keyuse array. */ + POSITION best_positions[MAX_TABLES+1]; /* Copy of JOIN::best_positions */ + /* Copies of the JOIN_TAB::keyuse pointers for each JOIN_TAB. */ + KEYUSE *join_tab_keyuse[MAX_TABLES]; + /* Copies of JOIN_TAB::checked_keys for each JOIN_TAB. */ + key_map join_tab_checked_keys[MAX_TABLES]; + public: + Query_plan_state() + { + keyuse.elements= 0; + keyuse.buffer= NULL; + } + Query_plan_state(JOIN *join); + ~Query_plan_state() + { + delete_dynamic(&keyuse); + } + }; + /* Results of reoptimizing a JOIN via JOIN::reoptimize(). */ enum enum_reopt_result { REOPT_NEW_PLAN, /* there is a new reoptimized plan */ @@ -1383,13 +1408,10 @@ protected: }; /* Support for plan reoptimization with rewritten conditions. */ - enum_reopt_result reoptimize(Item *added_where, table_map join_tables); - int save_query_plan(DYNAMIC_ARRAY *save_keyuse, POSITION *save_positions, - KEYUSE **save_join_tab_keyuse, - key_map *save_join_tab_checked_keys); - void restore_query_plan(DYNAMIC_ARRAY *save_keyuse, POSITION *save_positions, - KEYUSE **save_join_tab_keyuse, - key_map *save_join_tab_checked_keys); + enum_reopt_result reoptimize(Item *added_where, table_map join_tables, + Query_plan_state *save_to); + void save_query_plan(Query_plan_state *save_to); + void restore_query_plan(Query_plan_state *restore_from); /* Choose a subquery plan for a table-less subquery. */ bool choose_tableless_subquery_plan();