Skip to content

Commit 291be49

Browse files
committed
MDEV-23811: With large number of indexes optimizer chooses an inefficient plan
This bug could manifest itself for a query with WHERE condition containing top level OR formula such that each conjunct contained a single-range condition supported by the same index. One of these range conditions must be fully covered by another range condition that is used later in the OR formula. Additionally at least one of these condition should be ANDed with a sargable range condition supported by a different index. There were several attempts to fix related problems for OR conditions after the backport of range optimizer code from MySQL (commit 0e19f3e). Unfortunately the first of these fixes contained typo remained unnoticed until recently. This typo bug led to rejection of valid range accesses. This patch fixed this typo bug. The fix revealed another two bugs: one in a constructor for SEL_ARG, the other in the function tree_or(). Both are fixed in this patch.
1 parent 1595189 commit 291be49

File tree

5 files changed

+201
-11
lines changed

5 files changed

+201
-11
lines changed

mysql-test/r/range.result

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1274,7 +1274,7 @@ SELECT * FROM t1 WHERE
12741274
5 <= a AND b = 3 OR
12751275
3 <= a;
12761276
id select_type table type possible_keys key key_len ref rows Extra
1277-
1 SIMPLE t1 range a a 5 NULL4Using where; Using index
1277+
1 SIMPLE t1 range a a 5 NULL3Using where; Using index
12781278
SELECT * FROM t1 WHERE
12791279
3 <= a AND a <= 5 OR
12801280
5 <= a AND b = 3 OR
@@ -3054,5 +3054,77 @@ a b
30543054
set eq_range_index_dive_limit=default;
30553055
drop table t1;
30563056
#
3057+
# MDEV-23811: Both disjunct of WHERE condition contain range conditions
3058+
# for the same index such that the second range condition
3059+
# fully covers the first one. Additionally one of the disjuncts
3060+
# contains a range condition for the other index.
3061+
#
3062+
create table t1 (
3063+
pk int primary key auto_increment, a int, b int,
3064+
index idx1(a), index idx2(b)
3065+
);
3066+
insert into t1(a,b) values
3067+
(5,50), (1,10), (3,30), (7,70), (8,80), (4,40), (2,20), (6,60);
3068+
insert into t1(a,b) select a+10, b+100 from t1;
3069+
insert into t1(a,b) select a+20, b+200 from t1;
3070+
insert into t1(a,b) select a+30, b+300 from t1;
3071+
insert into t1(a,b) select a,b from t1;
3072+
analyze table t1;
3073+
Table Op Msg_type Msg_text
3074+
test.t1 analyze status OK
3075+
explain select * from t1 where ((a between 3 and 4) and b < 100) or (a between 2 and 5);
3076+
id select_type table type possible_keys key key_len ref rows Extra
3077+
1 SIMPLE t1 range idx1,idx2 idx1 5 NULL 11 Using index condition; Using where
3078+
select * from t1 where ((a between 3 and 4) and b < 100) or (a between 2 and 5);
3079+
pk a b
3080+
7 2 20
3081+
71 2 20
3082+
3 3 30
3083+
67 3 30
3084+
6 4 40
3085+
70 4 40
3086+
1 5 50
3087+
65 5 50
3088+
explain select * from t1 where (a between 2 and 5) or ((a between 3 and 4) and b < 100);
3089+
id select_type table type possible_keys key key_len ref rows Extra
3090+
1 SIMPLE t1 range idx1,idx2 idx1 5 NULL 11 Using index condition; Using where
3091+
select * from t1 where (a between 2 and 5) or ((a between 3 and 4) and b < 100);
3092+
pk a b
3093+
7 2 20
3094+
71 2 20
3095+
3 3 30
3096+
67 3 30
3097+
6 4 40
3098+
70 4 40
3099+
1 5 50
3100+
65 5 50
3101+
explain select * from t1 where (a between 3 and 4) or ((a between 2 and 5) and b < 100);
3102+
id select_type table type possible_keys key key_len ref rows Extra
3103+
1 SIMPLE t1 range idx1,idx2 idx1 5 NULL 11 Using index condition; Using where
3104+
select * from t1 where (a between 3 and 4) or ((a between 2 and 5) and b < 100);
3105+
pk a b
3106+
7 2 20
3107+
71 2 20
3108+
3 3 30
3109+
67 3 30
3110+
6 4 40
3111+
70 4 40
3112+
1 5 50
3113+
65 5 50
3114+
explain select * from t1 where ((a between 2 and 5) and b < 100) or (a between 3 and 4);
3115+
id select_type table type possible_keys key key_len ref rows Extra
3116+
1 SIMPLE t1 range idx1,idx2 idx1 5 NULL 11 Using index condition; Using where
3117+
select * from t1 where ((a between 2 and 5) and b < 100) or (a between 3 and 4);
3118+
pk a b
3119+
7 2 20
3120+
71 2 20
3121+
3 3 30
3122+
67 3 30
3123+
6 4 40
3124+
70 4 40
3125+
1 5 50
3126+
65 5 50
3127+
drop table t1;
3128+
#
30573129
# End of 10.2 tests
30583130
#

mysql-test/r/range_mrr_icp.result

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1276,7 +1276,7 @@ SELECT * FROM t1 WHERE
12761276
5 <= a AND b = 3 OR
12771277
3 <= a;
12781278
id select_type table type possible_keys key key_len ref rows Extra
1279-
1 SIMPLE t1 range a a 5 NULL4Using where; Using index
1279+
1 SIMPLE t1 range a a 5 NULL3Using where; Using index
12801280
SELECT * FROM t1 WHERE
12811281
3 <= a AND a <= 5 OR
12821282
5 <= a AND b = 3 OR
@@ -3066,6 +3066,78 @@ a b
30663066
set eq_range_index_dive_limit=default;
30673067
drop table t1;
30683068
#
3069+
# MDEV-23811: Both disjunct of WHERE condition contain range conditions
3070+
# for the same index such that the second range condition
3071+
# fully covers the first one. Additionally one of the disjuncts
3072+
# contains a range condition for the other index.
3073+
#
3074+
create table t1 (
3075+
pk int primary key auto_increment, a int, b int,
3076+
index idx1(a), index idx2(b)
3077+
);
3078+
insert into t1(a,b) values
3079+
(5,50), (1,10), (3,30), (7,70), (8,80), (4,40), (2,20), (6,60);
3080+
insert into t1(a,b) select a+10, b+100 from t1;
3081+
insert into t1(a,b) select a+20, b+200 from t1;
3082+
insert into t1(a,b) select a+30, b+300 from t1;
3083+
insert into t1(a,b) select a,b from t1;
3084+
analyze table t1;
3085+
Table Op Msg_type Msg_text
3086+
test.t1 analyze status OK
3087+
explain select * from t1 where ((a between 3 and 4) and b < 100) or (a between 2 and 5);
3088+
id select_type table type possible_keys key key_len ref rows Extra
3089+
1 SIMPLE t1 range idx1,idx2 idx1 5 NULL 11 Using index condition; Using where; Rowid-ordered scan
3090+
select * from t1 where ((a between 3 and 4) and b < 100) or (a between 2 and 5);
3091+
pk a b
3092+
1 5 50
3093+
3 3 30
3094+
6 4 40
3095+
7 2 20
3096+
65 5 50
3097+
67 3 30
3098+
70 4 40
3099+
71 2 20
3100+
explain select * from t1 where (a between 2 and 5) or ((a between 3 and 4) and b < 100);
3101+
id select_type table type possible_keys key key_len ref rows Extra
3102+
1 SIMPLE t1 range idx1,idx2 idx1 5 NULL 11 Using index condition; Using where; Rowid-ordered scan
3103+
select * from t1 where (a between 2 and 5) or ((a between 3 and 4) and b < 100);
3104+
pk a b
3105+
1 5 50
3106+
3 3 30
3107+
6 4 40
3108+
7 2 20
3109+
65 5 50
3110+
67 3 30
3111+
70 4 40
3112+
71 2 20
3113+
explain select * from t1 where (a between 3 and 4) or ((a between 2 and 5) and b < 100);
3114+
id select_type table type possible_keys key key_len ref rows Extra
3115+
1 SIMPLE t1 range idx1,idx2 idx1 5 NULL 11 Using index condition; Using where; Rowid-ordered scan
3116+
select * from t1 where (a between 3 and 4) or ((a between 2 and 5) and b < 100);
3117+
pk a b
3118+
1 5 50
3119+
3 3 30
3120+
6 4 40
3121+
7 2 20
3122+
65 5 50
3123+
67 3 30
3124+
70 4 40
3125+
71 2 20
3126+
explain select * from t1 where ((a between 2 and 5) and b < 100) or (a between 3 and 4);
3127+
id select_type table type possible_keys key key_len ref rows Extra
3128+
1 SIMPLE t1 range idx1,idx2 idx1 5 NULL 11 Using index condition; Using where; Rowid-ordered scan
3129+
select * from t1 where ((a between 2 and 5) and b < 100) or (a between 3 and 4);
3130+
pk a b
3131+
1 5 50
3132+
3 3 30
3133+
6 4 40
3134+
7 2 20
3135+
65 5 50
3136+
67 3 30
3137+
70 4 40
3138+
71 2 20
3139+
drop table t1;
3140+
#
30693141
# End of 10.2 tests
30703142
#
30713143
set optimizer_switch=@mrr_icp_extra_tmp;

mysql-test/r/range_vs_index_merge_innodb.result

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,7 @@ WHERE ((ID < 200) AND (Name LIKE 'Ha%' OR (Country > 'A' AND Country < 'ARG')))
369369
OR ((ID BETWEEN 100 AND 200) AND
370370
(Name LIKE 'Pa%' OR (Population > 103000 AND Population < 104000)));
371371
id select_type table type possible_keys key key_len ref rows Extra
372-
1 SIMPLE Cityindex_mergePRIMARY,Population,Country,NameName,Population,PRIMARY39,4,4NULL307 Using sort_union(Name,Population,PRIMARY); Using where
372+
1 SIMPLE CityrangePRIMARY,Population,Country,Name PRIMARY4NULL200Using where
373373
SELECT * FROM City USE INDEX ()
374374
WHERE ((ID < 10) AND (Name LIKE 'H%' OR (Country > 'A' AND Country < 'ARG')))
375375
OR ((ID BETWEEN 100 AND 110) AND

mysql-test/t/range.test

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2095,6 +2095,48 @@ set eq_range_index_dive_limit=default;
20952095

20962096
drop table t1;
20972097

2098+
--echo #
2099+
--echo # MDEV-23811: Both disjunct of WHERE condition contain range conditions
2100+
--echo # for the same index such that the second range condition
2101+
--echo # fully covers the first one. Additionally one of the disjuncts
2102+
--echo # contains a range condition for the other index.
2103+
--echo #
2104+
2105+
create table t1 (
2106+
pk int primary key auto_increment, a int, b int,
2107+
index idx1(a), index idx2(b)
2108+
);
2109+
insert into t1(a,b) values
2110+
(5,50), (1,10), (3,30), (7,70), (8,80), (4,40), (2,20), (6,60);
2111+
insert into t1(a,b) select a+10, b+100 from t1;
2112+
insert into t1(a,b) select a+20, b+200 from t1;
2113+
insert into t1(a,b) select a+30, b+300 from t1;
2114+
insert into t1(a,b) select a,b from t1;
2115+
2116+
analyze table t1;
2117+
2118+
let $q1=
2119+
select * from t1 where ((a between 3 and 4) and b < 100) or (a between 2 and 5);
2120+
eval explain $q1;
2121+
eval $q1;
2122+
2123+
let $q2=
2124+
select * from t1 where (a between 2 and 5) or ((a between 3 and 4) and b < 100);
2125+
eval explain $q2;
2126+
eval $q2;
2127+
2128+
let $q3=
2129+
select * from t1 where (a between 3 and 4) or ((a between 2 and 5) and b < 100);
2130+
eval explain $q3;
2131+
eval $q3;
2132+
2133+
let $q4=
2134+
select * from t1 where ((a between 2 and 5) and b < 100) or (a between 3 and 4);
2135+
eval explain $q4;
2136+
eval $q4;
2137+
2138+
drop table t1;
2139+
20982140
--echo #
20992141
--echo # End of 10.2 tests
21002142
--echo #

sql/opt_range.cc

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1852,6 +1852,9 @@ SEL_ARG::SEL_ARG(SEL_ARG &arg) :Sql_alloc()
18521852
next_key_part=arg.next_key_part;
18531853
max_part_no= arg.max_part_no;
18541854
use_count=1; elements=1;
1855+
next= 0;
1856+
if (next_key_part)
1857+
++next_key_part->use_count;
18551858
}
18561859

18571860

@@ -8869,9 +8872,15 @@ tree_or(RANGE_OPT_PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2)
88698872
}
88708873
bool no_imerge_from_ranges= FALSE;
88718874

8875+
SEL_TREE *rt1= tree1;
8876+
SEL_TREE *rt2= tree2;
88728877
/* Build the range part of the tree for the formula (1) */
88738878
if (sel_trees_can_be_ored(param, tree1, tree2, &ored_keys))
88748879
{
8880+
if (no_merges1)
8881+
rt1= new SEL_TREE(tree1, TRUE, param);
8882+
if (no_merges2)
8883+
rt2= new SEL_TREE(tree2, TRUE, param);
88758884
bool must_be_ored= sel_trees_must_be_ored(param, tree1, tree2, ored_keys);
88768885
no_imerge_from_ranges= must_be_ored;
88778886

@@ -8929,12 +8938,6 @@ tree_or(RANGE_OPT_PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2)
89298938
else if (!no_ranges1 && !no_ranges2 && !no_imerge_from_ranges)
89308939
{
89318940
/* Build the imerge part of the tree for the formula (1) */
8932-
SEL_TREE *rt1= tree1;
8933-
SEL_TREE *rt2= tree2;
8934-
if (no_merges1)
8935-
rt1= new SEL_TREE(tree1, TRUE, param);
8936-
if (no_merges2)
8937-
rt2= new SEL_TREE(tree2, TRUE, param);
89388941
if (!rt1 || !rt2 ||
89398942
result->merges.push_back(imerge_from_ranges) ||
89408943
imerge_from_ranges->or_sel_tree(param, rt1) ||
@@ -9597,10 +9600,11 @@ key_or(RANGE_OPT_PARAM *param, SEL_ARG *key1,SEL_ARG *key2)
95979600

95989601
if (!tmp->next_key_part)
95999602
{
9603+
SEL_ARG *key2_next= key2->next;
96009604
if (key2->use_count)
96019605
{
96029606
SEL_ARG *key2_cpy= new SEL_ARG(*key2);
9603-
if (key2_cpy)
9607+
if (!key2_cpy)
96049608
return 0;
96059609
key2= key2_cpy;
96069610
}
@@ -9621,7 +9625,7 @@ key_or(RANGE_OPT_PARAM *param, SEL_ARG *key1,SEL_ARG *key2)
96219625
Move on to next range in key2
96229626
*/
96239627
key2->increment_use_count(-1); // Free not used tree
9624-
key2=key2->next;
9628+
key2=key2_next;
96259629
continue;
96269630
}
96279631
else

0 commit comments

Comments
 (0)