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
......
This diff is collapsed.
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
This diff is collapsed.
...@@ -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; int cmp_next(QUICK_RANGE *range);
double read_time; public:
QUICK_RANGE_SELECT(THD *thd, TABLE *table,uint index_arg,bool no_alloc=0,
MEM_ROOT *parent_alloc=NULL);
~QUICK_RANGE_SELECT();
QUICK_SELECT(THD *thd, TABLE *table,uint index_arg,bool no_alloc=0);
virtual ~QUICK_SELECT();
void reset(void) { next=0; it.rewind(); } void reset(void) { next=0; it.rewind(); }
int init() { return error=file->index_init(index); } int init();
virtual int get_next(); int get_next();
virtual bool reverse_sorted() { return 0; } bool reverse_sorted() { return 0; }
int cmp_next(QUICK_RANGE *range);
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,
...@@ -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 // ORDER BY range_key DESC
QUICK_SELECT_DESC *tmp=new QUICK_SELECT_DESC(select->quick, 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));
} }
......
...@@ -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)
{
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() && used_key_is_modified= (!select->quick->unique_key_range() &&
check_if_key_used(table, check_if_key_used(table,used_index,fields));
(used_index=select->quick->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