Commit 30b3c3bf authored by sergefp@mysql.com's avatar sergefp@mysql.com

Merged, most likely needs post-merge fix

parents 5e46a059 4016048d
...@@ -103,6 +103,7 @@ peter@mysql.com ...@@ -103,6 +103,7 @@ peter@mysql.com
peterg@mysql.com peterg@mysql.com
pgulutzan@linux.local pgulutzan@linux.local
pmartin@build.mysql2.com pmartin@build.mysql2.com
psergey@psergey.(none)
ram@deer.(none) ram@deer.(none)
ram@gw.mysql.r18.ru ram@gw.mysql.r18.ru
ram@gw.udmsearch.izhnet.ru ram@gw.udmsearch.izhnet.ru
......
drop table if exists t0, t1, t2, t3;
create table t0
(
key1 int not null,
INDEX i1(key1),
);
alter table t0 add key2 int not null, add index i2(key2);
alter table t0 add key3 int not null, add index i3(key3);
alter table t0 add key4 int not null, add index i4(key4);
alter table t0 add key5 int not null, add index i5(key5);
alter table t0 add key6 int not null, add index i6(key6);
alter table t0 add key7 int not null, add index i7(key7);
alter table t0 add key8 int not null, add index i8(key8);
update t0 set key2=key1,key3=key1,key4=key1,key5=key1,key6=key1,key7=key1,key8=1024-key1;
analyze table t0;
Table Op Msg_type Msg_text
test.t0 analyze status OK
explain select * from t0 where key1 < 3 or key1 > 1020;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t0 range i1 i1 4 NULL 55 Using where
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
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
2 2 2 2 2 2 2 1022
1021 1021 1021 1021 1021 1021 1021 3
1022 1022 1022 1022 1022 1022 1022 2
1023 1023 1023 1023 1023 1023 1023 1
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
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
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
32 32 32 32 32 32 32 992
33 33 33 33 33 33 33 991
34 34 34 34 34 34 34 990
35 35 35 35 35 35 35 989
36 36 36 36 36 36 36 988
37 37 37 37 37 37 37 987
38 38 38 38 38 38 38 986
39 39 39 39 39 39 39 985
explain select * from t0 ignore index (i2) where key1 < 3 or key2 <4;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t0 ALL i1 NULL NULL NULL 1024 Using where
explain select * from t0 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 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
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
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
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
explain select * from t0 where key2 = 45 or key1 is not null;
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 where key2 = 45 or key1 is null;
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 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
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 ALL i2,i3,i4 NULL NULL NULL 1024 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
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
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
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
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
2 2 2 2 2 2 2 1022
3 3 3 3 3 3 3 1021
4 4 4 4 4 4 4 1020
5 5 5 5 5 5 5 1019
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 i5,i6 4,4 NULL 4 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
1 SIMPLE t0 range i1,i2,i3 i3 4 NULL 96 Using where
explain select * from t0 where
(key1 < 3 or key2 < 3) and (key3 < 1000);
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t0 ALL i1,i2,i3 NULL NULL NULL 1024 Using where
explain select * from t0 where
((key1 < 4 or key2 < 4) and (key2 <5 or key3 < 4))
or
key2 > 5;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t0 ALL i1,i2,i3 NULL NULL NULL 1024 Using where
explain select * from t0 where
((key1 < 4 or key2 < 4) and (key2 <5 or key3 < 4))
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
select * from t0 where
((key1 < 4 or key2 < 4) and (key2 <5 or key3 < 4))
or
key1 < 7;
key1 key2 key3 key4 key5 key6 key7 key8
1 1 1 1 1 1 1 1023
2 2 2 2 2 2 2 1022
3 3 3 3 3 3 3 1021
4 4 4 4 4 4 4 1020
5 5 5 5 5 5 5 1019
6 6 6 6 6 6 6 1018
explain select * from t0 where
((key1 < 4 or key2 < 4) and (key3 <5 or key5 < 4))
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
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
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
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
explain select * from t0 where
((key3 <5 or key5 < 4) and (key1 < 4 or key2 < 4))
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 ALL i1,i2,i3,i5,i6 NULL NULL NULL 1024 Using where
explain select * from t0 force index(i1, i2, i3, i4, i5, i6 ) where
((key3 <5 or key5 < 4) and (key1 < 4 or key2 < 4))
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
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
2 2 2 2 2 2 2 1022
3 3 3 3 3 3 3 1021
4 4 4 4 4 4 4 1020
1021 1021 1021 1021 1021 1021 1021 3
1022 1022 1022 1022 1022 1022 1022 2
1023 1023 1023 1023 1023 1023 1023 1
1024 1024 1024 1024 1024 1024 1024 0
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
create table t2 like t0;
insert into t2 select * from t0;
alter table t2 add index i1_3(key1, key3);
alter table t2 add index i2_3(key2, key3);
alter table t2 drop index i1;
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
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
explain select key7 from t2 where key1 <100 or key2 < 100;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t2 ALL i1_3,i2_3 NULL NULL NULL 1024 Using where
create table t4 (
key1a int not null,
key1b int not null,
key2 int not null,
key2_1 int not null,
key2_2 int not null,
key3 int not null,
index i1a (key1a, key1b),
index i1b (key1b, key1a),
index i2_1(key2, key2_1),
index i2_2(key2, key2_1),
);
insert into t4 select key1,key1,key1 div 10, key1 % 10, key1 % 10, key1 from t0;
select * from t4 where key1a = 3 or key1b = 4;
key1a key1b key2 key2_1 key2_2 key3
3 3 0 3 3 3
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
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
explain select * from t4 where key2 = 1 and (key2_1 = 1 or key2_2 = 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
explain select * from t4 where key2_1 = 1 or key2_2 = 5;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t4 ALL NULL NULL NULL NULL 1024 Using where
create table t1 like t0;
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 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;
key1 key2 key3 key4 key5 key6 key7 key8 key1 key2 key3 key4 key5 key6 key7 key8
3 3 3 3 3 3 3 1021 3 3 3 3 3 3 3 1021
4 4 4 4 4 4 4 1020 4 4 4 4 4 4 4 1020
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 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 t1 ref i1 i1 4 test.t0.key1 1 Using where
explain
select * from t0,t1 where (t0.key1=t1.key1) and
(t0.key1=3 or t0.key2<4) and t1.key1=2;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t0 ref i1,i2 i1 4 const 1 Using where
1 SIMPLE t1 ref i1 i1 4 const 1 Using where
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
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
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
create table t3 like t0;
insert into t3 select * from t0;
alter table t3 add key9 int not null, add index i9(key9);
alter table t3 add keyA int not null, add index iA(keyA);
alter table t3 add keyB int not null, add index iB(keyB);
alter table t3 add keyC int not null, add index iC(keyC);
update t3 set key9=key1,keyA=key1,keyB=key1,keyC=key1;
explain 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
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
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
key9=9 or keyA=10 or keyB=11 or keyC=12;
key1 key2 key3 key4 key5 key6 key7 key8 key9 keyA keyB keyC
1 1 1 1 1 1 1 1023 1 1 1 1
2 2 2 2 2 2 2 1022 2 2 2 2
3 3 3 3 3 3 3 1021 3 3 3 3
4 4 4 4 4 4 4 1020 4 4 4 4
5 5 5 5 5 5 5 1019 5 5 5 5
6 6 6 6 6 6 6 1018 6 6 6 6
7 7 7 7 7 7 7 1017 7 7 7 7
1016 1016 1016 1016 1016 1016 1016 8 1016 1016 1016 1016
9 9 9 9 9 9 9 1015 9 9 9 9
10 10 10 10 10 10 10 1014 10 10 10 10
11 11 11 11 11 11 11 1013 11 11 11 11
12 12 12 12 12 12 12 1012 12 12 12 12
drop table t0, t1, t2, t3, t4;
drop table if exists t1;
create table t1
(
key1 int not null,
key2 int not null,
INDEX i1(key1),
INDEX i2(key2),
) type=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
select * from t1 where key1 < 5 or key2 > 197;
key1 key2
0 200
1 199
2 198
3 197
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
select * from t1 where key1 < 3 or key2 > 195;
key1 key2
0 200
1 199
2 198
4 196
3 197
alter table t1 add str1 char (255) not null,
add zeroval int not null default 0,
add str2 char (255) not null,
add str3 char (255) not null;
update t1 set str1='aaa', str2='bbb', str3=concat(key2, '-', key1 div 2, '_' ,if(key1 mod 2 = 0, 'a', 'A'));
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
select * from t1 where key1 < 5 or key2 > 197;
key1 key2 str1 zeroval str2 str3
0 200 aaa 0 bbb 200-0_a
1 199 aaa 0 bbb 199-0_A
2 198 aaa 0 bbb 198-1_a
3 197 aaa 0 bbb 197-1_A
4 196 aaa 0 bbb 196-2_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
select * from t1 where key1 < 3 or key2 > 195;
key1 key2 str1 zeroval str2 str3
0 200 aaa 0 bbb 200-0_a
1 199 aaa 0 bbb 199-0_A
2 198 aaa 0 bbb 198-1_a
4 196 aaa 0 bbb 196-2_a
3 197 aaa 0 bbb 197-1_A
drop table t1;
#
# Index merge tests
#
--disable_warnings
drop table if exists t0, t1, t2, t3;
--enable_warnings
# Create and fill a table with simple keys
create table t0
(
key1 int not null,
INDEX i1(key1),
);
--disable_query_log
insert into t0 values (1),(2),(3),(4),(5),(6),(7),(8);
let $1=7;
set @d=8;
while ($1)
{
eval insert into t0 select key1+@d from t0;
eval set @d=@d*2;
dec $1;
}
--enable_query_log
alter table t0 add key2 int not null, add index i2(key2);
alter table t0 add key3 int not null, add index i3(key3);
alter table t0 add key4 int not null, add index i4(key4);
alter table t0 add key5 int not null, add index i5(key5);
alter table t0 add key6 int not null, add index i6(key6);
alter table t0 add key7 int not null, add index i7(key7);
alter table t0 add key8 int not null, add index i8(key8);
update t0 set key2=key1,key3=key1,key4=key1,key5=key1,key6=key1,key7=key1,key8=1024-key1;
analyze table t0;
# 1. One index
explain select * from t0 where key1 < 3 or key1 > 1020;
# 2. Simple cases
explain
select * from t0 where key1 < 3 or key2 > 1020;
select * from t0 where key1 < 3 or key2 > 1020;
explain select * from t0 where key1 < 3 or key2 <4;
explain
select * from t0 where (key1 > 30 and key1<35) or (key2 >32 and key2 < 40);
select * from t0 where (key1 > 30 and key1<35) or (key2 >32 and key2 < 40);
# 3. Check that index_merge doesn't break "ignore/force/use index"
explain select * from t0 ignore index (i2) where key1 < 3 or key2 <4;
explain select * from t0 where (key1 < 3 or key2 <4) and key3 = 50;
explain select * from t0 use index (i1,i2) where (key1 < 3 or key2 <4) and key3 = 50;
explain select * from t0 where (key1 > 1 or key2 > 2);
explain select * from t0 force index (i1,i2) where (key1 > 1 or key2 > 2);
# 4. Check if conjuncts are grouped by keyuse
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);
# 5. Check index_merge with conjuncts that are always true/false
# verify fallback to "range" if there is only one non-confluent condition
explain select * from t0 where key2 = 45 or key1 <=> null;
explain select * from t0 where key2 = 45 or key1 is not null;
explain select * from t0 where key2 = 45 or key1 is null;
# the last conj. is always false and will be discarded
explain select * from t0 where key2=10 or key3=3 or key4 <=> null;
# the last conj. is always true and will cause 'all' scan
explain select * from t0 where key2=10 or key3=3 or key4 is null;
# some more complicated cases
explain select key1 from t0 where (key1 <=> null) or (key2 < 5) or
(key3=10) or (key4 <=> null);
explain select key1 from t0 where (key1 <=> null) or (key1 < 5) or
(key3=10) or (key4 <=> null);
# 6.Several ways to do index_merge, (ignored) index_merge vs. range
explain select * from t0 where
(key1 < 3 or key2 < 3) and (key3 < 4 or key4 < 4) and (key5 < 5 or key6 < 5);
explain
select * from t0 where (key1 < 3 or key2 < 6) and (key1 < 7 or key3 < 4);
select * from t0 where (key1 < 3 or key2 < 6) and (key1 < 7 or key3 < 4);
explain select * from t0 where
(key1 < 3 or key2 < 3) and (key3 < 4 or key4 < 4) and (key5 < 2 or key6 < 2);
# now index_merge is not used at all when "range" is possible
explain select * from t0 where
(key1 < 3 or key2 < 3) and (key3 < 100);
# this even can cause "all" scan:
explain select * from t0 where
(key1 < 3 or key2 < 3) and (key3 < 1000);
# 7. Complex cases
# tree_or(List<SEL_IMERGE>, range SEL_TREE).
explain select * from t0 where
((key1 < 4 or key2 < 4) and (key2 <5 or key3 < 4))
or
key2 > 5;
explain select * from t0 where
((key1 < 4 or key2 < 4) and (key2 <5 or key3 < 4))
or
key1 < 7;
select * from t0 where
((key1 < 4 or key2 < 4) and (key2 <5 or key3 < 4))
or
key1 < 7;
# tree_or(List<SEL_IMERGE>, List<SEL_IMERGE>).
explain select * from t0 where
((key1 < 4 or key2 < 4) and (key3 <5 or key5 < 4))
or
((key5 < 5 or key6 < 6) and (key7 <7 or key8 < 4));
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));
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));
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));
explain select * from t0 where
((key3 <5 or key5 < 4) and (key1 < 4 or key2 < 4))
or
((key3 >=5 or key5 < 2) and (key5 < 5 or key6 < 6));
explain select * from t0 force index(i1, i2, i3, i4, i5, i6 ) where
((key3 <5 or key5 < 4) and (key1 < 4 or key2 < 4))
or
((key3 >=5 or key5 < 2) and (key5 < 5 or key6 < 6));
# 8. Verify that "order by" after index merge uses filesort
select * from t0 where key1 < 5 or key8 < 4 order by key1;
explain
select * from t0 where key1 < 5 or key8 < 4 order by key1;
# 9. Check that index_merge cost is compared to 'index' where possible
create table t2 like t0;
insert into t2 select * from t0;
alter table t2 add index i1_3(key1, key3);
alter table t2 add index i2_3(key2, key3);
alter table t2 drop index i1;
alter table t2 drop index i2;
alter table t2 add index i321(key3, key2, key1);
# index_merge vs 'index', index_merge is better.
explain select key3 from t2 where key1 = 100 or key2 = 100;
# index_merge vs 'index', 'index' is better.
explain select key3 from t2 where key1 <100 or key2 < 100;
# index_merge vs 'all', index_merge is better.
explain select key7 from t2 where key1 <100 or key2 < 100;
# 10. Multipart keys.
create table t4 (
key1a int not null,
key1b int not null,
key2 int not null,
key2_1 int not null,
key2_2 int not null,
key3 int not null,
index i1a (key1a, key1b),
index i1b (key1b, key1a),
index i2_1(key2, key2_1),
index i2_2(key2, key2_1),
);
insert into t4 select key1,key1,key1 div 10, key1 % 10, key1 % 10, key1 from t0;
# the following will be handled by index_merge:
select * from t4 where key1a = 3 or key1b = 4;
explain select * from t4 where key1a = 3 or key1b = 4;
# and the following will not
explain select * from t4 where key2 = 1 and (key2_1 = 1 or key3 = 5);
explain select * from t4 where key2 = 1 and (key2_1 = 1 or key2_2 = 5);
explain select * from t4 where key2_1 = 1 or key2_2 = 5;
# 11. Multitable selects
create table t1 like t0;
insert into t1 select * from t0;
# index_merge on first table in join
explain select * from t0 left join t1 on (t0.key1=t1.key1)
where t0.key1=3 or t0.key2=4;
select * from t0 left join t1 on (t0.key1=t1.key1)
where t0.key1=3 or t0.key2=4;
explain
select * from t0,t1 where (t0.key1=t1.key1) and ( t0.key1=3 or t0.key2=4);
# index_merge vs. ref
explain
select * from t0,t1 where (t0.key1=t1.key1) and
(t0.key1=3 or t0.key2=4) and t1.key1<200;
# index_merge vs. ref
explain
select * from t0,t1 where (t0.key1=t1.key1) and
(t0.key1=3 or t0.key2<4) and t1.key1=2;
# index_merge on second table in join
explain select * from t0,t1 where t0.key1 = 5 and
(t1.key1 = t0.key1 or t1.key8 = t0.key1);
# index_merge inside union
explain select * from t1 where key1=3 or key2=4
union select * from t1 where key1<4 or key3=5;
# index merge in subselect
explain select * from (select * from t1 where key1 = 3 or key2 =3) as Z where key8 >5;
# 12. check for long index_merges.
create table t3 like t0;
insert into t3 select * from t0;
alter table t3 add key9 int not null, add index i9(key9);
alter table t3 add keyA int not null, add index iA(keyA);
alter table t3 add keyB int not null, add index iB(keyB);
alter table t3 add keyC int not null, add index iC(keyC);
update t3 set key9=key1,keyA=key1,keyB=key1,keyC=key1;
explain 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
key9=9 or keyA=10 or keyB=11 or keyC=12;
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
key9=9 or keyA=10 or keyB=11 or keyC=12;
drop table t0, t1, t2, t3, t4;
#
# Index merge tests
#
-- source include/have_innodb.inc
--disable_warnings
drop table if exists t1;
--enable_warnings
create table t1
(
key1 int not null,
key2 int not null,
INDEX i1(key1),
INDEX i2(key2),
) type=innodb;
--disable_query_log
let $1=200;
while ($1)
{
eval insert into t1 values (200-$1, $1);
dec $1;
}
--enable_query_log
# No primary key
explain select * from t1 where key1 < 5 or key2 > 197;
select * from t1 where key1 < 5 or key2 > 197;
explain select * from t1 where key1 < 3 or key2 > 195;
select * from t1 where key1 < 3 or key2 > 195;
# Primary key as case-sensitive string with \0s.
# also make primary key be longer then max. index length of MyISAM.
alter table t1 add str1 char (255) not null,
add zeroval int not null default 0,
add str2 char (255) not null,
add str3 char (255) not null;
update t1 set str1='aaa', str2='bbb', str3=concat(key2, '-', key1 div 2, '_' ,if(key1 mod 2 = 0, 'a', 'A'));
alter table t1 add primary key (str1, zeroval, str2, str3);
explain select * from t1 where key1 < 5 or key2 > 197;
select * from t1 where key1 < 5 or key2 > 197;
explain select * from t1 where key1 < 3 or key2 > 195;
select * from t1 where key1 < 3 or key2 > 195;
drop table t1;
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
** Create a FT or QUICK RANGE based on a key ** Create a FT or QUICK RANGE based on a key
****************************************************************************/ ****************************************************************************/
QUICK_SELECT *get_ft_or_quick_select_for_ref(THD *thd, TABLE *table, QUICK_RANGE_SELECT *get_ft_or_quick_select_for_ref(THD *thd, TABLE *table,
JOIN_TAB *tab) JOIN_TAB *tab)
{ {
if (tab->type == JT_FT) if (tab->type == JT_FT)
......
...@@ -24,18 +24,19 @@ ...@@ -24,18 +24,19 @@
#pragma interface /* gcc class implementation */ #pragma interface /* gcc class implementation */
#endif #endif
class FT_SELECT: public QUICK_SELECT { class FT_SELECT: public QUICK_RANGE_SELECT {
public: public:
TABLE_REF *ref; TABLE_REF *ref;
FT_SELECT(THD *thd, TABLE *table, TABLE_REF *tref) : FT_SELECT(THD *thd, TABLE *table, TABLE_REF *tref) :
QUICK_SELECT (thd, table, tref->key, 1), ref(tref) { init(); } QUICK_RANGE_SELECT (thd, table, tref->key, 1), ref(tref) { init(); }
int init() { return error=file->ft_init(); } int init() { return error=file->ft_init(); }
int get_next() { return error=file->ft_read(record); } int get_next() { return error=file->ft_read(record); }
int get_type() { return QS_TYPE_FULLTEXT; }
}; };
QUICK_SELECT *get_ft_or_quick_select_for_ref(THD *thd, TABLE *table, QUICK_RANGE_SELECT *get_ft_or_quick_select_for_ref(THD *thd, TABLE *table,
JOIN_TAB *tab); JOIN_TAB *tab);
#endif #endif
...@@ -267,14 +267,17 @@ public: ...@@ -267,14 +267,17 @@ public:
SEL_ARG *clone_tree(); SEL_ARG *clone_tree();
}; };
class SEL_IMERGE;
class SEL_TREE :public Sql_alloc class SEL_TREE :public Sql_alloc
{ {
public: public:
enum Type { IMPOSSIBLE, ALWAYS, MAYBE, KEY, KEY_SMALLER } type; enum Type { IMPOSSIBLE, ALWAYS, MAYBE, KEY, KEY_SMALLER } type;
SEL_TREE(enum Type type_arg) :type(type_arg) {} SEL_TREE(enum Type type_arg) :type(type_arg) {}
SEL_TREE() :type(KEY) { bzero((char*) keys,sizeof(keys));} SEL_TREE() :type(KEY), keys_map(0) { bzero((char*) keys,sizeof(keys));}
SEL_ARG *keys[MAX_KEY]; SEL_ARG *keys[MAX_KEY];
key_map keys_map; /* bitmask of non-NULL elements in keys */
List<SEL_IMERGE> merges; /* possible ways to read rows using index_merge */
}; };
...@@ -302,10 +305,19 @@ static ha_rows check_quick_keys(PARAM *param,uint index,SEL_ARG *key_tree, ...@@ -302,10 +305,19 @@ static ha_rows check_quick_keys(PARAM *param,uint index,SEL_ARG *key_tree,
char *min_key,uint min_key_flag, char *min_key,uint min_key_flag,
char *max_key, uint max_key_flag); char *max_key, uint max_key_flag);
static QUICK_SELECT *get_quick_select(PARAM *param,uint index, QUICK_RANGE_SELECT *get_quick_select(PARAM *param,uint index,
SEL_ARG *key_tree); SEL_ARG *key_tree, MEM_ROOT *alloc = NULL);
static int get_quick_select_params(SEL_TREE *tree, PARAM& param,
key_map& needed_reg, TABLE *head,
bool index_read_can_be_used,
double* read_time,
ha_rows* records,
SEL_ARG*** key_to_read);
#ifndef DBUG_OFF #ifndef DBUG_OFF
static void print_quick(QUICK_SELECT *quick,const key_map* needed_reg); void print_quick_sel_imerge(QUICK_INDEX_MERGE_SELECT *quick,
const key_map *needed_reg);
void print_quick_sel_range(QUICK_RANGE_SELECT *quick, const key_map *needed_reg);
#endif #endif
static SEL_TREE *tree_and(PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2); static SEL_TREE *tree_and(PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2);
static SEL_TREE *tree_or(PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2); static SEL_TREE *tree_or(PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2);
...@@ -313,16 +325,234 @@ static SEL_ARG *sel_add(SEL_ARG *key1,SEL_ARG *key2); ...@@ -313,16 +325,234 @@ static SEL_ARG *sel_add(SEL_ARG *key1,SEL_ARG *key2);
static SEL_ARG *key_or(SEL_ARG *key1,SEL_ARG *key2); static SEL_ARG *key_or(SEL_ARG *key1,SEL_ARG *key2);
static SEL_ARG *key_and(SEL_ARG *key1,SEL_ARG *key2,uint clone_flag); static SEL_ARG *key_and(SEL_ARG *key1,SEL_ARG *key2,uint clone_flag);
static bool get_range(SEL_ARG **e1,SEL_ARG **e2,SEL_ARG *root1); static bool get_range(SEL_ARG **e1,SEL_ARG **e2,SEL_ARG *root1);
static bool get_quick_keys(PARAM *param,QUICK_SELECT *quick,KEY_PART *key, bool get_quick_keys(PARAM *param,QUICK_RANGE_SELECT *quick,KEY_PART *key,
SEL_ARG *key_tree,char *min_key,uint min_key_flag, SEL_ARG *key_tree,char *min_key,uint min_key_flag,
char *max_key,uint max_key_flag); char *max_key,uint max_key_flag);
static bool eq_tree(SEL_ARG* a,SEL_ARG *b); static bool eq_tree(SEL_ARG* a,SEL_ARG *b);
static SEL_ARG null_element(SEL_ARG::IMPOSSIBLE); static SEL_ARG null_element(SEL_ARG::IMPOSSIBLE);
static bool null_part_in_key(KEY_PART *key_part, const char *key, uint length); static bool null_part_in_key(KEY_PART *key_part, const char *key, uint length);
bool sel_trees_can_be_ored(SEL_TREE *tree1, SEL_TREE *tree2, PARAM* param);
/*
SEL_IMERGE is a list of possible ways to do index merge, i.e. it is
a condition in the following form:
(t_1||t_2||...||t_N) && (next)
where all t_i are SEL_TREEs, next is another SEL_IMERGE and no pair
(t_i,t_j) contains SEL_ARGS for the same index.
SEL_TREE contained in SEL_IMERGE always has merges=NULL.
This class relies on memory manager to do the cleanup.
*/
class SEL_IMERGE : public Sql_alloc
{
enum { PREALLOCED_TREES= 10};
public:
SEL_TREE *trees_prealloced[PREALLOCED_TREES];
SEL_TREE **trees; /* trees used to do index_merge */
SEL_TREE **trees_next; /* last of these trees */
SEL_TREE **trees_end; /* end of allocated space */
SEL_ARG ***best_keys; /* best keys to read in SEL_TREEs */
SEL_IMERGE() :
trees(&trees_prealloced[0]),
trees_next(trees),
trees_end(trees + PREALLOCED_TREES)
{}
int or_sel_tree(PARAM *param, SEL_TREE *tree);
int or_sel_tree_with_checks(PARAM *param, SEL_TREE *new_tree);
int or_sel_imerge_with_checks(PARAM *param, SEL_IMERGE* imerge);
};
/*
Add SEL_TREE to this index_merge without any checks,
NOTES
This function implements the following:
(x_1||...||x_N) || t = (x_1||...||x_N||t), where x_i, t are SEL_TREEs
RETURN
0 - OK
-1 - Out of memory.
*/
int SEL_IMERGE::or_sel_tree(PARAM *param, SEL_TREE *tree)
{
if (trees_next == trees_end)
{
const int realloc_ratio= 2; /* Double size for next round */
uint old_elements= (trees_end - trees);
uint old_size= sizeof(SEL_TREE**) * old_elements;
uint new_size= old_size * realloc_ratio;
SEL_TREE **new_trees;
if (!(new_trees= (SEL_TREE**)alloc_root(param->mem_root, new_size)))
return -1;
memcpy(new_trees, trees, old_size);
trees= new_trees;
trees_next= trees + old_elements;
trees_end= trees + old_elements * realloc_ratio;
}
*(trees_next++)= tree;
return 0;
}
/*
Perform OR operation on this SEL_IMERGE and supplied SEL_TREE new_tree,
combining new_tree with one of the trees in this SEL_IMERGE if they both
have SEL_ARGs for the same key.
SYNOPSIS
or_sel_tree_with_checks()
param PARAM from SQL_SELECT::test_quick_select
new_tree SEL_TREE with type KEY or KEY_SMALLER.
NOTES
This does the following:
(t_1||...||t_k)||new_tree =
either
= (t_1||...||t_k||new_tree)
or
= (t_1||....||(t_j|| new_tree)||...||t_k),
where t_i, y are SEL_TREEs.
new_tree is combined with the first t_j it has a SEL_ARG on common
key with. As a consequence of this, choice of keys to do index_merge
read may depend on the order of conditions in WHERE part of the query.
RETURN
0 OK
1 One of the trees was combined with new_tree to SEL_TREE::ALWAYS,
and (*this) should be discarded.
-1 An error occurred.
*/
int SEL_IMERGE::or_sel_tree_with_checks(PARAM *param, SEL_TREE *new_tree)
{
for (SEL_TREE** tree = trees;
tree != trees_next;
tree++)
{
if (sel_trees_can_be_ored(*tree, new_tree, param))
{
*tree = tree_or(param, *tree, new_tree);
if (!*tree)
return 1;
if (((*tree)->type == SEL_TREE::MAYBE) ||
((*tree)->type == SEL_TREE::ALWAYS))
return 1;
/* SEL_TREE::IMPOSSIBLE is impossible here */
return 0;
}
}
/* new tree cannot be combined with any of existing trees */
return or_sel_tree(param, new_tree);
}
/*
Perform OR operation on this index_merge and supplied index_merge list.
RETURN
0 - OK
1 - One of conditions in result is always TRUE and this SEL_IMERGE
should be discarded.
-1 - An error occurred
*/
int SEL_IMERGE::or_sel_imerge_with_checks(PARAM *param, SEL_IMERGE* imerge)
{
for (SEL_TREE** tree= imerge->trees;
tree != imerge->trees_next;
tree++)
{
if (or_sel_tree_with_checks(param, *tree))
return 1;
}
return 0;
}
/*
Perform AND operation on two index_merge lists, storing result in *im1.
*/
inline void imerge_list_and_list(List<SEL_IMERGE> *im1, List<SEL_IMERGE> *im2)
{
im1->concat(im2);
}
/*
Perform OR operation on 2 index_merge lists, storing result in first list.
NOTES
The following conversion is implemented:
(a_1 &&...&& a_N)||(b_1 &&...&& b_K) = AND_i,j(a_i || b_j) =>
=> (a_1||b_1).
i.e. all conjuncts except the first one are currently dropped.
This is done to avoid producing N*K ways to do index_merge.
If (a_1||b_1) produce a condition that is always true, NULL is
returned and index_merge is discarded. (while it is actually
possible to try harder).
As a consequence of this, choice of keys to do index_merge
read may depend on the order of conditions in WHERE part of
the query.
RETURN
0 OK, result is stored in *im1
other Error, both passed lists are unusable
*/
int imerge_list_or_list(PARAM *param,
List<SEL_IMERGE> *im1,
List<SEL_IMERGE> *im2)
{
SEL_IMERGE *imerge= im1->head();
im1->empty();
im1->push_back(imerge);
return imerge->or_sel_imerge_with_checks(param, im2->head());
}
/*
Perform OR operation on index_merge list and key tree.
RETURN
0 OK, result is stored in *im1
other Error
*/
int imerge_list_or_tree(PARAM *param,
List<SEL_IMERGE> *im1,
SEL_TREE *tree)
{
SEL_IMERGE *imerge;
List_iterator<SEL_IMERGE> it(*im1);
while((imerge= it++))
{
if (imerge->or_sel_tree_with_checks(param, tree))
it.remove();
}
return im1->is_empty();
}
/*************************************************************************** /***************************************************************************
** Basic functions for SQL_SELECT and QUICK_SELECT ** Basic functions for SQL_SELECT and QUICK_RANGE_SELECT
***************************************************************************/ ***************************************************************************/
/* make a select from mysql info /* make a select from mysql info
...@@ -379,11 +609,19 @@ SQL_SELECT::~SQL_SELECT() ...@@ -379,11 +609,19 @@ SQL_SELECT::~SQL_SELECT()
#undef index // Fix for Unixware 7 #undef index // Fix for Unixware 7
QUICK_SELECT::QUICK_SELECT(THD *thd, TABLE *table, uint key_nr, bool no_alloc) QUICK_SELECT_I::QUICK_SELECT_I()
:dont_free(0),error(0),index(key_nr),max_used_key_length(0), :max_used_key_length(0),
used_key_parts(0), head(table), it(ranges),range(0) used_key_parts(0)
{}
QUICK_RANGE_SELECT::QUICK_RANGE_SELECT(THD *thd, TABLE *table, uint key_nr,
bool no_alloc, MEM_ROOT *parent_alloc)
:dont_free(0),error(0),it(ranges),range(0)
{ {
if (!no_alloc) index= key_nr;
head= table;
if (!no_alloc && !parent_alloc)
{ {
// Allocates everything through the internal memroot // Allocates everything through the internal memroot
init_sql_alloc(&alloc, thd->variables.range_alloc_block_size, 0); init_sql_alloc(&alloc, thd->variables.range_alloc_block_size, 0);
...@@ -391,12 +629,16 @@ QUICK_SELECT::QUICK_SELECT(THD *thd, TABLE *table, uint key_nr, bool no_alloc) ...@@ -391,12 +629,16 @@ QUICK_SELECT::QUICK_SELECT(THD *thd, TABLE *table, uint key_nr, bool no_alloc)
} }
else else
bzero((char*) &alloc,sizeof(alloc)); bzero((char*) &alloc,sizeof(alloc));
file=head->file; file= head->file;
record=head->record[0]; record= head->record[0];
init(); }
int QUICK_RANGE_SELECT::init()
{
return (error= file->index_init(index));
} }
QUICK_SELECT::~QUICK_SELECT() QUICK_RANGE_SELECT::~QUICK_RANGE_SELECT()
{ {
if (!dont_free) if (!dont_free)
{ {
...@@ -405,6 +647,42 @@ QUICK_SELECT::~QUICK_SELECT() ...@@ -405,6 +647,42 @@ QUICK_SELECT::~QUICK_SELECT()
} }
} }
QUICK_INDEX_MERGE_SELECT::QUICK_INDEX_MERGE_SELECT(THD *thd, TABLE *table)
:cur_quick_it(quick_selects), index_merge(thd)
{
index= MAX_KEY;
head= table;
init_sql_alloc(&alloc,1024,0);
}
int QUICK_INDEX_MERGE_SELECT::init()
{
int error;
cur_quick_it.rewind();
cur_quick_select= cur_quick_it++;
if (error= index_merge.init(head))
return error;
return cur_quick_select->init();
}
void QUICK_INDEX_MERGE_SELECT::reset()
{
cur_quick_select->reset();
}
bool
QUICK_INDEX_MERGE_SELECT::push_quick_back(QUICK_RANGE_SELECT *quick_sel_range)
{
return quick_selects.push_back(quick_sel_range);
}
QUICK_INDEX_MERGE_SELECT::~QUICK_INDEX_MERGE_SELECT()
{
quick_selects.delete_elements();
free_root(&alloc,MYF(0));
}
QUICK_RANGE::QUICK_RANGE() QUICK_RANGE::QUICK_RANGE()
:min_key(0),max_key(0),min_length(0),max_length(0), :min_key(0),max_key(0),min_length(0),max_length(0),
flag(NO_MIN_RANGE | NO_MAX_RANGE) flag(NO_MIN_RANGE | NO_MAX_RANGE)
...@@ -598,6 +876,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use, ...@@ -598,6 +876,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
uint basflag; uint basflag;
uint idx; uint idx;
double scan_time; double scan_time;
QUICK_INDEX_MERGE_SELECT *quick_imerge;
DBUG_ENTER("test_quick_select"); DBUG_ENTER("test_quick_select");
DBUG_PRINT("enter",("keys_to_use: %lu prev_tables: %lu const_tables: %lu", DBUG_PRINT("enter",("keys_to_use: %lu prev_tables: %lu const_tables: %lu",
keys_to_use.to_ulonglong(), (ulong) prev_tables, keys_to_use.to_ulonglong(), (ulong) prev_tables,
...@@ -691,70 +970,211 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use, ...@@ -691,70 +970,211 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
read_time= (double) HA_POS_ERROR; read_time= (double) HA_POS_ERROR;
} }
else if (tree->type == SEL_TREE::KEY || else if (tree->type == SEL_TREE::KEY ||
tree->type == SEL_TREE::KEY_SMALLER) tree->type == SEL_TREE::KEY_SMALLER)
{ {
SEL_ARG **key,**end,**best_key=0; /*
It is possible to use a quick select (but maybe it would be slower
than 'all' table scan).
for (idx=0,key=tree->keys, end=key+param.keys ; */
key != end ; SEL_ARG **best_key= 0;
key++,idx++) ha_rows found_records;
{ double found_read_time= read_time;
ha_rows found_records;
double found_read_time; if (!get_quick_select_params(tree, param, needed_reg, head, true,
if (*key) &found_read_time, &found_records,
{ &best_key))
uint keynr= param.real_keynr[idx]; {
if ((*key)->type == SEL_ARG::MAYBE_KEY || /*
(*key)->maybe_flag) Ok, quick select is better than 'all' table scan and we have its
needed_reg.set_bit(keynr); parameters, so construct it.
*/
found_records=check_quick_select(&param, idx, *key); read_time= found_read_time;
if (found_records != HA_POS_ERROR && found_records > 2 && records= found_records;
head->used_keys.is_set(keynr) &&
(head->file->index_flags(keynr) & HA_KEY_READ_ONLY)) if ((quick= get_quick_select(&param,(uint) (best_key-tree->keys),
{ *best_key)) && (!quick->init()))
/* {
We can resolve this by only reading through this key. quick->records= records;
Assume that we will read trough the whole key range quick->read_time= read_time;
and that all key blocks are half full (normally things are }
much better). }
*/
uint keys_per_block= (head->file->block_size/2/ /*
(head->key_info[keynr].key_length+ btw, tree type SEL_TREE::INDEX_MERGE was not introduced
head->file->ref_length) + 1); intentionally
found_read_time=((double) (found_records+keys_per_block-1)/ */
(double) keys_per_block);
} /* if no range select could be built, try using index_merge */
else if (!quick && !tree->merges.is_empty())
found_read_time= (head->file->read_time(keynr, {
param.range_count, DBUG_PRINT("info",("No range reads possible,"
found_records)+ " trying to construct index_merge"));
(double) found_records / TIME_FOR_COMPARE); SEL_IMERGE *imerge;
if (read_time > found_read_time && found_records != HA_POS_ERROR) SEL_IMERGE *min_imerge= NULL;
{ double min_imerge_cost= DBL_MAX;
read_time=found_read_time; ha_rows min_imerge_records;
records=found_records;
best_key=key; List_iterator_fast<SEL_IMERGE> it(tree->merges);
} while ((imerge= it++))
} {
} double imerge_cost= 0;
if (best_key && records) ha_rows imerge_total_records= 0;
{ double tree_read_time;
if ((quick=get_quick_select(&param,(uint) (best_key-tree->keys), ha_rows tree_records;
*best_key))) imerge->best_keys=
{ (SEL_ARG***)alloc_root(&alloc,
quick->records=records; (imerge->trees_next - imerge->trees)*
quick->read_time=read_time; sizeof(void*));
} for (SEL_TREE **ptree= imerge->trees;
} ptree != imerge->trees_next;
ptree++)
{
tree_read_time= read_time;
if (get_quick_select_params(*ptree, param, needed_reg, head,
false,
&tree_read_time, &tree_records,
&(imerge->best_keys[ptree -
imerge->trees])))
goto imerge_fail;
imerge_cost += tree_read_time;
imerge_total_records += tree_records;
}
imerge_total_records= min(imerge_total_records,
head->file->records);
imerge_cost += imerge_total_records / TIME_FOR_COMPARE;
if (imerge_cost < min_imerge_cost)
{
min_imerge= imerge;
min_imerge_cost= imerge_cost;
min_imerge_records= imerge_total_records;
}
imerge_fail:;
}
if (!min_imerge)
goto end_free;
records= min_imerge_records;
/* ok, got minimal imerge, *min_imerge, with cost min_imerge_cost */
if (head->used_keys)
{
/* check if "ALL" +"using index" read would be faster */
int key_for_use= find_shortest_key(head, head->used_keys);
ha_rows total_table_records= (0 == head->file->records)? 1 :
head->file->records;
uint keys_per_block= (head->file->block_size/2/
(head->key_info[key_for_use].key_length+
head->file->ref_length) + 1);
double all_index_scan_read_time= ((double)(total_table_records+
keys_per_block-1)/
(double) keys_per_block);
DBUG_PRINT("info",
("'all' scan will be using key %d, read time %g",
key_for_use, all_index_scan_read_time));
if (all_index_scan_read_time < min_imerge_cost)
{
DBUG_PRINT("info",
("index merge would be slower, "
"will do full 'index' scan"));
goto end_free;
}
}
else
{
/* check if "ALL" would be faster */
if (read_time < min_imerge_cost)
{
DBUG_PRINT("info",
("index merge would be slower, "
"will do full table scan"));
goto end_free;
}
}
if (!(quick= quick_imerge= new QUICK_INDEX_MERGE_SELECT(thd, head)))
goto end_free;
quick->records= min_imerge_records;
quick->read_time= min_imerge_cost;
my_pthread_setspecific_ptr(THR_MALLOC, &quick_imerge->alloc);
QUICK_RANGE_SELECT *new_quick;
for (SEL_TREE **ptree = min_imerge->trees;
ptree != min_imerge->trees_next;
ptree++)
{
SEL_ARG **tree_best_key=
min_imerge->best_keys[ptree - min_imerge->trees];
if ((new_quick= get_quick_select(&param,
(uint)(tree_best_key-
(*ptree)->keys),
*tree_best_key,
&quick_imerge->alloc)))
{
new_quick->records= min_imerge_records;
new_quick->read_time= min_imerge_cost;
/*
QUICK_RANGE_SELECT::QUICK_RANGE_SELECT leaves THR_MALLOC
pointing to its allocator, restore it back
*/
quick_imerge->last_quick_select= new_quick;
if (quick_imerge->push_quick_back(new_quick))
{
delete new_quick;
delete quick;
quick= quick_imerge= NULL;
goto end_free;
}
}
else
{
delete quick;
quick= quick_imerge= NULL;
goto end_free;
}
}
free_root(&alloc,MYF(0));
my_pthread_setspecific_ptr(THR_MALLOC,old_root);
if (quick->init())
{
delete quick;
quick= quick_imerge= NULL;
DBUG_PRINT("error",
("Failed to allocate index merge structures,"
"falling back to full scan."));
}
else
{
/* with 'using filesort' quick->reset() is not called */
quick->reset();
}
goto end;
}
} }
} }
end_free:
free_root(&alloc,MYF(0)); // Return memory & allocator free_root(&alloc,MYF(0)); // Return memory & allocator
my_pthread_setspecific_ptr(THR_MALLOC,old_root); my_pthread_setspecific_ptr(THR_MALLOC,old_root);
end:
thd->no_errors=0; thd->no_errors=0;
} }
DBUG_EXECUTE("info",print_quick(quick,&needed_reg););
DBUG_EXECUTE("info",
{
if (quick_imerge)
print_quick_sel_imerge(quick_imerge, &needed_reg);
else
print_quick_sel_range((QUICK_RANGE_SELECT*)quick, &needed_reg);
}
);
/* /*
Assume that if the user is using 'limit' we will only need to scan Assume that if the user is using 'limit' we will only need to scan
limit rows if we are using a key limit rows if we are using a key
...@@ -762,6 +1182,77 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use, ...@@ -762,6 +1182,77 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
DBUG_RETURN(records ? test(quick) : -1); DBUG_RETURN(records ? test(quick) : -1);
} }
/*
Calculate quick select read time, # of records, and best key to use
without constructing QUICK_SELECT
*/
static int get_quick_select_params(SEL_TREE *tree, PARAM& param,
key_map& needed_reg, TABLE *head,
bool index_read_can_be_used,
double* read_time, ha_rows* records,
SEL_ARG*** key_to_read)
{
int idx;
int result = 1;
/*
Note that there may be trees that have type SEL_TREE::KEY but contain
no key reads at all. For example, tree for expression "key1 is not null"
where key1 is defined as "not null".
*/
SEL_ARG **key,**end;
for (idx= 0,key=tree->keys, end=key+param.keys ;
key != end ;
key++,idx++)
{
ha_rows found_records;
double found_read_time;
if (*key)
{
uint keynr= param.real_keynr[idx];
if ((*key)->type == SEL_ARG::MAYBE_KEY ||
(*key)->maybe_flag)
needed_reg.set_bit(keynr);
key_map usable_keys = index_read_can_be_used?
(head->used_keys & ((key_map) 1 << keynr)) : 0;
found_records=check_quick_select(&param, idx, *key);
if (found_records != HA_POS_ERROR && found_records > 2 &&
usable_keys &&
(head->file->index_flags(keynr) & HA_KEY_READ_ONLY))
{
/*
We can resolve this by only reading through this key.
Assume that we will read trough the whole key range
and that all key blocks are half full (normally things are
much better).
*/
uint keys_per_block= (head->file->block_size/2/
(head->key_info[keynr].key_length+
head->file->ref_length) + 1);
found_read_time=((double) (found_records+keys_per_block-1)/
(double) keys_per_block);
}
else
found_read_time= (head->file->read_time(keynr,
param.range_count,
found_records)+
(double) found_records / TIME_FOR_COMPARE);
if (*read_time > found_read_time && found_records != HA_POS_ERROR)
{
*read_time= found_read_time;
*records= found_records;
*key_to_read= key;
result = 0;
}
}
}
return result;
}
/* make a select tree of all keys in condition */ /* make a select tree of all keys in condition */
static SEL_TREE *get_mm_tree(PARAM *param,COND *cond) static SEL_TREE *get_mm_tree(PARAM *param,COND *cond)
...@@ -948,6 +1439,7 @@ get_mm_parts(PARAM *param, Field *field, Item_func::Functype type, ...@@ -948,6 +1439,7 @@ get_mm_parts(PARAM *param, Field *field, Item_func::Functype type,
} }
sel_arg->part=(uchar) key_part->part; sel_arg->part=(uchar) key_part->part;
tree->keys[key_part->key]=sel_add(tree->keys[key_part->key],sel_arg); tree->keys[key_part->key]=sel_add(tree->keys[key_part->key],sel_arg);
tree->keys_map |= 1 << key_part->key;
} }
} }
...@@ -1220,6 +1712,8 @@ tree_and(PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2) ...@@ -1220,6 +1712,8 @@ tree_and(PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2)
DBUG_RETURN(tree1); DBUG_RETURN(tree1);
} }
bool trees_have_key = false;
key_map result_keys= 0;
/* Join the trees key per key */ /* Join the trees key per key */
SEL_ARG **key1,**key2,**end; SEL_ARG **key1,**key2,**end;
for (key1= tree1->keys,key2= tree2->keys,end=key1+param->keys ; for (key1= tree1->keys,key2= tree2->keys,end=key1+param->keys ;
...@@ -1228,6 +1722,7 @@ tree_and(PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2) ...@@ -1228,6 +1722,7 @@ tree_and(PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2)
uint flag=0; uint flag=0;
if (*key1 || *key2) if (*key1 || *key2)
{ {
trees_have_key = true;
if (*key1 && !(*key1)->simple_key()) if (*key1 && !(*key1)->simple_key())
flag|=CLONE_KEY1_MAYBE; flag|=CLONE_KEY1_MAYBE;
if (*key2 && !(*key2)->simple_key()) if (*key2 && !(*key2)->simple_key())
...@@ -1236,17 +1731,57 @@ tree_and(PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2) ...@@ -1236,17 +1731,57 @@ tree_and(PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2)
if ((*key1)->type == SEL_ARG::IMPOSSIBLE) if ((*key1)->type == SEL_ARG::IMPOSSIBLE)
{ {
tree1->type= SEL_TREE::IMPOSSIBLE; tree1->type= SEL_TREE::IMPOSSIBLE;
break; DBUG_RETURN(tree1);
} }
result_keys |= 1 << (key1 - tree1->keys);
#ifdef EXTRA_DEBUG #ifdef EXTRA_DEBUG
(*key1)->test_use_count(*key1); (*key1)->test_use_count(*key1);
#endif #endif
} }
} }
tree1->keys_map= result_keys;
/* dispose index_merge if there is a "range" option */
if (trees_have_key)
{
tree1->merges.empty();
DBUG_RETURN(tree1);
}
/* ok, both trees are index_merge trees */
imerge_list_and_list(&tree1->merges, &tree2->merges);
DBUG_RETURN(tree1); DBUG_RETURN(tree1);
} }
/*
Check if two SEL_TREES can be combined into one without using index_merge
*/
bool sel_trees_can_be_ored(SEL_TREE *tree1, SEL_TREE *tree2, PARAM* param)
{
key_map common_keys= tree1->keys_map & tree2->keys_map;
DBUG_ENTER("sel_trees_can_be_ored");
if (!common_keys)
DBUG_RETURN(false);
/* trees have a common key, check if they refer to same key part */
SEL_ARG **key1,**key2;
for (uint key_no=0; key_no < param->keys; key_no++, common_keys= common_keys >> 1)
{
if (common_keys & 1)
{
key1= tree1->keys + key_no;
key2= tree2->keys + key_no;
if ((*key1)->part == (*key2)->part)
{
DBUG_RETURN(true);
}
}
}
DBUG_RETURN(false);
}
static SEL_TREE * static SEL_TREE *
tree_or(PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2) tree_or(PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2)
...@@ -1263,19 +1798,61 @@ tree_or(PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2) ...@@ -1263,19 +1798,61 @@ tree_or(PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2)
if (tree2->type == SEL_TREE::MAYBE) if (tree2->type == SEL_TREE::MAYBE)
DBUG_RETURN(tree2); DBUG_RETURN(tree2);
/* Join the trees key per key */ SEL_TREE *result= 0;
SEL_ARG **key1,**key2,**end; key_map result_keys= 0;
SEL_TREE *result=0; if (sel_trees_can_be_ored(tree1, tree2, param))
for (key1= tree1->keys,key2= tree2->keys,end=key1+param->keys ;
key1 != end ; key1++,key2++)
{ {
*key1=key_or(*key1,*key2); /* Join the trees key per key */
if (*key1) SEL_ARG **key1,**key2,**end;
for (key1= tree1->keys,key2= tree2->keys,end= key1+param->keys ;
key1 != end ; key1++,key2++)
{ {
result=tree1; // Added to tree1 *key1=key_or(*key1,*key2);
if (*key1)
{
result=tree1; // Added to tree1
result_keys |= 1 << (key1 - tree1->keys);
#ifdef EXTRA_DEBUG #ifdef EXTRA_DEBUG
(*key1)->test_use_count(*key1); (*key1)->test_use_count(*key1);
#endif #endif
}
}
if (result)
result->keys_map= result_keys;
}
else
{
/* ok, two trees have KEY type but cannot be used without index merge */
if (tree1->merges.is_empty() && tree2->merges.is_empty())
{
SEL_IMERGE *merge;
/* both trees are "range" trees, produce new index merge structure */
if (!(result= new SEL_TREE()) || !(merge= new SEL_IMERGE()) ||
(result->merges.push_back(merge)) ||
(merge->or_sel_tree(param, tree1)) ||
(merge->or_sel_tree(param, tree2)))
result= NULL;
else
result->type= tree1->type;
}
else if (!tree1->merges.is_empty() && !tree2->merges.is_empty())
{
if (imerge_list_or_list(param, &tree1->merges, &tree2->merges))
result= new SEL_TREE(SEL_TREE::ALWAYS);
else
result= tree1;
}
else
{
/* one tree is index merge tree and another is range tree */
if (tree1->merges.is_empty())
swap(SEL_TREE*, tree1, tree2);
/* add tree2 to tree1->merges, checking if it collapses to ALWAYS */
if (imerge_list_or_tree(param, &tree1->merges, tree2))
result= new SEL_TREE(SEL_TREE::ALWAYS);
else
result= tree1;
} }
} }
DBUG_RETURN(result); DBUG_RETURN(result);
...@@ -2268,15 +2845,17 @@ check_quick_keys(PARAM *param,uint idx,SEL_ARG *key_tree, ...@@ -2268,15 +2845,17 @@ check_quick_keys(PARAM *param,uint idx,SEL_ARG *key_tree,
/**************************************************************************** /****************************************************************************
** change a tree to a structure to be used by quick_select ** change a tree to a structure to be used by quick_select
** This uses it's own malloc tree ** This uses it's own malloc tree
** The caller should call QUICK_SELCT::init for returned quick select
****************************************************************************/ ****************************************************************************/
QUICK_RANGE_SELECT *
static QUICK_SELECT * get_quick_select(PARAM *param,uint idx,SEL_ARG *key_tree,
get_quick_select(PARAM *param,uint idx,SEL_ARG *key_tree) MEM_ROOT *parent_alloc)
{ {
QUICK_SELECT *quick; QUICK_RANGE_SELECT *quick;
DBUG_ENTER("get_quick_select"); DBUG_ENTER("get_quick_select");
if ((quick=new QUICK_SELECT(param->thd, param->table, if ((quick=new QUICK_RANGE_SELECT(param->thd, param->table,
param->real_keynr[idx]))) param->real_keynr[idx],test(parent_alloc),
parent_alloc)))
{ {
if (quick->error || if (quick->error ||
get_quick_keys(param,quick,param->key[idx],key_tree,param->min_key,0, get_quick_keys(param,quick,param->key[idx],key_tree,param->min_key,0,
...@@ -2288,9 +2867,10 @@ get_quick_select(PARAM *param,uint idx,SEL_ARG *key_tree) ...@@ -2288,9 +2867,10 @@ get_quick_select(PARAM *param,uint idx,SEL_ARG *key_tree)
else else
{ {
quick->key_parts=(KEY_PART*) quick->key_parts=(KEY_PART*)
memdup_root(&quick->alloc,(char*) param->key[idx], memdup_root(parent_alloc? parent_alloc : &quick->alloc,
sizeof(KEY_PART)* (char*) param->key[idx],
param->table->key_info[param->real_keynr[idx]].key_parts); sizeof(KEY_PART)*
param->table->key_info[param->real_keynr[idx]].key_parts);
} }
} }
DBUG_RETURN(quick); DBUG_RETURN(quick);
...@@ -2300,9 +2880,8 @@ get_quick_select(PARAM *param,uint idx,SEL_ARG *key_tree) ...@@ -2300,9 +2880,8 @@ get_quick_select(PARAM *param,uint idx,SEL_ARG *key_tree)
/* /*
** Fix this to get all possible sub_ranges ** Fix this to get all possible sub_ranges
*/ */
bool
static bool get_quick_keys(PARAM *param,QUICK_RANGE_SELECT *quick,KEY_PART *key,
get_quick_keys(PARAM *param,QUICK_SELECT *quick,KEY_PART *key,
SEL_ARG *key_tree,char *min_key,uint min_key_flag, SEL_ARG *key_tree,char *min_key,uint min_key_flag,
char *max_key, uint max_key_flag) char *max_key, uint max_key_flag)
{ {
...@@ -2411,7 +2990,7 @@ get_quick_keys(PARAM *param,QUICK_SELECT *quick,KEY_PART *key, ...@@ -2411,7 +2990,7 @@ get_quick_keys(PARAM *param,QUICK_SELECT *quick,KEY_PART *key,
Return 1 if there is only one range and this uses the whole primary key Return 1 if there is only one range and this uses the whole primary key
*/ */
bool QUICK_SELECT::unique_key_range() bool QUICK_RANGE_SELECT::unique_key_range()
{ {
if (ranges.elements == 1) if (ranges.elements == 1)
{ {
...@@ -2448,16 +3027,23 @@ static bool null_part_in_key(KEY_PART *key_part, const char *key, uint length) ...@@ -2448,16 +3027,23 @@ static bool null_part_in_key(KEY_PART *key_part, const char *key, uint length)
** Create a QUICK RANGE based on a key ** Create a QUICK RANGE based on a key
****************************************************************************/ ****************************************************************************/
QUICK_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table, TABLE_REF *ref) QUICK_RANGE_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table,
TABLE_REF *ref)
{ {
table->file->index_end(); // Remove old cursor table->file->index_end(); // Remove old cursor
QUICK_SELECT *quick=new QUICK_SELECT(thd, table, ref->key, 1); QUICK_RANGE_SELECT *quick=new QUICK_RANGE_SELECT(thd, table, ref->key, 1);
KEY *key_info = &table->key_info[ref->key]; KEY *key_info = &table->key_info[ref->key];
KEY_PART *key_part; KEY_PART *key_part;
uint part; uint part;
if (!quick) if (!quick)
return 0; /* no ranges found */ return 0; /* no ranges found */
if (quick->init())
{
delete quick;
return 0;
}
if (cp_buffer_from_ref(ref)) if (cp_buffer_from_ref(ref))
{ {
if (thd->is_fatal_error) if (thd->is_fatal_error)
...@@ -2495,11 +3081,204 @@ err: ...@@ -2495,11 +3081,204 @@ err:
return 0; return 0;
} }
INDEX_MERGE::INDEX_MERGE(THD *thd_arg) :
dont_save(false), thd(thd_arg)
{}
String *INDEX_MERGE::Item_rowid::val_str(String *str)
{
str->set_quick((char*)head->file->ref, head->file->ref_length, collation.collation);
return str;
}
/*
Initialize index_merge operation.
RETURN
0 - OK
other - error.
*/
int INDEX_MERGE::init(TABLE *table)
{
DBUG_ENTER("INDEX_MERGE::init");
head= table;
if (!(rowid_item= new Item_rowid(table)))
DBUG_RETURN(1);
tmp_table_param.copy_field= 0;
tmp_table_param.end_write_records= HA_POS_ERROR;
tmp_table_param.group_length= table->file->ref_length;
tmp_table_param.group_parts= 1;
tmp_table_param.group_null_parts= 0;
tmp_table_param.hidden_field_count= 0;
tmp_table_param.field_count= 0;
tmp_table_param.func_count= 1;
tmp_table_param.sum_func_count= 0;
tmp_table_param.quick_group= 1;
bzero(&order, sizeof(ORDER));
order.item= (Item**)&rowid_item;
order.asc= 1;
fields.push_back(rowid_item);
temp_table= create_tmp_table(thd,
&tmp_table_param,
fields,
&order,
false,
0,
SELECT_DISTINCT,
HA_POS_ERROR,
(char *)"");
DBUG_RETURN(!temp_table);
}
/*
Check if record with ROWID record_pos has already been processed and
if not - store the ROWID value.
RETURN
0 - record has not been processed yet
1 - record has already been processed.
-1 - an error occurred and query processing should be terminated.
Error code is stored in INDEX_MERGE::error
*/
int INDEX_MERGE::check_record_in()
{
return (dont_save)?
check_record() :
put_record();
}
/*
Stop remembering records in check().
(this should be called just before the last key scan)
RETURN
0 - OK
1 - error occurred initializing table index.
*/
int INDEX_MERGE::start_last_quick_select()
{
int result= 0;
if (!temp_table->uniques)
{
dont_save= true;
result= temp_table->file->index_init(0);
}
return result;
}
inline int INDEX_MERGE::put_record()
{
DBUG_ENTER("INDEX_MERGE::put_record");
copy_funcs(tmp_table_param.items_to_copy);
if ((error= temp_table->file->write_row(temp_table->record[0])))
{
if (error == HA_ERR_FOUND_DUPP_KEY ||
error == HA_ERR_FOUND_DUPP_UNIQUE)
DBUG_RETURN(1);
DBUG_PRINT("info",
("Error writing row to temp. table: %d, converting to myisam",
error));
if (create_myisam_from_heap(current_thd, temp_table, &tmp_table_param,
error,1))
{
DBUG_PRINT("info", ("Table conversion failed, bailing out"));
DBUG_RETURN(-1);
}
}
DBUG_RETURN(0);
}
inline int INDEX_MERGE::check_record()
{
int result= 1;
DBUG_ENTER("INDEX_MERGE::check_record");
if ((error= temp_table->file->index_read(temp_table->record[0],
head->file->ref,
head->file->ref_length,
HA_READ_KEY_EXACT)))
{
if (error != HA_ERR_KEY_NOT_FOUND)
result= -1;
else
result= 0;
}
DBUG_RETURN(result);
}
INDEX_MERGE::~INDEX_MERGE()
{
if (temp_table)
{
DBUG_PRINT("info", ("Freeing temp. table"));
free_tmp_table(current_thd, temp_table);
}
/* rowid_item is freed automatically */
list_node* node;
node= fields.first_node();
fields.remove(&node);
}
int QUICK_INDEX_MERGE_SELECT::get_next()
{
int result;
int put_result;
DBUG_ENTER("QUICK_INDEX_MERGE_SELECT::get_next");
do
{
while ((result= cur_quick_select->get_next()) == HA_ERR_END_OF_FILE)
{
cur_quick_select= cur_quick_it++;
if (!cur_quick_select)
break;
cur_quick_select->init();
cur_quick_select->reset();
if (last_quick_select == cur_quick_select)
{
if ((result= index_merge.start_last_quick_select()))
DBUG_RETURN(result);
}
}
if (result)
{
/*
table read error (including HA_ERR_END_OF_FILE on last quick select
in index_merge)
*/
DBUG_RETURN(result);
}
cur_quick_select->file->position(cur_quick_select->record);
put_result= index_merge.check_record_in();
}while(put_result == 1); /* While record is processed */
DBUG_RETURN((put_result != -1) ? result : index_merge.error);
}
/* get next possible record using quick-struct */ /* get next possible record using quick-struct */
int QUICK_SELECT::get_next() int QUICK_RANGE_SELECT::get_next()
{ {
DBUG_ENTER("get_next"); DBUG_ENTER("QUICK_RANGE_SELECT::get_next");
for (;;) for (;;)
{ {
...@@ -2586,7 +3365,7 @@ int QUICK_SELECT::get_next() ...@@ -2586,7 +3365,7 @@ int QUICK_SELECT::get_next()
Returns 0 if key <= range->max_key Returns 0 if key <= range->max_key
*/ */
int QUICK_SELECT::cmp_next(QUICK_RANGE *range_arg) int QUICK_RANGE_SELECT::cmp_next(QUICK_RANGE *range_arg)
{ {
if (range_arg->flag & NO_MAX_RANGE) if (range_arg->flag & NO_MAX_RANGE)
return 0; /* key can't be to large */ return 0; /* key can't be to large */
...@@ -2627,8 +3406,9 @@ int QUICK_SELECT::cmp_next(QUICK_RANGE *range_arg) ...@@ -2627,8 +3406,9 @@ int QUICK_SELECT::cmp_next(QUICK_RANGE *range_arg)
for now, this seems to work right at least. for now, this seems to work right at least.
*/ */
QUICK_SELECT_DESC::QUICK_SELECT_DESC(QUICK_SELECT *q, uint used_key_parts) QUICK_SELECT_DESC::QUICK_SELECT_DESC(QUICK_RANGE_SELECT *q,
: QUICK_SELECT(*q), rev_it(rev_ranges) uint used_key_parts)
: QUICK_RANGE_SELECT(*q), rev_it(rev_ranges)
{ {
bool not_read_after_key = file->table_flags() & HA_NOT_READ_AFTER_KEY; bool not_read_after_key = file->table_flags() & HA_NOT_READ_AFTER_KEY;
QUICK_RANGE *r; QUICK_RANGE *r;
...@@ -2895,7 +3675,23 @@ print_key(KEY_PART *key_part,const char *key,uint used_length) ...@@ -2895,7 +3675,23 @@ print_key(KEY_PART *key_part,const char *key,uint used_length)
} }
} }
static void print_quick(QUICK_SELECT *quick,const key_map* needed_reg) void print_quick_sel_imerge(QUICK_INDEX_MERGE_SELECT *quick,
const key_map *needed_reg)
{
DBUG_ENTER("print_param");
if (! _db_on_ || !quick)
DBUG_VOID_RETURN;
List_iterator_fast<QUICK_RANGE_SELECT> it(quick->quick_selects);
QUICK_RANGE_SELECT* quick_range_sel;
while ((quick_range_sel= it++))
{
print_quick_sel_range(quick_range_sel, needed_reg);
}
DBUG_VOID_RETURN;
}
static void print_quick_sel_range(QUICK_RANGE_SELECT *quick,const key_map *needed_reg)
{ {
QUICK_RANGE *range; QUICK_RANGE *range;
char buf[MAX_KEY/8+1]; char buf[MAX_KEY/8+1];
......
...@@ -65,41 +65,198 @@ class QUICK_RANGE :public Sql_alloc { ...@@ -65,41 +65,198 @@ class QUICK_RANGE :public Sql_alloc {
} }
}; };
class INDEX_MERGE;
class QUICK_SELECT { /*
Quick select interface.
This class is parent for all QUICK_*_SELECT and FT_SELECT classes.
*/
class QUICK_SELECT_I
{
public: public:
ha_rows records; /* estimate of # of records to be retrieved */
double read_time; /* time to perform this retrieval */
TABLE *head;
/*
the only index this quick select uses, or MAX_KEY for
QUICK_INDEX_MERGE_SELECT
*/
uint index;
uint max_used_key_length, used_key_parts;
QUICK_SELECT_I();
virtual ~QUICK_SELECT_I(){};
virtual int init() = 0;
virtual void reset(void) = 0;
virtual int get_next() = 0; /* get next record to retrieve */
virtual bool reverse_sorted() = 0;
virtual bool unique_key_range() { return false; }
enum {
QS_TYPE_RANGE = 0,
QS_TYPE_INDEX_MERGE = 1,
QS_TYPE_RANGE_DESC = 2,
QS_TYPE_FULLTEXT = 3
};
/* Get type of this quick select - one of the QS_* values */
virtual int get_type() = 0;
};
struct st_qsel_param;
class SEL_ARG;
class QUICK_RANGE_SELECT : public QUICK_SELECT_I
{
protected:
bool next,dont_free; bool next,dont_free;
public:
int error; int error;
uint index, max_used_key_length, used_key_parts;
TABLE *head;
handler *file; handler *file;
byte *record; byte *record;
protected:
friend void print_quick_sel_range(QUICK_RANGE_SELECT *quick,
key_map needed_reg);
friend QUICK_RANGE_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table,
struct st_table_ref *ref);
friend bool get_quick_keys(struct st_qsel_param *param,
QUICK_RANGE_SELECT *quick,KEY_PART *key,
SEL_ARG *key_tree,char *min_key,uint min_key_flag,
char *max_key, uint max_key_flag);
friend QUICK_RANGE_SELECT *get_quick_select(struct st_qsel_param*,uint idx,
SEL_ARG *key_tree,
MEM_ROOT *alloc);
friend class QUICK_SELECT_DESC;
List<QUICK_RANGE> ranges; List<QUICK_RANGE> ranges;
List_iterator<QUICK_RANGE> it; List_iterator<QUICK_RANGE> it;
QUICK_RANGE *range; QUICK_RANGE *range;
MEM_ROOT alloc; MEM_ROOT alloc;
KEY_PART *key_parts;
KEY_PART *key_parts;
ha_rows records;
double read_time;
QUICK_SELECT(THD *thd, TABLE *table,uint index_arg,bool no_alloc=0);
virtual ~QUICK_SELECT();
void reset(void) { next=0; it.rewind(); }
int init() { return error=file->index_init(index); }
virtual int get_next();
virtual bool reverse_sorted() { return 0; }
int cmp_next(QUICK_RANGE *range); int cmp_next(QUICK_RANGE *range);
public:
QUICK_RANGE_SELECT(THD *thd, TABLE *table,uint index_arg,bool no_alloc=0,
MEM_ROOT *parent_alloc=NULL);
~QUICK_RANGE_SELECT();
void reset(void) { next=0; it.rewind(); }
int init();
int get_next();
bool reverse_sorted() { return 0; }
bool unique_key_range(); bool unique_key_range();
int get_type() { return QS_TYPE_RANGE; }
};
/*
Helper class for keeping track of rows that have been passed to output
in index_merge access method.
NOTES
Current implementation uses a temporary table to store ROWIDs of rows that
have been passed to output. In the future it might be changed to use more
efficient mechanisms, like Unique class.
*/
class INDEX_MERGE
{
public:
INDEX_MERGE(THD *thd_arg);
~INDEX_MERGE();
int init(TABLE *table);
int check_record_in();
int start_last_quick_select();
int error;
private:
/* The only field in temporary table */
class Item_rowid : public Item_str_func
{
TABLE *head; /* source table */
public:
Item_rowid(TABLE *table) : head(table)
{
max_length= table->file->ref_length;
collation.set(&my_charset_bin);
};
const char *func_name() const { return "rowid"; }
bool const_item() const { return 0; }
String *val_str(String *);
void fix_length_and_dec()
{}
};
/* Check if record has been processed and save it if it wasn't */
inline int put_record();
/* Check if record has been processed without saving it */
inline int check_record();
/* If true, check_record_in does't store ROWIDs it is passed. */
bool dont_save;
THD *thd;
TABLE *head; /* source table */
TABLE *temp_table; /* temp. table used for values storage */
TMP_TABLE_PARAM tmp_table_param; /* temp. table creation parameters */
Item_rowid *rowid_item; /* the only field in temp. table */
List<Item> fields; /* temp. table fields list
(the only element is rowid_item) */
ORDER order; /* key for temp. table (rowid_item) */
}; };
class QUICK_SELECT_DESC: public QUICK_SELECT /*
Index merge quick select.
It is implemented as a container for several QUICK_RANGE_SELECTs.
*/
class QUICK_INDEX_MERGE_SELECT : public QUICK_SELECT_I
{
public:
QUICK_INDEX_MERGE_SELECT(THD *thd, TABLE *table);
~QUICK_INDEX_MERGE_SELECT();
int init();
void reset(void);
int get_next();
bool reverse_sorted() { return false; }
bool unique_key_range() { return false; }
int get_type() { return QS_TYPE_INDEX_MERGE; }
bool push_quick_back(QUICK_RANGE_SELECT *quick_sel_range);
/* range quick selects this index_merge read consists of */
List<QUICK_RANGE_SELECT> quick_selects;
/* quick select which is currently used for rows retrieval */
List_iterator_fast<QUICK_RANGE_SELECT> cur_quick_it;
QUICK_RANGE_SELECT* cur_quick_select;
/*
Last element in quick_selects list.
INDEX_MERGE::start_last_quick_select is called before retrieving
rows for it.
*/
QUICK_RANGE_SELECT* last_quick_select;
/*
Used to keep track of what records have been already passed to output
when doing index_merge access (NULL means no index_merge)
*/
INDEX_MERGE index_merge;
MEM_ROOT alloc;
};
class QUICK_SELECT_DESC: public QUICK_RANGE_SELECT
{ {
public: public:
QUICK_SELECT_DESC(QUICK_SELECT *q, uint used_key_parts); QUICK_SELECT_DESC(QUICK_RANGE_SELECT *q, uint used_key_parts);
int get_next(); int get_next();
bool reverse_sorted() { return 1; } bool reverse_sorted() { return 1; }
int get_type() { return QS_TYPE_RANGE_DESC; }
private: private:
int cmp_prev(QUICK_RANGE *range); int cmp_prev(QUICK_RANGE *range);
bool range_reads_after_key(QUICK_RANGE *range); bool range_reads_after_key(QUICK_RANGE *range);
...@@ -114,7 +271,7 @@ private: ...@@ -114,7 +271,7 @@ private:
class SQL_SELECT :public Sql_alloc { class SQL_SELECT :public Sql_alloc {
public: public:
QUICK_SELECT *quick; // If quick-select used QUICK_SELECT_I *quick; // If quick-select used
COND *cond; // where condition COND *cond; // where condition
TABLE *head; TABLE *head;
IO_CACHE file; // Positions to used records IO_CACHE file; // Positions to used records
...@@ -134,7 +291,7 @@ class SQL_SELECT :public Sql_alloc { ...@@ -134,7 +291,7 @@ class SQL_SELECT :public Sql_alloc {
ha_rows limit, bool force_quick_range=0); ha_rows limit, bool force_quick_range=0);
}; };
QUICK_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table, QUICK_RANGE_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table,
struct st_table_ref *ref); struct st_table_ref *ref);
#endif #endif
...@@ -575,8 +575,8 @@ int THD::send_explain_fields(select_result *result) ...@@ -575,8 +575,8 @@ int THD::send_explain_fields(select_result *result)
item->maybe_null=1; item->maybe_null=1;
field_list.push_back(item=new Item_empty_string("key",NAME_LEN)); field_list.push_back(item=new Item_empty_string("key",NAME_LEN));
item->maybe_null=1; item->maybe_null=1;
field_list.push_back(item=new Item_return_int("key_len",3, field_list.push_back(item=new Item_empty_string("key_len",
MYSQL_TYPE_LONGLONG)); NAME_LEN*MAX_KEY));
item->maybe_null=1; item->maybe_null=1;
field_list.push_back(item=new Item_empty_string("ref", field_list.push_back(item=new Item_empty_string("ref",
NAME_LEN*MAX_REF_PARTS)); NAME_LEN*MAX_REF_PARTS));
......
...@@ -135,6 +135,12 @@ public: ...@@ -135,6 +135,12 @@ public:
last= &first; last= &first;
return tmp->info; return tmp->info;
} }
inline void concat(base_list *list)
{
*last= list->first;
last= list->last;
elements+= list->elements;
}
inline list_node* last_node() { return *last; } inline list_node* last_node() { return *last; }
inline list_node* first_node() { return first;} inline list_node* first_node() { return first;}
inline void *head() { return first->info; } inline void *head() { return first->info; }
...@@ -255,6 +261,7 @@ public: ...@@ -255,6 +261,7 @@ public:
} }
empty(); empty();
} }
inline void concat(List<T> *list) { base_list::concat(list); }
}; };
......
...@@ -32,7 +32,8 @@ ...@@ -32,7 +32,8 @@
const char *join_type_str[]={ "UNKNOWN","system","const","eq_ref","ref", const char *join_type_str[]={ "UNKNOWN","system","const","eq_ref","ref",
"MAYBE_REF","ALL","range","index","fulltext", "MAYBE_REF","ALL","range","index","fulltext",
"ref_or_null","unique_subquery","index_subquery" "ref_or_null","unique_subquery","index_subquery",
"index_merge"
}; };
const key_map key_map_empty(0); const key_map key_map_empty(0);
...@@ -116,7 +117,7 @@ static int join_read_next_same_or_null(READ_RECORD *info); ...@@ -116,7 +117,7 @@ static int join_read_next_same_or_null(READ_RECORD *info);
static COND *make_cond_for_table(COND *cond,table_map table, static COND *make_cond_for_table(COND *cond,table_map table,
table_map used_table); table_map used_table);
static Item* part_of_refkey(TABLE *form,Field *field); static Item* part_of_refkey(TABLE *form,Field *field);
static uint find_shortest_key(TABLE *table, const key_map *usable_keys); uint find_shortest_key(TABLE *table, const key_map *usable_keys);
static bool test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order, static bool test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,
ha_rows select_limit, bool no_changes); ha_rows select_limit, bool no_changes);
static int create_sort_index(THD *thd, JOIN *join, ORDER *order, static int create_sort_index(THD *thd, JOIN *join, ORDER *order,
...@@ -3417,7 +3418,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) ...@@ -3417,7 +3418,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
with key reading */ with key reading */
if (tab->needed_reg.is_clear_all() && tab->type != JT_EQ_REF if (tab->needed_reg.is_clear_all() && tab->type != JT_EQ_REF
&& tab->type != JT_FT && (tab->type != JT_REF || && tab->type != JT_FT && (tab->type != JT_REF ||
(uint) tab->ref.key == tab->quick->index)) (uint) tab->ref.key == tab->quick->index))
{ {
sel->quick=tab->quick; // Use value from get_quick_... sel->quick=tab->quick; // Use value from get_quick_...
sel->quick_keys.clear_all(); sel->quick_keys.clear_all();
...@@ -6745,7 +6746,7 @@ static int test_if_order_by_key(ORDER *order, TABLE *table, uint idx, ...@@ -6745,7 +6746,7 @@ static int test_if_order_by_key(ORDER *order, TABLE *table, uint idx,
return reverse; return reverse;
} }
static uint find_shortest_key(TABLE *table, const key_map *usable_keys) uint find_shortest_key(TABLE *table, const key_map *usable_keys)
{ {
uint min_length= (uint) ~0; uint min_length= (uint) ~0;
uint best= MAX_KEY; uint best= MAX_KEY;
...@@ -6879,6 +6880,9 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit, ...@@ -6879,6 +6880,9 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
} }
else if (select && select->quick) // Range found by opt_range else if (select && select->quick) // Range found by opt_range
{ {
/* assume results are not ordered when index merge is used */
if (select->quick->get_type() == QUICK_SELECT_I::QS_TYPE_INDEX_MERGE)
DBUG_RETURN(0);
ref_key= select->quick->index; ref_key= select->quick->index;
ref_key_parts= select->quick->used_key_parts; ref_key_parts= select->quick->used_key_parts;
} }
...@@ -6913,6 +6917,10 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit, ...@@ -6913,6 +6917,10 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
} }
else else
{ {
/*
We have verified above that select->quick is not
index_merge quick select.
*/
select->quick->index= new_ref_key; select->quick->index= new_ref_key;
select->quick->init(); select->quick->init();
} }
...@@ -6934,10 +6942,13 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit, ...@@ -6934,10 +6942,13 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
*/ */
if (!select->quick->reverse_sorted()) if (!select->quick->reverse_sorted())
{ {
if (table->file->index_flags(ref_key) & HA_NOT_READ_PREFIX_LAST) if (table->file->index_flags(ref_key) & HA_NOT_READ_PREFIX_LAST ||
(select->quick->get_type() ==
QUICK_SELECT_I::QS_TYPE_INDEX_MERGE))
DBUG_RETURN(0); // Use filesort DBUG_RETURN(0); // Use filesort
// ORDER BY range_key DESC
QUICK_SELECT_DESC *tmp=new QUICK_SELECT_DESC(select->quick, // ORDER BY range_key DESC
QUICK_SELECT_DESC *tmp=new QUICK_SELECT_DESC((QUICK_RANGE_SELECT*)(select->quick),
used_key_parts); used_key_parts);
if (!tmp || tmp->error) if (!tmp || tmp->error)
{ {
...@@ -7079,8 +7090,11 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order, ...@@ -7079,8 +7090,11 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order,
{ {
select->quick=tab->quick; select->quick=tab->quick;
tab->quick=0; tab->quick=0;
/* We can only use 'Only index' if quick key is same as ref_key */ /*
if (table->key_read && (uint) tab->ref.key != select->quick->index) We can only use 'Only index' if quick key is same as ref_key
and in index_merge 'Only index' cannot be used
*/
if (table->key_read && ((uint) tab->ref.key != select->quick->index))
{ {
table->key_read=0; table->key_read=0;
table->file->extra(HA_EXTRA_NO_KEYREAD); table->file->extra(HA_EXTRA_NO_KEYREAD);
...@@ -8880,12 +8894,15 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, ...@@ -8880,12 +8894,15 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
JOIN_TAB *tab=join->join_tab+i; JOIN_TAB *tab=join->join_tab+i;
TABLE *table=tab->table; TABLE *table=tab->table;
char buff[512],*buff_ptr=buff; char buff[512],*buff_ptr=buff;
char buff1[512], buff2[512]; char buff1[512], buff2[512], buff3[512];
char keylen_str_buf[64];
char derived_name[64]; char derived_name[64];
String tmp1(buff1,sizeof(buff1),cs); String tmp1(buff1,sizeof(buff1),cs);
String tmp2(buff2,sizeof(buff2),cs); String tmp2(buff2,sizeof(buff2),cs);
String tmp3(buff3,sizeof(buff3),cs);
tmp1.length(0); tmp1.length(0);
tmp2.length(0); tmp2.length(0);
tmp3.length(0);
item_list.empty(); item_list.empty();
item_list.push_back(new Item_int((int32) item_list.push_back(new Item_int((int32)
...@@ -8894,7 +8911,13 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, ...@@ -8894,7 +8911,13 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
strlen(join->select_lex->type), strlen(join->select_lex->type),
cs)); cs));
if (tab->type == JT_ALL && tab->select && tab->select->quick) if (tab->type == JT_ALL && tab->select && tab->select->quick)
tab->type= JT_RANGE; {
if (tab->select->quick->get_type() ==
QUICK_SELECT_I::QS_TYPE_INDEX_MERGE)
tab->type = JT_INDEX_MERGE;
else
tab->type = JT_RANGE;
}
if (table->derived_select_number) if (table->derived_select_number)
{ {
/* Derived table name generation */ /* Derived table name generation */
...@@ -8930,10 +8953,14 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, ...@@ -8930,10 +8953,14 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
if (tab->ref.key_parts) if (tab->ref.key_parts)
{ {
KEY *key_info=table->key_info+ tab->ref.key; KEY *key_info=table->key_info+ tab->ref.key;
register uint length;
item_list.push_back(new Item_string(key_info->name, item_list.push_back(new Item_string(key_info->name,
strlen(key_info->name), strlen(key_info->name),
system_charset_info)); system_charset_info));
item_list.push_back(new Item_int((int32) tab->ref.key_length)); length= longlong2str(tab->ref.key_length, keylen_str_buf, 10) -
keylen_str_buf;
item_list.push_back(new Item_string(keylen_str_buf, length,
system_charset_info));
for (store_key **ref=tab->ref.key_copy ; *ref ; ref++) for (store_key **ref=tab->ref.key_copy ; *ref ; ref++)
{ {
if (tmp2.length()) if (tmp2.length())
...@@ -8945,18 +8972,60 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, ...@@ -8945,18 +8972,60 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
else if (tab->type == JT_NEXT) else if (tab->type == JT_NEXT)
{ {
KEY *key_info=table->key_info+ tab->index; KEY *key_info=table->key_info+ tab->index;
register uint length;
item_list.push_back(new Item_string(key_info->name, item_list.push_back(new Item_string(key_info->name,
strlen(key_info->name),cs)); strlen(key_info->name),cs));
item_list.push_back(new Item_int((int32) key_info->key_length)); length= longlong2str(key_info->key_length, keylen_str_buf, 10) -
keylen_str_buf;
item_list.push_back(new Item_string(keylen_str_buf,
length,
system_charset_info));
item_list.push_back(item_null); item_list.push_back(item_null);
} }
else if (tab->select && tab->select->quick) else if (tab->select && tab->select->quick)
{ {
KEY *key_info=table->key_info+ tab->select->quick->index; if (tab->select->quick->get_type() ==
item_list.push_back(new Item_string(key_info->name, QUICK_SELECT_I::QS_TYPE_INDEX_MERGE)
strlen(key_info->name),cs)); {
item_list.push_back(new Item_int((int32) tab->select->quick-> QUICK_INDEX_MERGE_SELECT *quick_imerge=
max_used_key_length)); (QUICK_INDEX_MERGE_SELECT*)tab->select->quick;
QUICK_RANGE_SELECT *quick;
List_iterator_fast<QUICK_RANGE_SELECT> it(quick_imerge->
quick_selects);
while ((quick= it++))
{
KEY *key_info= table->key_info + quick->index;
register uint length;
if (tmp3.length())
tmp3.append(',');
tmp3.append(key_info->name);
if (tmp2.length())
tmp2.append(',');
length= longlong2str(quick->max_used_key_length, keylen_str_buf,
10) -
keylen_str_buf;
tmp2.append(keylen_str_buf, length);
}
}
else
{
KEY *key_info= table->key_info + tab->select->quick->index;
register uint length;
tmp3.append(key_info->name);
length= longlong2str(tab->select->quick->max_used_key_length,
keylen_str_buf, 10) -
keylen_str_buf;
tmp2.append(keylen_str_buf, length);
}
item_list.push_back(new Item_string(tmp3.ptr(),tmp3.length(),cs));
item_list.push_back(new Item_string(tmp2.ptr(),tmp2.length(),cs));
item_list.push_back(item_null); item_list.push_back(item_null);
} }
else else
......
...@@ -76,7 +76,7 @@ typedef struct st_join_cache { ...@@ -76,7 +76,7 @@ typedef struct st_join_cache {
enum join_type { JT_UNKNOWN,JT_SYSTEM,JT_CONST,JT_EQ_REF,JT_REF,JT_MAYBE_REF, enum join_type { JT_UNKNOWN,JT_SYSTEM,JT_CONST,JT_EQ_REF,JT_REF,JT_MAYBE_REF,
JT_ALL, JT_RANGE, JT_NEXT, JT_FT, JT_REF_OR_NULL, JT_ALL, JT_RANGE, JT_NEXT, JT_FT, JT_REF_OR_NULL,
JT_UNIQUE_SUBQUERY, JT_INDEX_SUBQUERY}; JT_UNIQUE_SUBQUERY, JT_INDEX_SUBQUERY, JT_INDEX_MERGE};
class JOIN; class JOIN;
...@@ -85,7 +85,7 @@ typedef struct st_join_table { ...@@ -85,7 +85,7 @@ typedef struct st_join_table {
KEYUSE *keyuse; /* pointer to first used key */ KEYUSE *keyuse; /* pointer to first used key */
SQL_SELECT *select; SQL_SELECT *select;
COND *select_cond; COND *select_cond;
QUICK_SELECT *quick; QUICK_SELECT_I *quick;
Item *on_expr; Item *on_expr;
const char *info; const char *info;
byte *null_ref_key; byte *null_ref_key;
...@@ -311,6 +311,7 @@ void copy_fields(TMP_TABLE_PARAM *param); ...@@ -311,6 +311,7 @@ void copy_fields(TMP_TABLE_PARAM *param);
void copy_funcs(Item **func_ptr); void copy_funcs(Item **func_ptr);
bool create_myisam_from_heap(THD *thd, TABLE *table, TMP_TABLE_PARAM *param, bool create_myisam_from_heap(THD *thd, TABLE *table, TMP_TABLE_PARAM *param,
int error, bool ignore_last_dupp_error); int error, bool ignore_last_dupp_error);
uint find_shortest_key(TABLE *table, key_map usable_keys);
/* functions from opt_sum.cc */ /* functions from opt_sum.cc */
int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds); int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds);
......
...@@ -181,9 +181,39 @@ TEST_join(JOIN *join) ...@@ -181,9 +181,39 @@ TEST_join(JOIN *join)
" quick select checked for each record (keys: %s)\n", " quick select checked for each record (keys: %s)\n",
tab->select->quick_keys.print(buf)); tab->select->quick_keys.print(buf));
else if (tab->select->quick) else if (tab->select->quick)
fprintf(DBUG_FILE," quick select used on key %s, length: %d\n", {
int quick_type= tab->select->quick->get_type();
if ((quick_type == QUICK_SELECT_I::QS_TYPE_RANGE) ||
(quick_type == QUICK_SELECT_I::QS_TYPE_RANGE_DESC))
{
fprintf(DBUG_FILE,
" quick select used on key %s, length: %d\n",
form->key_info[tab->select->quick->index].name, form->key_info[tab->select->quick->index].name,
tab->select->quick->max_used_key_length); tab->select->quick->max_used_key_length);
}
else if (quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_MERGE)
{
QUICK_INDEX_MERGE_SELECT *quick_imerge=
(QUICK_INDEX_MERGE_SELECT*)tab->select->quick;
QUICK_RANGE_SELECT *quick;
fprintf(DBUG_FILE,
" index_merge quick select used\n");
List_iterator_fast<QUICK_RANGE_SELECT> it(quick_imerge->quick_selects);
while ((quick = it++))
{
fprintf(DBUG_FILE,
" range quick select: key %s, length: %d\n",
form->key_info[quick->index].name,
quick->max_used_key_length);
}
}
else
{
fprintf(DBUG_FILE,
" quick select of unknown nature used\n");
}
}
else else
VOID(fputs(" select used\n",DBUG_FILE)); VOID(fputs(" select used\n",DBUG_FILE));
} }
......
...@@ -117,7 +117,7 @@ int st_select_lex_unit::prepare(THD *thd, select_result *sel_result, ...@@ -117,7 +117,7 @@ int st_select_lex_unit::prepare(THD *thd, select_result *sel_result,
{ {
SELECT_LEX *lex_select_save= thd->lex->current_select; SELECT_LEX *lex_select_save= thd->lex->current_select;
SELECT_LEX *select_cursor,*sl; SELECT_LEX *select_cursor,*sl;
DBUG_ENTER("st_select_lex_unit::prepare"); DBUG_ENTER("st_select_lex_unit::prepare");
/* /*
result object should be reassigned even if preparing already done for result object should be reassigned even if preparing already done for
......
...@@ -177,10 +177,18 @@ int mysql_update(THD *thd, ...@@ -177,10 +177,18 @@ int mysql_update(THD *thd,
init_ftfuncs(thd, &thd->lex->select_lex, 1); init_ftfuncs(thd, &thd->lex->select_lex, 1);
/* Check if we are modifying a key that we are used to search with */ /* Check if we are modifying a key that we are used to search with */
if (select && select->quick) if (select && select->quick)
used_key_is_modified= (!select->quick->unique_key_range() && {
check_if_key_used(table, if (select->quick->get_type() != QUICK_SELECT_I::QS_TYPE_INDEX_MERGE)
(used_index=select->quick->index), {
fields)); used_index= select->quick->index;
used_key_is_modified= (!select->quick->unique_key_range() &&
check_if_key_used(table,used_index,fields));
}
else
{
used_key_is_modified= true;
}
}
else if ((used_index=table->file->key_used_on_scan) < MAX_KEY) else if ((used_index=table->file->key_used_on_scan) < MAX_KEY)
used_key_is_modified=check_if_key_used(table, used_index, fields); used_key_is_modified=check_if_key_used(table, used_index, fields);
else else
...@@ -698,8 +706,26 @@ static bool safe_update_on_fly(JOIN_TAB *join_tab, List<Item> *fields) ...@@ -698,8 +706,26 @@ static bool safe_update_on_fly(JOIN_TAB *join_tab, List<Item> *fields)
case JT_ALL: case JT_ALL:
/* If range search on index */ /* If range search on index */
if (join_tab->quick) if (join_tab->quick)
return !check_if_key_used(table, join_tab->quick->index, {
*fields); if (join_tab->quick->get_type() != QUICK_SELECT_I::QS_TYPE_INDEX_MERGE)
{
return !check_if_key_used(table,join_tab->quick->index,*fields);
}
else
{
QUICK_INDEX_MERGE_SELECT *qsel_imerge=
(QUICK_INDEX_MERGE_SELECT*)(join_tab->quick);
List_iterator_fast<QUICK_RANGE_SELECT> it(qsel_imerge->quick_selects);
QUICK_RANGE_SELECT *quick;
while ((quick= it++))
{
if (check_if_key_used(table, quick->index, *fields))
return 0;
}
return 1;
}
}
/* If scanning in clustered key */ /* If scanning in clustered key */
if ((table->file->table_flags() & HA_PRIMARY_KEY_IN_READ_INDEX) && if ((table->file->table_flags() & HA_PRIMARY_KEY_IN_READ_INDEX) &&
table->primary_key < MAX_KEY) table->primary_key < MAX_KEY)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment