diff --git a/mysql-test/r/subselect4.result b/mysql-test/r/subselect4.result index e22a0f04a4ddc8e0cdfb2ef2b6ce18e4b35d8ea2..a8102486f6bf8d95983a93a6a04bd1221d4348a0 100644 --- a/mysql-test/r/subselect4.result +++ b/mysql-test/r/subselect4.result @@ -1655,52 +1655,6 @@ f1b f2b f3b set @@optimizer_switch=@save_optimizer_switch; drop table t0,t1,t2; # -# LP BUG#718593 Crash in substitute_for_best_equal_field -> eliminate_item_equal -> -# Item_field::find_item_equal -> Item_equal::contains -# -set @save_optimizer_switch=@@optimizer_switch; -SET @@optimizer_switch = 'semijoin=off'; -CREATE TABLE t1 ( f3 int(11), f10 varchar(1), f11 varchar(1)) ; -INSERT IGNORE INTO t1 VALUES (6,'f','f'),(2,'d','d'); -CREATE TABLE t2 ( f12 int(11), f13 int(11)) ; -insert into t2 values (1,2), (3,4); -EXPLAIN -SELECT * FROM t2 -WHERE ( f12 ) IN ( -SELECT alias2.f3 -FROM t1 AS alias1 JOIN t1 AS alias2 ON alias2.f10 = alias1.f11 -WHERE alias1.f11 OR alias1.f3 = 50 AND alias1.f10 -); -id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY t2 ALL NULL NULL NULL NULL 2 Using where -2 DEPENDENT SUBQUERY alias1 ALL NULL NULL NULL NULL 2 Using where -2 DEPENDENT SUBQUERY alias2 ALL NULL NULL NULL NULL 2 Using where; Using join buffer (flat, BNL join) -SELECT * FROM t2 -WHERE ( f12 ) IN ( -SELECT alias2.f3 -FROM t1 AS alias1 JOIN t1 AS alias2 ON alias2.f10 = alias1.f11 -WHERE alias1.f11 OR alias1.f3 = 50 AND alias1.f10 -); -f12 f13 -EXPLAIN -SELECT * FROM t2 -WHERE ( f12 ) IN ( -SELECT alias2.f3 -FROM t1 AS alias1, t1 AS alias2 -WHERE (alias2.f10 = alias1.f11) AND (alias1.f11 OR alias1.f3 = 50 AND alias1.f10)); -id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY t2 ALL NULL NULL NULL NULL 2 Using where -2 DEPENDENT SUBQUERY alias1 ALL NULL NULL NULL NULL 2 Using where -2 DEPENDENT SUBQUERY alias2 ALL NULL NULL NULL NULL 2 Using where; Using join buffer (flat, BNL join) -SELECT * FROM t2 -WHERE ( f12 ) IN ( -SELECT alias2.f3 -FROM t1 AS alias1, t1 AS alias2 -WHERE (alias2.f10 = alias1.f11) AND (alias1.f11 OR alias1.f3 = 50 AND alias1.f10)); -f12 f13 -set @@optimizer_switch=@save_optimizer_switch; -drop table t1, t2; -# # LP BUG#715759 Wrong result with in_to_exists=on in maria-5.3-mwl89 # set @save_optimizer_switch=@@optimizer_switch; diff --git a/mysql-test/r/subselect_mat_cost_bugs.result b/mysql-test/r/subselect_mat_cost_bugs.result index cd0a6c716bba1707a27895444e996a46ea385eb5..18f2513a76da47dd6373fd23b30822433fda213a 100644 --- a/mysql-test/r/subselect_mat_cost_bugs.result +++ b/mysql-test/r/subselect_mat_cost_bugs.result @@ -229,6 +229,72 @@ FROM t1 STRAIGHT_JOIN t2 ON t2.f3 = t1.f3 WHERE t2.f3 = 'c'); f2 f3 drop table t1,t2,t3; +# +# LP BUG#718593 Crash in substitute_for_best_equal_field -> eliminate_item_equal -> +# Item_field::find_item_equal -> Item_equal::contains # -# LP BUG#718593 Crash in substitute_for_best_equal_field -> -# eliminate_item_equal -> Item_field::find_item_equal -> Item_equal::contains +set @save_optimizer_switch=@@optimizer_switch; +SET @@optimizer_switch = 'semijoin=off'; +CREATE TABLE t1 ( f3 int(11), f10 varchar(1), f11 varchar(1)) ; +INSERT IGNORE INTO t1 VALUES (6,'f','f'),(2,'d','d'); +CREATE TABLE t2 ( f12 int(11), f13 int(11)) ; +insert into t2 values (1,2), (3,4); +EXPLAIN +SELECT * FROM t2 +WHERE ( f12 ) IN ( +SELECT alias2.f3 +FROM t1 AS alias1 JOIN t1 AS alias2 ON alias2.f10 = alias1.f11 +WHERE alias1.f11 OR alias1.f3 = 50 AND alias1.f10 +); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t2 ALL NULL NULL NULL NULL 2 Using where +2 SUBQUERY alias1 ALL NULL NULL NULL NULL 2 Using where +2 SUBQUERY alias2 ALL NULL NULL NULL NULL 2 Using where; Using join buffer (flat, BNL join) +SELECT * FROM t2 +WHERE ( f12 ) IN ( +SELECT alias2.f3 +FROM t1 AS alias1 JOIN t1 AS alias2 ON alias2.f10 = alias1.f11 +WHERE alias1.f11 OR alias1.f3 = 50 AND alias1.f10 +); +f12 f13 +EXPLAIN +SELECT * FROM t2 +WHERE ( f12 ) IN ( +SELECT alias2.f3 +FROM t1 AS alias1, t1 AS alias2 +WHERE (alias2.f10 = alias1.f11) AND (alias1.f11 OR alias1.f3 = 50 AND alias1.f10)); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t2 ALL NULL NULL NULL NULL 2 Using where +2 SUBQUERY alias1 ALL NULL NULL NULL NULL 2 Using where +2 SUBQUERY alias2 ALL NULL NULL NULL NULL 2 Using where; Using join buffer (flat, BNL join) +SELECT * FROM t2 +WHERE ( f12 ) IN ( +SELECT alias2.f3 +FROM t1 AS alias1, t1 AS alias2 +WHERE (alias2.f10 = alias1.f11) AND (alias1.f11 OR alias1.f3 = 50 AND alias1.f10)); +f12 f13 +set @@optimizer_switch=@save_optimizer_switch; +drop table t1, t2; +# +# MWL#89: test introduced after Sergey Petrunia's review - test that +# keyparts wihtout index prefix are used with the IN-EXISTS strategy. +# +create table t1 (c1 int); +insert into t1 values (1), (2), (3); +create table t2 (kp1 int, kp2 int, c2 int, filler char(100)); +insert into t2 values (0,0,0,'filler'),(0,1,1,'filler'),(0,2,2,'filler'),(0,3,3,'filler'); +create index key1 on t2 (kp1, kp2); +create index key2 on t2 (kp1); +create index key3 on t2 (kp2); +set session optimizer_switch='default'; +analyze table t2; +Table Op Msg_type Msg_text +test.t2 analyze status OK +explain +select c1 from t1 where c1 in (select kp1 from t2 where kp2 = 10 and c2 = 4) or c1 > 7; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 Using where +2 DEPENDENT SUBQUERY t2 index_subquery key1,key2,key3 key1 10 func,const 1 Using where +select c1 from t1 where c1 in (select kp1 from t2 where kp2 = 10 and c2 = 4) or c1 > 7; +c1 +drop table t1, t2; diff --git a/mysql-test/t/subselect4.test b/mysql-test/t/subselect4.test index 33a6329401f3f605328c4ebd180b5142aacb5e98..768f37c1b6c225a0017cbce99cd7eda1bea6eded 100644 --- a/mysql-test/t/subselect4.test +++ b/mysql-test/t/subselect4.test @@ -1328,48 +1328,6 @@ set @@optimizer_switch=@save_optimizer_switch; drop table t0,t1,t2; ---echo # ---echo # LP BUG#718593 Crash in substitute_for_best_equal_field -> eliminate_item_equal -> ---echo # Item_field::find_item_equal -> Item_equal::contains ---echo # - -set @save_optimizer_switch=@@optimizer_switch; -SET @@optimizer_switch = 'semijoin=off'; - -CREATE TABLE t1 ( f3 int(11), f10 varchar(1), f11 varchar(1)) ; -INSERT IGNORE INTO t1 VALUES (6,'f','f'),(2,'d','d'); - -CREATE TABLE t2 ( f12 int(11), f13 int(11)) ; -insert into t2 values (1,2), (3,4); - -EXPLAIN -SELECT * FROM t2 -WHERE ( f12 ) IN ( - SELECT alias2.f3 - FROM t1 AS alias1 JOIN t1 AS alias2 ON alias2.f10 = alias1.f11 - WHERE alias1.f11 OR alias1.f3 = 50 AND alias1.f10 -); -SELECT * FROM t2 -WHERE ( f12 ) IN ( - SELECT alias2.f3 - FROM t1 AS alias1 JOIN t1 AS alias2 ON alias2.f10 = alias1.f11 - WHERE alias1.f11 OR alias1.f3 = 50 AND alias1.f10 -); - -EXPLAIN -SELECT * FROM t2 -WHERE ( f12 ) IN ( - SELECT alias2.f3 - FROM t1 AS alias1, t1 AS alias2 - WHERE (alias2.f10 = alias1.f11) AND (alias1.f11 OR alias1.f3 = 50 AND alias1.f10)); -SELECT * FROM t2 -WHERE ( f12 ) IN ( - SELECT alias2.f3 - FROM t1 AS alias1, t1 AS alias2 - WHERE (alias2.f10 = alias1.f11) AND (alias1.f11 OR alias1.f3 = 50 AND alias1.f10)); - -set @@optimizer_switch=@save_optimizer_switch; -drop table t1, t2; --echo # --echo # LP BUG#715759 Wrong result with in_to_exists=on in maria-5.3-mwl89 diff --git a/mysql-test/t/subselect_mat_cost_bugs.test b/mysql-test/t/subselect_mat_cost_bugs.test index bc052ea04b109572e5536f3b49f1aa311d493290..0240c9203b3f4b7399c841b5faf4cba5c40bdd64 100644 --- a/mysql-test/t/subselect_mat_cost_bugs.test +++ b/mysql-test/t/subselect_mat_cost_bugs.test @@ -258,54 +258,71 @@ WHERE ( f2 ) IN (SELECT t1.f1 drop table t1,t2,t3; +--echo # +--echo # LP BUG#718593 Crash in substitute_for_best_equal_field -> eliminate_item_equal -> +--echo # Item_field::find_item_equal -> Item_equal::contains --echo # ---echo # LP BUG#718593 Crash in substitute_for_best_equal_field -> ---echo # eliminate_item_equal -> Item_field::find_item_equal -> Item_equal::contains ---disable_parsing # not yet fixed +set @save_optimizer_switch=@@optimizer_switch; +SET @@optimizer_switch = 'semijoin=off'; CREATE TABLE t1 ( f3 int(11), f10 varchar(1), f11 varchar(1)) ; INSERT IGNORE INTO t1 VALUES (6,'f','f'),(2,'d','d'); CREATE TABLE t2 ( f12 int(11), f13 int(11)) ; - -set @save_optimizer_switch=@@optimizer_switch; -set @@optimizer_switch='materialization=off,in_to_exists=on,semijoin=off'; +insert into t2 values (1,2), (3,4); EXPLAIN SELECT * FROM t2 -WHERE (f12) IN ( - SELECT alias2.f3 - FROM t1 AS alias1, t1 AS alias2 - WHERE (alias2.f10 = alias1.f11) AND - (alias1.f11 OR alias1.f3 = 50 AND alias1.f10)); - +WHERE ( f12 ) IN ( + SELECT alias2.f3 + FROM t1 AS alias1 JOIN t1 AS alias2 ON alias2.f10 = alias1.f11 + WHERE alias1.f11 OR alias1.f3 = 50 AND alias1.f10 +); SELECT * FROM t2 -WHERE (f12) IN ( - SELECT alias2.f3 - FROM t1 AS alias1, t1 AS alias2 - WHERE (alias2.f10 = alias1.f11) AND - (alias1.f11 OR alias1.f3 = 50 AND alias1.f10)); - -insert into t2 values (1,2), (3,4); +WHERE ( f12 ) IN ( + SELECT alias2.f3 + FROM t1 AS alias1 JOIN t1 AS alias2 ON alias2.f10 = alias1.f11 + WHERE alias1.f11 OR alias1.f3 = 50 AND alias1.f10 +); EXPLAIN SELECT * FROM t2 -WHERE (f12) IN ( - SELECT alias2.f3 - FROM t1 AS alias1, t1 AS alias2 - WHERE (alias2.f10 = alias1.f11) AND - (alias1.f11 OR alias1.f3 = 50 AND alias1.f10)); - +WHERE ( f12 ) IN ( + SELECT alias2.f3 + FROM t1 AS alias1, t1 AS alias2 + WHERE (alias2.f10 = alias1.f11) AND (alias1.f11 OR alias1.f3 = 50 AND alias1.f10)); SELECT * FROM t2 -WHERE (f12) IN ( - SELECT alias2.f3 - FROM t1 AS alias1, t1 AS alias2 - WHERE (alias2.f10 = alias1.f11) AND - (alias1.f11 OR alias1.f3 = 50 AND alias1.f10)); - -set session optimizer_switch=@save_optimizer_switch; +WHERE ( f12 ) IN ( + SELECT alias2.f3 + FROM t1 AS alias1, t1 AS alias2 + WHERE (alias2.f10 = alias1.f11) AND (alias1.f11 OR alias1.f3 = 50 AND alias1.f10)); +set @@optimizer_switch=@save_optimizer_switch; drop table t1, t2; ---enable_parsing + +--echo # +--echo # MWL#89: test introduced after Sergey Petrunia's review - test that +--echo # keyparts wihtout index prefix are used with the IN-EXISTS strategy. +--echo # + +create table t1 (c1 int); +insert into t1 values (1), (2), (3); + +create table t2 (kp1 int, kp2 int, c2 int, filler char(100)); +insert into t2 values (0,0,0,'filler'),(0,1,1,'filler'),(0,2,2,'filler'),(0,3,3,'filler'); + +create index key1 on t2 (kp1, kp2); +create index key2 on t2 (kp1); +create index key3 on t2 (kp2); + +set session optimizer_switch='default'; + +analyze table t2; + +explain +select c1 from t1 where c1 in (select kp1 from t2 where kp2 = 10 and c2 = 4) or c1 > 7; +select c1 from t1 where c1 in (select kp1 from t2 where kp2 = 10 and c2 = 4) or c1 > 7; + +drop table t1, t2; diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index c2c592e2b885bac491556224b3d71b008d903f47..ab3c2422f30f3fd57de12b76825dcb5200d25a44 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -2096,13 +2096,14 @@ 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); + return args[0]->is_expensive_processor(arg) || + args[1]->is_expensive_processor(arg); } bool Item_in_optimizer::is_expensive() { - return args[1]->is_expensive(); + return args[0]->is_expensive() || args[1]->is_expensive(); } diff --git a/sql/item_subselect.h b/sql/item_subselect.h index 8d2580cc604d1391e4857a748498c5c1c6e08928..1b1be973cf9bf2a2ebf6a0d568b88567af967546 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -464,7 +464,7 @@ class Item_in_subselect :public Item_exists_subselect Item_in_subselect() :Item_exists_subselect(), left_expr_cache(0), first_execution(TRUE), optimizer(0), abort_on_null(0), - pushed_cond_guards(NULL), func(NULL), in_strategy(0), + pushed_cond_guards(NULL), func(NULL), in_strategy(SUBS_NOT_TRANSFORMED), upper_item(0) {} void cleanup(); diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc index a9aa4d0b11dd9e9de7b9b8e3ec77c8aeabe4d663..fb573e3b31c7a982a6f6422f4b27448003f73877 100644 --- a/sql/opt_subselect.cc +++ b/sql/opt_subselect.cc @@ -205,18 +205,19 @@ int check_and_do_in_subquery_rewrites(JOIN *join) !optimizer_flag(thd, OPTIMIZER_SWITCH_MATERIALIZATION)) my_error(ER_ILLEGAL_SUBQUERY_OPTIMIZER_SWITCHES, MYF(0)); + /* + If the subquery predicate is IN/=ANY, analyse and set all possible + subquery execution strategies based on optimizer switches and syntactic + properties. + */ if (in_subs) { - /* Subquery predicate is an IN/=ANY predicate. */ - if (optimizer_flag(thd, OPTIMIZER_SWITCH_IN_TO_EXISTS)) - in_subs->in_strategy|= SUBS_IN_TO_EXISTS; - if (optimizer_flag(thd, OPTIMIZER_SWITCH_MATERIALIZATION)) - in_subs->in_strategy|= SUBS_MATERIALIZATION; - /* Check if the subquery predicate can be executed via materialization. The required conditions are: - 1. Subquery is a single SELECT (not a UNION) + 0. The materialization optimizer switch was set. + 1. Subquery is a single SELECT (not a UNION). + TODO: this is a limitation that can be fixed 2. Subquery is not a table-less query. In this case there is no point in materializing. 2A The upper query is not a table-less SELECT ... FROM DUAL. We @@ -230,7 +231,7 @@ int check_and_do_in_subquery_rewrites(JOIN *join) non-top-level queries because it cannot handle NULLs correctly. 4. Subquery is non-correlated TODO: - This is an overly restrictive condition. It can be extended to: + This condition is too restrictive (limitation). It can be extended to: (Subquery is non-correlated || Subquery is correlated to any query outer to IN predicate || (Subquery is correlated to the immediate outer query && @@ -240,30 +241,30 @@ int check_and_do_in_subquery_rewrites(JOIN *join) (*) The subquery must be part of a SELECT statement. The current condition also excludes multi-table update statements. */ - if (!(in_subs->in_strategy & SUBS_MATERIALIZATION && - !select_lex->is_part_of_union() && // 1 - parent_unit->first_select()->leaf_tables && // 2 - thd->lex->sql_command == SQLCOM_SELECT && // * - select_lex->outer_select()->leaf_tables && // 2A - subquery_types_allow_materialization(in_subs) && - // psergey-todo: duplicated_subselect_card_check: where it's done? - (in_subs->is_top_level_item() || //3 - optimizer_flag(thd, - OPTIMIZER_SWITCH_PARTIAL_MATCH_ROWID_MERGE) || //3 - optimizer_flag(thd, - OPTIMIZER_SWITCH_PARTIAL_MATCH_TABLE_SCAN)) && //3 - !in_subs->is_correlated)) //4 + if (optimizer_flag(thd, OPTIMIZER_SWITCH_MATERIALIZATION) && // 0 + !select_lex->is_part_of_union() && // 1 + parent_unit->first_select()->leaf_tables && // 2 + thd->lex->sql_command == SQLCOM_SELECT && // * + select_lex->outer_select()->leaf_tables && // 2A + subquery_types_allow_materialization(in_subs) && + (in_subs->is_top_level_item() || //3 + optimizer_flag(thd, + OPTIMIZER_SWITCH_PARTIAL_MATCH_ROWID_MERGE) || //3 + optimizer_flag(thd, + OPTIMIZER_SWITCH_PARTIAL_MATCH_TABLE_SCAN)) && //3 + !in_subs->is_correlated) //4 { - /* Materialization is not possible based on syntactic properties. */ - in_subs->in_strategy&= ~SUBS_MATERIALIZATION; + in_subs->in_strategy|= SUBS_MATERIALIZATION; } - if (!in_subs->in_strategy) + /* + IN-TO-EXISTS is the only universal strategy. Choose it if the user + allowed it via an optimizer switch, or if materialization is not + possible. + */ + if (optimizer_flag(thd, OPTIMIZER_SWITCH_IN_TO_EXISTS) || + !in_subs->in_strategy) { - /* - If neither materialization is possible, nor the user chose - IN-TO-EXISTS, choose IN-TO-EXISTS as the only universal strategy. - */ in_subs->in_strategy|= SUBS_IN_TO_EXISTS; } } @@ -3703,10 +3704,9 @@ bool JOIN::choose_subquery_plan(table_map join_tables) enum_reopt_result reopt_result= REOPT_NONE; Item_in_subselect *in_subs; - if (select_lex->master_unit()->item && - select_lex->master_unit()->item->is_in_predicate()) + if (is_in_subquery()) { - in_subs= (Item_in_subselect*) select_lex->master_unit()->item; + in_subs= (Item_in_subselect*) unit->item; if (in_subs->create_in_to_exists_cond(this)) return true; } @@ -3943,11 +3943,10 @@ bool JOIN::choose_subquery_plan(table_map join_tables) bool JOIN::choose_tableless_subquery_plan() { DBUG_ASSERT(!tables_list || !tables); - if (select_lex->master_unit()->item) + if (unit->item) { - DBUG_ASSERT(select_lex->master_unit()->item->type() == - Item::SUBSELECT_ITEM); - Item_subselect *subs_predicate= select_lex->master_unit()->item; + DBUG_ASSERT(unit->item->type() == Item::SUBSELECT_ITEM); + Item_subselect *subs_predicate= unit->item; /* If the optimizer determined that his query has an empty result, @@ -3990,4 +3989,3 @@ bool JOIN::choose_tableless_subquery_plan() } return FALSE; } - diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index f1640bf71ed3718cc12ae58a5c9978110c8188f1..48f0b3bdbd59826916b88101ebb190c90de56a1d 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -3123,7 +3123,7 @@ bool st_select_lex::optimize_unflattened_subqueries() un->set_limit(un->global_parameters); un->thd->lex->current_select= sl; save_options= inner_join->select_options; - if (un->outer_select()->options & SELECT_DESCRIBE) + if (options & SELECT_DESCRIBE) { /* Optimize the subquery in the context of EXPLAIN. */ sl->set_explain_type(); diff --git a/sql/sql_list.h b/sql/sql_list.h index e90f16aeb999df9ed02058da70770a2cf3cc39bc..7d9f3d26ec85e9028ce29d5be53c5d02bec784fc 100644 --- a/sql/sql_list.h +++ b/sql/sql_list.h @@ -260,7 +260,7 @@ class base_list :public Sql_alloc list_node *node= first; list_node *list_first= list->first; elements=0; - while (node->info && node != list_first) + while (node != &end_of_list && node != list_first) { prev= &node->next; node= node->next; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 151c22dc52c07455e608621591b86b728290c0b3..76b1e182eb0e398a752393f05c4ca3e09e9d3363 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -57,7 +57,8 @@ static bool update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse, uint tables, COND *conds, table_map table_map, SELECT_LEX *select_lex, st_sargable_param **sargables); -static bool sort_and_filter_keyuse(DYNAMIC_ARRAY *keyuse); +static bool sort_and_filter_keyuse(DYNAMIC_ARRAY *keyuse, + bool skip_unprefixed_keyparts); static int sort_keyuse(KEYUSE *a,KEYUSE *b); static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, KEYUSE *org_keyuse, table_map used_tables); @@ -914,7 +915,6 @@ JOIN::optimize() "Impossible HAVING" : "Impossible WHERE"; tables= 0; error= 0; - choose_tableless_subquery_plan(); goto setup_subq_exit; } } @@ -966,7 +966,6 @@ JOIN::optimize() zero_result_cause= "No matching min/max row"; tables= 0; error=0; - choose_tableless_subquery_plan(); goto setup_subq_exit; } if (res > 1) @@ -1007,7 +1006,6 @@ JOIN::optimize() { DBUG_PRINT("info",("No tables")); error= 0; - choose_tableless_subquery_plan(); goto setup_subq_exit; } error= -1; // Error is sent to client @@ -1508,6 +1506,9 @@ JOIN::optimize() DBUG_RETURN(0); setup_subq_exit: + /* Choose an execution strategy for this JOIN. */ + if (!tables_list || !tables) + choose_tableless_subquery_plan(); /* Even with zero matching rows, subqueries in the HAVING clause may need to be evaluated if there are aggregate functions in the query. @@ -3036,7 +3037,16 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, COND *conds, if (update_ref_and_keys(join->thd, keyuse_array, stat, join->tables, conds, ~outer_join, join->select_lex, &sargables)) goto error; - if (keyuse_array->elements && sort_and_filter_keyuse(keyuse_array)) + /* + Keyparts without prefixes may be useful if this JOIN is a subquery, and + if the subquery may be executed via the IN-EXISTS strategy. + */ + bool skip_unprefixed_keyparts= + !(join->is_in_subquery() && + ((Item_in_subselect*)join->unit->item)->in_strategy & SUBS_IN_TO_EXISTS); + + if (keyuse_array->elements && + sort_and_filter_keyuse(keyuse_array, skip_unprefixed_keyparts)) goto error; DBUG_EXECUTE("opt", print_keyuse_array(keyuse_array);); } @@ -4505,7 +4515,8 @@ update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab, Special treatment for ft-keys. */ -static bool sort_and_filter_keyuse(DYNAMIC_ARRAY *keyuse) +static bool sort_and_filter_keyuse(DYNAMIC_ARRAY *keyuse, + bool skip_unprefixed_keyparts) { KEYUSE key_end, *prev, *save_pos, *use; uint found_eq_constant, i; @@ -4532,12 +4543,12 @@ static bool sort_and_filter_keyuse(DYNAMIC_ARRAY *keyuse) { if (use->key == prev->key && use->table == prev->table) { - if (prev->keypart+1 < use->keypart || + if ((prev->keypart+1 < use->keypart && skip_unprefixed_keyparts) || (prev->keypart == use->keypart && found_eq_constant)) continue; /* remove */ } - else if (use->keypart != 0) // First found must be 0 - continue; + else if (use->keypart != 0 && skip_unprefixed_keyparts) + continue; /* remove - first found must be 0 */ } prev= use; @@ -16938,8 +16949,7 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order, select= tab->select; /* Currently ORDER BY ... LIMIT is not supported in subqueries. */ - DBUG_ASSERT(join->group_list || - !(join->unit->item && join->unit->item->is_in_predicate())); + DBUG_ASSERT(join->group_list || !join->is_in_subquery()); /* When there is SQL_BIG_RESULT do not sort using index for GROUP BY, @@ -20659,7 +20669,7 @@ JOIN::reoptimize(Item *added_where, table_map join_tables, /* added_keyuse contents is copied, and it is no longer needed. */ delete_dynamic(&added_keyuse); - if (sort_and_filter_keyuse(&keyuse)) + if (sort_and_filter_keyuse(&keyuse, true)) return REOPT_ERROR; optimize_keyuse(this, &keyuse); diff --git a/sql/sql_select.h b/sql/sql_select.h index f6862c4655497361ee39bfa3acd2a1a6c5611816..f67b79fff471a91ac098964e8d7b2dc6714d44cb 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -1083,6 +1083,11 @@ class JOIN :public Sql_alloc double *read_time_arg, double *record_count_arg); /* defined in opt_subselect.cc */ bool transform_max_min_subquery(); + /* True if this JOIN is a subquery under an IN predicate. */ + bool is_in_subquery() + { + return (unit->item && unit->item->is_in_predicate()); + } private: /**