summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mysql-test/r/index_merge.result62
-rw-r--r--mysql-test/r/index_merge_innodb.result8
-rw-r--r--mysql-test/r/index_merge_ror.result77
-rw-r--r--mysql-test/r/index_merge_ror_cpk.result16
-rw-r--r--mysql-test/t/index_merge_ror.test36
-rw-r--r--mysql-test/t/index_merge_ror_cpk.test3
-rw-r--r--mysys/my_bitmap.c22
-rw-r--r--sql/opt_range.cc1156
-rw-r--r--sql/opt_range.h197
-rw-r--r--sql/sql_select.cc52
10 files changed, 1144 insertions, 485 deletions
diff --git a/mysql-test/r/index_merge.result b/mysql-test/r/index_merge.result
index 6859d9db728..c9c231c2d5a 100644
--- a/mysql-test/r/index_merge.result
+++ b/mysql-test/r/index_merge.result
@@ -21,7 +21,7 @@ id select_type table type possible_keys key key_len ref rows Extra
explain
select * from t0 where key1 < 3 or key2 > 1020;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t0 index_merge i1,i2 i1,i2 4,4 NULL 31 Using where
+1 SIMPLE t0 index_merge i1,i2 i1,i2 4,4 NULL 31 Using sort_union(i1,i2); Using where
select * from t0 where key1 < 3 or key2 > 1020;
key1 key2 key3 key4 key5 key6 key7 key8
1 1 1 1 1 1 1 1023
@@ -32,11 +32,11 @@ key1 key2 key3 key4 key5 key6 key7 key8
1024 1024 1024 1024 1024 1024 1024 0
explain select * from t0 where key1 < 3 or key2 <4;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t0 index_merge i1,i2 i1,i2 4,4 NULL 7 Using where
+1 SIMPLE t0 index_merge i1,i2 i1,i2 4,4 NULL 7 Using sort_union(i1,i2); Using where
explain
select * from t0 where (key1 > 30 and key1<35) or (key2 >32 and key2 < 40);
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t0 index_merge i1,i2 i1,i2 4,4 NULL 9 Using where
+1 SIMPLE t0 index_merge i1,i2 i1,i2 4,4 NULL 9 Using sort_union(i1,i2); Using where
select * from t0 where (key1 > 30 and key1<35) or (key2 >32 and key2 < 40);
key1 key2 key3 key4 key5 key6 key7 key8
31 31 31 31 31 31 31 993
@@ -56,18 +56,18 @@ id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t0 ref i1,i2,i3 i3 4 const 1 Using where
explain select * from t0 use index (i1,i2) where (key1 < 3 or key2 <4) and key3 = 50;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t0 index_merge i1,i2 i1,i2 4,4 NULL 7 Using where
+1 SIMPLE t0 index_merge i1,i2 i1,i2 4,4 NULL 7 Using sort_union(i1,i2); Using where
explain select * from t0 where (key1 > 1 or key2 > 2);
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t0 ALL i1,i2 NULL NULL NULL 1024 Using where
explain select * from t0 force index (i1,i2) where (key1 > 1 or key2 > 2);
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t0 index_merge i1,i2 i1,i2 4,4 NULL 1024 Using where
+1 SIMPLE t0 index_merge i1,i2 i1,i2 4,4 NULL 1024 Using sort_union(i1,i2); Using where
explain
select * from t0 where key1<3 or key2<3 or (key1>5 and key1<8) or
(key1>10 and key1<12) or (key2>100 and key2<110);
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t0 index_merge i1,i2 i1,i2 4,4 NULL 17 Using where
+1 SIMPLE t0 index_merge i1,i2 i1,i2 4,4 NULL 17 Using sort_union(i1,i2); Using where
explain select * from t0 where key2 = 45 or key1 <=> null;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t0 range i1,i2 i2 4 NULL 1 Using where
@@ -79,26 +79,26 @@ id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t0 range i1,i2 i2 4 NULL 1 Using where
explain select * from t0 where key2=10 or key3=3 or key4 <=> null;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t0 index_merge i2,i3,i4 i2,i3 4,4 NULL 2 Using where
+1 SIMPLE t0 index_merge i2,i3,i4 i2,i3 4,4 NULL 2 Using union(i2,i3); Using where
explain select * from t0 where key2=10 or key3=3 or key4 is null;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t0 index_merge i2,i3,i4 i2,i3 4,4 NULL 2 Using where
+1 SIMPLE t0 index_merge i2,i3,i4 i2,i3 4,4 NULL 2 Using union(i2,i3); Using where
explain select key1 from t0 where (key1 <=> null) or (key2 < 5) or
(key3=10) or (key4 <=> null);
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t0 index_merge i1,i2,i3,i4 i2,i3 4,4 NULL 6 Using where
+1 SIMPLE t0 index_merge i1,i2,i3,i4 i2,i3 4,4 NULL 6 Using sort_union(i2,i3); Using where
explain select key1 from t0 where (key1 <=> null) or (key1 < 5) or
(key3=10) or (key4 <=> null);
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t0 index_merge i1,i3,i4 i1,i3 4,4 NULL 5 Using where
+1 SIMPLE t0 index_merge i1,i3,i4 i1,i3 4,4 NULL 5 Using sort_union(i1,i3); Using where
explain select * from t0 where
(key1 < 3 or key2 < 3) and (key3 < 4 or key4 < 4) and (key5 < 5 or key6 < 5);
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t0 index_merge i1,i2,i3,i4,i5,i6 i1,i2 4,4 NULL 6 Using where
+1 SIMPLE t0 index_merge i1,i2,i3,i4,i5,i6 i1,i2 4,4 NULL 6 Using sort_union(i1,i2); Using where
explain
select * from t0 where (key1 < 3 or key2 < 6) and (key1 < 7 or key3 < 4);
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t0 index_merge i1,i2,i3 i1,i2 4,4 NULL 9 Using where
+1 SIMPLE t0 index_merge i1,i2,i3 i1,i2 4,4 NULL 9 Using sort_union(i1,i2); Using where
select * from t0 where (key1 < 3 or key2 < 6) and (key1 < 7 or key3 < 4);
key1 key2 key3 key4 key5 key6 key7 key8
1 1 1 1 1 1 1 1023
@@ -109,7 +109,7 @@ key1 key2 key3 key4 key5 key6 key7 key8
explain select * from t0 where
(key1 < 3 or key2 < 3) and (key3 < 4 or key4 < 4) and (key5 < 2 or key6 < 2);
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t0 index_merge i1,i2,i3,i4,i5,i6 i1,i2 4,4 NULL 6 Using where
+1 SIMPLE t0 index_merge i1,i2,i3,i4,i5,i6 i1,i2 4,4 NULL 6 Using sort_union(i1,i2); Using where
explain select * from t0 where
(key1 < 3 or key2 < 3) and (key3 < 100);
id select_type table type possible_keys key key_len ref rows Extra
@@ -129,7 +129,7 @@ explain select * from t0 where
or
key1 < 7;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t0 index_merge i1,i2,i3 i1,i2 4,4 NULL 10 Using where
+1 SIMPLE t0 index_merge i1,i2,i3 i1,i2 4,4 NULL 10 Using sort_union(i1,i2); Using where
select * from t0 where
((key1 < 4 or key2 < 4) and (key2 <5 or key3 < 4))
or
@@ -146,25 +146,25 @@ explain select * from t0 where
or
((key5 < 5 or key6 < 6) and (key7 <7 or key8 < 4));
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t0 index_merge i1,i2,i3,i5,i6,i7,i8 i1,i2,i5,i6 4,4,4,4 NULL 19 Using where
+1 SIMPLE t0 index_merge i1,i2,i3,i5,i6,i7,i8 i1,i2,i5,i6 4,4,4,4 NULL 19 Using sort_union(i1,i2,i5,i6); Using where
explain select * from t0 where
((key3 <5 or key5 < 4) and (key1 < 4 or key2 < 4))
or
((key7 <7 or key8 < 4) and (key5 < 5 or key6 < 6));
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t0 index_merge i1,i2,i3,i5,i6,i7,i8 i3,i5,i7,i8 4,4,4,4 NULL 21 Using where
+1 SIMPLE t0 index_merge i1,i2,i3,i5,i6,i7,i8 i3,i5,i7,i8 4,4,4,4 NULL 21 Using sort_union(i3,i5,i7,i8); Using where
explain select * from t0 where
((key3 <5 or key5 < 4) and (key1 < 4 or key2 < 4))
or
((key3 <7 or key5 < 2) and (key5 < 5 or key6 < 6));
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t0 index_merge i1,i2,i3,i5,i6 i3,i5 4,4 NULL 11 Using where
+1 SIMPLE t0 index_merge i1,i2,i3,i5,i6 i3,i5 4,4 NULL 11 Using sort_union(i3,i5); Using where
explain select * from t0 where
((key3 <5 or key5 < 4) and (key1 < 4 or key2 < 4))
or
(((key3 <7 and key7 < 6) or key5 < 2) and (key5 < 5 or key6 < 6));
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t0 index_merge i1,i2,i3,i5,i6,i7 i3,i5 4,4 NULL 11 Using where
+1 SIMPLE t0 index_merge i1,i2,i3,i5,i6,i7 i3,i5 4,4 NULL 11 Using sort_union(i3,i5); Using where
explain select * from t0 where
((key3 <5 or key5 < 4) and (key1 < 4 or key2 < 4))
or
@@ -176,7 +176,7 @@ explain select * from t0 force index(i1, i2, i3, i4, i5, i6 ) where
or
((key3 >=5 or key5 < 2) and (key5 < 5 or key6 < 6));
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t0 index_merge i1,i2,i3,i5,i6 i3,i5 0,4 NULL 1024 Using where
+1 SIMPLE t0 index_merge i1,i2,i3,i5,i6 i3,i5 0,4 NULL 1024 Using sort_union(i3,i5); Using where
select * from t0 where key1 < 5 or key8 < 4 order by key1;
key1 key2 key3 key4 key5 key6 key7 key8
1 1 1 1 1 1 1 1023
@@ -190,7 +190,7 @@ key1 key2 key3 key4 key5 key6 key7 key8
explain
select * from t0 where key1 < 5 or key8 < 4 order by key1;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t0 index_merge i1,i8 i1,i8 4,4 NULL 9 Using where; Using filesort
+1 SIMPLE t0 index_merge i1,i8 i1,i8 4,4 NULL 9 Using sort_union(i1,i8); Using where; Using filesort
create table t2 like t0;
insert into t2 select * from t0;
alter table t2 add index i1_3(key1, key3);
@@ -200,7 +200,7 @@ alter table t2 drop index i2;
alter table t2 add index i321(key3, key2, key1);
explain select key3 from t2 where key1 = 100 or key2 = 100;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t2 index_merge i1_3,i2_3 i1_3,i2_3 4,4 NULL 2 Using where
+1 SIMPLE t2 index_merge i1_3,i2_3 i1_3,i2_3 4,4 NULL 2 Using sort_union(i1_3,i2_3); Using where
explain select key3 from t2 where key1 <100 or key2 < 100;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t2 index i1_3,i2_3 i321 12 NULL 1024 Using where; Using index
@@ -226,7 +226,7 @@ key1a key1b key2 key2_1 key2_2 key3
4 4 0 4 4 4
explain select * from t4 where key1a = 3 or key1b = 4;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t4 index_merge i1a,i1b i1a,i1b 4,4 NULL 2 Using where
+1 SIMPLE t4 index_merge i1a,i1b i1a,i1b 4,4 NULL 2 Using sort_union(i1a,i1b); Using where
explain select * from t4 where key2 = 1 and (key2_1 = 1 or key3 = 5);
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t4 ref i2_1,i2_2 i2_1 4 const 10 Using where
@@ -241,7 +241,7 @@ insert into t1 select * from t0;
explain select * from t0 left join t1 on (t0.key1=t1.key1)
where t0.key1=3 or t0.key2=4;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t0 index_merge i1,i2 i1,i2 4,4 NULL 2 Using where
+1 SIMPLE t0 index_merge i1,i2 i1,i2 4,4 NULL 2 Using union(i1,i2); Using where
1 SIMPLE t1 ref i1 i1 4 test.t0.key1 1
select * from t0 left join t1 on (t0.key1=t1.key1)
where t0.key1=3 or t0.key2=4;
@@ -251,13 +251,13 @@ key1 key2 key3 key4 key5 key6 key7 key8 key1 key2 key3 key4 key5 key6 key7 key8
explain
select * from t0,t1 where (t0.key1=t1.key1) and ( t0.key1=3 or t0.key2=4);
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t0 index_merge i1,i2 i1,i2 4,4 NULL 2 Using where
+1 SIMPLE t0 index_merge i1,i2 i1,i2 4,4 NULL 2 Using union(i1,i2); Using where
1 SIMPLE t1 ref i1 i1 4 test.t0.key1 1
explain
select * from t0,t1 where (t0.key1=t1.key1) and
(t0.key1=3 or t0.key2=4) and t1.key1<200;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t0 index_merge i1,i2 i1,i2 4,4 NULL 2 Using where
+1 SIMPLE t0 index_merge i1,i2 i1,i2 4,4 NULL 2 Using union(i1,i2); Using where
1 SIMPLE t1 ref i1 i1 4 test.t0.key1 1 Using where
explain
select * from t0,t1 where (t0.key1=t1.key1) and
@@ -269,7 +269,7 @@ explain select * from t0,t1 where t0.key1 = 5 and
(t1.key1 = t0.key1 or t1.key8 = t0.key1);
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t0 ref i1 i1 4 const 1 Using where
-1 SIMPLE t1 index_merge i1,i8 i1,i8 4,4 NULL 2 Using where
+1 SIMPLE t1 index_merge i1,i8 i1,i8 4,4 NULL 2 Using union(i1,i8); Using where
explain select * from t0,t1 where t0.key1 < 3 and
(t1.key1 = t0.key1 or t1.key8 = t0.key1);
id select_type table type possible_keys key key_len ref rows Extra
@@ -278,12 +278,12 @@ id select_type table type possible_keys key key_len ref rows Extra
explain select * from t1 where key1=3 or key2=4
union select * from t1 where key1<4 or key3=5;
id select_type table type possible_keys key key_len ref rows Extra
-1 PRIMARY t1 index_merge i1,i2 i1,i2 4,4 NULL 2 Using where
-2 UNION t1 index_merge i1,i3 i1,i3 4,4 NULL 5 Using where
+1 PRIMARY t1 index_merge i1,i2 i1,i2 4,4 NULL 2 Using union(i1,i2); Using where
+2 UNION t1 index_merge i1,i3 i1,i3 4,4 NULL 5 Using sort_union(i1,i3); Using where
explain select * from (select * from t1 where key1 = 3 or key2 =3) as Z where key8 >5;
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY <derived2> system NULL NULL NULL NULL 1
-2 DERIVED t1 index_merge i1,i2 i1,i2 4,4 NULL 2 Using where
+2 DERIVED t1 index_merge i1,i2 i1,i2 4,4 NULL 2 Using union(i1,i2); Using where
create table t3 like t0;
insert into t3 select * from t0;
alter table t3 add key9 int not null, add index i9(key9);
@@ -296,7 +296,7 @@ key1=1 or key2=2 or key3=3 or key4=4 or
key5=5 or key6=6 or key7=7 or key8=8 or
key9=9 or keyA=10 or keyB=11 or keyC=12;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t3 index_merge i1,i2,i3,i4,i5,i6,i7,i8,i9,iA,iB,iC i1,i2,i3,i4,i5,i6,i7,i8,i9,iA,iB,iC 4,4,4,4,4,4,4,4,4,4,4,4 NULL 12 Using where
+1 SIMPLE t3 index_merge i1,i2,i3,i4,i5,i6,i7,i8,i9,iA,iB,iC i1,i2,i3,i4,i5,i6,i7,i8,i9,iA,iB,iC 4,4,4,4,4,4,4,4,4,4,4,4 NULL 12 Using union(i1,i2,i3,i4,i5,i6,i7,i8,i9,iA,iB,iC); Using where
select * from t3 where
key1=1 or key2=2 or key3=3 or key4=4 or
key5=5 or key6=6 or key7=7 or key8=8 or
@@ -316,7 +316,7 @@ key1 key2 key3 key4 key5 key6 key7 key8 key9 keyA keyB keyC
1016 1016 1016 1016 1016 1016 1016 8 1016 1016 1016 1016
explain select * from t0 where key1 < 3 or key2 < 4;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t0 index_merge i1,i2 i1,i2 4,4 NULL 7 Using where
+1 SIMPLE t0 index_merge i1,i2 i1,i2 4,4 NULL 7 Using sort_union(i1,i2); Using where
select * from t0 where key1 < 3 or key2 < 4;
key1 key2 key3 key4 key5 key6 key7 key8
1 1 1 1 1 1 1 1023
diff --git a/mysql-test/r/index_merge_innodb.result b/mysql-test/r/index_merge_innodb.result
index 91718f00f9b..1920ff79731 100644
--- a/mysql-test/r/index_merge_innodb.result
+++ b/mysql-test/r/index_merge_innodb.result
@@ -8,7 +8,7 @@ INDEX i2(key2),
) engine=innodb;
explain select * from t1 where key1 < 5 or key2 > 197;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 index_merge i1,i2 i1,i2 4,4 NULL 8 Using where
+1 SIMPLE t1 index_merge i1,i2 i1,i2 4,4 NULL 8 Using sort_union(i1,i2); Using where
select * from t1 where key1 < 5 or key2 > 197;
key1 key2
0 200
@@ -18,7 +18,7 @@ key1 key2
4 196
explain select * from t1 where key1 < 3 or key2 > 195;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 index_merge i1,i2 i1,i2 4,4 NULL 8 Using where
+1 SIMPLE t1 index_merge i1,i2 i1,i2 4,4 NULL 8 Using sort_union(i1,i2); Using where
select * from t1 where key1 < 3 or key2 > 195;
key1 key2
0 200
@@ -34,7 +34,7 @@ update t1 set str1='aaa', str2='bbb', str3=concat(key2, '-', key1 div 2, '_' ,if
alter table t1 add primary key (str1, zeroval, str2, str3);
explain select * from t1 where key1 < 5 or key2 > 197;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 index_merge i1,i2 i1,i2 4,4 NULL 8 Using where
+1 SIMPLE t1 index_merge i1,i2 i1,i2 4,4 NULL 8 Using sort_union(i1,i2); Using where
select * from t1 where key1 < 5 or key2 > 197;
key1 key2 str1 zeroval str2 str3
4 196 aaa 0 bbb 196-2_a
@@ -44,7 +44,7 @@ key1 key2 str1 zeroval str2 str3
0 200 aaa 0 bbb 200-0_a
explain select * from t1 where key1 < 3 or key2 > 195;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 index_merge i1,i2 i1,i2 4,4 NULL 8 Using where
+1 SIMPLE t1 index_merge i1,i2 i1,i2 4,4 NULL 8 Using sort_union(i1,i2); Using where
select * from t1 where key1 < 3 or key2 > 195;
key1 key2 str1 zeroval str2 str3
4 196 aaa 0 bbb 196-2_a
diff --git a/mysql-test/r/index_merge_ror.result b/mysql-test/r/index_merge_ror.result
index 94d03e8bf03..3049f2857f0 100644
--- a/mysql-test/r/index_merge_ror.result
+++ b/mysql-test/r/index_merge_ror.result
@@ -1,16 +1,16 @@
-drop table if exists t1,t0;
+drop table if exists t0,t1,t2;
select count(*) from t1;
count(*)
64801
explain select key1,key2 from t1 where key1=100 and key2=100;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 index_merge key1,key2 key1,key2 5,5 NULL 3 Using where; Using index
+1 SIMPLE t1 index_merge key1,key2 key1,key2 5,5 NULL 3 Using intersect(key1,key2); Using where; Using index
select key1,key2 from t1 where key1=100 and key2=100;
key1 key2
100 100
explain select key1,key2,key3,key4,filler1 from t1 where key1=100 and key2=100 or key3=100 and key4=100;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 index_merge key1,key2,key3,key4 key1,key2,key3,key4 5,5,5,5 NULL 8 Using where
+1 SIMPLE t1 index_merge key1,key2,key3,key4 key1,key2,key3,key4 5,5,5,5 NULL 8 Using union(intersect(key1,key2),intersect(key3,key4)); Using where
select key1,key2,key3,key4,filler1 from t1 where key1=100 and key2=100 or key3=100 and key4=100;
key1 key2 key3 key4 filler1
100 100 100 100 key1-key2-key3-key4
@@ -18,21 +18,21 @@ insert into t1 (key1, key2, key3, key4, filler1) values (100, 100, -1, -1, 'key1
insert into t1 (key1, key2, key3, key4, filler1) values (-1, -1, 100, 100, 'key4-key3');
explain select key1,key2,filler1 from t1 where key1=100 and key2=100;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 index_merge key1,key2 key1,key2 5,5 NULL 3 Using where
+1 SIMPLE t1 index_merge key1,key2 key1,key2 5,5 NULL 3 Using intersect(key1,key2); Using where
select key1,key2,filler1 from t1 where key1=100 and key2=100;
key1 key2 filler1
100 100 key1-key2-key3-key4
100 100 key1-key2
explain select key1,key2 from t1 where key1=100 and key2=100;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 index_merge key1,key2 key1,key2 5,5 NULL 3 Using where; Using index
+1 SIMPLE t1 index_merge key1,key2 key1,key2 5,5 NULL 3 Using intersect(key1,key2); Using where; Using index
select key1,key2 from t1 where key1=100 and key2=100;
key1 key2
100 100
100 100
explain select key1,key2,key3,key4 from t1 where key1=100 and key2=100 or key3=100 and key4=100;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 index_merge key1,key2,key3,key4 key1,key2,key3,key4 5,5,5,5 NULL 8 Using where
+1 SIMPLE t1 index_merge key1,key2,key3,key4 key1,key2,key3,key4 5,5,5,5 NULL 8 Using union(intersect(key1,key2),intersect(key3,key4)); Using where
select key1,key2,key3,key4 from t1 where key1=100 and key2=100 or key3=100 and key4=100;
key1 key2 key3 key4
100 100 100 100
@@ -40,7 +40,7 @@ key1 key2 key3 key4
-1 -1 100 100
explain select key1,key2,key3,key4,filler1 from t1 where key1=100 and key2=100 or key3=100 and key4=100;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 index_merge key1,key2,key3,key4 key1,key2,key3,key4 5,5,5,5 NULL 8 Using where
+1 SIMPLE t1 index_merge key1,key2,key3,key4 key1,key2,key3,key4 5,5,5,5 NULL 8 Using union(intersect(key1,key2),intersect(key3,key4)); Using where
select key1,key2,key3,key4,filler1 from t1 where key1=100 and key2=100 or key3=100 and key4=100;
key1 key2 key3 key4 filler1
100 100 100 100 key1-key2-key3-key4
@@ -48,14 +48,14 @@ key1 key2 key3 key4 filler1
-1 -1 100 100 key4-key3
explain select key1,key2,key3 from t1 where key1=100 and key2=100 and key3=100;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 index_merge key1,key2,key3 key1,key2,key3 5,5,5 NULL 1 Using where; Using index
+1 SIMPLE t1 index_merge key1,key2,key3 key1,key2,key3 5,5,5 NULL 1 Using intersect(key1,key2,key3); Using where; Using index
select key1,key2,key3 from t1 where key1=100 and key2=100 and key3=100;
key1 key2 key3
100 100 100
insert into t1 (key1,key2,key3,key4,filler1) values (101,101,101,101, 'key1234-101');
explain select key1,key2,key3,key4,filler1 from t1 where key1=100 and key2=100 or key3=101;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 index_merge key1,key2,key3 key1,key2,key3 5,5,5 NULL 5 Using where
+1 SIMPLE t1 index_merge key1,key2,key3 key1,key2,key3 5,5,5 NULL 5 Using union(intersect(key1,key2),key3); Using where
select key1,key2,key3,key4,filler1 from t1 where key1=100 and key2=100 or key3=101;
key1 key2 key3 key4 filler1
100 100 100 100 key1-key2-key3-key4
@@ -72,19 +72,19 @@ select key1,key2,filler1 from t1 where key2=100 and key2=200;
key1 key2 filler1
explain select key1,key2,key3,key4,filler1 from t1 where key1=100 and key2=100 or key3=100 and key4=100;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 index_merge key1,key2,key3,key4 key1,key2,key3,key4 5,5,5,5 NULL 8 Using where
+1 SIMPLE t1 index_merge key1,key2,key3,key4 key1,key2,key3,key4 5,5,5,5 NULL 8 Using union(intersect(key1,key2),intersect(key3,key4)); Using where
select key1,key2,key3,key4,filler1 from t1 where key1=100 and key2=100 or key3=100 and key4=100;
key1 key2 key3 key4 filler1
-1 -1 100 100 key4-key3
delete from t1 where key3=100 and key4=100;
explain select key1,key2,key3,key4,filler1 from t1 where key1=100 and key2=100 or key3=100 and key4=100;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 index_merge key1,key2,key3,key4 key1,key2,key3,key4 5,5,5,5 NULL 8 Using where
+1 SIMPLE t1 index_merge key1,key2,key3,key4 key1,key2,key3,key4 5,5,5,5 NULL 8 Using union(intersect(key1,key2),intersect(key3,key4)); Using where
select key1,key2,key3,key4,filler1 from t1 where key1=100 and key2=100 or key3=100 and key4=100;
key1 key2 key3 key4 filler1
explain select key1,key2 from t1 where key1=100 and key2=100;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 index_merge key1,key2 key1,key2 5,5 NULL 3 Using where; Using index
+1 SIMPLE t1 index_merge key1,key2 key1,key2 5,5 NULL 3 Using intersect(key1,key2); Using where; Using index
select key1,key2 from t1 where key1=100 and key2=100;
key1 key2
insert into t1 (key1, key2, key3, key4, filler1) values (100, 100, 200, 200,'key1-key2-key3-key4-1');
@@ -92,7 +92,7 @@ insert into t1 (key1, key2, key3, key4, filler1) values (100, 100, 200, 200,'key
insert into t1 (key1, key2, key3, key4, filler1) values (100, 100, 200, 200,'key1-key2-key3-key4-3');
explain select key1,key2,key3,key4,filler1 from t1 where key3=200 or (key1=100 and key2=100) or key4=200;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 index_merge key1,key2,key3,key4 key3,key1,key2,key4 5,5,5,5 NULL 16 Using where
+1 SIMPLE t1 index_merge key1,key2,key3,key4 key3,key1,key2,key4 5,5,5,5 NULL 16 Using union(key3,intersect(key1,key2),key4); Using where
select key1,key2,key3,key4,filler1 from t1 where key3=200 or (key1=100 and key2=100) or key4=200;
key1 key2 key3 key4 filler1
100 100 200 200 key1-key2-key3-key4-3
@@ -101,7 +101,7 @@ key1 key2 key3 key4 filler1
insert into t1 (key1, key2, key3, key4, filler1) values (-1, -1, -1, 200,'key4');
explain select key1,key2,key3,key4,filler1 from t1 where key3=200 or (key1=100 and key2=100) or key4=200;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 index_merge key1,key2,key3,key4 key3,key1,key2,key4 5,5,5,5 NULL 18 Using where
+1 SIMPLE t1 index_merge key1,key2,key3,key4 key3,key1,key2,key4 5,5,5,5 NULL 18 Using union(key3,intersect(key1,key2),key4); Using where
select key1,key2,key3,key4,filler1 from t1 where key3=200 or (key1=100 and key2=100) or key4=200;
key1 key2 key3 key4 filler1
100 100 200 200 key1-key2-key3-key4-3
@@ -111,7 +111,7 @@ key1 key2 key3 key4 filler1
insert into t1 (key1, key2, key3, key4, filler1) values (-1, -1, 200, -1,'key3');
explain select key1,key2,key3,key4,filler1 from t1 where key3=200 or (key1=100 and key2=100) or key4=200;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 index_merge key1,key2,key3,key4 key3,key1,key2,key4 5,5,5,5 NULL 20 Using where
+1 SIMPLE t1 index_merge key1,key2,key3,key4 key3,key1,key2,key4 5,5,5,5 NULL 20 Using union(key3,intersect(key1,key2),key4); Using where
select key1,key2,key3,key4,filler1 from t1 where key3=200 or (key1=100 and key2=100) or key4=200;
key1 key2 key3 key4 filler1
100 100 200 200 key1-key2-key3-key4-3
@@ -121,10 +121,10 @@ key1 key2 key3 key4 filler1
-1 -1 200 -1 key3
explain select * from t1 where st_a=1 and st_b=1;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 index_merge sta_swt12a,sta_swt1a,sta_swt2a,sta_swt21a,st_a,stb_swt1a_2b,stb_swt1b,st_b st_a,st_b 4,4 NULL 2508 Using where
+1 SIMPLE t1 index_merge sta_swt12a,sta_swt1a,sta_swt2a,sta_swt21a,st_a,stb_swt1a_2b,stb_swt1b,st_b st_a,st_b 4,4 NULL 2508 Using intersect(st_a,st_b); Using where
explain select st_a,st_b from t1 where st_a=1 and st_b=1;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 index_merge sta_swt12a,sta_swt1a,sta_swt2a,sta_swt21a,st_a,stb_swt1a_2b,stb_swt1b,st_b st_a,st_b 4,4 NULL 2508 Using where; Using index
+1 SIMPLE t1 index_merge sta_swt12a,sta_swt1a,sta_swt2a,sta_swt21a,st_a,stb_swt1a_2b,stb_swt1b,st_b st_a,st_b 4,4 NULL 2508 Using intersect(st_a,st_b); Using where; Using index
explain select st_a from t1 ignore index (st_a) where st_a=1 and st_b=1;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ref sta_swt12a,sta_swt1a,sta_swt2a,sta_swt21a,stb_swt1a_2b,stb_swt1b,st_b st_b 4 const 14720 Using where
@@ -136,33 +136,60 @@ id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ref stb_swt1a_2b,stb_swt1b,st_b stb_swt1b 8 const,const 3757 Using where
explain select * from t1 where st_a=1 and swt1a=1 and swt2a=1 and st_b=1 and swt1b=1 and swt2b=1;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 index_merge sta_swt12a,sta_swt1a,sta_swt2a,sta_swt21a,st_a,stb_swt1a_2b,stb_swt1b,st_b sta_swt12a,stb_swt1a_2b 12,12 NULL 42 Using where
+1 SIMPLE t1 index_merge sta_swt12a,sta_swt1a,sta_swt2a,sta_swt21a,st_a,stb_swt1a_2b,stb_swt1b,st_b sta_swt12a,stb_swt1a_2b 12,12 NULL 42 Using intersect(sta_swt12a,stb_swt1a_2b); Using where
explain select * from t1 ignore index (sta_swt21a, stb_swt1a_2b)
where st_a=1 and swt1a=1 and swt2a=1 and st_b=1 and swt1b=1 and swt2b=1;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 index_merge sta_swt12a,sta_swt1a,sta_swt2a,st_a,stb_swt1b,st_b sta_swt12a,stb_swt1b 12,8 NULL 42 Using where
+1 SIMPLE t1 index_merge sta_swt12a,sta_swt1a,sta_swt2a,st_a,stb_swt1b,st_b sta_swt12a,stb_swt1b 12,8 NULL 42 Using intersect(sta_swt12a,stb_swt1b); Using where
explain select * from t1 ignore index (sta_swt21a, sta_swt12a, stb_swt1a_2b)
where st_a=1 and swt1a=1 and swt2a=1 and st_b=1 and swt1b=1 and swt2b=1;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 index_merge sta_swt1a,sta_swt2a,st_a,stb_swt1b,st_b sta_swt1a,stb_swt1b 8,8 NULL 163 Using where
+1 SIMPLE t1 index_merge sta_swt1a,sta_swt2a,st_a,stb_swt1b,st_b sta_swt1a,sta_swt2a,stb_swt1b 8,8,8 NULL 41 Using intersect(sta_swt1a,sta_swt2a,stb_swt1b); Using where
explain select * from t1 ignore index (sta_swt21a, sta_swt12a, stb_swt1a_2b, stb_swt1b)
where st_a=1 and swt1a=1 and swt2a=1 and st_b=1 and swt1b=1 and swt2b=1;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 index_merge sta_swt1a,sta_swt2a,st_a,st_b sta_swt1a,st_b 8,4 NULL 640 Using where
+1 SIMPLE t1 index_merge sta_swt1a,sta_swt2a,st_a,st_b sta_swt1a,sta_swt2a,st_b 8,8,4 NULL 159 Using intersect(sta_swt1a,sta_swt2a,st_b); Using where
explain select * from t1
where st_a=1 and swt1a=1 and swt2a=1 and st_b=1 and swt1b=1;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 index_merge sta_swt12a,sta_swt1a,sta_swt2a,sta_swt21a,st_a,stb_swt1a_2b,stb_swt1b,st_b sta_swt12a,stb_swt1a_2b 12,12 NULL 42 Using where
+1 SIMPLE t1 index_merge sta_swt12a,sta_swt1a,sta_swt2a,sta_swt21a,st_a,stb_swt1a_2b,stb_swt1b,st_b sta_swt12a,stb_swt1a_2b 12,12 NULL 42 Using intersect(sta_swt12a,stb_swt1a_2b); Using where
explain select * from t1
where st_a=1 and swt1a=1 and st_b=1 and swt1b=1 and swt1b=1;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 index_merge sta_swt12a,sta_swt1a,sta_swt2a,sta_swt21a,st_a,stb_swt1a_2b,stb_swt1b,st_b sta_swt1a,stb_swt1b 8,8 NULL 163 Using where
+1 SIMPLE t1 index_merge sta_swt12a,sta_swt1a,sta_swt2a,sta_swt21a,st_a,stb_swt1a_2b,stb_swt1b,st_b sta_swt1a,stb_swt1b 8,8 NULL 163 Using intersect(sta_swt1a,stb_swt1b); Using where
explain select st_a from t1
where st_a=1 and swt1a=1 and st_b=1 and swt1b=1 and swt1b=1;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 index_merge sta_swt12a,sta_swt1a,sta_swt2a,sta_swt21a,st_a,stb_swt1a_2b,stb_swt1b,st_b sta_swt1a,stb_swt1b 8,8 NULL 163 Using where; Using index
+1 SIMPLE t1 index_merge sta_swt12a,sta_swt1a,sta_swt2a,sta_swt21a,st_a,stb_swt1a_2b,stb_swt1b,st_b sta_swt1a,stb_swt1b 8,8 NULL 163 Using intersect(sta_swt1a,stb_swt1b); Using where; Using index
explain select st_a from t1
where st_a=1 and swt1a=1 and st_b=1 and swt1b=1 and swt1b=1;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 index_merge sta_swt12a,sta_swt1a,sta_swt2a,sta_swt21a,st_a,stb_swt1a_2b,stb_swt1b,st_b sta_swt1a,stb_swt1b 8,8 NULL 163 Using where; Using index
+1 SIMPLE t1 index_merge sta_swt12a,sta_swt1a,sta_swt2a,sta_swt21a,st_a,stb_swt1a_2b,stb_swt1b,st_b sta_swt1a,stb_swt1b 8,8 NULL 163 Using intersect(sta_swt1a,stb_swt1b); Using where; Using index
drop table t0,t1;
+create table t2 (
+a char(10),
+b char(10),
+filler1 char(255),
+filler2 char(255),
+key(a(5)),
+key(b(5))
+);
+select count(a) from t2 where a='BBBBBBBB';
+count(a)
+4
+select count(a) from t2 where b='BBBBBBBB';
+count(a)
+4
+explain select count(a) from t2 where a='AAAAAAAA' and b='AAAAAAAA';
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t2 ref a,b a 6 const 4 Using where
+select count(a) from t2 where a='AAAAAAAA' and b='AAAAAAAA';
+count(a)
+4
+select count(a) from t2 ignore index(a,b) where a='AAAAAAAA' and b='AAAAAAAA';
+count(a)
+4
+insert into t2 values ('ab', 'ab', 'uh', 'oh');
+explain select a from t2 where a='ab';
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t2 ref a a 6 const 1 Using where
diff --git a/mysql-test/r/index_merge_ror_cpk.result b/mysql-test/r/index_merge_ror_cpk.result
index 51c1aee5b2c..f4bef25045b 100644
--- a/mysql-test/r/index_merge_ror_cpk.result
+++ b/mysql-test/r/index_merge_ror_cpk.result
@@ -41,7 +41,7 @@ pk1 pk2 key1 key2 pktail1ok pktail2ok pktail3bad pktail4bad pktail5bad pk2copy b
1 19 0 0 0 0 0 0 0 19 0 filler-data-19 filler2
explain select pk1,pk2 from t1 where key1 = 10 and key2=10 and 2*pk1+1 < 2*96+1;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 index_merge key1,key2 key1,key2 4,4 NULL 1 Using where; Using index
+1 SIMPLE t1 index_merge key1,key2 key1,key2 4,4 NULL 1 Using intersect(key1,key2); Using where; Using index
select pk1,pk2 from t1 where key1 = 10 and key2=10 and 2*pk1+1 < 2*96+1;
pk1 pk2
95 50
@@ -59,13 +59,19 @@ id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ref key1 key1 4 const 101 Using where
explain select * from t1 where pk1 < 7500 and key1 = 10;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 index_merge PRIMARY,key1 key1,PRIMARY 4,4 NULL 38 Using where
+1 SIMPLE t1 index_merge PRIMARY,key1 key1,PRIMARY 4,4 NULL 38 Using intersect(key1,PRIMARY); Using where
explain select * from t1 where pktail1ok=1 and key1=10;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 index_merge key1,pktail1ok key1,pktail1ok 4,4 NULL 1 Using where
+1 SIMPLE t1 index_merge key1,pktail1ok key1,pktail1ok 4,4 NULL 1 Using intersect(key1,pktail1ok); Using where
explain select * from t1 where pktail2ok=1 and key1=10;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 index_merge key1,pktail2ok key1,pktail2ok 4,4 NULL 1 Using where
+1 SIMPLE t1 index_merge key1,pktail2ok key1,pktail2ok 4,4 NULL 1 Using intersect(key1,pktail2ok); Using where
+select ' The following is actually a deficiency, it uses sort_union currently:' as 'note:';
+note:
+ The following is actually a deficiency, it uses sort_union currently:
+explain select * from t1 where (pktail2ok=1 and pk1< 50000) or key1=10;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 index_merge PRIMARY,key1,pktail2ok pktail2ok,key1 8,4 NULL 199 Using sort_union(pktail2ok,key1); Using where
explain select * from t1 where pktail3bad=1 and key1=10;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ref key1,pktail3bad pktail3bad 4 const 98 Using where
@@ -77,7 +83,7 @@ id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ref key1,pktail5bad pktail5bad 4 const 99 Using where
explain select pk1,pk2,key1,key2 from t1 where key1 = 10 and key2=10 limit 10;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 index_merge key1,key2 key1,key2 4,4 NULL 1 Using where; Using index
+1 SIMPLE t1 index_merge key1,key2 key1,key2 4,4 NULL 1 Using intersect(key1,key2); Using where; Using index
select pk1,pk2,key1,key2 from t1 where key1 = 10 and key2=10 limit 10;
pk1 pk2 key1 key2
95 50 10 10
diff --git a/mysql-test/t/index_merge_ror.test b/mysql-test/t/index_merge_ror.test
index 95b6204f424..223bd241d96 100644
--- a/mysql-test/t/index_merge_ror.test
+++ b/mysql-test/t/index_merge_ror.test
@@ -2,7 +2,7 @@
# ROR-index_merge tests.
#
--disable_warnings
-drop table if exists t1,t0;
+drop table if exists t0,t1,t2;
--enable_warnings
--disable_query_log
create table t1
@@ -213,3 +213,37 @@ explain select st_a from t1
drop table t0,t1;
+# 'Partially' covered fields test
+
+create table t2 (
+ a char(10),
+ b char(10),
+ filler1 char(255),
+ filler2 char(255),
+ key(a(5)),
+ key(b(5))
+);
+
+--disable_query_log
+let $1=8;
+while ($1)
+{
+ eval insert into t2 values (repeat(char($1+64), 8),repeat(char($1+64), 8),'filler1', 'filler2');
+ dec $1;
+}
+insert into t2 select * from t2;
+insert into t2 select * from t2;
+--enable_query_log
+
+# The table row buffer is reused. Fill it with rows that don't match.
+select count(a) from t2 where a='BBBBBBBB';
+select count(a) from t2 where b='BBBBBBBB';
+
+# BUG#1:
+explain select count(a) from t2 where a='AAAAAAAA' and b='AAAAAAAA';
+select count(a) from t2 where a='AAAAAAAA' and b='AAAAAAAA';
+select count(a) from t2 ignore index(a,b) where a='AAAAAAAA' and b='AAAAAAAA';
+
+insert into t2 values ('ab', 'ab', 'uh', 'oh');
+explain select a from t2 where a='ab';
+drop table t2;
diff --git a/mysql-test/t/index_merge_ror_cpk.test b/mysql-test/t/index_merge_ror_cpk.test
index 52e946c9be6..4ba5b1a0af3 100644
--- a/mysql-test/t/index_merge_ror_cpk.test
+++ b/mysql-test/t/index_merge_ror_cpk.test
@@ -69,6 +69,9 @@ explain select * from t1 where pk1 < 7500 and key1 = 10;
explain select * from t1 where pktail1ok=1 and key1=10;
explain select * from t1 where pktail2ok=1 and key1=10;
+select ' The following is actually a deficiency, it uses sort_union currently:' as 'note:';
+explain select * from t1 where (pktail2ok=1 and pk1< 50000) or key1=10;
+
explain select * from t1 where pktail3bad=1 and key1=10;
explain select * from t1 where pktail4bad=1 and key1=10;
explain select * from t1 where pktail5bad=1 and key1=10;
diff --git a/mysys/my_bitmap.c b/mysys/my_bitmap.c
index 3c4caa413a9..a6a37fd5441 100644
--- a/mysys/my_bitmap.c
+++ b/mysys/my_bitmap.c
@@ -28,6 +28,9 @@
* when both arguments are bitmaps, they must be of the same size
* bitmap_intersect() is an exception :)
(for for Bitmap::intersect(ulonglong map2buff))
+
+ If THREAD is defined all bitmap operations except bitmap_init/bitmap_free
+ are thread-safe.
TODO:
Make assembler THREAD safe versions of these using test-and-set instructions
@@ -332,29 +335,36 @@ void bitmap_union(MY_BITMAP *map, const MY_BITMAP *map2)
/*
- Get number of set bits in the bitmap
+ SYNOPSIS
+ bitmap_bits_set()
+ map
+ RETURN
+ Number of set bits in the bitmap.
*/
+
uint bitmap_bits_set(const MY_BITMAP *map)
{
- uchar *m= map->bitmap,
- *end= map->bitmap+map->bitmap_size;
+ uchar *m= map->bitmap;
+ uchar *end= m + map->bitmap_size;
uint res= 0;
DBUG_ASSERT(map->bitmap);
-
bitmap_lock((MY_BITMAP *)map);
while (m < end)
{
res+= my_count_bits_ushort(*m++);
}
-
bitmap_unlock((MY_BITMAP *)map);
return res;
}
/*
- Return number of first zero bit or MY_BIT_NONE if all bits are set.
+ SYNOPSIS
+ bitmap_get_first()
+ map
+ RETURN
+ Number of first unset bit in the bitmap or MY_BIT_NONE if all bits are set.
*/
uint bitmap_get_first(const MY_BITMAP *map)
diff --git a/sql/opt_range.cc b/sql/opt_range.cc
index eb6eefccf02..ee328dadad6 100644
--- a/sql/opt_range.cc
+++ b/sql/opt_range.cc
@@ -199,19 +199,7 @@ public:
void store(uint length,char **min_key,uint min_key_flag,
char **max_key, uint max_key_flag)
{
- if ((min_flag & GEOM_FLAG) ||
- (!(min_flag & NO_MIN_RANGE) &&
- !(min_key_flag & (NO_MIN_RANGE | NEAR_MIN))))
- {
- if (maybe_null && *min_value)
- {
- **min_key=1;
- bzero(*min_key+1,length);
- }
- else
- memcpy(*min_key,min_value,length+(int) maybe_null);
- (*min_key)+= length+(int) maybe_null;
- }
+ store_min(length, min_key, min_key_flag);
if (!(max_flag & NO_MAX_RANGE) &&
!(max_key_flag & (NO_MAX_RANGE | NEAR_MAX)))
{
@@ -298,6 +286,7 @@ public:
class SEL_IMERGE;
+
class SEL_TREE :public Sql_alloc
{
public:
@@ -319,7 +308,7 @@ public:
/* The members below are filled/used only after get_mm_tree is done */
key_map ror_scans_map; /* bitmask of ROR scan-able elements in keys */
- uint n_ror_scans;
+ uint n_ror_scans; /* number of set bits in ror_scans_map */
struct st_ror_scan_info **ror_scans; /* list of ROR key scans */
struct st_ror_scan_info **ror_scans_end; /* last ROR scan */
@@ -330,7 +319,8 @@ public:
typedef struct st_qsel_param {
THD *thd;
TABLE *table;
- KEY_PART *key_parts,*key_parts_end,*key[MAX_KEY];
+ KEY_PART *key_parts,*key_parts_end;
+ KEY_PART *key[MAX_KEY]; /* First key parts of keys used in the query */
MEM_ROOT *mem_root;
table_map prev_tables,read_tables,current_table;
uint baseflag, max_key_part, range_count;
@@ -353,7 +343,8 @@ typedef struct st_qsel_param {
uint *imerge_cost_buff; /* buffer for index_merge cost estimates */
uint imerge_cost_buff_size; /* size of the buffer */
- bool is_ror_scan; /* true if last checked tree->key can be used for ROR-scan */
+ /* true if last checked tree->key can be used for ROR-scan */
+ bool is_ror_scan;
} PARAM;
class TABLE_READ_PLAN;
@@ -625,7 +616,6 @@ int imerge_list_or_list(PARAM *param,
RETURN
0 OK, result is stored in *im1.
other Error
-
*/
int imerge_list_or_tree(PARAM *param,
@@ -767,11 +757,12 @@ QUICK_INDEX_MERGE_SELECT::QUICK_INDEX_MERGE_SELECT(THD *thd_param,
:cur_quick_it(quick_selects),pk_quick_select(NULL),unique(NULL),
thd(thd_param)
{
+ DBUG_ENTER("QUICK_INDEX_MERGE_SELECT::QUICK_INDEX_MERGE_SELECT");
index= MAX_KEY;
head= table;
- reset_called= false;
bzero(&read_record, sizeof(read_record));
init_sql_alloc(&alloc,1024,0);
+ DBUG_VOID_RETURN;
}
int QUICK_INDEX_MERGE_SELECT::init()
@@ -785,11 +776,7 @@ int QUICK_INDEX_MERGE_SELECT::reset()
{
int result;
DBUG_ENTER("QUICK_INDEX_MERGE_SELECT::reset");
- if (reset_called)
- DBUG_RETURN(0);
-
- reset_called= true;
- result = cur_quick_select->reset() || prepare_unique();
+ result= cur_quick_select->reset() || prepare_unique();
DBUG_RETURN(result);
}
@@ -810,10 +797,12 @@ QUICK_INDEX_MERGE_SELECT::push_quick_back(QUICK_RANGE_SELECT *quick_sel_range)
QUICK_INDEX_MERGE_SELECT::~QUICK_INDEX_MERGE_SELECT()
{
+ DBUG_ENTER("QUICK_INDEX_MERGE_SELECT::~QUICK_INDEX_MERGE_SELECT");
delete unique;
quick_selects.delete_elements();
delete pk_quick_select;
free_root(&alloc,MYF(0));
+ DBUG_VOID_RETURN;
}
@@ -821,8 +810,7 @@ QUICK_ROR_INTERSECT_SELECT::QUICK_ROR_INTERSECT_SELECT(THD *thd_param,
TABLE *table,
bool retrieve_full_rows,
MEM_ROOT *parent_alloc)
- : cpk_quick(NULL), thd(thd_param), reset_called(false),
- need_to_fetch_row(retrieve_full_rows)
+ : cpk_quick(NULL), thd(thd_param), need_to_fetch_row(retrieve_full_rows)
{
index= MAX_KEY;
head= table;
@@ -835,28 +823,50 @@ QUICK_ROR_INTERSECT_SELECT::QUICK_ROR_INTERSECT_SELECT(THD *thd_param,
head->file->ref_length);
}
+
+/*
+ Do post-constructor initialization.
+ SYNOPSIS
+ QUICK_ROR_INTERSECT_SELECT::init()
+
+ RETURN
+ 0 OK
+ other Error code
+*/
+
int QUICK_ROR_INTERSECT_SELECT::init()
{
- /* Check if last_rowid was allocated in ctor */
+ /* Check if last_rowid was successfully allocated in ctor */
return !last_rowid;
}
/*
- Init this quick select to be a ROR child scan.
- NOTE
- QUICK_ROR_INTERSECT_SELECT::reset() may choose not to call this function
- but reuse its handler object for doing one of range scans. It duplicates
- a part of this function' code.
+ Initialize this quick select to be a ROR-merged scan.
+
+ SYNOPSIS
+ QUICK_RANGE_SELECT::init_ror_merged_scan()
+ reuse_handler If true, use head->file, otherwise create a separate
+ handler object
+
+ NOTES
+ This function creates and prepares for subsequent use a separate handler
+ object if it can't reuse head->file. The reason for this is that during
+ ROR-merge several key scans are performed simultaneously, and a single
+ handler is only capable of preserving context of a single key scan.
+
+ In ROR-merge the quick select doing merge does full records retrieval,
+ merged quick selects read only keys.
+
RETURN
0 ROR child scan initialized, ok to use.
1 error
*/
-int QUICK_RANGE_SELECT::init_ror_child_scan(bool reuse_handler)
+int QUICK_RANGE_SELECT::init_ror_merged_scan(bool reuse_handler)
{
handler *save_file= file;
- DBUG_ENTER("QUICK_RANGE_SELECT::init_ror_child_scan");
+ DBUG_ENTER("QUICK_RANGE_SELECT::init_ror_merged_scan");
if (reuse_handler)
{
@@ -894,7 +904,7 @@ int QUICK_RANGE_SELECT::init_ror_child_scan(bool reuse_handler)
file->close();
goto failure;
}
- free_file= true;
+ free_file= true;
last_rowid= file->ref;
DBUG_RETURN(0);
@@ -903,32 +913,42 @@ failure:
DBUG_RETURN(1);
}
-int QUICK_ROR_INTERSECT_SELECT::init_ror_child_scan(bool reuse_handler)
+
+/*
+ Initialize this quick select to be a part of a ROR-merged scan.
+ SYNOPSIS
+ QUICK_ROR_INTERSECT_SELECT::init_ror_merged_scan()
+ reuse_handler If true, use head->file, otherwise create separate
+ handler object.
+ RETURN
+ 0 OK
+ other error code
+*/
+int QUICK_ROR_INTERSECT_SELECT::init_ror_merged_scan(bool reuse_handler)
{
List_iterator_fast<QUICK_RANGE_SELECT> quick_it(quick_selects);
QUICK_RANGE_SELECT* quick;
- DBUG_ENTER("QUICK_ROR_INTERSECT_SELECT::init_ror_child_scan");
+ DBUG_ENTER("QUICK_ROR_INTERSECT_SELECT::init_ror_merged_scan");
/* Initialize all merged "children" quick selects */
- quick_it.rewind();
DBUG_ASSERT(!(need_to_fetch_row && !reuse_handler));
if (!need_to_fetch_row && reuse_handler)
{
quick= quick_it++;
/*
- There is no use for this->file. Reuse it for first of merged range
+ There is no use of this->file. Use it for the first of merged range
selects.
*/
- if (quick->init_ror_child_scan(true))
+ if (quick->init_ror_merged_scan(true))
DBUG_RETURN(1);
quick->file->extra(HA_EXTRA_KEYREAD_PRESERVE_FIELDS);
}
while((quick= quick_it++))
{
- if (quick->init_ror_child_scan(false))
+ if (quick->init_ror_merged_scan(false))
DBUG_RETURN(1);
quick->file->extra(HA_EXTRA_KEYREAD_PRESERVE_FIELDS);
- /* Share the same record structure in intersect*/
+ /* All merged scans share the same record buffer in intersection. */
quick->record= head->record[0];
}
@@ -940,27 +960,44 @@ int QUICK_ROR_INTERSECT_SELECT::init_ror_child_scan(bool reuse_handler)
DBUG_RETURN(0);
}
+
+/*
+ Initialize quick select for row retrieval.
+ SYNOPSIS
+ reset()
+ RETURN
+ 0 OK
+ other Error code
+*/
+
int QUICK_ROR_INTERSECT_SELECT::reset()
{
int result;
DBUG_ENTER("QUICK_ROR_INTERSECT_SELECT::reset");
- result= init_ror_child_scan(true);
+ result= init_ror_merged_scan(true);
DBUG_RETURN(result);
}
+
+/*
+ Add a merged quick select to this ROR-intersection quick select.
+
+ SYNOPSIS
+ QUICK_ROR_INTERSECT_SELECT::push_quick_back()
+ quick Quick select to be added. The quick select must return
+ rows in rowid order.
+ NOTES
+ This call can only be made before init() is called.
+
+ RETURN
+ false OK
+ true Out of memory.
+*/
+
bool
QUICK_ROR_INTERSECT_SELECT::push_quick_back(QUICK_RANGE_SELECT *quick)
{
- bool res;
- if (head->file->primary_key_is_clustered() &&
- quick->index == head->primary_key)
- {
- cpk_quick= quick;
- res= 0;
- }
- else
- res= quick_selects.push_back(quick);
- return res;
+ return quick_selects.push_back(quick);
}
QUICK_ROR_INTERSECT_SELECT::~QUICK_ROR_INTERSECT_SELECT()
@@ -974,7 +1011,7 @@ QUICK_ROR_INTERSECT_SELECT::~QUICK_ROR_INTERSECT_SELECT()
QUICK_ROR_UNION_SELECT::QUICK_ROR_UNION_SELECT(THD *thd_param,
TABLE *table)
- : thd(thd_param), reset_called(false)
+ : thd(thd_param)
{
index= MAX_KEY;
head= table;
@@ -984,6 +1021,17 @@ QUICK_ROR_UNION_SELECT::QUICK_ROR_UNION_SELECT(THD *thd_param,
my_pthread_setspecific_ptr(THR_MALLOC,&alloc);
}
+
+/*
+ Do post-constructor initialization.
+ SYNOPSIS
+ QUICK_ROR_UNION_SELECT::init()
+
+ RETURN
+ 0 OK
+ other Error code
+*/
+
int QUICK_ROR_UNION_SELECT::init()
{
if (init_queue(&queue, quick_selects.elements, 0,
@@ -1000,8 +1048,11 @@ int QUICK_ROR_UNION_SELECT::init()
return 0;
}
+
/*
- Comparison function to be used by priority queue.
+ Comparison function to be used QUICK_ROR_UNION_SELECT::queue priority
+ queue.
+
SYNPOSIS
QUICK_ROR_UNION_SELECT::queue_cmp()
arg Pointer to QUICK_ROR_UNION_SELECT
@@ -1010,11 +1061,22 @@ int QUICK_ROR_UNION_SELECT::init()
*/
int QUICK_ROR_UNION_SELECT::queue_cmp(void *arg, byte *val1, byte *val2)
{
- QUICK_ROR_UNION_SELECT *self= (QUICK_ROR_UNION_SELECT*)arg;
+ QUICK_ROR_UNION_SELECT *self= (QUICK_ROR_UNION_SELECT*)arg;
return self->head->file->cmp_ref(((QUICK_SELECT_I*)val1)->last_rowid,
((QUICK_SELECT_I*)val2)->last_rowid);
}
+
+/*
+ Initialize quick select for row retrieval.
+ SYNOPSIS
+ reset()
+
+ RETURN
+ 0 OK
+ other Error code
+*/
+
int QUICK_ROR_UNION_SELECT::reset()
{
QUICK_SELECT_I* quick;
@@ -1028,7 +1090,7 @@ int QUICK_ROR_UNION_SELECT::reset()
List_iterator_fast<QUICK_SELECT_I> it(quick_selects);
while ((quick= it++))
{
- if (quick->init_ror_child_scan(false))
+ if (quick->init_ror_merged_scan(false))
DBUG_RETURN(1);
if ((error= quick->get_next()))
{
@@ -1245,13 +1307,33 @@ public:
double read_cost;
ha_rows records; /* estimate of #rows to be examined */
+ /*
+ If true, the scan returns rows in rowid order. This is used only for
+ scans that can be both ROR and non-ROR.
+ */
bool is_ror;
- /* Create quck select for this plan */
+
+ /*
+ Create quick select for this plan.
+ SYNOPSIS
+ make_quick()
+ param Parameter from test_quick_select
+ retrieve_full_rows If true, created quick select will do full record
+ retrieval.
+ parent_alloc Memory pool to use, if any.
+
+ NOTES
+ retrieve_full_rows is ignored by some implementations.
+
+ RETURN
+ created quick select
+ NULL on any error.
+ */
virtual QUICK_SELECT_I *make_quick(PARAM *param,
bool retrieve_full_rows,
MEM_ROOT *parent_alloc=NULL) = 0;
- /* Table read plans are allocated on MEM_ROOT and must not be deleted */
+ /* Table read plans are allocated on MEM_ROOT and are never deleted */
static void *operator new(size_t size, MEM_ROOT *mem_root)
{ return (void*) alloc_root(mem_root, (uint) size); }
static void operator delete(void *ptr,size_t size) {}
@@ -1262,11 +1344,19 @@ class TRP_ROR_UNION;
class TRP_INDEX_MERGE;
+/*
+ Plan for a QUICK_RANGE_SELECT scan.
+ TRP_RANGE::make_quick ignores retrieve_full_rows parameter because
+ QUICK_RANGE_SELECT doesn't distinguish between 'index only' scans and full
+ record retrieval scans.
+*/
+
class TRP_RANGE : public TABLE_READ_PLAN
{
public:
- SEL_ARG *key;
- uint key_idx; /* key number in param->keys */
+ SEL_ARG *key; /* set of intervals to be used in "range" method retrieval */
+ uint key_idx; /* key number in PARAM::key */
+
TRP_RANGE(SEL_ARG *key_arg, uint idx_arg)
: key(key_arg), key_idx(idx_arg)
{}
@@ -1276,7 +1366,6 @@ public:
{
DBUG_ENTER("TRP_RANGE::make_quick");
QUICK_RANGE_SELECT *quick;
- /* ignore retrieve_full_rows there as it is set/used elsewhere */
if ((quick= get_quick_select(param, key_idx, key, parent_alloc)))
{
quick->records= records;
@@ -1286,45 +1375,68 @@ public:
}
};
+
+/* Plan for QUICK_ROR_INTERSECT_SELECT scan. */
+
class TRP_ROR_INTERSECT : public TABLE_READ_PLAN
{
public:
QUICK_SELECT_I *make_quick(PARAM *param, bool retrieve_full_rows,
MEM_ROOT *parent_alloc);
+
+ /* Array of pointers to ROR range scans used in this intersection */
struct st_ror_scan_info **first_scan;
- struct st_ror_scan_info **last_scan;
+ struct st_ror_scan_info **last_scan; /* End of the above array */
+ struct st_ror_scan_info *cpk_scan; /* Clustered PK scan, if there is one */
bool is_covering; /* true if no row retrieval phase is necessary */
- double index_scan_costs;
+ double index_scan_costs; /* SUM(cost(index_scan)) */
};
+
/*
- ROR-union is currently never covering.
+ Plan for QUICK_ROR_UNION_SELECT scan.
+ QUICK_ROR_UNION_SELECT always retrieves full rows, so retrieve_full_rows
+ is ignored by make_quick.
*/
+
class TRP_ROR_UNION : public TABLE_READ_PLAN
{
public:
QUICK_SELECT_I *make_quick(PARAM *param, bool retrieve_full_rows,
MEM_ROOT *parent_alloc);
- TABLE_READ_PLAN **first_ror;
- TABLE_READ_PLAN **last_ror;
-
+ TABLE_READ_PLAN **first_ror; /* array of ptrs to plans for merged scans */
+ TABLE_READ_PLAN **last_ror; /* end of the above array */
};
+
+/*
+ Plan for QUICK_INDEX_MERGE_SELECT scan.
+ QUICK_ROR_INTERSECT_SELECT always retrieves full rows, so retrieve_full_rows
+ is ignored by make_quick.
+*/
+
class TRP_INDEX_MERGE : public TABLE_READ_PLAN
{
public:
QUICK_SELECT_I *make_quick(PARAM *param, bool retrieve_full_rows,
MEM_ROOT *parent_alloc);
- TRP_RANGE **range_scans;
- TRP_RANGE **range_scans_end;
+ TRP_RANGE **range_scans; /* array of ptrs to plans of merged scans */
+ TRP_RANGE **range_scans_end; /* end of the array */
};
/*
- Fill param->needed_fields with bitmap of fields used in the query
- NOTE
- Do not put clustered PK members into it as they are implicitly present in
- all keys.
+ Fill param->needed_fields with bitmap of fields used in the query.
+ SYNOPSIS
+ fill_used_fields_bitmap()
+ param Parameter from test_quick_select function.
+
+ NOTES
+ Clustered PK members are not put into the bitmap as they are implicitly
+ present in all keys (and it is impossible to avoid reading them).
+ RETURN
+ 0 Ok
+ 1 Out of memory.
*/
static int fill_used_fields_bitmap(PARAM *param)
@@ -1348,6 +1460,7 @@ static int fill_used_fields_bitmap(PARAM *param)
pk= param->table->primary_key;
if (param->table->file->primary_key_is_clustered() && pk != MAX_KEY)
{
+ /* The table uses clustered PK and it is not internally generated */
KEY_PART_INFO *key_part= param->table->key_info[pk].key_part;
KEY_PART_INFO *key_part_end= key_part +
param->table->key_info[pk].key_parts;
@@ -1361,28 +1474,39 @@ static int fill_used_fields_bitmap(PARAM *param)
/*
- Test if a key can be used in different ranges
+ Test if a key can be used in different ranges
SYNOPSIS
- SQL_SELECT::test_quick_select(thd,keys_to_use, prev_tables,
- limit, force_quick_range)
+ SQL_SELECT::test_quick_select()
+ thd Current thread
+ keys_to_use Keys to use for range retrieval
+ prev_tables Tables assumed to be already read when the scan is
+ performed (but not read at the moment of this call)
+ limit Query limit
+ force_quick_range Prefer to use range (instead of full table scan) even
+ if it is more expensive.
+
+ NOTES
+ Updates the following in the select parameter:
+ needed_reg - Bits for keys with may be used if all prev regs are read
+ quick - Parameter to use when reading records.
+
+ In the table struct the following information is updated:
+ quick_keys - Which keys can be used
+ quick_rows - How many rows the key matches
- Updates the following in the select parameter:
- needed_reg - Bits for keys with may be used if all prev regs are read
- quick - Parameter to use when reading records.
+ TODO
+ Check if this function really needs to modify keys_to_use, and change the
+ code to pass it by reference if it doesn't.
+
+ In addition to force_quick_range other means can be (an usually are) used
+ to make this function prefer range over full table scan. Figure out if
+ force_quick_range is really needed.
- In the table struct the following information is updated:
- quick_keys - Which keys can be used
- quick_rows - How many rows the key matches
-
- RETURN VALUES
- -1 if impossible select
- 0 if can't use quick_select
- 1 if found usable range
-
- TODO
- check if the function really needs to modify keys_to_use, and change the
- code to pass it by reference if not
+ RETURN
+ -1 if impossible select (i.e. certainly no rows will be selected)
+ 0 if can't use quick_select
+ 1 if found usable ranges and quick select has been successfully created.
*/
int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
@@ -1393,6 +1517,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
uint idx;
double scan_time;
DBUG_ENTER("test_quick_select");
+ printf("\nQUERY: %s\n", thd->query);
DBUG_PRINT("enter",("keys_to_use: %lu prev_tables: %lu const_tables: %lu",
keys_to_use.to_ulonglong(), (ulong) prev_tables,
(ulong) const_tables));
@@ -1485,6 +1610,18 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
}
param.key_parts_end=key_parts;
+ /* Calculate cost of full index read for the shortest covering index */
+ if (!head->used_keys.is_clear_all())
+ {
+ int key_for_use= find_shortest_key(head, &head->used_keys);
+ double key_read_time= get_index_only_read_time(&param, records,
+ key_for_use);
+ DBUG_PRINT("info", ("'all'+'using index' scan will be using key %d, "
+ "read time %g", key_for_use, key_read_time));
+ if (key_read_time < read_time)
+ read_time= key_read_time;
+ }
+
if ((tree=get_mm_tree(&param,cond)))
{
if (tree->type == SEL_TREE::IMPOSSIBLE)
@@ -1499,8 +1636,6 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
/*
It is possible to use a quick select (but maybe it would be slower
than 'all' table scan).
- Btw, tree type SEL_TREE::INDEX_MERGE was not introduced
- intentionally.
*/
if (tree->merges.is_empty())
{
@@ -1522,7 +1657,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
{
/*
Get best non-covering ROR-intersection plan and prepare data for
- building covering ROR-intersection
+ building covering ROR-intersection.
*/
if ((new_trp= get_best_ror_intersect(&param, tree, false,
best_read_time,
@@ -1550,25 +1685,6 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
LINT_INIT(new_conj_trp); /* no empty index_merge lists possible */
DBUG_PRINT("info",("No range reads possible,"
" trying to construct index_merge"));
-
- /*
- Calculate cost of 'all'+index_only scan if it is possible.
- It is possible that table can be read in two ways:
- a) 'all' + index_only
- b) index_merge without index_only.
- */
- if (!head->used_keys.is_clear_all())
- {
- int key_for_use= find_shortest_key(head, &head->used_keys);
- ha_rows total_table_records= (0 == head->file->records)? 1 :
- head->file->records;
- read_time = get_index_only_read_time(&param, total_table_records,
- key_for_use);
- DBUG_PRINT("info",
- ("'all'+'using index' scan will be using key %d, "
- "read time %g", key_for_use, read_time));
- }
-
List_iterator_fast<SEL_IMERGE> it(tree->merges);
while ((imerge= it++))
{
@@ -1579,8 +1695,9 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
}
best_trp= best_conj_trp;
}
+ my_pthread_setspecific_ptr(THR_MALLOC, old_root);
- my_pthread_setspecific_ptr(THR_MALLOC, old_root);
+ /* If we got a read plan, create a quick select from it. */
if (best_trp)
{
records= best_trp->records;
@@ -1589,7 +1706,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
delete quick;
quick= NULL;
}
- }
+ }
}
}
my_pthread_setspecific_ptr(THR_MALLOC, old_root);
@@ -1608,19 +1725,22 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
/*
- Get cost of 'sweep' full row retrieveal of #records rows.
+ Get cost of 'sweep' full records retrieval.
+ SYNOPSIS
+ get_sweep_read_cost()
+ param Parameter from test_quick_select
+ records # of records to be retrieved
RETURN
- 0 - ok
- 1 - sweep is more expensive then full table scan.
+ cost of sweep
*/
-bool get_sweep_read_cost(const PARAM *param, ha_rows records, double* cost,
- double index_reads_cost, double max_cost)
+double get_sweep_read_cost(const PARAM *param, ha_rows records)
{
+ double result;
if (param->table->file->primary_key_is_clustered())
{
- *cost= param->table->file->read_time(param->table->primary_key,
- records, records);
+ result= param->table->file->read_time(param->table->primary_key,
+ records, records);
}
else
{
@@ -1630,8 +1750,8 @@ bool get_sweep_read_cost(const PARAM *param, ha_rows records, double* cost,
n_blocks * (1.0 - pow(1.0 - 1.0/n_blocks, rows2double(records)));
if (busy_blocks < 1.0)
busy_blocks= 1.0;
- DBUG_PRINT("info",("sweep: nblocks= %g, busy_blocks=%g index_blocks=%g",
- n_blocks, busy_blocks, index_reads_cost));
+ DBUG_PRINT("info",("sweep: nblocks=%g, busy_blocks=%g", n_blocks,
+ busy_blocks));
/*
Disabled: Bail out if # of blocks to read is bigger than # of blocks in
table data file.
@@ -1642,7 +1762,7 @@ bool get_sweep_read_cost(const PARAM *param, ha_rows records, double* cost,
if (!join || join->tables == 1)
{
/* No join, assume reading is done in one 'sweep' */
- *cost= busy_blocks*(DISK_SEEK_BASE_COST +
+ result= busy_blocks*(DISK_SEEK_BASE_COST +
DISK_SEEK_PROP_COST*n_blocks/busy_blocks);
}
else
@@ -1651,11 +1771,11 @@ bool get_sweep_read_cost(const PARAM *param, ha_rows records, double* cost,
Possibly this is a join with source table being non-last table, so
assume that disk seeks are random here.
*/
- *cost = busy_blocks;
+ result= busy_blocks;
}
}
- DBUG_PRINT("info",("returning cost=%g", *cost));
- return 0;
+ DBUG_PRINT("info",("returning cost=%g", result));
+ return result;
}
@@ -1663,15 +1783,11 @@ bool get_sweep_read_cost(const PARAM *param, ha_rows records, double* cost,
Get best plan for a SEL_IMERGE disjunctive expression.
SYNOPSIS
get_best_disjunct_quick()
- param
- imerge
+ param Parameter from check_quick_select function
+ imerge Expression to use
read_time Don't create scans with cost > read_time
- RETURN
- read plan
- NULL - OOM or no read scan could be built.
NOTES
-
index_merge cost is calculated as follows:
index_merge_cost =
cost(index_reads) + (see #1)
@@ -1723,11 +1839,14 @@ bool get_sweep_read_cost(const PARAM *param, ha_rows records, double* cost,
ROR-union cost is calculated in the same way index_merge, but instead of
Unique a priority queue is used.
+ RETURN
+ Created read plan
+ NULL - Out of memory or no read scan could be built.
*/
static
TABLE_READ_PLAN *get_best_disjunct_quick(PARAM *param, SEL_IMERGE *imerge,
- double read_time)
+ double read_time)
{
SEL_TREE **ptree;
TRP_INDEX_MERGE *imerge_trp= NULL;
@@ -1823,12 +1942,11 @@ TABLE_READ_PLAN *get_best_disjunct_quick(PARAM *param, SEL_IMERGE *imerge,
}
/* Calculate cost(rowid_to_row_scan) */
- if (get_sweep_read_cost(param, non_cpk_scan_records, &sweep_cost,
- blocks_in_index_read, read_time))
- goto build_ror_index_merge;
- imerge_cost += sweep_cost;
+ imerge_cost += get_sweep_read_cost(param, non_cpk_scan_records);
DBUG_PRINT("info",("index_merge cost with rowid-to-row scan: %g",
imerge_cost));
+ if (imerge_cost > read_time)
+ goto build_ror_index_merge;
/* Add Unique operations cost */
unique_calc_buff_size=
@@ -1923,7 +2041,7 @@ skip_to_ror_scan:
/*
rows to retrieve=
SUM(rows_in_scan_i) - table_rows * PROD(rows_in_scan_i / table_rows).
- This is valid because index_merge constructuion guarantees that conditions
+ This is valid because index_merge construction guarantees that conditions
in disjunction do not share key parts.
*/
roru_total_records -= (ha_rows)(roru_intersect_part*
@@ -1937,14 +2055,12 @@ skip_to_ror_scan:
See get_merge_buffers_cost function for queue_use_cost formula derivation.
*/
- if (get_sweep_read_cost(param, roru_total_records, &sweep_cost,
- roru_index_costs, read_time))
- DBUG_RETURN(NULL);
double roru_total_cost;
roru_total_cost= roru_index_costs +
rows2double(roru_total_records)*log(n_child_scans) /
- (TIME_FOR_COMPARE_ROWID * M_LN2) +
- sweep_cost;
+ (TIME_FOR_COMPARE_ROWID * M_LN2) +
+ get_sweep_read_cost(param, roru_total_records);
+
DBUG_PRINT("info", ("ROR-union: cost %g, %d members", roru_total_cost,
n_child_scans));
TRP_ROR_UNION* roru;
@@ -1965,10 +2081,9 @@ skip_to_ror_scan:
/*
Calculate cost of 'index only' scan for given index and number of records.
- (We can resolve this by only reading through this key.)
SYNOPSIS
- get_whole_index_read_time()
+ get_index_only_read_time()
param parameters structure
records #of records to read
keynr key to read
@@ -1992,35 +2107,40 @@ inline double get_index_only_read_time(const PARAM* param, ha_rows records,
typedef struct st_ror_scan_info
{
- uint idx; /* # of used key in param->keys */
- uint keynr; /* # of used key in table */
- ha_rows records;
- SEL_ARG *sel_arg;
- /* Fields used in the query and covered by this ROR scan */
+ uint idx; /* # of used key in param->keys */
+ uint keynr; /* # of used key in table */
+ ha_rows records; /* estimate of # records this scan will return */
+
+ /* Set of intervals over key fields that will be used for row retrieval. */
+ SEL_ARG *sel_arg;
+
+ /* Fields used in the query and covered by this ROR scan. */
MY_BITMAP covered_fields;
- uint used_fields_covered;
- int key_rec_length; /* length of key record with rowid */
+ uint used_fields_covered; /* # of set bits in covered_fields */
+ int key_rec_length; /* length of key record (including rowid) */
/*
- Array of
- #rows(keypart_1=c1 AND ... AND key_part_i=c_i) /
- #rows(keypart_1=c1 AND ... AND key_part_{i+1}=c_{i+1}) values
- */
- double *key_part_rows;
- double index_read_cost;
- uint first_uncovered_field;
- uint key_components;
-}ROR_SCAN_INFO;
+ Cost of reading all index records with values in sel_arg intervals set
+ (assuming there is no need to access full table records)
+ */
+ double index_read_cost;
+ uint first_uncovered_field; /* first unused bit in covered_fields */
+ uint key_components; /* # of parts in the key */
+} ROR_SCAN_INFO;
/*
- Create ROR_SCAN_INFO* structure for condition sel_arg on key idx.
+ Create ROR_SCAN_INFO* structure with a single ROR scan on index idx using
+ sel_arg set of intervals.
+
SYNOPSIS
make_ror_scan()
- param
- idx index of key in param->keys
+ param Parameter from test_quick_select function
+ idx Index of key in param->keys
+ sel_arg Set of intervals for a given key
RETURN
- NULL - OOM.
+ NULL - out of memory
+ ROR scan structure containing a scan for {idx, sel_arg}
*/
static
@@ -2049,10 +2169,6 @@ ROR_SCAN_INFO *make_ror_scan(const PARAM *param, int idx, SEL_ARG *sel_arg)
param->fields_bitmap_size*8, false))
DBUG_RETURN(NULL);
bitmap_clear_all(&ror_scan->covered_fields);
- if (!(ror_scan->key_part_rows=
- (double*)alloc_root(param->mem_root, sizeof(double)*
- param->table->key_info[keynr].key_parts)))
- DBUG_RETURN(NULL);
KEY_PART_INFO *key_part= param->table->key_info[keynr].key_part;
KEY_PART_INFO *key_part_end= key_part +
@@ -2069,50 +2185,23 @@ ROR_SCAN_INFO *make_ror_scan(const PARAM *param, int idx, SEL_ARG *sel_arg)
ror_scan->index_read_cost=
get_index_only_read_time(param, param->table->quick_rows[ror_scan->keynr],
ror_scan->keynr);
- /*
- Calculate # rows estimates for
- (key_part1=c1)
- (key_part1=c1 AND key_part2=c2)
- ...and so on
- */
- char key_val[MAX_KEY_LENGTH+MAX_FIELD_WIDTH]; /* values of current key */
- char *key_ptr= key_val;
- ha_rows records;
- ha_rows prev_records= param->table->file->records;
- double *rows_diff= ror_scan->key_part_rows;
- key_part= param->table->key_info[keynr].key_part;
- SEL_ARG *arg= ror_scan->sel_arg;
- /*
- We have #rows estimate already for first key part, so do first loop
- iteration separately:
- */
- arg->store_min(key_part->length, &key_ptr, 0);
- prev_records= param->table->quick_rows[ror_scan->keynr];
- *(rows_diff++)= rows2double(prev_records) / param->table->file->records;
- arg=arg->next_key_part;
- for(; arg; ++key_part, arg= arg->next_key_part)
- {
- arg->store_min(key_part->length, &key_ptr, 0);
- records=
- param->table->file->records_in_range(ror_scan->keynr,
- (byte*)key_val, key_ptr - key_val,
- HA_READ_KEY_EXACT,
- (byte*)key_val, key_ptr - key_val,
- HA_READ_AFTER_KEY);
- if (records == HA_POS_ERROR)
- return NULL; /* shouldn't happen actually */
- *(rows_diff++)= rows2double(records) / rows2double(prev_records);
- prev_records= records;
- }
DBUG_RETURN(ror_scan);
}
-/*
- Order ROR_SCAN_INFO** by
- E(#records_matched) * key_record_length
+/*
+ Compare two ROR_SCAN_INFO** by E(#records_matched) * key_record_length.
+ SYNOPSIS
+ cmp_ror_scan_info()
+ a ptr to first compared value
+ b ptr to second compared value
+
+ RETURN
+ -1 a < b
+ 0 a = b
+ 1 a > b
*/
-int cmp_ror_scan_info(ROR_SCAN_INFO** a, ROR_SCAN_INFO** b)
+static int cmp_ror_scan_info(ROR_SCAN_INFO** a, ROR_SCAN_INFO** b)
{
double val1= rows2double((*a)->records) * (*a)->key_rec_length;
double val2= rows2double((*b)->records) * (*b)->key_rec_length;
@@ -2120,12 +2209,22 @@ int cmp_ror_scan_info(ROR_SCAN_INFO** a, ROR_SCAN_INFO** b)
}
/*
- Order ROR_SCAN_INFO** by
+ Compare two ROR_SCAN_INFO** by
(#covered fields in F desc,
#components asc,
number of first not covered component asc)
+
+ SYNOPSIS
+ cmp_ror_scan_info_covering()
+ a ptr to first compared value
+ b ptr to second compared value
+
+ RETURN
+ -1 a < b
+ 0 a = b
+ 1 a > b
*/
-int cmp_ror_scan_info_covering(ROR_SCAN_INFO** a, ROR_SCAN_INFO** b)
+static int cmp_ror_scan_info_covering(ROR_SCAN_INFO** a, ROR_SCAN_INFO** b)
{
if ((*a)->used_fields_covered > (*b)->used_fields_covered)
return -1;
@@ -2160,10 +2259,37 @@ typedef struct
*/
double records_fract;
ha_rows index_records; /* sum(#records to look in indexes) */
-}ROR_INTERSECT_INFO;
+} ROR_INTERSECT_INFO;
-/* Allocate a ROR_INTERSECT and initialize it to contain zero scans */
+/*
+ Re-initialize an allocated intersect info to contain zero scans.
+ SYNOPSIS
+ info Intersection info structure to re-initialize.
+*/
+
+static void ror_intersect_reinit(ROR_INTERSECT_INFO *info)
+{
+ info->is_covering= false;
+ info->index_scan_costs= 0.0f;
+ info->records_fract= 1.0f;
+ bitmap_clear_all(&info->covered_fields);
+}
+
+/*
+ Allocate a ROR_INTERSECT_INFO and initialize it to contain zero scans.
+
+ SYNOPSIS
+ ror_intersect_init()
+ param Parameter from test_quick_select
+ is_index_only If true, set ROR_INTERSECT_INFO to be covering
+
+ RETURN
+ allocated structure
+ NULL on error
+*/
+
+static
ROR_INTERSECT_INFO* ror_intersect_init(const PARAM *param, bool is_index_only)
{
ROR_INTERSECT_INFO *info;
@@ -2172,42 +2298,46 @@ ROR_INTERSECT_INFO* ror_intersect_init(const PARAM *param, bool is_index_only)
sizeof(ROR_INTERSECT_INFO))))
return NULL;
info->param= param;
- info->is_covering= is_index_only;
- info->index_scan_costs= 0.0f;
- info->records_fract= 1.0f;
-
if (!(buf= (uchar*)alloc_root(param->mem_root, param->fields_bitmap_size)))
return NULL;
if (bitmap_init(&info->covered_fields, buf, param->fields_bitmap_size*8,
false))
return NULL;
- bitmap_clear_all(&info->covered_fields);
+ ror_intersect_reinit(info);
return info;
}
+
/*
- Check if it makes sense to add a ROR scan to ROR-intersection, and if yes
- update parameters of ROR-intersection, including its cost.
+ Check if adding a ROR scan to a ROR-intersection reduces its cost of
+ ROR-intersection and if yes, update parameters of ROR-intersection,
+ including its cost.
- RETURN
- true ROR scan added to ROR-intersection, cost updated.
- false It doesn't make sense to add this ROR scan to this ROR-intersection.
+ SYNOPSIS
+ ror_intersect_add()
+ param Parameter from test_quick_select
+ info ROR-intersection structure to add the scan to.
+ ror_scan ROR scan info to add.
+ is_cpk_scan If true, add the scan as CPK scan (this can be inferred
+ from other parameters and is passed separately only to
+ avoid duplicating the inference code)
- NOTE
- Adding a ROR scan to ROR-intersect "makes sense" iff selectivt
+ NOTES
+ Adding a ROR scan to ROR-intersect "makes sense" iff the cost of ROR-
+ intersection decreases. The cost of ROR-intersection is caclulated as
+ follows:
- Cost of ROR-intersection is calulated as follows:
- cost= SUM_i(key_scan_cost_i) + cost_of_full_rows_retrieval
+ cost= SUM_i(key_scan_cost_i) + cost_of_full_rows_retrieval
if (union of indexes used covers all needed fields)
cost_of_full_rows_retrieval= 0;
else
{
cost_of_full_rows_retrieval=
- cost_of_sweep_read(E(rows_to_retrive), rows_in_table);
+ cost_of_sweep_read(E(rows_to_retrieve), rows_in_table);
}
- E(rows_to_retrive) is calulated as follows:
+ E(rows_to_retrieve) is caclulated as follows:
Suppose we have a condition on several keys
cond=k_11=c_11 AND k_12=c_12 AND ... // parts of first key
k_21=c_21 AND k_22=c_22 AND ... // parts of second key
@@ -2233,13 +2363,13 @@ ROR_INTERSECT_INFO* ror_intersect_init(const PARAM *param, bool is_index_only)
save R to be cond and proceed to recursion step.
Recursion step:
- We have set of fixed fields/satisfied conditions) F, probability P(F),
+ We have a set of fixed fields/satisfied conditions) F, probability P(F),
and remaining conjunction R
Pick next key part on current key and its condition "k_ij=c_ij".
We will add "k_ij=c_ij" into F and update P(F).
- Lets denote k_ij as t, R = t AND R1, where i1 may still contain t. Then
+ Lets denote k_ij as t, R = t AND R1, where R1 may still contain t. Then
- P((t AND R1)|F) = P(t|F) * P(R1|t|F) = P(t|F) * P(R|(t AND F)) (2)
+ P((t AND R1)|F) = P(t|F) * P(R1|t|F) = P(t|F) * P(R1|(t AND F)) (2)
(where '|' mean conditional probability, not "or")
@@ -2253,72 +2383,153 @@ ROR_INTERSECT_INFO* ror_intersect_init(const PARAM *param, bool is_index_only)
P(t|F) = P(t|(fields_before_t_in_key AND other_fields)) =
= P(t|fields_before_t_in_key).
- P(t|fields_before_t_in_key)= #distinct_values(fields_before_t_in_key) /
- #distinct_values(fields_before_t_in_key, t)
+ P(t|fields_before_t_in_key) = #records(fields_before_t_in_key) /
+ #records(fields_before_t_in_key, t)
- The second multiplier is calculated by applying this step recusively.
+ The second multiplier is calculated by applying this step recursively.
- This function applies recursion steps for all fixed key members of
- one key, accumulating sets of covered fields and
- The very first step described is done as recursion step with
- P(fixed fields)=1 and empty set of fixed fields.
+ IMPLEMENTATION
+ This function calculates the result of application of the "recursion step"
+ described above for all fixed key members of a single key, accumulating set
+ of covered fields, selectivity, etc.
+
+ The calculation is conducted as follows:
+ Lets denote #records(keypart1, ... keypartK) as n_k. We need to calculate
+
+ n_{k1} n_{k_2}
+ --------- * --------- * .... (3)
+ n_{k1-1} n_{k2_1}
+
+ where k1,k2,... are key parts which fields were not yet marked as fixed
+ ( this is result of application of option b) of the recursion step for
+ parts of a single key).
+ Since it is reasonable to expect that most of the fields are not marked
+ as fixed, we calcualate (3) as
+
+ n_{i1} n_{i_2}
+ (3) = n_{max_key_part} / ( --------- * --------- * .... )
+ n_{i1-1} n_{i2_1}
+
+ where i1,i2, .. are key parts that were already marked as fixed.
+
+ In order to minimize number of expensive records_in_range calls we group
+ and reduce adjacent fractions.
+
+ RETURN
+ true ROR scan added to ROR-intersection, cost updated.
+ false It doesn't make sense to add this ROR scan to this ROR-intersection.
*/
-bool ror_intersect_add(ROR_INTERSECT_INFO *info, ROR_SCAN_INFO* ror_scan)
+bool ror_intersect_add(const PARAM *param, ROR_INTERSECT_INFO *info,
+ ROR_SCAN_INFO* ror_scan, bool is_cpk_scan=false)
{
int i;
SEL_ARG *sel_arg;
KEY_PART_INFO *key_part=
info->param->table->key_info[ror_scan->keynr].key_part;
double selectivity_mult= 1.0;
+ char key_val[MAX_KEY_LENGTH+MAX_FIELD_WIDTH]; /* key values tuple */
+
DBUG_ENTER("ror_intersect_add");
DBUG_PRINT("info", ("Current selectivity= %g", info->records_fract));
DBUG_PRINT("info", ("Adding scan on %s",
info->param->table->key_info[ror_scan->keynr].name));
+ SEL_ARG *tuple_arg= NULL;
+ char *key_ptr= key_val;
+ bool cur_covered, prev_covered=
+ bitmap_is_set(&info->covered_fields, key_part->fieldnr);
+
+ ha_rows prev_records= param->table->file->records;
+
for(i= 0, sel_arg= ror_scan->sel_arg; sel_arg;
i++, sel_arg= sel_arg->next_key_part)
{
- if (!bitmap_is_set(&info->covered_fields, (key_part + i)->fieldnr))
+ cur_covered= bitmap_is_set(&info->covered_fields, (key_part + i)->fieldnr);
+ if (cur_covered != prev_covered)
{
- /*
- P(t|F) = P(t|(fields_before_t_in_key AND other_fields)) =
- = P(t|fields_before_t_in_key).
- */
- selectivity_mult *= ror_scan->key_part_rows[i];
+ /* create (part1val, ..., part{n-1}val) tuple. */
+ {
+ if (!tuple_arg)
+ {
+ tuple_arg= ror_scan->sel_arg;
+ tuple_arg->store_min(key_part->length, &key_ptr, 0);
+ }
+ while (tuple_arg->next_key_part != sel_arg)
+ {
+ tuple_arg= tuple_arg->next_key_part;
+ tuple_arg->store_min(key_part->length, &key_ptr, 0);
+ }
+ }
+ ha_rows records;
+ records= param->table->file->
+ records_in_range(ror_scan->keynr,
+ (byte*)key_val, key_ptr - key_val,
+ HA_READ_KEY_EXACT,
+ (byte*)key_val, key_ptr - key_val,
+ HA_READ_AFTER_KEY);
+ if (cur_covered)
+ {
+ /* uncovered -> covered */
+ double tmp= rows2double(records)/rows2double(prev_records);
+ printf(" Selectivity multiplier: %g\n", tmp);
+ DBUG_PRINT("info", ("Selectivity multiplier: %g", tmp));
+ selectivity_mult *= tmp;
+ prev_records= HA_POS_ERROR;
+ }
+ else
+ {
+ /* covered -> uncovered */
+ prev_records= records;
+ }
}
+ else printf("no change, skipping\n");
+ prev_covered= cur_covered;
+ }
+ if (!prev_covered)
+ {
+ double tmp= rows2double(param->table->quick_rows[ror_scan->keynr]) /
+ rows2double(prev_records);
+ DBUG_PRINT("info", ("Selectivity multiplier: %g", tmp));
+ selectivity_mult *= tmp;
}
+
if (selectivity_mult == 1.0)
{
/* Don't add this scan if it doesn't improve selectivity. */
- DBUG_PRINT("info", ("This scan doesn't improve selectivity."));
+ DBUG_PRINT("info", ("The scan doesn't improve selectivity."));
DBUG_RETURN(false);
}
- info->records_fract *= selectivity_mult;
-
- bitmap_union(&info->covered_fields, &ror_scan->covered_fields);
-
- ha_rows scan_records= info->param->table->quick_rows[ror_scan->keynr];
- info->index_scan_costs += ror_scan->index_read_cost;
+ info->records_fract *= selectivity_mult;
+ ha_rows cur_scan_records= info->param->table->quick_rows[ror_scan->keynr];
+ if (is_cpk_scan)
+ {
+ info->index_scan_costs += rows2double(cur_scan_records)*
+ TIME_FOR_COMPARE_ROWID;
+ }
+ else
+ {
+ info->index_records += cur_scan_records;
+ info->index_scan_costs += ror_scan->index_read_cost;
+ bitmap_union(&info->covered_fields, &ror_scan->covered_fields);
+ }
+
if (!info->is_covering && bitmap_is_subset(&info->param->needed_fields,
&info->covered_fields))
{
DBUG_PRINT("info", ("ROR-intersect is covering now"));
- /* ROR-intersect became covering */
info->is_covering= true;
}
- info->index_records += scan_records;
+ printf(" records: %g\n", rows2double(param->table->file->records) *
+ info->records_fract);
info->total_cost= info->index_scan_costs;
if (!info->is_covering)
{
ha_rows table_recs= info->param->table->file->records;
- double sweep_cost;
- get_sweep_read_cost(info->param,
- (ha_rows)(table_recs*info->records_fract),
- &sweep_cost, info->index_scan_costs, DBL_MAX);
-
- info->total_cost += sweep_cost;
+ info->total_cost +=
+ get_sweep_read_cost(info->param,
+ (ha_rows)(info->records_fract*table_recs));
}
DBUG_PRINT("info", ("New selectivity= %g", info->records_fract));
DBUG_PRINT("info", ("New cost= %g, %scovering", info->total_cost,
@@ -2326,24 +2537,23 @@ bool ror_intersect_add(ROR_INTERSECT_INFO *info, ROR_SCAN_INFO* ror_scan)
DBUG_RETURN(true);
}
+
/*
Get best ROR-intersection plan using non-covering ROR-intersection search
algorithm. The returned plan may be covering.
SYNOPSIS
get_best_ror_intersect()
- param
- tree
+ param Parameter from test_quick_select function.
+ tree Transformed restriction condition to be used to look
+ for ROR scans.
force_index_only If true, don't calculate costs of full rows retrieval.
read_time Do not return read plans with cost > read_time.
are_all_covering [out] set to true if union of all scans covers all
fields needed by the query (and it is possible to build
a covering ROR-intersection)
- RETURN
- ROR-intersection table read plan
- NULL if OOM or no plan found.
- NOTE
+ NOTES
get_key_scans_params must be called before for the same SEL_TREE before
this function can be called.
@@ -2383,6 +2593,10 @@ bool ror_intersect_add(ROR_INTERSECT_INFO *info, ROR_SCAN_INFO* ror_scan)
Clustered PK scan has special handling in ROR-intersection: it is not used
to retrieve rows, instead its condition is used to filter row references
we get from scans on other keys.
+
+ RETURN
+ ROR-intersection table read plan
+ NULL if out of memory or no suitable plan found.
*/
static
@@ -2398,20 +2612,34 @@ TRP_ROR_INTERSECT *get_best_ror_intersect(const PARAM *param, SEL_TREE *tree,
if (tree->n_ror_scans < 2)
DBUG_RETURN(NULL);
- /* Collect ROR-able SEL_ARGs and create ROR_SCAN_INFO for each of them */
+ /*
+ Collect ROR-able SEL_ARGs and create ROR_SCAN_INFO for each of them.
+ Also find and save clustered PK scan if there is one.
+ */
ROR_SCAN_INFO **cur_ror_scan;
+ ROR_SCAN_INFO *cpk_scan= NULL;
+ bool cpk_scan_used= false;
if (!(tree->ror_scans= (ROR_SCAN_INFO**)alloc_root(param->mem_root,
sizeof(ROR_SCAN_INFO*)*
param->keys)))
return NULL;
-
+ uint cpk_no= (param->table->file->primary_key_is_clustered())?
+ param->table->primary_key : MAX_KEY;
+
for (idx= 0, cur_ror_scan= tree->ror_scans; idx < param->keys; idx++)
{
+ ROR_SCAN_INFO *scan;
if (!tree->ror_scans_map.is_set(idx))
continue;
- if (!(*cur_ror_scan= make_ror_scan(param, idx, tree->keys[idx])))
+ if (!(scan= make_ror_scan(param, idx, tree->keys[idx])))
return NULL;
- cur_ror_scan++;
+ if (param->real_keynr[idx] == cpk_no)
+ {
+ cpk_scan= scan;
+ tree->n_ror_scans--;
+ }
+ else
+ *(cur_ror_scan++)= scan;
}
tree->ror_scans_end= cur_ror_scan;
@@ -2421,7 +2649,7 @@ TRP_ROR_INTERSECT *get_best_ror_intersect(const PARAM *param, SEL_TREE *tree,
/*
Ok, [ror_scans, ror_scans_end) is array of ptrs to initialized
ROR_SCAN_INFOs.
- Get a minimal key scan using an approximate algorithm.
+ Now, get a minimal key scan using an approximate algorithm.
*/
qsort(tree->ror_scans, tree->n_ror_scans, sizeof(ROR_SCAN_INFO*),
(qsort_cmp)cmp_ror_scan_info);
@@ -2452,12 +2680,14 @@ TRP_ROR_INTERSECT *get_best_ror_intersect(const PARAM *param, SEL_TREE *tree,
LINT_INIT(best_index_scan_costs);
cur_ror_scan= tree->ror_scans;
+ /* Start with one scan */
+ intersect_scans_best= intersect_scans;
while (cur_ror_scan != tree->ror_scans_end && !intersect->is_covering)
{
/* S= S + first(R); */
- if (ror_intersect_add(intersect, *cur_ror_scan))
+ if (ror_intersect_add(param, intersect, *cur_ror_scan))
*(intersect_scans_end++)= *cur_ror_scan;
- /* R= R-first(R); */
+ /* R= R - first(R); */
cur_ror_scan++;
if (intersect->total_cost < min_cost)
@@ -2471,17 +2701,47 @@ TRP_ROR_INTERSECT *get_best_ror_intersect(const PARAM *param, SEL_TREE *tree,
best_index_scan_costs= intersect->index_scan_costs;
}
}
-
- /* Ok, return ROR-intersect plan if we have found one */
+
+ DBUG_EXECUTE("info",print_ror_scans_arr(param->table,
+ "best ROR-intersection",
+ intersect_scans,
+ intersect_scans_best););
+
*are_all_covering= intersect->is_covering;
uint best_num= intersect_scans_best - intersect_scans;
+ /*
+ Ok, found the best ROR-intersection of non-CPK key scans.
+ Check if we should add a CPK scan.
+
+ If the obtained ROR-intersection is covering, it doesn't make sense
+ to add CPK scan - Clustered PK contains all fields and if we're doing
+ CPK scan doing other CPK scans will only add more overhead.
+ */
+ if (cpk_scan && !intersect->is_covering)
+ {
+ /*
+ Handle the special case: ROR-intersect(PRIMARY, key1) is
+ the best, but cost(range(key1)) > cost(best_non_ror_range_scan)
+ */
+ if (best_num == 0)
+ {
+ ror_intersect_reinit(intersect);
+ if (!ror_intersect_add(param, intersect, cpk_scan))
+ DBUG_RETURN(NULL); /* shouldn't happen actually actually */
+ *(intersect_scans_end++)= *cur_ror_scan;
+ best_num++;
+ }
+
+ if (ror_intersect_add(param, intersect, cpk_scan))
+ cpk_scan_used= true;
+ best_rows= (ha_rows)(intersect->records_fract*
+ rows2double(param->table->file->records));
+ }
+
+ /* Ok, return ROR-intersect plan if we have found one */
TRP_ROR_INTERSECT *trp= NULL;
- if (intersect_scans_best && best_num > 1)
+ if (best_num > 1 || cpk_scan_used)
{
- DBUG_EXECUTE("info",print_ror_scans_arr(param->table,
- "used for ROR-intersect",
- intersect_scans,
- intersect_scans_best););
if (!(trp= new (param->mem_root) TRP_ROR_INTERSECT))
DBUG_RETURN(trp);
if (!(trp->first_scan=
@@ -2494,7 +2754,8 @@ TRP_ROR_INTERSECT *get_best_ror_intersect(const PARAM *param, SEL_TREE *tree,
trp->read_cost= min_cost;
trp->records= best_rows? best_rows : 1;
trp->index_scan_costs= best_index_scan_costs;
- }
+ trp->cpk_scan= cpk_scan;
+ }
DBUG_RETURN(trp);
}
@@ -2503,15 +2764,15 @@ TRP_ROR_INTERSECT *get_best_ror_intersect(const PARAM *param, SEL_TREE *tree,
Get best covering ROR-intersection.
SYNOPSIS
get_best_covering_ror_intersect()
- param
- tree SEL_TREE
- read_time Dont return table read plans with cost > read_time.
+ param Parameter from test_quick_select function.
+ tree SEL_TREE with sets of intervals for different keys.
+ read_time Don't return table read plans with cost > read_time.
RETURN
Best covering ROR-intersection plan
NULL if no plan found.
- NOTE
+ NOTES
get_best_ror_intersect must be called for a tree before calling this
function for it.
This function invalidates tree->ror_scans member values.
@@ -2563,7 +2824,6 @@ TRP_ROR_INTERSECT *get_best_covering_ror_intersect(PARAM *param,
ha_rows records=0;
bool all_covered;
- /* Start will all scans and remove one by one until */
DBUG_PRINT("info", ("Building covering ROR-intersection"));
DBUG_EXECUTE("info", print_ror_scans_arr(param->table,
"building covering ROR-I",
@@ -2805,12 +3065,23 @@ QUICK_SELECT_I *TRP_ROR_INTERSECT::make_quick(PARAM *param,
DBUG_RETURN(NULL);
}
}
+ if (cpk_scan)
+ {
+ if (!(quick= get_quick_select(param, cpk_scan->idx,
+ cpk_scan->sel_arg, alloc)))
+ {
+ delete quick_intrsect;
+ DBUG_RETURN(NULL);
+ }
+ quick_intrsect->cpk_quick= quick;
+ }
quick_intrsect->records= records;
quick_intrsect->read_time= read_cost;
}
DBUG_RETURN(quick_intrsect);
}
+
QUICK_SELECT_I *TRP_ROR_UNION::make_quick(PARAM *param,
bool retrieve_full_rows,
MEM_ROOT *parent_alloc)
@@ -2820,8 +3091,8 @@ QUICK_SELECT_I *TRP_ROR_UNION::make_quick(PARAM *param,
QUICK_SELECT_I *quick;
DBUG_ENTER("TRP_ROR_UNION::make_quick");
/*
- It is currently impossible to construct a ROR-union that will
- not retrieve full rows, ingore retrieve_full_rows.
+ It is impossible to construct a ROR-union that will not retrieve full
+ rows, ignore retrieve_full_rows parameter.
*/
if ((quick_roru= new QUICK_ROR_UNION_SELECT(param->thd, param->table)))
{
@@ -4248,7 +4519,7 @@ SEL_ARG *rb_delete_fixup(SEL_ARG *root,SEL_ARG *key,SEL_ARG *par)
}
- /* Test that the proporties for a red-black tree holds */
+ /* Test that the properties for a red-black tree hold */
#ifdef EXTRA_DEBUG
int test_rb_tree(SEL_ARG *element,SEL_ARG *parent)
@@ -4336,13 +4607,26 @@ void SEL_ARG::test_use_count(SEL_ARG *root)
#endif
+/*
+ Calculate estimate of number records that will be retrieved by a range
+ scan on given index using given SEL_ARG intervals tree.
+ SYNOPSIS
+ check_quick_select
+ param Parameter from test_quick_select
+ idx Number of index to use in PARAM::key SEL_TREE::key
+ tree Transformed selection condition, tree->key[idx] holds intervals
+ tree to be used for scanning.
+ NOTES
+ param->is_ror_scan is set to reflect if the key scan is a ROR (see
+ is_key_scan_ror function for more info)
+ param->table->quick_*, param->range_count (and maybe others) are
+ updated with data of given key scan, see check_quick_keys for details.
+
+ RETURN
+ Estimate # of records to be retrieved.
+ HA_POS_ERROR if estimate calculation failed due to table handler problems.
-/*****************************************************************************
-** Check how many records we will find by using the found tree
-** NOTE
-** param->table->quick_* and param->range_count (and maybe others) are
-** updated with data of given key scan.
-*****************************************************************************/
+*/
static ha_rows
check_quick_select(PARAM *param,uint idx,SEL_ARG *tree)
@@ -4396,30 +4680,40 @@ check_quick_select(PARAM *param,uint idx,SEL_ARG *tree)
}
-/*
+/*
+ Recursively calculate estimate of # rows that will be retrieved by
+ key scan on key idx.
SYNOPSIS
check_quick_keys()
- param
- idx key to use, its number in list of used keys (that is,
- param->real_keynr[idx] holds the key number in table)
-
- key_tree SEL_ARG tree which cost is calculated.
- min_key buffer with min key value tuple
- min_key_flag
- max_key buffer with max key value tuple
+ param Parameter from test_quick select function.
+ idx Number of key to use in PARAM::keys in list of used keys
+ (param->real_keynr[idx] holds the key number in table)
+ key_tree SEL_ARG tree being examined.
+ min_key Buffer with partial min key value tuple
+ min_key_flag
+ max_key Buffer with partial max key value tuple
max_key_flag
- NOTE
- The function does the recursive descent on the tree via left, right, and
- next_key_part edges. The #rows estimates are calculated at the leaf nodes.
+ NOTES
+ The function does the recursive descent on the tree via SEL_ARG::left,
+ SEL_ARG::right, and SEL_ARG::next_key_part edges. The #rows estimates
+ are calculated using records_in_range calls at the leaf nodes and then
+ summed.
- param->min_key and param->max_key are used to hold key segment values.
+ param->min_key and param->max_key are used to hold prefixes of key value
+ tuples.
The side effects are:
+
param->max_key_part is updated to hold the maximum number of key parts used
in scan minus 1.
- param->range_count is updated.
- param->is_ror_scan is updated.
+
+ param->range_count is incremented if the function finds a range that
+ wasn't counted by the caller.
+
+ param->is_ror_scan is cleared if the function detects that the key scan is
+ not a Rowid-Ordered Retrieval scan ( see comments for is_key_scan_ror
+ function for description of which key scans are ROR scans)
*/
static ha_rows
@@ -4432,6 +4726,12 @@ check_quick_keys(PARAM *param,uint idx,SEL_ARG *key_tree,
param->max_key_part=max(param->max_key_part,key_tree->part);
if (key_tree->left != &null_element)
{
+ /*
+ There are at least two intervals for current key part, i.e. condition
+ was converted to something like
+ (keyXpartY less/equals c1) OR (keyXpartY more/equals c2).
+ This is not a ROR scan if the key is not Clustered Primary Key.
+ */
param->is_ror_scan= false;
records=check_quick_keys(param,idx,key_tree->left,min_key,min_key_flag,
max_key,max_key_flag);
@@ -4441,12 +4741,25 @@ check_quick_keys(PARAM *param,uint idx,SEL_ARG *key_tree,
uint tmp_min_flag,tmp_max_flag,keynr;
char *tmp_min_key=min_key,*tmp_max_key=max_key;
-
+
key_tree->store(param->key[idx][key_tree->part].part_length,
&tmp_min_key,min_key_flag,&tmp_max_key,max_key_flag);
uint min_key_length= (uint) (tmp_min_key- param->min_key);
uint max_key_length= (uint) (tmp_max_key- param->max_key);
+ if (param->is_ror_scan)
+ {
+ /*
+ If the index doesn't cover entire key, mark the scan as non-ROR scan.
+ (TODO sergeyp: investigate if we could do better here)
+ */
+ uint16 fieldnr= param->table->key_info[param->real_keynr[idx]].
+ key_part[key_tree->part].fieldnr - 1;
+ if (param->table->field[fieldnr]->key_length() !=
+ param->key[idx][key_tree->part].part_length)
+ param->is_ror_scan= false;
+ }
+
if (key_tree->next_key_part &&
key_tree->next_key_part->part == key_tree->part+1 &&
key_tree->next_key_part->type == SEL_ARG::KEY_RANGE)
@@ -4461,7 +4774,10 @@ check_quick_keys(PARAM *param,uint idx,SEL_ARG *key_tree,
goto end; // Ugly, but efficient
}
else
+ {
+ /* The interval for current key part is not c1 <= keyXpartY <= c1 */
param->is_ror_scan= false;
+ }
tmp_min_flag=key_tree->min_flag;
tmp_max_flag=key_tree->max_flag;
@@ -4493,6 +4809,15 @@ check_quick_keys(PARAM *param,uint idx,SEL_ARG *key_tree,
{
if (param->is_ror_scan)
{
+ /*
+ If we get here, the condition on the key was converted to form
+ "(keyXpart1 = c1) AND ... AND (keyXpart{key_tree->part - 1} = cN) AND
+ somecond(keyXpart{key_tree->part})"
+ Check if
+ somecond is "keyXpart{key_tree->part} = const" and
+ uncovered "tail" of KeyX parts is either empty or is identical to
+ first members of clustered primary key.
+ */
if (!(min_key_length == max_key_length &&
!memcmp(min_key,max_key, (uint) (tmp_max_key - max_key)) &&
!key_tree->min_flag && !key_tree->max_flag &&
@@ -4530,6 +4855,12 @@ check_quick_keys(PARAM *param,uint idx,SEL_ARG *key_tree,
records+=tmp;
if (key_tree->right != &null_element)
{
+ /*
+ There are at least two intervals for current key part, i.e. condition
+ was converted to something like
+ (keyXpartY less/equals c1) OR (keyXpartY more/equals c2).
+ This is not a ROR scan if the key is not Clustered Primary Key.
+ */
param->is_ror_scan= false;
tmp=check_quick_keys(param,idx,key_tree->right,min_key,min_key_flag,
max_key,max_key_flag);
@@ -4540,11 +4871,46 @@ check_quick_keys(PARAM *param,uint idx,SEL_ARG *key_tree,
return records;
}
+
/*
- Check if key scan on key keynr with first nparts key parts fixed is a
- ROR scan. This function doesn't handle clustered PK scans or HASH index
- scans.
+ Check if key scan on given index with equality conditions on first n key
+ parts is a ROR scan.
+
+ SYNOPSIS
+ is_key_scan_ror()
+ param Parameter from test_quick_select
+ keynr Number of key in the table. The key must not be a clustered
+ primary key.
+ nparts Number of first key parts for which equality conditions
+ are present.
+
+ NOTES
+ ROR (Rowid Ordered Retrieval) key scan is a key scan that produces
+ ordered sequence of rowids (ha_xxx::cmp_ref is the comparison function)
+
+ An index scan is a ROR scan if it is done using a condition in form
+
+ "key1_1=c_1 AND ... AND key1_n=c_n" (1)
+
+ where the index is defined on (key1_1, ..., key1_N [,a_1, ..., a_n])
+
+ and the table has a clustered Primary Key
+
+ PRIMARY KEY(a_1, ..., a_n, b1, ..., b_k) with first key parts being
+ identical to uncovered parts ot the key being scanned (2)
+
+ Scans on HASH indexes are not ROR scans,
+ any range scan on clustered primary key is ROR scan (3)
+
+ Check (1) is made in check_quick_keys()
+ Check (3) is made check_quick_select()
+ Check (2) is made by this function.
+
+ RETURN
+ true If the scan is ROR-scan
+ false otherwise
*/
+
static bool is_key_scan_ror(PARAM *param, uint keynr, uint8 nparts)
{
KEY *table_key= param->table->key_info + keynr;
@@ -4564,7 +4930,8 @@ static bool is_key_scan_ror(PARAM *param, uint keynr, uint8 nparts)
for(;(key_part!=key_part_end) && (pk_part != pk_part_end);
++key_part, ++pk_part)
{
- if (key_part->field != pk_part->field)
+ if ((key_part->field != pk_part->field) ||
+ (key_part->length != pk_part->length))
return false;
}
return (key_part == key_part_end);
@@ -4573,17 +4940,26 @@ static bool is_key_scan_ror(PARAM *param, uint keynr, uint8 nparts)
/*
Create a QUICK_RANGE_SELECT from given key and SEL_ARG tree for that key.
- This uses it's own malloc tree.
+
SYNOPSIS
get_quick_select()
param
- idx index of used key in param->key.
+ idx Index of used key in param->key.
key_tree SEL_ARG tree for the used key
- parent_alloc if not NULL, use it to allocate memory for
+ parent_alloc If not NULL, use it to allocate memory for
quick select data. Otherwise use quick->alloc.
+
+ RETURN
+ NULL on error
+ otherwise created quick select
+
+ NOTES
+ The caller must call QUICK_SELCT::init for returned quick select
- The caller should call QUICK_SELCT::init for returned quick select
+ CAUTION! This function may change THR_MALLOC to a MEM_ROOT which will be
+ deallocated when the returned quick select is deleted.
*/
+
QUICK_RANGE_SELECT *
get_quick_select(PARAM *param,uint idx,SEL_ARG *key_tree,
MEM_ROOT *parent_alloc)
@@ -5008,15 +5384,23 @@ int QUICK_INDEX_MERGE_SELECT::get_next()
/*
+ Retrieve next record.
+ SYNOPSIS
+ QUICK_ROR_INTERSECT_SELECT::get_next()
+
NOTES
- Invariant on enter/exit: all intersected selects have retrieved index
- records with rowid <= some_rowid_val and no intersected select has
+ Invariant on enter/exit: all intersected selects have retrieved all index
+ records with rowid <= some_rowid_val and no intersected select has
retrieved any index records with rowid > some_rowid_val.
We start fresh and loop until we have retrieved the same rowid in each of
the key scans or we got an error.
If a Clustered PK scan is present, it is used only to check if row
- satisfies its conditions (and never used for row retrieval).
+ satisfies its condition (and never used for row retrieval).
+
+ RETURN
+ 0 - Ok
+ other - Error code if any error occurred.
*/
int QUICK_ROR_INTERSECT_SELECT::get_next()
@@ -5090,10 +5474,18 @@ int QUICK_ROR_INTERSECT_SELECT::get_next()
/*
+ Retrieve next record.
+ SYNOPSIS
+ QUICK_ROR_UNION_SELECT::get_next()
+
NOTES
Enter/exit invariant:
For each quick select in the queue a {key,rowid} tuple has been
retrieved but the corresponding row hasn't been passed to output.
+
+ RETURN
+ 0 - Ok
+ other - Error code if any error occurred.
*/
int QUICK_ROR_UNION_SELECT::get_next()
@@ -5107,7 +5499,7 @@ int QUICK_ROR_UNION_SELECT::get_next()
{
if (!queue.elements)
DBUG_RETURN(HA_ERR_END_OF_FILE);
- /* Ok, we have a queue with > 1 scans */
+ /* Ok, we have a queue with >= 1 scans */
quick= (QUICK_SELECT_I*)queue_top(&queue);
memcpy(cur_rowid, quick->last_rowid, rowid_length);
@@ -5548,8 +5940,78 @@ bool QUICK_SELECT_DESC::test_if_null_range(QUICK_RANGE *range_arg,
#endif
-void QUICK_RANGE_SELECT::fill_keys_and_lengths(String *key_names,
- String *used_lengths)
+void QUICK_RANGE_SELECT::add_info_string(String *str)
+{
+ KEY *key_info= head->key_info + index;
+ str->append(key_info->name);
+}
+
+void QUICK_INDEX_MERGE_SELECT::add_info_string(String *str)
+{
+ QUICK_RANGE_SELECT *quick;
+ bool first= true;
+ List_iterator_fast<QUICK_RANGE_SELECT> it(quick_selects);
+ str->append("sort_union(");
+ while ((quick= it++))
+ {
+ if (!first)
+ str->append(',');
+ else
+ first= false;
+ quick->add_info_string(str);
+ }
+ if (pk_quick_select)
+ {
+ str->append(',');
+ pk_quick_select->add_info_string(str);
+ }
+ str->append(')');
+}
+
+void QUICK_ROR_INTERSECT_SELECT::add_info_string(String *str)
+{
+ bool first= true;
+ QUICK_RANGE_SELECT *quick;
+ List_iterator_fast<QUICK_RANGE_SELECT> it(quick_selects);
+ str->append("intersect(");
+ while ((quick= it++))
+ {
+ KEY *key_info= head->key_info + quick->index;
+ if (!first)
+ str->append(',');
+ else
+ first= false;
+ str->append(key_info->name);
+ }
+ if (cpk_quick)
+ {
+ KEY *key_info= head->key_info + cpk_quick->index;
+ str->append(',');
+ str->append(key_info->name);
+ }
+ str->append(')');
+}
+
+void QUICK_ROR_UNION_SELECT::add_info_string(String *str)
+{
+ bool first= true;
+ QUICK_SELECT_I *quick;
+ List_iterator_fast<QUICK_SELECT_I> it(quick_selects);
+ str->append("union(");
+ while ((quick= it++))
+ {
+ if (!first)
+ str->append(',');
+ else
+ first= false;
+ quick->add_info_string(str);
+ }
+ str->append(')');
+}
+
+
+void QUICK_RANGE_SELECT::add_keys_and_lengths(String *key_names,
+ String *used_lengths)
{
char buf[64];
uint length;
@@ -5559,22 +6021,27 @@ void QUICK_RANGE_SELECT::fill_keys_and_lengths(String *key_names,
used_lengths->append(buf, length);
}
-void QUICK_INDEX_MERGE_SELECT::fill_keys_and_lengths(String *key_names,
- String *used_lengths)
+void QUICK_INDEX_MERGE_SELECT::add_keys_and_lengths(String *key_names,
+ String *used_lengths)
{
char buf[64];
uint length;
+ bool first= true;
QUICK_RANGE_SELECT *quick;
+
List_iterator_fast<QUICK_RANGE_SELECT> it(quick_selects);
while ((quick= it++))
{
- KEY *key_info= head->key_info + quick->index;
- if (key_names->length())
+ if (first)
+ first= false;
+ else
+ {
key_names->append(',');
- key_names->append(key_info->name);
-
- if (used_lengths->length())
used_lengths->append(',');
+ }
+
+ KEY *key_info= head->key_info + quick->index;
+ key_names->append(key_info->name);
length= longlong2str(quick->max_used_key_length, buf, 10) - buf;
used_lengths->append(buf, length);
}
@@ -5589,8 +6056,8 @@ void QUICK_INDEX_MERGE_SELECT::fill_keys_and_lengths(String *key_names,
}
}
-void QUICK_ROR_INTERSECT_SELECT::fill_keys_and_lengths(String *key_names,
- String *used_lengths)
+void QUICK_ROR_INTERSECT_SELECT::add_keys_and_lengths(String *key_names,
+ String *used_lengths)
{
char buf[64];
uint length;
@@ -5600,17 +6067,18 @@ void QUICK_ROR_INTERSECT_SELECT::fill_keys_and_lengths(String *key_names,
while ((quick= it++))
{
KEY *key_info= head->key_info + quick->index;
- if (!first)
- key_names->append(',');
- key_names->append(key_info->name);
-
if (first)
first= false;
else
+ {
+ key_names->append(',');
used_lengths->append(',');
+ }
+ key_names->append(key_info->name);
length= longlong2str(quick->max_used_key_length, buf, 10) - buf;
used_lengths->append(buf, length);
}
+
if (cpk_quick)
{
KEY *key_info= head->key_info + cpk_quick->index;
@@ -5622,8 +6090,8 @@ void QUICK_ROR_INTERSECT_SELECT::fill_keys_and_lengths(String *key_names,
}
}
-void QUICK_ROR_UNION_SELECT::fill_keys_and_lengths(String *key_names,
- String *used_lengths)
+void QUICK_ROR_UNION_SELECT::add_keys_and_lengths(String *key_names,
+ String *used_lengths)
{
bool first= true;
QUICK_SELECT_I *quick;
@@ -5637,7 +6105,7 @@ void QUICK_ROR_UNION_SELECT::fill_keys_and_lengths(String *key_names,
used_lengths->append(',');
key_names->append(',');
}
- quick->fill_keys_and_lengths(key_names, used_lengths);
+ quick->add_keys_and_lengths(key_names, used_lengths);
}
}
@@ -5850,7 +6318,7 @@ void QUICK_ROR_UNION_SELECT::dbug_dump(int indent, bool verbose)
#endif
/*****************************************************************************
-** Instansiate templates
+** Instantiate templates
*****************************************************************************/
#ifdef __GNUC__
diff --git a/sql/opt_range.h b/sql/opt_range.h
index 1b522421a01..d992bbb2456 100644
--- a/sql/opt_range.h
+++ b/sql/opt_range.h
@@ -77,27 +77,59 @@ public:
ha_rows records; /* estimate of # of records to be retrieved */
double read_time; /* time to perform this retrieval */
TABLE *head;
-
/*
- index this quick select uses, or MAX_KEY for quick selects
+ Index this quick select uses, or MAX_KEY for quick selects
that use several indexes
*/
uint index;
- /* applicable iff index!= MAX_KEY */
- uint max_used_key_length, used_key_parts;
+ /*
+ Total length of first used_key_parts parts of the key.
+ Applicable if index!= MAX_KEY.
+ */
+ uint max_used_key_length;
+
+ /*
+ Max. number of (first) key parts this quick select uses for retrieval.
+ eg. for "(key1p1=c1 AND key1p2=c2) OR key1p1=c2" used_key_parts == 2.
+ Applicable if index!= MAX_KEY.
+ */
+ uint used_key_parts;
QUICK_SELECT_I();
virtual ~QUICK_SELECT_I(){};
- /*
- Call init() immediately after creation of quick select. if init() call
- fails, reset() or get_next() must not be called.
+
+ /*
+ Do post-constructor initialization.
+ SYNOPSIS
+ init()
+
+ init() performs initializations that should have been in constructor if
+ it was possible to return errors from constructors. The join optimizer may
+ create and then delete quick selects without retrieving any rows so init()
+ must not contain any IO or CPU intensive code.
+
+ If init() call fails the only valid action is to delete this quick select,
+ reset() and get_next() must not be called.
+
+ RETURN
+ 0 OK
+ other Error code
*/
virtual int init() = 0;
/*
- Call reset() before first get_next call. get_next must not be called if
- reset() call fails.
+ Initialize quick select for row retrieval.
+ SYNOPSIS
+ reset()
+
+ reset() should be called when it is certain that row retrieval will be
+ necessary. This call may do heavyweight initialization like buffering first
+ N records etc. If reset() call fails get_next() must not be called.
+
+ RETURN
+ 0 OK
+ other Error code
*/
virtual int reset(void) = 0;
virtual int get_next() = 0; /* get next record to retrieve */
@@ -117,30 +149,54 @@ public:
virtual int get_type() = 0;
/*
- Initialize this quick select as a child of a index union or intersection
- scan. This call replaces init() call.
+ Initialize this quick select as a merged scan inside a ROR-union or a ROR-
+ intersection scan. The caller must not additionally call init() if this
+ function is called.
+ SYNOPSIS
+ init_ror_merged_scan()
+ reuse_handler quick select may use (q: psergey??)
+ (q: is this natural that we do it this way)
+ NOTES
+ psergey?
*/
- virtual int init_ror_child_scan(bool reuse_handler)
+ virtual int init_ror_merged_scan(bool reuse_handler)
{ DBUG_ASSERT(0); return 1; }
- virtual void cleanup_ror_child_scan() { DBUG_ASSERT(0); }
+ /* Save ROWID of last retrieved row in file->ref. (psergey: or table->ref?) */
virtual void save_last_pos(){};
/*
- Fill key_names with list of keys this quick select used;
- fill used_lenghth with correponding used lengths.
+ Append comma-separated list of keys this quick select uses to key_names;
+ append comma-separated list of corresponding used lengths to used_lengths.
This is used by select_describe.
*/
- virtual void fill_keys_and_lengths(String *key_names,
- String *used_lengths)=0;
-
+ virtual void add_keys_and_lengths(String *key_names,
+ String *used_lengths)=0;
+
+ /*
+ Append text representation of quick select structure (what and how is
+ merged) to str. The result is added to "Extra" field in EXPLAIN output.
+ This function is implemented only by quick selects that merge other quick
+ selects output and/or can produce output suitable for merging.
+ */
+ virtual void add_info_string(String *str) {};
+ /*
+ Return 1 if any index used by this quick select
+ a) uses field that is listed in passed field list or
+ b) is automatically updated (like a timestamp)
+ */
virtual bool check_if_keys_used(List<Item> *fields);
/*
- rowid of last row retrieved by this quick select. This is used only
- when doing ROR-index_merge selects
+ rowid of last row retrieved by this quick select. This is used only when
+ doing ROR-index_merge selects
*/
byte *last_rowid;
+
+ /*
+ Table record buffer used by this quick select.
+ Currently this is always the same as head->record[0]. psergey: check that!
+ */
byte *record;
#ifndef DBUG_OFF
/*
@@ -155,6 +211,10 @@ public:
struct st_qsel_param;
class SEL_ARG;
+/*
+ Quick select that does a range scan on a single key. The records are
+ returned in key order.
+*/
class QUICK_RANGE_SELECT : public QUICK_SELECT_I
{
protected:
@@ -163,7 +223,11 @@ public:
int error;
protected:
handler *file;
- bool free_file; /* if true, this quick select "owns" file and will free it */
+ /*
+ If true, this quick select has its "own" handler object which should be
+ closed no later then this quick select is deleted.
+ */
+ bool free_file;
protected:
friend
@@ -207,15 +271,16 @@ public:
int get_next();
bool reverse_sorted() { return 0; }
bool unique_key_range();
- int init_ror_child_scan(bool reuse_handler);
+ int init_ror_merged_scan(bool reuse_handler);
void save_last_pos()
{
file->position(record);
};
int get_type() { return QS_TYPE_RANGE; }
- void fill_keys_and_lengths(String *key_names, String *used_lengths);
+ void add_keys_and_lengths(String *key_names, String *used_lengths);
+ void add_info_string(String *str);
#ifndef DBUG_OFF
- virtual void dbug_dump(int indent, bool verbose);
+ void dbug_dump(int indent, bool verbose);
#endif
};
@@ -291,10 +356,11 @@ public:
bool reverse_sorted() { return false; }
bool unique_key_range() { return false; }
int get_type() { return QS_TYPE_INDEX_MERGE; }
- void fill_keys_and_lengths(String *key_names, String *used_lengths);
+ void add_keys_and_lengths(String *key_names, String *used_lengths);
+ void add_info_string(String *str);
bool check_if_keys_used(List<Item> *fields);
#ifndef DBUG_OFF
- virtual void dbug_dump(int indent, bool verbose);
+ void dbug_dump(int indent, bool verbose);
#endif
bool push_quick_back(QUICK_RANGE_SELECT *quick_sel_range);
@@ -317,7 +383,6 @@ public:
THD *thd;
int prepare_unique();
- bool reset_called;
/* used to get rows collected in Unique */
READ_RECORD read_record;
@@ -326,9 +391,22 @@ public:
/*
Rowid-Ordered Retrieval (ROR) index intersection quick select.
- This quick select produces an intersection of records returned by several
- QUICK_RANGE_SELECTs that return data ordered by rowid.
+ This quick select produces intersection of row sequences returned
+ by several QUICK_RANGE_SELECTs it "merges".
+
+ All merged QUICK_RANGE_SELECTs must return rowids in rowid order.
+ QUICK_ROR_INTERSECT_SELECT will return rows in rowid order, too.
+
+ All merged quick selects retrieve {rowid, covered_fields} tuples (not full
+ table records).
+ QUICK_ROR_INTERSECT_SELECT retrieves full records if it is not being used
+ by QUICK_ROR_INTERSECT_SELECT and all merged quick selects together don't
+ cover needed all fields.
+
+ If one of the merged quick selects is a Clustered PK range scan, it is
+ used only to filter rowid sequence produced by other merged quick selects.
*/
+
class QUICK_ROR_INTERSECT_SELECT : public QUICK_SELECT_I
{
public:
@@ -343,28 +421,46 @@ public:
bool reverse_sorted() { return false; }
bool unique_key_range() { return false; }
int get_type() { return QS_TYPE_ROR_INTERSECT; }
- void fill_keys_and_lengths(String *key_names, String *used_lengths);
+ void add_keys_and_lengths(String *key_names, String *used_lengths);
+ void add_info_string(String *str);
bool check_if_keys_used(List<Item> *fields);
#ifndef DBUG_OFF
- virtual void dbug_dump(int indent, bool verbose);
+ void dbug_dump(int indent, bool verbose);
#endif
- int init_ror_child_scan(bool reuse_handler);
+ int init_ror_merged_scan(bool reuse_handler);
bool push_quick_back(QUICK_RANGE_SELECT *quick_sel_range);
- /* range quick selects this intersection consists of */
+ /*
+ Range quick selects this intersection consists of, not including
+ cpk_quick.
+ */
List<QUICK_RANGE_SELECT> quick_selects;
+ /*
+ Merged quick select that uses Clustered PK, if there is one. This quick
+ select is not used for row retrieval, it is used for row retrieval.
+ */
QUICK_RANGE_SELECT *cpk_quick;
- MEM_ROOT alloc;
- THD *thd;
- bool reset_called;
- bool need_to_fetch_row;
+
+ MEM_ROOT alloc; /* Memory pool for this and merged quick selects data. */
+ THD *thd; /* current thread */
+ bool need_to_fetch_row; /* if true, do retrieve full table records. */
};
+
/*
Rowid-Ordered Retrieval index union select.
+ This quick select produces union of row sequences returned by several
+ quick select it "merges".
+ All merged quick selects must return rowids in rowid order.
+ QUICK_ROR_UNION_SELECT will return rows in rowid order, too.
+
+ All merged quick selects are set not to retrieve full table records.
+ ROR-union quick select always retrieves full records.
+
*/
+
class QUICK_ROR_UNION_SELECT : public QUICK_SELECT_I
{
public:
@@ -377,33 +473,30 @@ public:
bool reverse_sorted() { return false; }
bool unique_key_range() { return false; }
int get_type() { return QS_TYPE_ROR_UNION; }
- void fill_keys_and_lengths(String *key_names, String *used_lengths);
+ void add_keys_and_lengths(String *key_names, String *used_lengths);
+ void add_info_string(String *str);
bool check_if_keys_used(List<Item> *fields);
#ifndef DBUG_OFF
- virtual void dbug_dump(int indent, bool verbose);
+ void dbug_dump(int indent, bool verbose);
#endif
bool push_quick_back(QUICK_SELECT_I *quick_sel_range);
- /* range quick selects this index_merge read consists of */
- List<QUICK_SELECT_I> quick_selects;
-
- QUEUE queue;
- MEM_ROOT alloc;
-
- THD *thd;
- byte *cur_rowid;
- byte *prev_rowid;
- uint rowid_length;
- bool reset_called;
- bool have_prev_rowid;
+ List<QUICK_SELECT_I> quick_selects; /* Merged quick selects */
+ QUEUE queue; /* Priority queue for merge operation */
+ MEM_ROOT alloc; /* Memory pool for this and merged quick selects data. */
+
+ THD *thd; /* current thread */
+ byte *cur_rowid; /* buffer used in get_next() */
+ byte *prev_rowid; /* rowid of last row returned by get_next() */
+ bool have_prev_rowid; /* true if prev_rowid has valid data */
+ uint rowid_length; /* table rowid length */
private:
static int queue_cmp(void *arg, byte *val1, byte *val2);
};
-
class QUICK_SELECT_DESC: public QUICK_RANGE_SELECT
{
public:
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 037aedd5f61..27015dbd04d 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -10077,7 +10077,7 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
select_result *result=join->result;
Item *item_null= new Item_null();
CHARSET_INFO *cs= &my_charset_latin1;
- int quick_type= -1;
+ int quick_type;
DBUG_ENTER("select_describe");
DBUG_PRINT("info", ("Select 0x%lx, type %s, message %s",
(ulong)join->select_lex, join->select_lex->type,
@@ -10104,17 +10104,20 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
{
JOIN_TAB *tab=join->join_tab+i;
TABLE *table=tab->table;
- char buff[512],*buff_ptr=buff;
+ char buff[512];
char buff1[512], buff2[512], buff3[512];
char keylen_str_buf[64];
char derived_name[64];
+ String extra(buff, sizeof(buff),cs);
String tmp1(buff1,sizeof(buff1),cs);
String tmp2(buff2,sizeof(buff2),cs);
String tmp3(buff3,sizeof(buff3),cs);
+ extra.length(0);
tmp1.length(0);
tmp2.length(0);
tmp3.length(0);
+ quick_type= -1;
item_list.empty();
item_list.push_back(new Item_int((int32)
join->select_lex->select_number));
@@ -10165,7 +10168,7 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
else
item_list.push_back(item_null);
- /* Build key,key_len, and ref values and add them to item_list */
+ /* Build "key", "key_len", and "ref" values and add them to item_list */
if (tab->ref.key_parts)
{
KEY *key_info=table->key_info+ tab->ref.key;
@@ -10200,7 +10203,7 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
}
else if (tab->select && tab->select->quick)
{
- tab->select->quick->fill_keys_and_lengths(&tmp2, &tmp3);
+ tab->select->quick->add_keys_and_lengths(&tmp2, &tmp3);
item_list.push_back(new Item_string(tmp2.ptr(),tmp2.length(),cs));
item_list.push_back(new Item_string(tmp3.ptr(),tmp3.length(),cs));
item_list.push_back(item_null);
@@ -10211,9 +10214,11 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
item_list.push_back(item_null);
item_list.push_back(item_null);
}
+ /* Add "rows" field to item_list. */
item_list.push_back(new Item_int((longlong) (ulonglong)
join->best_positions[i]. records_read,
21));
+ /* Build "Extra" field and add it to item_list. */
my_bool key_read=table->key_read;
if (tab->type == JT_NEXT && table->used_keys.is_set(tab->index))
key_read=1;
@@ -10225,38 +10230,51 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
item_list.push_back(new Item_string(tab->info,strlen(tab->info),cs));
else
{
+ if (quick_type == QUICK_SELECT_I::QS_TYPE_ROR_UNION ||
+ quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT ||
+ quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_MERGE)
+ {
+ extra.append("; Using ");
+ tab->select->quick->add_info_string(&extra);
+ }
if (tab->select)
{
if (tab->use_quick == 2)
{
char buf[MAX_KEY/8+1];
- sprintf(buff_ptr,"; Range checked for each record (index map: 0x%s)",
- tab->keys.print(buf));
- buff_ptr=strend(buff_ptr);
+ extra.append("; Range checked for each record (index map: 0x");
+ extra.append(tab->keys.print(buf));
+ extra.append(')');
}
else
- buff_ptr=strmov(buff_ptr,"; Using where");
+ extra.append("; Using where");
}
if (key_read)
- buff_ptr= strmov(buff_ptr,"; Using index");
+ extra.append("; Using index");
if (table->reginfo.not_exists_optimize)
- buff_ptr= strmov(buff_ptr,"; Not exists");
+ extra.append("; Not exists");
if (need_tmp_table)
{
need_tmp_table=0;
- buff_ptr= strmov(buff_ptr,"; Using temporary");
+ extra.append("; Using temporary");
}
if (need_order)
{
need_order=0;
- buff_ptr= strmov(buff_ptr,"; Using filesort");
+ extra.append("; Using filesort");
}
if (distinct & test_all_bits(used_tables,thd->used_tables))
- buff_ptr= strmov(buff_ptr,"; Distinct");
- if (buff_ptr == buff)
- buff_ptr+= 2; // Skip inital "; "
- item_list.push_back(new Item_string(buff+2,(uint) (buff_ptr - buff)-2,
- cs));
+ extra.append("; Distinct");
+
+ /* Skip initial "; "*/
+ const char *str= extra.ptr();
+ uint32 len= extra.length();
+ if (len)
+ {
+ str += 2;
+ len -= 2;
+ }
+ item_list.push_back(new Item_string(str, len, cs));
}
// For next iteration
used_tables|=table->map;