Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
M
mariadb
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
mariadb
Commits
30b3c3bf
Commit
30b3c3bf
authored
Nov 21, 2003
by
sergefp@mysql.com
Browse files
Options
Browse Files
Download
Plain Diff
Merged, most likely needs post-merge fix
parents
5e46a059
4016048d
Changes
16
Hide whitespace changes
Inline
Side-by-side
Showing
16 changed files
with
1930 additions
and
154 deletions
+1930
-154
BitKeeper/etc/logging_ok
BitKeeper/etc/logging_ok
+1
-0
mysql-test/r/index_merge.result
mysql-test/r/index_merge.result
+312
-0
mysql-test/r/index_merge_innodb.result
mysql-test/r/index_merge_innodb.result
+55
-0
mysql-test/t/index_merge.test
mysql-test/t/index_merge.test
+267
-0
mysql-test/t/index_merge_innodb.test
mysql-test/t/index_merge_innodb.test
+54
-0
sql/opt_ft.cc
sql/opt_ft.cc
+1
-1
sql/opt_ft.h
sql/opt_ft.h
+4
-3
sql/opt_range.cc
sql/opt_range.cc
+898
-102
sql/opt_range.h
sql/opt_range.h
+175
-18
sql/sql_class.cc
sql/sql_class.cc
+2
-2
sql/sql_list.h
sql/sql_list.h
+7
-0
sql/sql_select.cc
sql/sql_select.cc
+87
-18
sql/sql_select.h
sql/sql_select.h
+3
-2
sql/sql_test.cc
sql/sql_test.cc
+31
-1
sql/sql_union.cc
sql/sql_union.cc
+1
-1
sql/sql_update.cc
sql/sql_update.cc
+32
-6
No files found.
BitKeeper/etc/logging_ok
View file @
30b3c3bf
...
...
@@ -103,6 +103,7 @@ peter@mysql.com
peterg@mysql.com
pgulutzan@linux.local
pmartin@build.mysql2.com
psergey@psergey.(none)
ram@deer.(none)
ram@gw.mysql.r18.ru
ram@gw.udmsearch.izhnet.ru
...
...
mysql-test/r/index_merge.result
0 → 100644
View file @
30b3c3bf
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;
mysql-test/r/index_merge_innodb.result
0 → 100644
View file @
30b3c3bf
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;
mysql-test/t/index_merge.test
0 → 100644
View file @
30b3c3bf
#
# 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
;
mysql-test/t/index_merge_innodb.test
0 → 100644
View file @
30b3c3bf
#
# 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
;
sql/opt_ft.cc
View file @
30b3c3bf
...
...
@@ -26,7 +26,7 @@
** 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
)
{
if
(
tab
->
type
==
JT_FT
)
...
...
sql/opt_ft.h
View file @
30b3c3bf
...
...
@@ -24,18 +24,19 @@
#pragma interface
/* gcc class implementation */
#endif
class
FT_SELECT
:
public
QUICK_SELECT
{
class
FT_SELECT
:
public
QUICK_
RANGE_
SELECT
{
public:
TABLE_REF
*
ref
;
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
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
);
#endif
sql/opt_range.cc
View file @
30b3c3bf
...
...
@@ -267,14 +267,17 @@ public:
SEL_ARG
*
clone_tree
();
};
class
SEL_IMERGE
;
class
SEL_TREE
:
public
Sql_alloc
{
public:
enum
Type
{
IMPOSSIBLE
,
ALWAYS
,
MAYBE
,
KEY
,
KEY_SMALLER
}
type
;
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
];
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,
char
*
min_key
,
uint
min_key_flag
,
char
*
max_key
,
uint
max_key_flag
);
static
QUICK_SELECT
*
get_quick_select
(
PARAM
*
param
,
uint
index
,
SEL_ARG
*
key_tree
);
QUICK_RANGE_SELECT
*
get_quick_select
(
PARAM
*
param
,
uint
index
,
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
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
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
);
...
...
@@ -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_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_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
,
char
*
max_key
,
uint
max_key_flag
);
static
bool
eq_tree
(
SEL_ARG
*
a
,
SEL_ARG
*
b
);
static
SEL_ARG
null_element
(
SEL_ARG
::
IMPOSSIBLE
);
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
...
...
@@ -379,11 +609,19 @@ SQL_SELECT::~SQL_SELECT()
#undef index // Fix for Unixware 7
QUICK_SELECT
::
QUICK_SELECT
(
THD
*
thd
,
TABLE
*
table
,
uint
key_nr
,
bool
no_alloc
)
:
dont_free
(
0
),
error
(
0
),
index
(
key_nr
),
max_used_key_length
(
0
),
used_key_parts
(
0
),
head
(
table
),
it
(
ranges
),
range
(
0
)
QUICK_SELECT_I
::
QUICK_SELECT_I
()
:
max_used_key_length
(
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
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)
}
else
bzero
((
char
*
)
&
alloc
,
sizeof
(
alloc
));
file
=
head
->
file
;
record
=
head
->
record
[
0
];
init
();
file
=
head
->
file
;
record
=
head
->
record
[
0
];
}
int
QUICK_RANGE_SELECT
::
init
()
{
return
(
error
=
file
->
index_init
(
index
));
}
QUICK_
SELECT
::~
QUICK
_SELECT
()
QUICK_
RANGE_SELECT
::~
QUICK_RANGE
_SELECT
()
{
if
(
!
dont_free
)
{
...
...
@@ -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
()
:
min_key
(
0
),
max_key
(
0
),
min_length
(
0
),
max_length
(
0
),
flag
(
NO_MIN_RANGE
|
NO_MAX_RANGE
)
...
...
@@ -598,6 +876,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
uint
basflag
;
uint
idx
;
double
scan_time
;
QUICK_INDEX_MERGE_SELECT
*
quick_imerge
;
DBUG_ENTER
(
"test_quick_select"
);
DBUG_PRINT
(
"enter"
,(
"keys_to_use: %lu prev_tables: %lu const_tables: %lu"
,
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,
read_time
=
(
double
)
HA_POS_ERROR
;
}
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
;
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
);
found_records
=
check_quick_select
(
&
param
,
idx
,
*
key
);
if
(
found_records
!=
HA_POS_ERROR
&&
found_records
>
2
&&
head
->
used_keys
.
is_set
(
keynr
)
&&
(
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
;
best_key
=
key
;
}
}
}
if
(
best_key
&&
records
)
{
if
((
quick
=
get_quick_select
(
&
param
,(
uint
)
(
best_key
-
tree
->
keys
),
*
best_key
)))
{
quick
->
records
=
records
;
quick
->
read_time
=
read_time
;
}
}
/*
It is possible to use a quick select (but maybe it would be slower
than 'all' table scan).
*/
SEL_ARG
**
best_key
=
0
;
ha_rows
found_records
;
double
found_read_time
=
read_time
;
if
(
!
get_quick_select_params
(
tree
,
param
,
needed_reg
,
head
,
true
,
&
found_read_time
,
&
found_records
,
&
best_key
))
{
/*
Ok, quick select is better than 'all' table scan and we have its
parameters, so construct it.
*/
read_time
=
found_read_time
;
records
=
found_records
;
if
((
quick
=
get_quick_select
(
&
param
,(
uint
)
(
best_key
-
tree
->
keys
),
*
best_key
))
&&
(
!
quick
->
init
()))
{
quick
->
records
=
records
;
quick
->
read_time
=
read_time
;
}
}
/*
btw, tree type SEL_TREE::INDEX_MERGE was not introduced
intentionally
*/
/* if no range select could be built, try using index_merge */
if
(
!
quick
&&
!
tree
->
merges
.
is_empty
())
{
DBUG_PRINT
(
"info"
,(
"No range reads possible,"
" trying to construct index_merge"
));
SEL_IMERGE
*
imerge
;
SEL_IMERGE
*
min_imerge
=
NULL
;
double
min_imerge_cost
=
DBL_MAX
;
ha_rows
min_imerge_records
;
List_iterator_fast
<
SEL_IMERGE
>
it
(
tree
->
merges
);
while
((
imerge
=
it
++
))
{
double
imerge_cost
=
0
;
ha_rows
imerge_total_records
=
0
;
double
tree_read_time
;
ha_rows
tree_records
;
imerge
->
best_keys
=
(
SEL_ARG
***
)
alloc_root
(
&
alloc
,
(
imerge
->
trees_next
-
imerge
->
trees
)
*
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
my_pthread_setspecific_ptr
(
THR_MALLOC
,
old_root
);
end:
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
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,
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 */
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,
}
sel_arg
->
part
=
(
uchar
)
key_part
->
part
;
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)
DBUG_RETURN
(
tree1
);
}
bool
trees_have_key
=
false
;
key_map
result_keys
=
0
;
/* Join the trees key per key */
SEL_ARG
**
key1
,
**
key2
,
**
end
;
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)
uint
flag
=
0
;
if
(
*
key1
||
*
key2
)
{
trees_have_key
=
true
;
if
(
*
key1
&&
!
(
*
key1
)
->
simple_key
())
flag
|=
CLONE_KEY1_MAYBE
;
if
(
*
key2
&&
!
(
*
key2
)
->
simple_key
())
...
...
@@ -1236,17 +1731,57 @@ tree_and(PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2)
if
((
*
key1
)
->
type
==
SEL_ARG
::
IMPOSSIBLE
)
{
tree1
->
type
=
SEL_TREE
::
IMPOSSIBLE
;
break
;
DBUG_RETURN
(
tree1
)
;
}
result_keys
|=
1
<<
(
key1
-
tree1
->
keys
);
#ifdef EXTRA_DEBUG
(
*
key1
)
->
test_use_count
(
*
key1
);
#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
);
}
/*
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
*
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
)
DBUG_RETURN
(
tree2
);
/* Join the trees key per key */
SEL_ARG
**
key1
,
**
key2
,
**
end
;
SEL_TREE
*
result
=
0
;
for
(
key1
=
tree1
->
keys
,
key2
=
tree2
->
keys
,
end
=
key1
+
param
->
keys
;
key1
!=
end
;
key1
++
,
key2
++
)
SEL_TREE
*
result
=
0
;
key_map
result_keys
=
0
;
if
(
sel_trees_can_be_ored
(
tree1
,
tree2
,
param
))
{
*
key1
=
key_or
(
*
key1
,
*
key2
);
if
(
*
key1
)
/* Join the trees key per key */
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
(
*
key1
)
->
test_use_count
(
*
key1
);
(
*
key1
)
->
test_use_count
(
*
key1
);
#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
);
...
...
@@ -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
** This uses it's own malloc tree
** The caller should call QUICK_SELCT::init for returned quick select
****************************************************************************/
static
QUICK_SELECT
*
get_quick_select
(
PARAM
*
param
,
uint
idx
,
SEL_ARG
*
key_tree
)
QUICK_RANGE_SELECT
*
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"
);
if
((
quick
=
new
QUICK_SELECT
(
param
->
thd
,
param
->
table
,
param
->
real_keynr
[
idx
])))
if
((
quick
=
new
QUICK_RANGE_SELECT
(
param
->
thd
,
param
->
table
,
param
->
real_keynr
[
idx
],
test
(
parent_alloc
),
parent_alloc
)))
{
if
(
quick
->
error
||
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)
else
{
quick
->
key_parts
=
(
KEY_PART
*
)
memdup_root
(
&
quick
->
alloc
,(
char
*
)
param
->
key
[
idx
],
sizeof
(
KEY_PART
)
*
param
->
table
->
key_info
[
param
->
real_keynr
[
idx
]].
key_parts
);
memdup_root
(
parent_alloc
?
parent_alloc
:
&
quick
->
alloc
,
(
char
*
)
param
->
key
[
idx
],
sizeof
(
KEY_PART
)
*
param
->
table
->
key_info
[
param
->
real_keynr
[
idx
]].
key_parts
);
}
}
DBUG_RETURN
(
quick
);
...
...
@@ -2300,9 +2880,8 @@ get_quick_select(PARAM *param,uint idx,SEL_ARG *key_tree)
/*
** Fix this to get all possible sub_ranges
*/
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
,
char
*
max_key
,
uint
max_key_flag
)
{
...
...
@@ -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
*/
bool
QUICK_SELECT
::
unique_key_range
()
bool
QUICK_
RANGE_
SELECT
::
unique_key_range
()
{
if
(
ranges
.
elements
==
1
)
{
...
...
@@ -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
****************************************************************************/
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
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_PART
*
key_part
;
uint
part
;
if
(
!
quick
)
return
0
;
/* no ranges found */
if
(
quick
->
init
())
{
delete
quick
;
return
0
;
}
if
(
cp_buffer_from_ref
(
ref
))
{
if
(
thd
->
is_fatal_error
)
...
...
@@ -2495,11 +3081,204 @@ err:
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 */
int
QUICK_SELECT
::
get_next
()
int
QUICK_
RANGE_
SELECT
::
get_next
()
{
DBUG_ENTER
(
"get_next"
);
DBUG_ENTER
(
"
QUICK_RANGE_SELECT::
get_next"
);
for
(;;)
{
...
...
@@ -2586,7 +3365,7 @@ int QUICK_SELECT::get_next()
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
)
return
0
;
/* key can't be to large */
...
...
@@ -2627,8 +3406,9 @@ int QUICK_SELECT::cmp_next(QUICK_RANGE *range_arg)
for now, this seems to work right at least.
*/
QUICK_SELECT_DESC
::
QUICK_SELECT_DESC
(
QUICK_SELECT
*
q
,
uint
used_key_parts
)
:
QUICK_SELECT
(
*
q
),
rev_it
(
rev_ranges
)
QUICK_SELECT_DESC
::
QUICK_SELECT_DESC
(
QUICK_RANGE_SELECT
*
q
,
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
;
QUICK_RANGE
*
r
;
...
...
@@ -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
;
char
buf
[
MAX_KEY
/
8
+
1
];
...
...
sql/opt_range.h
View file @
30b3c3bf
...
...
@@ -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:
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
;
public:
int
error
;
uint
index
,
max_used_key_length
,
used_key_parts
;
TABLE
*
head
;
handler
*
file
;
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_iterator
<
QUICK_RANGE
>
it
;
QUICK_RANGE
*
range
;
MEM_ROOT
alloc
;
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
;
}
KEY_PART
*
key_parts
;
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
();
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:
QUICK_SELECT_DESC
(
QUICK_SELECT
*
q
,
uint
used_key_parts
);
QUICK_SELECT_DESC
(
QUICK_
RANGE_
SELECT
*
q
,
uint
used_key_parts
);
int
get_next
();
bool
reverse_sorted
()
{
return
1
;
}
int
get_type
()
{
return
QS_TYPE_RANGE_DESC
;
}
private:
int
cmp_prev
(
QUICK_RANGE
*
range
);
bool
range_reads_after_key
(
QUICK_RANGE
*
range
);
...
...
@@ -114,7 +271,7 @@ private:
class
SQL_SELECT
:
public
Sql_alloc
{
public:
QUICK_SELECT
*
quick
;
// If quick-select used
QUICK_SELECT
_I
*
quick
;
// If quick-select used
COND
*
cond
;
// where condition
TABLE
*
head
;
IO_CACHE
file
;
// Positions to used records
...
...
@@ -134,7 +291,7 @@ class SQL_SELECT :public Sql_alloc {
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
);
#endif
sql/sql_class.cc
View file @
30b3c3bf
...
...
@@ -575,8 +575,8 @@ int THD::send_explain_fields(select_result *result)
item
->
maybe_null
=
1
;
field_list
.
push_back
(
item
=
new
Item_empty_string
(
"key"
,
NAME_LEN
));
item
->
maybe_null
=
1
;
field_list
.
push_back
(
item
=
new
Item_
return_int
(
"key_len"
,
3
,
MYSQL_TYPE_LONGLONG
));
field_list
.
push_back
(
item
=
new
Item_
empty_string
(
"key_len"
,
NAME_LEN
*
MAX_KEY
));
item
->
maybe_null
=
1
;
field_list
.
push_back
(
item
=
new
Item_empty_string
(
"ref"
,
NAME_LEN
*
MAX_REF_PARTS
));
...
...
sql/sql_list.h
View file @
30b3c3bf
...
...
@@ -135,6 +135,12 @@ public:
last
=
&
first
;
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
*
first_node
()
{
return
first
;}
inline
void
*
head
()
{
return
first
->
info
;
}
...
...
@@ -255,6 +261,7 @@ public:
}
empty
();
}
inline
void
concat
(
List
<
T
>
*
list
)
{
base_list
::
concat
(
list
);
}
};
...
...
sql/sql_select.cc
View file @
30b3c3bf
...
...
@@ -32,7 +32,8 @@
const
char
*
join_type_str
[]
=
{
"UNKNOWN"
,
"system"
,
"const"
,
"eq_ref"
,
"ref"
,
"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
);
...
...
@@ -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
,
table_map
used_table
);
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
,
ha_rows
select_limit
,
bool
no_changes
);
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)
with key reading */
if
(
tab
->
needed_reg
.
is_clear_all
()
&&
tab
->
type
!=
JT_EQ_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_keys
.
clear_all
();
...
...
@@ -6745,7 +6746,7 @@ static int test_if_order_by_key(ORDER *order, TABLE *table, uint idx,
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
best
=
MAX_KEY
;
...
...
@@ -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
{
/* 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_parts
=
select
->
quick
->
used_key_parts
;
}
...
...
@@ -6913,6 +6917,10 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
}
else
{
/*
We have verified above that select->quick is not
index_merge quick select.
*/
select
->
quick
->
index
=
new_ref_key
;
select
->
quick
->
init
();
}
...
...
@@ -6934,10 +6942,13 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
*/
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
// 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
);
if
(
!
tmp
||
tmp
->
error
)
{
...
...
@@ -7079,8 +7090,11 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order,
{
select
->
quick
=
tab
->
quick
;
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
->
file
->
extra
(
HA_EXTRA_NO_KEYREAD
);
...
...
@@ -8880,12 +8894,15 @@ 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
buff1
[
512
],
buff2
[
512
];
char
buff1
[
512
],
buff2
[
512
],
buff3
[
512
];
char
keylen_str_buf
[
64
];
char
derived_name
[
64
];
String
tmp1
(
buff1
,
sizeof
(
buff1
),
cs
);
String
tmp2
(
buff2
,
sizeof
(
buff2
),
cs
);
String
tmp3
(
buff3
,
sizeof
(
buff3
),
cs
);
tmp1
.
length
(
0
);
tmp2
.
length
(
0
);
tmp3
.
length
(
0
);
item_list
.
empty
();
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,
strlen
(
join
->
select_lex
->
type
),
cs
));
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
)
{
/* Derived table name generation */
...
...
@@ -8930,10 +8953,14 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
if
(
tab
->
ref
.
key_parts
)
{
KEY
*
key_info
=
table
->
key_info
+
tab
->
ref
.
key
;
register
uint
length
;
item_list
.
push_back
(
new
Item_string
(
key_info
->
name
,
strlen
(
key_info
->
name
),
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
++
)
{
if
(
tmp2
.
length
())
...
...
@@ -8945,18 +8972,60 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
else
if
(
tab
->
type
==
JT_NEXT
)
{
KEY
*
key_info
=
table
->
key_info
+
tab
->
index
;
register
uint
length
;
item_list
.
push_back
(
new
Item_string
(
key_info
->
name
,
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
);
}
else
if
(
tab
->
select
&&
tab
->
select
->
quick
)
{
KEY
*
key_info
=
table
->
key_info
+
tab
->
select
->
quick
->
index
;
item_list
.
push_back
(
new
Item_string
(
key_info
->
name
,
strlen
(
key_info
->
name
),
cs
));
item_list
.
push_back
(
new
Item_int
((
int32
)
tab
->
select
->
quick
->
max_used_key_length
));
if
(
tab
->
select
->
quick
->
get_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
;
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
);
}
else
...
...
sql/sql_select.h
View file @
30b3c3bf
...
...
@@ -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
,
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
;
...
...
@@ -85,7 +85,7 @@ typedef struct st_join_table {
KEYUSE
*
keyuse
;
/* pointer to first used key */
SQL_SELECT
*
select
;
COND
*
select_cond
;
QUICK_SELECT
*
quick
;
QUICK_SELECT
_I
*
quick
;
Item
*
on_expr
;
const
char
*
info
;
byte
*
null_ref_key
;
...
...
@@ -311,6 +311,7 @@ void copy_fields(TMP_TABLE_PARAM *param);
void
copy_funcs
(
Item
**
func_ptr
);
bool
create_myisam_from_heap
(
THD
*
thd
,
TABLE
*
table
,
TMP_TABLE_PARAM
*
param
,
int
error
,
bool
ignore_last_dupp_error
);
uint
find_shortest_key
(
TABLE
*
table
,
key_map
usable_keys
);
/* functions from opt_sum.cc */
int
opt_sum_query
(
TABLE_LIST
*
tables
,
List
<
Item
>
&
all_fields
,
COND
*
conds
);
...
...
sql/sql_test.cc
View file @
30b3c3bf
...
...
@@ -181,9 +181,39 @@ TEST_join(JOIN *join)
" quick select checked for each record (keys: %s)
\n
"
,
tab
->
select
->
quick_keys
.
print
(
buf
));
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
,
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
VOID
(
fputs
(
" select used
\n
"
,
DBUG_FILE
));
}
...
...
sql/sql_union.cc
View file @
30b3c3bf
...
...
@@ -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
*
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
...
...
sql/sql_update.cc
View file @
30b3c3bf
...
...
@@ -177,10 +177,18 @@ int mysql_update(THD *thd,
init_ftfuncs
(
thd
,
&
thd
->
lex
->
select_lex
,
1
);
/* Check if we are modifying a key that we are used to search with */
if
(
select
&&
select
->
quick
)
used_key_is_modified
=
(
!
select
->
quick
->
unique_key_range
()
&&
check_if_key_used
(
table
,
(
used_index
=
select
->
quick
->
index
),
fields
));
{
if
(
select
->
quick
->
get_type
()
!=
QUICK_SELECT_I
::
QS_TYPE_INDEX_MERGE
)
{
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
)
used_key_is_modified
=
check_if_key_used
(
table
,
used_index
,
fields
);
else
...
...
@@ -698,8 +706,26 @@ static bool safe_update_on_fly(JOIN_TAB *join_tab, List<Item> *fields)
case
JT_ALL
:
/* If range search on index */
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
((
table
->
file
->
table_flags
()
&
HA_PRIMARY_KEY_IN_READ_INDEX
)
&&
table
->
primary_key
<
MAX_KEY
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment