From 6ac8e9b93c8ca84f0fe6bf42a55532092ed2e948 Mon Sep 17 00:00:00 2001 From: "bell@sanja.is.com.ua" <> Date: Tue, 12 Aug 2003 12:38:03 +0300 Subject: [PATCH] optimisation of independent ALL/ANY with aggregate function (WL#1115) (SCRUM) --- mysql-test/r/subselect.result | 9 ++++ mysql-test/t/subselect.test | 6 +++ sql/item_subselect.cc | 84 +++++++++++++++++++++------------ sql/item_subselect.h | 10 ++++ sql/sql_class.cc | 89 +++++++++++++++++++++++++++++++++++ sql/sql_class.h | 16 +++++++ 6 files changed, 184 insertions(+), 30 deletions(-) diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index c8d4e7a6b39..3455dea5ec6 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -1347,4 +1347,13 @@ explain select * from t3 where a >= all (select b from t2); id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t3 ALL NULL NULL NULL NULL 3 Using where 2 SUBQUERY t2 system NULL NULL NULL NULL 0 const row not found +insert into t2 values (2,2), (2,1), (3,3), (3,1); +select * from t3 where a > all (select max(b) from t2 group by a); +a +6 +7 +explain select * from t3 where a > all (select max(b) from t2 group by a); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t3 ALL NULL NULL NULL NULL 3 Using where +2 SUBQUERY t2 ALL NULL NULL NULL NULL 4 Using temporary; Using filesort drop table if exists t2, t3; diff --git a/mysql-test/t/subselect.test b/mysql-test/t/subselect.test index 1ffc7f4f72b..09fde8d61b0 100644 --- a/mysql-test/t/subselect.test +++ b/mysql-test/t/subselect.test @@ -877,6 +877,7 @@ insert into t1 values (1); insert into t2 values (1); select * from t1 where exists (select s1 from t2 having max(t2.s1)=t1.s1); drop table t1,t2; + # # update subquery with wrong field (to force name resolving # in UPDATE name space) @@ -897,4 +898,9 @@ create table t3 (a int); insert into t3 values (6),(7),(3); select * from t3 where a >= all (select b from t2); explain select * from t3 where a >= all (select b from t2); + +# optimized static ALL/ANY with grouping +insert into t2 values (2,2), (2,1), (3,3), (3,1); +select * from t3 where a > all (select max(b) from t2 group by a); +explain select * from t3 where a > all (select max(b) from t2 group by a); drop table if exists t2, t3; diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 77300dd2cf3..c6e421d763c 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -152,8 +152,8 @@ inline table_map Item_subselect::used_tables() const } Item_singlerow_subselect::Item_singlerow_subselect(THD *thd, - st_select_lex *select_lex): - Item_subselect(), value(0) + st_select_lex *select_lex) + :Item_subselect(), value(0) { DBUG_ENTER("Item_singlerow_subselect::Item_singlerow_subselect"); init(thd, select_lex, new select_singlerow_subselect(this)); @@ -163,6 +163,19 @@ Item_singlerow_subselect::Item_singlerow_subselect(THD *thd, DBUG_VOID_RETURN; } +Item_maxmin_subselect::Item_maxmin_subselect(THD *thd, + st_select_lex *select_lex, + bool max) + :Item_singlerow_subselect() +{ + DBUG_ENTER("Item_maxmin_subselect::Item_maxmin_subselect"); + init(thd, select_lex, new select_max_min_finder_subselect(this, max)); + max_columns= 1; + maybe_null= 1; + max_columns= 1; + DBUG_VOID_RETURN; +} + void Item_singlerow_subselect::reset() { null_value= 1; @@ -499,38 +512,50 @@ Item_in_subselect::single_value_transformer(JOIN *join, (func == &Item_bool_func2::gt_creator || func == &Item_bool_func2::lt_creator || func == &Item_bool_func2::ge_creator || - func == &Item_bool_func2::le_creator) && - !select_lex->group_list.elements && - !select_lex->with_sum_func) + func == &Item_bool_func2::le_creator)) { - Item *item; - subs_type type= substype(); - if (func == &Item_bool_func2::le_creator || - func == &Item_bool_func2::lt_creator) + Item *subs; + if (!select_lex->group_list.elements && + !select_lex->with_sum_func) { - /* - (ALL && (> || =>)) || (ANY && (< || =<)) - for ALL condition is inverted - */ - item= new Item_sum_max(*select_lex->ref_pointer_array); + Item *item; + subs_type type= substype(); + if (func == &Item_bool_func2::le_creator || + func == &Item_bool_func2::lt_creator) + { + /* + (ALL && (> || =>)) || (ANY && (< || =<)) + for ALL condition is inverted + */ + item= new Item_sum_max(*select_lex->ref_pointer_array); + } + else + { + /* + (ALL && (< || =<)) || (ANY && (> || =>)) + for ALL condition is inverted + */ + item= new Item_sum_min(*select_lex->ref_pointer_array); + } + *select_lex->ref_pointer_array= item; + select_lex->item_list.empty(); + select_lex->item_list.push_back(item); + + if (item->fix_fields(thd, join->tables_list, &item)) + { + DBUG_RETURN(ERROR); + } + subs= new Item_singlerow_subselect(thd, select_lex); } else { - /* - (ALL && (< || =<)) || (ANY && (> || =>)) - for ALL condition is inverted - */ - item= new Item_sum_min(*select_lex->ref_pointer_array); + // remove LIMIT placed by ALL/ANY subquery + select_lex->master_unit()->global_parameters->select_limit= + HA_POS_ERROR; + subs= new Item_maxmin_subselect(thd, select_lex, + (func == &Item_bool_func2::le_creator || + func == &Item_bool_func2::lt_creator)); } - *select_lex->ref_pointer_array= item; - select_lex->item_list.empty(); - select_lex->item_list.push_back(item); - - if (item->fix_fields(thd, join->tables_list, &item)) - { - DBUG_RETURN(ERROR); - } - // left expression belong to outer select SELECT_LEX *current= thd->lex.current_select, *up; thd->lex.current_select= up= current->return_after_parsing(); @@ -540,8 +565,7 @@ Item_in_subselect::single_value_transformer(JOIN *join, DBUG_RETURN(ERROR); } thd->lex.current_select= current; - substitution= (*func)(left_expr, - new Item_singlerow_subselect(thd, select_lex)); + substitution= (*func)(left_expr, subs); DBUG_RETURN(OK); } diff --git a/sql/item_subselect.h b/sql/item_subselect.h index 9cfa304bb47..d3aa8f59e49 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -130,6 +130,7 @@ public: max_length= item->max_length; decimals= item->decimals; } + Item_singlerow_subselect() :Item_subselect(), value(0), row (0) {} subs_type substype() { return SINGLEROW_SUBS; } @@ -153,6 +154,15 @@ public: friend class select_singlerow_subselect; }; +/* used in static ALL/ANY optimisation */ +class Item_maxmin_subselect: public Item_singlerow_subselect +{ +public: + Item_maxmin_subselect(THD *thd, st_select_lex *select_lex, bool max); + Item_maxmin_subselect(Item_maxmin_subselect *item) + :Item_singlerow_subselect(item) {} +}; + /* exists subselect */ class Item_exists_subselect :public Item_subselect diff --git a/sql/sql_class.cc b/sql/sql_class.cc index c233ffd422a..6f72d592b6f 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -986,6 +986,95 @@ bool select_singlerow_subselect::send_data(List &items) DBUG_RETURN(0); } +bool select_max_min_finder_subselect::send_data(List &items) +{ + DBUG_ENTER("select_max_min_finder_subselect::send_data"); + Item_singlerow_subselect *it= (Item_singlerow_subselect *)item; + List_iterator_fast li(items); + Item *val_item= li++; + if (it->assigned()) + { + cache->store(val_item); + if ((this->*op)()) + it->store(0, cache); + } + else + { + if (!cache) + { + cache= Item_cache::get_cache(val_item->result_type()); + switch (val_item->result_type()) + { + case REAL_RESULT: + op= &select_max_min_finder_subselect::cmp_real; + break; + case INT_RESULT: + op= &select_max_min_finder_subselect::cmp_int; + break; + case STRING_RESULT: + op= &select_max_min_finder_subselect::cmp_str; + break; + case ROW_RESULT: + // This case should never be choosen + DBUG_ASSERT(0); + op= 0; + } + } + cache->store(val_item); + it->store(0, cache); + } + it->assigned(1); + DBUG_RETURN(0); +} + +bool select_max_min_finder_subselect::cmp_real() +{ + Item *maxmin= ((Item_singlerow_subselect *)item)->el(0); + double val1= cache->val(), val2= maxmin->val(); + if (fmax) + return (cache->null_value && !maxmin->null_value) || + (!cache->null_value && !maxmin->null_value && + val1 > val2); + else + return (maxmin->null_value && !cache->null_value) || + (!cache->null_value && !maxmin->null_value && + val1 < val2); +} + +bool select_max_min_finder_subselect::cmp_int() +{ + Item *maxmin= ((Item_singlerow_subselect *)item)->el(0); + longlong val1= cache->val_int(), val2= maxmin->val_int(); + if (fmax) + return (cache->null_value && !maxmin->null_value) || + (!cache->null_value && !maxmin->null_value && + val1 > val2); + else + return (maxmin->null_value && !cache->null_value) || + (!cache->null_value && !maxmin->null_value && + val1 < val2); +} + +bool select_max_min_finder_subselect::cmp_str() +{ + String *val1, *val2, buf1, buf2; + Item *maxmin= ((Item_singlerow_subselect *)item)->el(0); + /* + as far as both operand is Item_cache buf1 & buf2 will not be used, + but added for safety + */ + val1= cache->val_str(&buf1); + val2= maxmin->val_str(&buf1); + if (fmax) + return (cache->null_value && !maxmin->null_value) || + (!cache->null_value && !maxmin->null_value && + sortcmp(val1, val2, cache->collation.collation) > 0) ; + else + return (maxmin->null_value && !cache->null_value) || + (!cache->null_value && !maxmin->null_value && + sortcmp(val1, val2, cache->collation.collation) < 0); +} + bool select_exists_subselect::send_data(List &items) { DBUG_ENTER("select_exists_subselect::send_data"); diff --git a/sql/sql_class.h b/sql/sql_class.h index f6336cb7dd9..0c17c7b2b49 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -920,6 +920,22 @@ public: bool send_data(List &items); }; +/* used in independent ALL/ANY optimisation */ +class select_max_min_finder_subselect :public select_subselect +{ + Item_cache *cache; + bool (select_max_min_finder_subselect::*op)(); + bool fmax; +public: + select_max_min_finder_subselect(Item_subselect *item, bool mx) + :select_subselect(item), cache(0), fmax(mx) + {} + bool send_data(List &items); + bool cmp_real(); + bool cmp_int(); + bool cmp_str(); +}; + /* EXISTS subselect interface class */ class select_exists_subselect :public select_subselect {