Loading include/queues.h +1 −0 Original line number Diff line number Diff line Loading @@ -53,6 +53,7 @@ int resize_queue(QUEUE *queue, uint max_elements); void delete_queue(QUEUE *queue); void queue_insert(QUEUE *queue,byte *element); byte *queue_remove(QUEUE *queue,uint idx); #define queue_remove_all(queue) { (queue)->elements= 0; } void _downheap(QUEUE *queue,uint idx); void queue_fix(QUEUE *queue); #define is_queue_inited(queue) ((queue)->root != 0) Loading mysql-test/r/index_merge.result +52 −1 Original line number Diff line number Diff line Loading @@ -335,4 +335,55 @@ key1 key2 key3 key4 key5 key6 key7 key8 select count(*) from t0; count(*) 1021 drop table t4; create table t4 (a int); insert into t4 values (1),(4),(3); set @save_join_buffer_size=@@join_buffer_size; set join_buffer_size= 4000; show variables like 'join_buffer_size'; Variable_name Value join_buffer_size 8228 explain select max(A.key1 + B.key1 + A.key2 + B.key2 + A.key3 + B.key3 + A.key4 + B.key4 + A.key5 + B.key5) from t0 as A force index(i1,i2), t0 as B force index (i1,i2) where (A.key1 < 500000 or A.key2 < 3) and (B.key1 < 500000 or B.key2 < 3); id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE A index_merge i1,i2 i1,i2 4,4 NULL 1016 Using sort_union(i1,i2); Using where 1 SIMPLE B index_merge i1,i2 i1,i2 4,4 NULL 1016 Using sort_union(i1,i2); Using where select max(A.key1 + B.key1 + A.key2 + B.key2 + A.key3 + B.key3 + A.key4 + B.key4 + A.key5 + B.key5) from t0 as A force index(i1,i2), t0 as B force index (i1,i2) where (A.key1 < 500000 or A.key2 < 3) and (B.key1 < 500000 or B.key2 < 3); max(A.key1 + B.key1 + A.key2 + B.key2 + A.key3 + B.key3 + A.key4 + B.key4 + A.key5 + B.key5) 10240 update t0 set key1=1; explain select max(A.key1 + B.key1 + A.key2 + B.key2 + A.key3 + B.key3 + A.key4 + B.key4 + A.key5 + B.key5) from t0 as A force index(i1,i2), t0 as B force index (i1,i2) where (A.key1 = 1 or A.key2 = 1) and (B.key1 = 1 or B.key2 = 1); id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE A index_merge i1,i2 i1,i2 4,4 NULL 1020 Using union(i1,i2); Using where 1 SIMPLE B index_merge i1,i2 i1,i2 4,4 NULL 1020 Using union(i1,i2); Using where select max(A.key1 + B.key1 + A.key2 + B.key2 + A.key3 + B.key3 + A.key4 + B.key4 + A.key5 + B.key5) from t0 as A force index(i1,i2), t0 as B force index (i1,i2) where (A.key1 = 1 or A.key2 = 1) and (B.key1 = 1 or B.key2 = 1); max(A.key1 + B.key1 + A.key2 + B.key2 + A.key3 + B.key3 + A.key4 + B.key4 + A.key5 + B.key5) 8194 alter table t0 add filler1 char(200), add filler2 char(200), add filler3 char(200); update t0 set key2=1, key3=1, key4=1, key5=1,key6=1,key7=1 where key7 < 500; explain select max(A.key1 + B.key1 + A.key2 + B.key2 + A.key3 + B.key3 + A.key4 + B.key4 + A.key5 + B.key5) from t0 as A, t0 as B where (A.key1 = 1 and A.key2 = 1 and A.key3 = 1 and A.key4=1 and A.key5=1 and A.key6=1 and A.key7 = 1 or A.key8=1) and (B.key1 = 1 and B.key2 = 1 and B.key3 = 1 and B.key4=1 and B.key5=1 and B.key6=1 and B.key7 = 1 or B.key8=1); id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE A index_merge i1,i2,i3,i4,i5,i6,i7,i8 i2,i3,i4,i5,i6,i8 4,4,4,4,4,4 NULL 16 Using union(intersect(i2,i3,i4,i5,i6),i8); Using where 1 SIMPLE B index_merge i1,i2,i3,i4,i5,i6,i7,i8 i2,i3,i4,i5,i6,i8 4,4,4,4,4,4 NULL 16 Using union(intersect(i2,i3,i4,i5,i6),i8); Using where select max(A.key1 + B.key1 + A.key2 + B.key2 + A.key3 + B.key3 + A.key4 + B.key4 + A.key5 + B.key5) from t0 as A, t0 as B where (A.key1 = 1 and A.key2 = 1 and A.key3 = 1 and A.key4=1 and A.key5=1 and A.key6=1 and A.key7 = 1 or A.key8=1) and (B.key1 = 1 and B.key2 = 1 and B.key3 = 1 and B.key4=1 and B.key5=1 and B.key6=1 and B.key7 = 1 or B.key8=1); max(A.key1 + B.key1 + A.key2 + B.key2 + A.key3 + B.key3 + A.key4 + B.key4 + A.key5 + B.key5) 8186 set join_buffer_size= @save_join_buffer_size; drop table t0, t1, t2, t3, t4; mysql-test/t/index_merge.test +46 −3 Original line number Diff line number Diff line # # Index merge tests # --disable_warnings drop table if exists t0, t1, t2, t3, t4; --enable_warnings Loading Loading @@ -278,4 +277,48 @@ delete from t0 where key1 < 3 or key2 < 4; select * from t0 where key1 < 3 or key2 < 4; select count(*) from t0; # Test for BUG#4177 drop table t4; create table t4 (a int); insert into t4 values (1),(4),(3); set @save_join_buffer_size=@@join_buffer_size; set join_buffer_size= 4000; show variables like 'join_buffer_size'; explain select max(A.key1 + B.key1 + A.key2 + B.key2 + A.key3 + B.key3 + A.key4 + B.key4 + A.key5 + B.key5) from t0 as A force index(i1,i2), t0 as B force index (i1,i2) where (A.key1 < 500000 or A.key2 < 3) and (B.key1 < 500000 or B.key2 < 3); select max(A.key1 + B.key1 + A.key2 + B.key2 + A.key3 + B.key3 + A.key4 + B.key4 + A.key5 + B.key5) from t0 as A force index(i1,i2), t0 as B force index (i1,i2) where (A.key1 < 500000 or A.key2 < 3) and (B.key1 < 500000 or B.key2 < 3); update t0 set key1=1; explain select max(A.key1 + B.key1 + A.key2 + B.key2 + A.key3 + B.key3 + A.key4 + B.key4 + A.key5 + B.key5) from t0 as A force index(i1,i2), t0 as B force index (i1,i2) where (A.key1 = 1 or A.key2 = 1) and (B.key1 = 1 or B.key2 = 1); select max(A.key1 + B.key1 + A.key2 + B.key2 + A.key3 + B.key3 + A.key4 + B.key4 + A.key5 + B.key5) from t0 as A force index(i1,i2), t0 as B force index (i1,i2) where (A.key1 = 1 or A.key2 = 1) and (B.key1 = 1 or B.key2 = 1); alter table t0 add filler1 char(200), add filler2 char(200), add filler3 char(200); update t0 set key2=1, key3=1, key4=1, key5=1,key6=1,key7=1 where key7 < 500; explain select max(A.key1 + B.key1 + A.key2 + B.key2 + A.key3 + B.key3 + A.key4 + B.key4 + A.key5 + B.key5) from t0 as A, t0 as B where (A.key1 = 1 and A.key2 = 1 and A.key3 = 1 and A.key4=1 and A.key5=1 and A.key6=1 and A.key7 = 1 or A.key8=1) and (B.key1 = 1 and B.key2 = 1 and B.key3 = 1 and B.key4=1 and B.key5=1 and B.key6=1 and B.key7 = 1 or B.key8=1); select max(A.key1 + B.key1 + A.key2 + B.key2 + A.key3 + B.key3 + A.key4 + B.key4 + A.key5 + B.key5) from t0 as A, t0 as B where (A.key1 = 1 and A.key2 = 1 and A.key3 = 1 and A.key4=1 and A.key5=1 and A.key6=1 and A.key7 = 1 or A.key8=1) and (B.key1 = 1 and B.key2 = 1 and B.key3 = 1 and B.key4=1 and B.key5=1 and B.key6=1 and B.key7 = 1 or B.key8=1); set join_buffer_size= @save_join_buffer_size; # Test for BUG#4177 ends drop table t0, t1, t2, t3, t4; sql/opt_range.cc +79 −35 Original line number Diff line number Diff line Loading @@ -777,8 +777,7 @@ QUICK_RANGE_SELECT::~QUICK_RANGE_SELECT() QUICK_INDEX_MERGE_SELECT::QUICK_INDEX_MERGE_SELECT(THD *thd_param, TABLE *table) :cur_quick_it(quick_selects),pk_quick_select(NULL),unique(NULL), thd(thd_param) :pk_quick_select(NULL), thd(thd_param) { DBUG_ENTER("QUICK_INDEX_MERGE_SELECT::QUICK_INDEX_MERGE_SELECT"); index= MAX_KEY; Loading @@ -790,17 +789,14 @@ QUICK_INDEX_MERGE_SELECT::QUICK_INDEX_MERGE_SELECT(THD *thd_param, int QUICK_INDEX_MERGE_SELECT::init() { cur_quick_it.rewind(); cur_quick_select= cur_quick_it++; return 0; DBUG_ENTER("QUICK_INDEX_MERGE_SELECT::init"); DBUG_RETURN(0); } int QUICK_INDEX_MERGE_SELECT::reset() { int result; DBUG_ENTER("QUICK_INDEX_MERGE_SELECT::reset"); result= cur_quick_select->reset() || prepare_unique(); DBUG_RETURN(result); DBUG_RETURN(read_keys_and_merge()); } bool Loading @@ -820,8 +816,12 @@ QUICK_INDEX_MERGE_SELECT::push_quick_back(QUICK_RANGE_SELECT *quick_sel_range) QUICK_INDEX_MERGE_SELECT::~QUICK_INDEX_MERGE_SELECT() { List_iterator_fast<QUICK_RANGE_SELECT> quick_it(quick_selects); QUICK_RANGE_SELECT* quick; DBUG_ENTER("QUICK_INDEX_MERGE_SELECT::~QUICK_INDEX_MERGE_SELECT"); delete unique; quick_it.rewind(); while ((quick= quick_it++)) quick->file= NULL; quick_selects.delete_elements(); delete pk_quick_select; free_root(&alloc,MYF(0)); Loading @@ -833,7 +833,8 @@ QUICK_ROR_INTERSECT_SELECT::QUICK_ROR_INTERSECT_SELECT(THD *thd_param, TABLE *table, bool retrieve_full_rows, MEM_ROOT *parent_alloc) : cpk_quick(NULL), thd(thd_param), need_to_fetch_row(retrieve_full_rows) : cpk_quick(NULL), thd(thd_param), need_to_fetch_row(retrieve_full_rows), scans_inited(false) { index= MAX_KEY; head= table; Loading @@ -859,8 +860,9 @@ QUICK_ROR_INTERSECT_SELECT::QUICK_ROR_INTERSECT_SELECT(THD *thd_param, int QUICK_ROR_INTERSECT_SELECT::init() { DBUG_ENTER("QUICK_ROR_INTERSECT_SELECT::init"); /* Check if last_rowid was successfully allocated in ctor */ return !last_rowid; DBUG_RETURN(!last_rowid); } Loading Loading @@ -953,7 +955,7 @@ int QUICK_ROR_INTERSECT_SELECT::init_ror_merged_scan(bool reuse_handler) DBUG_ENTER("QUICK_ROR_INTERSECT_SELECT::init_ror_merged_scan"); /* Initialize all merged "children" quick selects */ DBUG_ASSERT(!(need_to_fetch_row && !reuse_handler)); DBUG_ASSERT(!need_to_fetch_row || reuse_handler); if (!need_to_fetch_row && reuse_handler) { quick= quick_it++; Loading Loading @@ -995,7 +997,14 @@ int QUICK_ROR_INTERSECT_SELECT::init_ror_merged_scan(bool reuse_handler) int QUICK_ROR_INTERSECT_SELECT::reset() { DBUG_ENTER("QUICK_ROR_INTERSECT_SELECT::reset"); DBUG_RETURN(init_ror_merged_scan(TRUE)); if (!scans_inited && init_ror_merged_scan(TRUE)) DBUG_RETURN(1); scans_inited= true; List_iterator_fast<QUICK_RANGE_SELECT> it(quick_selects); QUICK_RANGE_SELECT *quick; while ((quick= it++)) quick->reset(); DBUG_RETURN(0); } Loading Loading @@ -1034,7 +1043,7 @@ QUICK_ROR_INTERSECT_SELECT::~QUICK_ROR_INTERSECT_SELECT() QUICK_ROR_UNION_SELECT::QUICK_ROR_UNION_SELECT(THD *thd_param, TABLE *table) :thd(thd_param) : thd(thd_param), scans_inited(false) { index= MAX_KEY; head= table; Loading @@ -1057,18 +1066,19 @@ QUICK_ROR_UNION_SELECT::QUICK_ROR_UNION_SELECT(THD *thd_param, int QUICK_ROR_UNION_SELECT::init() { DBUG_ENTER("QUICK_ROR_UNION_SELECT::init"); if (init_queue(&queue, quick_selects.elements, 0, FALSE , QUICK_ROR_UNION_SELECT::queue_cmp, (void*) this)) { bzero(&queue, sizeof(QUEUE)); return 1; DBUG_RETURN(1); } if (!(cur_rowid= (byte*)alloc_root(&alloc, 2*head->file->ref_length))) return 1; DBUG_RETURN(1); prev_rowid= cur_rowid + head->file->ref_length; return 0; DBUG_RETURN(0); } Loading Loading @@ -1106,6 +1116,18 @@ int QUICK_ROR_UNION_SELECT::reset() int error; DBUG_ENTER("QUICK_ROR_UNION_SELECT::reset"); have_prev_rowid= FALSE; if (!scans_inited) { QUICK_SELECT_I *quick; List_iterator_fast<QUICK_SELECT_I> it(quick_selects); while ((quick= it++)) { if (quick->init_ror_merged_scan(FALSE)) DBUG_RETURN(1); } scans_inited= true; } queue_remove_all(&queue); /* Initialize scans for merged quick selects and put all merged quick selects into the queue. Loading @@ -1113,7 +1135,7 @@ int QUICK_ROR_UNION_SELECT::reset() List_iterator_fast<QUICK_SELECT_I> it(quick_selects); while ((quick= it++)) { if (quick->init_ror_merged_scan(FALSE)) if (quick->reset()) DBUG_RETURN(1); if ((error= quick->get_next())) { Loading Loading @@ -1591,7 +1613,6 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use, DBUG_PRINT("enter",("keys_to_use: %lu prev_tables: %lu const_tables: %lu", keys_to_use.to_ulonglong(), (ulong) prev_tables, (ulong) const_tables)); delete quick; quick=0; needed_reg.clear_all(); Loading Loading @@ -5553,22 +5574,29 @@ QUICK_RANGE_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table, /* Fetch all row ids into unique. Perform key scans for all used indexes (except CPK), get rowids and merge them into an ordered non-recurrent sequence of rowids. The merge/duplicate removal is performed using Unique class. We put all rowids into Unique, get the sorted sequence and destroy the Unique. If table has a clustered primary key that covers all rows (TRUE for bdb and innodb currently) and one of the index_merge scans is a scan on PK, then primary key scan rowids are not put into Unique and also rows that will be retrieved by PK scan are not put into Unique rows that will be retrieved by PK scan are not put into Unique and primary key scan is not performed here, it is performed later separately. RETURN 0 OK other error */ int QUICK_INDEX_MERGE_SELECT::prepare_unique() int QUICK_INDEX_MERGE_SELECT::read_keys_and_merge() { List_iterator_fast<QUICK_RANGE_SELECT> cur_quick_it(quick_selects); QUICK_RANGE_SELECT* cur_quick; int result; Unique *unique; DBUG_ENTER("QUICK_INDEX_MERGE_SELECT::prepare_unique"); /* We're going to just read rowids. */ Loading @@ -5583,7 +5611,17 @@ int QUICK_INDEX_MERGE_SELECT::prepare_unique() */ head->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS); cur_quick_select->init(); cur_quick_it.rewind(); cur_quick= cur_quick_it++; DBUG_ASSERT(cur_quick); /* We reuse the same instance of handler so we need to call both init and reset here. */ if (cur_quick->init()) DBUG_RETURN(1); cur_quick->reset(); unique= new Unique(refpos_order_cmp, (void *)head->file, head->file->ref_length, Loading @@ -5592,24 +5630,28 @@ int QUICK_INDEX_MERGE_SELECT::prepare_unique() DBUG_RETURN(1); for (;;) { while ((result= cur_quick_select->get_next()) == HA_ERR_END_OF_FILE) while ((result= cur_quick->get_next()) == HA_ERR_END_OF_FILE) { cur_quick_select->range_end(); cur_quick_select= cur_quick_it++; if (!cur_quick_select) cur_quick->range_end(); cur_quick= cur_quick_it++; if (!cur_quick) break; if (cur_quick_select->init()) if (cur_quick->file->inited != handler::NONE) cur_quick->file->ha_index_end(); if (cur_quick->init()) DBUG_RETURN(1); /* QUICK_RANGE_SELECT::reset never fails */ cur_quick_select->reset(); cur_quick->reset(); } if (result) { if (result != HA_ERR_END_OF_FILE) { cur_quick->range_end(); DBUG_RETURN(result); } break; } Loading @@ -5620,8 +5662,8 @@ int QUICK_INDEX_MERGE_SELECT::prepare_unique() if (pk_quick_select && pk_quick_select->row_in_ranges()) continue; cur_quick_select->file->position(cur_quick_select->record); result= unique->unique_add((char*)cur_quick_select->file->ref); cur_quick->file->position(cur_quick->record); result= unique->unique_add((char*)cur_quick->file->ref); if (result) DBUG_RETURN(1); Loading @@ -5629,6 +5671,7 @@ int QUICK_INDEX_MERGE_SELECT::prepare_unique() /* ok, all row ids are in Unique */ result= unique->get(head); delete unique; doing_pk_scan= FALSE; /* start table scan */ init_read_record(&read_record, thd, head, (SQL_SELECT*) 0, 1, 1); Loading Loading @@ -5668,6 +5711,7 @@ int QUICK_INDEX_MERGE_SELECT::get_next() doing_pk_scan= TRUE; if ((result= pk_quick_select->init())) DBUG_RETURN(result); pk_quick_select->reset(); DBUG_RETURN(pk_quick_select->get_next()); } } Loading sql/opt_range.h +10 −8 Original line number Diff line number Diff line Loading @@ -127,7 +127,8 @@ class QUICK_SELECT_I reset() should be called when it is certain that row retrieval will be necessary. This call may do heavyweight initialization like buffering first N records etc. If reset() call fails get_next() must not be called. Note that reset() may be called several times if this quick select executes in a subselect. RETURN 0 OK other Error code Loading Loading @@ -274,6 +275,10 @@ class QUICK_RANGE_SELECT : public QUICK_SELECT_I next=0; range= NULL; cur_range= NULL; /* Note: in opt_range.cc there are places where it is assumed that this function always succeeds */ return 0; } int init(); Loading Loading @@ -388,21 +393,15 @@ class QUICK_INDEX_MERGE_SELECT : public QUICK_SELECT_I /* 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; /* quick select that uses clustered primary key (NULL if none) */ QUICK_RANGE_SELECT* pk_quick_select; /* true if this select is currently doing a clustered PK scan */ bool doing_pk_scan; Unique *unique; MEM_ROOT alloc; THD *thd; int prepare_unique(); int read_keys_and_merge(); /* used to get rows collected in Unique */ READ_RECORD read_record; Loading Loading @@ -465,6 +464,8 @@ class QUICK_ROR_INTERSECT_SELECT : public QUICK_SELECT_I MEM_ROOT alloc; /* Memory pool for this and merged quick selects data. */ THD *thd; /* current thread */ bool need_to_fetch_row; /* if true, do retrieve full table records. */ /* in top-level quick select, true if merged scans where initialized */ bool scans_inited; }; Loading Loading @@ -514,6 +515,7 @@ class QUICK_ROR_UNION_SELECT : public QUICK_SELECT_I uint rowid_length; /* table rowid length */ private: static int queue_cmp(void *arg, byte *val1, byte *val2); bool scans_inited; }; Loading Loading
include/queues.h +1 −0 Original line number Diff line number Diff line Loading @@ -53,6 +53,7 @@ int resize_queue(QUEUE *queue, uint max_elements); void delete_queue(QUEUE *queue); void queue_insert(QUEUE *queue,byte *element); byte *queue_remove(QUEUE *queue,uint idx); #define queue_remove_all(queue) { (queue)->elements= 0; } void _downheap(QUEUE *queue,uint idx); void queue_fix(QUEUE *queue); #define is_queue_inited(queue) ((queue)->root != 0) Loading
mysql-test/r/index_merge.result +52 −1 Original line number Diff line number Diff line Loading @@ -335,4 +335,55 @@ key1 key2 key3 key4 key5 key6 key7 key8 select count(*) from t0; count(*) 1021 drop table t4; create table t4 (a int); insert into t4 values (1),(4),(3); set @save_join_buffer_size=@@join_buffer_size; set join_buffer_size= 4000; show variables like 'join_buffer_size'; Variable_name Value join_buffer_size 8228 explain select max(A.key1 + B.key1 + A.key2 + B.key2 + A.key3 + B.key3 + A.key4 + B.key4 + A.key5 + B.key5) from t0 as A force index(i1,i2), t0 as B force index (i1,i2) where (A.key1 < 500000 or A.key2 < 3) and (B.key1 < 500000 or B.key2 < 3); id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE A index_merge i1,i2 i1,i2 4,4 NULL 1016 Using sort_union(i1,i2); Using where 1 SIMPLE B index_merge i1,i2 i1,i2 4,4 NULL 1016 Using sort_union(i1,i2); Using where select max(A.key1 + B.key1 + A.key2 + B.key2 + A.key3 + B.key3 + A.key4 + B.key4 + A.key5 + B.key5) from t0 as A force index(i1,i2), t0 as B force index (i1,i2) where (A.key1 < 500000 or A.key2 < 3) and (B.key1 < 500000 or B.key2 < 3); max(A.key1 + B.key1 + A.key2 + B.key2 + A.key3 + B.key3 + A.key4 + B.key4 + A.key5 + B.key5) 10240 update t0 set key1=1; explain select max(A.key1 + B.key1 + A.key2 + B.key2 + A.key3 + B.key3 + A.key4 + B.key4 + A.key5 + B.key5) from t0 as A force index(i1,i2), t0 as B force index (i1,i2) where (A.key1 = 1 or A.key2 = 1) and (B.key1 = 1 or B.key2 = 1); id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE A index_merge i1,i2 i1,i2 4,4 NULL 1020 Using union(i1,i2); Using where 1 SIMPLE B index_merge i1,i2 i1,i2 4,4 NULL 1020 Using union(i1,i2); Using where select max(A.key1 + B.key1 + A.key2 + B.key2 + A.key3 + B.key3 + A.key4 + B.key4 + A.key5 + B.key5) from t0 as A force index(i1,i2), t0 as B force index (i1,i2) where (A.key1 = 1 or A.key2 = 1) and (B.key1 = 1 or B.key2 = 1); max(A.key1 + B.key1 + A.key2 + B.key2 + A.key3 + B.key3 + A.key4 + B.key4 + A.key5 + B.key5) 8194 alter table t0 add filler1 char(200), add filler2 char(200), add filler3 char(200); update t0 set key2=1, key3=1, key4=1, key5=1,key6=1,key7=1 where key7 < 500; explain select max(A.key1 + B.key1 + A.key2 + B.key2 + A.key3 + B.key3 + A.key4 + B.key4 + A.key5 + B.key5) from t0 as A, t0 as B where (A.key1 = 1 and A.key2 = 1 and A.key3 = 1 and A.key4=1 and A.key5=1 and A.key6=1 and A.key7 = 1 or A.key8=1) and (B.key1 = 1 and B.key2 = 1 and B.key3 = 1 and B.key4=1 and B.key5=1 and B.key6=1 and B.key7 = 1 or B.key8=1); id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE A index_merge i1,i2,i3,i4,i5,i6,i7,i8 i2,i3,i4,i5,i6,i8 4,4,4,4,4,4 NULL 16 Using union(intersect(i2,i3,i4,i5,i6),i8); Using where 1 SIMPLE B index_merge i1,i2,i3,i4,i5,i6,i7,i8 i2,i3,i4,i5,i6,i8 4,4,4,4,4,4 NULL 16 Using union(intersect(i2,i3,i4,i5,i6),i8); Using where select max(A.key1 + B.key1 + A.key2 + B.key2 + A.key3 + B.key3 + A.key4 + B.key4 + A.key5 + B.key5) from t0 as A, t0 as B where (A.key1 = 1 and A.key2 = 1 and A.key3 = 1 and A.key4=1 and A.key5=1 and A.key6=1 and A.key7 = 1 or A.key8=1) and (B.key1 = 1 and B.key2 = 1 and B.key3 = 1 and B.key4=1 and B.key5=1 and B.key6=1 and B.key7 = 1 or B.key8=1); max(A.key1 + B.key1 + A.key2 + B.key2 + A.key3 + B.key3 + A.key4 + B.key4 + A.key5 + B.key5) 8186 set join_buffer_size= @save_join_buffer_size; drop table t0, t1, t2, t3, t4;
mysql-test/t/index_merge.test +46 −3 Original line number Diff line number Diff line # # Index merge tests # --disable_warnings drop table if exists t0, t1, t2, t3, t4; --enable_warnings Loading Loading @@ -278,4 +277,48 @@ delete from t0 where key1 < 3 or key2 < 4; select * from t0 where key1 < 3 or key2 < 4; select count(*) from t0; # Test for BUG#4177 drop table t4; create table t4 (a int); insert into t4 values (1),(4),(3); set @save_join_buffer_size=@@join_buffer_size; set join_buffer_size= 4000; show variables like 'join_buffer_size'; explain select max(A.key1 + B.key1 + A.key2 + B.key2 + A.key3 + B.key3 + A.key4 + B.key4 + A.key5 + B.key5) from t0 as A force index(i1,i2), t0 as B force index (i1,i2) where (A.key1 < 500000 or A.key2 < 3) and (B.key1 < 500000 or B.key2 < 3); select max(A.key1 + B.key1 + A.key2 + B.key2 + A.key3 + B.key3 + A.key4 + B.key4 + A.key5 + B.key5) from t0 as A force index(i1,i2), t0 as B force index (i1,i2) where (A.key1 < 500000 or A.key2 < 3) and (B.key1 < 500000 or B.key2 < 3); update t0 set key1=1; explain select max(A.key1 + B.key1 + A.key2 + B.key2 + A.key3 + B.key3 + A.key4 + B.key4 + A.key5 + B.key5) from t0 as A force index(i1,i2), t0 as B force index (i1,i2) where (A.key1 = 1 or A.key2 = 1) and (B.key1 = 1 or B.key2 = 1); select max(A.key1 + B.key1 + A.key2 + B.key2 + A.key3 + B.key3 + A.key4 + B.key4 + A.key5 + B.key5) from t0 as A force index(i1,i2), t0 as B force index (i1,i2) where (A.key1 = 1 or A.key2 = 1) and (B.key1 = 1 or B.key2 = 1); alter table t0 add filler1 char(200), add filler2 char(200), add filler3 char(200); update t0 set key2=1, key3=1, key4=1, key5=1,key6=1,key7=1 where key7 < 500; explain select max(A.key1 + B.key1 + A.key2 + B.key2 + A.key3 + B.key3 + A.key4 + B.key4 + A.key5 + B.key5) from t0 as A, t0 as B where (A.key1 = 1 and A.key2 = 1 and A.key3 = 1 and A.key4=1 and A.key5=1 and A.key6=1 and A.key7 = 1 or A.key8=1) and (B.key1 = 1 and B.key2 = 1 and B.key3 = 1 and B.key4=1 and B.key5=1 and B.key6=1 and B.key7 = 1 or B.key8=1); select max(A.key1 + B.key1 + A.key2 + B.key2 + A.key3 + B.key3 + A.key4 + B.key4 + A.key5 + B.key5) from t0 as A, t0 as B where (A.key1 = 1 and A.key2 = 1 and A.key3 = 1 and A.key4=1 and A.key5=1 and A.key6=1 and A.key7 = 1 or A.key8=1) and (B.key1 = 1 and B.key2 = 1 and B.key3 = 1 and B.key4=1 and B.key5=1 and B.key6=1 and B.key7 = 1 or B.key8=1); set join_buffer_size= @save_join_buffer_size; # Test for BUG#4177 ends drop table t0, t1, t2, t3, t4;
sql/opt_range.cc +79 −35 Original line number Diff line number Diff line Loading @@ -777,8 +777,7 @@ QUICK_RANGE_SELECT::~QUICK_RANGE_SELECT() QUICK_INDEX_MERGE_SELECT::QUICK_INDEX_MERGE_SELECT(THD *thd_param, TABLE *table) :cur_quick_it(quick_selects),pk_quick_select(NULL),unique(NULL), thd(thd_param) :pk_quick_select(NULL), thd(thd_param) { DBUG_ENTER("QUICK_INDEX_MERGE_SELECT::QUICK_INDEX_MERGE_SELECT"); index= MAX_KEY; Loading @@ -790,17 +789,14 @@ QUICK_INDEX_MERGE_SELECT::QUICK_INDEX_MERGE_SELECT(THD *thd_param, int QUICK_INDEX_MERGE_SELECT::init() { cur_quick_it.rewind(); cur_quick_select= cur_quick_it++; return 0; DBUG_ENTER("QUICK_INDEX_MERGE_SELECT::init"); DBUG_RETURN(0); } int QUICK_INDEX_MERGE_SELECT::reset() { int result; DBUG_ENTER("QUICK_INDEX_MERGE_SELECT::reset"); result= cur_quick_select->reset() || prepare_unique(); DBUG_RETURN(result); DBUG_RETURN(read_keys_and_merge()); } bool Loading @@ -820,8 +816,12 @@ QUICK_INDEX_MERGE_SELECT::push_quick_back(QUICK_RANGE_SELECT *quick_sel_range) QUICK_INDEX_MERGE_SELECT::~QUICK_INDEX_MERGE_SELECT() { List_iterator_fast<QUICK_RANGE_SELECT> quick_it(quick_selects); QUICK_RANGE_SELECT* quick; DBUG_ENTER("QUICK_INDEX_MERGE_SELECT::~QUICK_INDEX_MERGE_SELECT"); delete unique; quick_it.rewind(); while ((quick= quick_it++)) quick->file= NULL; quick_selects.delete_elements(); delete pk_quick_select; free_root(&alloc,MYF(0)); Loading @@ -833,7 +833,8 @@ QUICK_ROR_INTERSECT_SELECT::QUICK_ROR_INTERSECT_SELECT(THD *thd_param, TABLE *table, bool retrieve_full_rows, MEM_ROOT *parent_alloc) : cpk_quick(NULL), thd(thd_param), need_to_fetch_row(retrieve_full_rows) : cpk_quick(NULL), thd(thd_param), need_to_fetch_row(retrieve_full_rows), scans_inited(false) { index= MAX_KEY; head= table; Loading @@ -859,8 +860,9 @@ QUICK_ROR_INTERSECT_SELECT::QUICK_ROR_INTERSECT_SELECT(THD *thd_param, int QUICK_ROR_INTERSECT_SELECT::init() { DBUG_ENTER("QUICK_ROR_INTERSECT_SELECT::init"); /* Check if last_rowid was successfully allocated in ctor */ return !last_rowid; DBUG_RETURN(!last_rowid); } Loading Loading @@ -953,7 +955,7 @@ int QUICK_ROR_INTERSECT_SELECT::init_ror_merged_scan(bool reuse_handler) DBUG_ENTER("QUICK_ROR_INTERSECT_SELECT::init_ror_merged_scan"); /* Initialize all merged "children" quick selects */ DBUG_ASSERT(!(need_to_fetch_row && !reuse_handler)); DBUG_ASSERT(!need_to_fetch_row || reuse_handler); if (!need_to_fetch_row && reuse_handler) { quick= quick_it++; Loading Loading @@ -995,7 +997,14 @@ int QUICK_ROR_INTERSECT_SELECT::init_ror_merged_scan(bool reuse_handler) int QUICK_ROR_INTERSECT_SELECT::reset() { DBUG_ENTER("QUICK_ROR_INTERSECT_SELECT::reset"); DBUG_RETURN(init_ror_merged_scan(TRUE)); if (!scans_inited && init_ror_merged_scan(TRUE)) DBUG_RETURN(1); scans_inited= true; List_iterator_fast<QUICK_RANGE_SELECT> it(quick_selects); QUICK_RANGE_SELECT *quick; while ((quick= it++)) quick->reset(); DBUG_RETURN(0); } Loading Loading @@ -1034,7 +1043,7 @@ QUICK_ROR_INTERSECT_SELECT::~QUICK_ROR_INTERSECT_SELECT() QUICK_ROR_UNION_SELECT::QUICK_ROR_UNION_SELECT(THD *thd_param, TABLE *table) :thd(thd_param) : thd(thd_param), scans_inited(false) { index= MAX_KEY; head= table; Loading @@ -1057,18 +1066,19 @@ QUICK_ROR_UNION_SELECT::QUICK_ROR_UNION_SELECT(THD *thd_param, int QUICK_ROR_UNION_SELECT::init() { DBUG_ENTER("QUICK_ROR_UNION_SELECT::init"); if (init_queue(&queue, quick_selects.elements, 0, FALSE , QUICK_ROR_UNION_SELECT::queue_cmp, (void*) this)) { bzero(&queue, sizeof(QUEUE)); return 1; DBUG_RETURN(1); } if (!(cur_rowid= (byte*)alloc_root(&alloc, 2*head->file->ref_length))) return 1; DBUG_RETURN(1); prev_rowid= cur_rowid + head->file->ref_length; return 0; DBUG_RETURN(0); } Loading Loading @@ -1106,6 +1116,18 @@ int QUICK_ROR_UNION_SELECT::reset() int error; DBUG_ENTER("QUICK_ROR_UNION_SELECT::reset"); have_prev_rowid= FALSE; if (!scans_inited) { QUICK_SELECT_I *quick; List_iterator_fast<QUICK_SELECT_I> it(quick_selects); while ((quick= it++)) { if (quick->init_ror_merged_scan(FALSE)) DBUG_RETURN(1); } scans_inited= true; } queue_remove_all(&queue); /* Initialize scans for merged quick selects and put all merged quick selects into the queue. Loading @@ -1113,7 +1135,7 @@ int QUICK_ROR_UNION_SELECT::reset() List_iterator_fast<QUICK_SELECT_I> it(quick_selects); while ((quick= it++)) { if (quick->init_ror_merged_scan(FALSE)) if (quick->reset()) DBUG_RETURN(1); if ((error= quick->get_next())) { Loading Loading @@ -1591,7 +1613,6 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use, DBUG_PRINT("enter",("keys_to_use: %lu prev_tables: %lu const_tables: %lu", keys_to_use.to_ulonglong(), (ulong) prev_tables, (ulong) const_tables)); delete quick; quick=0; needed_reg.clear_all(); Loading Loading @@ -5553,22 +5574,29 @@ QUICK_RANGE_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table, /* Fetch all row ids into unique. Perform key scans for all used indexes (except CPK), get rowids and merge them into an ordered non-recurrent sequence of rowids. The merge/duplicate removal is performed using Unique class. We put all rowids into Unique, get the sorted sequence and destroy the Unique. If table has a clustered primary key that covers all rows (TRUE for bdb and innodb currently) and one of the index_merge scans is a scan on PK, then primary key scan rowids are not put into Unique and also rows that will be retrieved by PK scan are not put into Unique rows that will be retrieved by PK scan are not put into Unique and primary key scan is not performed here, it is performed later separately. RETURN 0 OK other error */ int QUICK_INDEX_MERGE_SELECT::prepare_unique() int QUICK_INDEX_MERGE_SELECT::read_keys_and_merge() { List_iterator_fast<QUICK_RANGE_SELECT> cur_quick_it(quick_selects); QUICK_RANGE_SELECT* cur_quick; int result; Unique *unique; DBUG_ENTER("QUICK_INDEX_MERGE_SELECT::prepare_unique"); /* We're going to just read rowids. */ Loading @@ -5583,7 +5611,17 @@ int QUICK_INDEX_MERGE_SELECT::prepare_unique() */ head->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS); cur_quick_select->init(); cur_quick_it.rewind(); cur_quick= cur_quick_it++; DBUG_ASSERT(cur_quick); /* We reuse the same instance of handler so we need to call both init and reset here. */ if (cur_quick->init()) DBUG_RETURN(1); cur_quick->reset(); unique= new Unique(refpos_order_cmp, (void *)head->file, head->file->ref_length, Loading @@ -5592,24 +5630,28 @@ int QUICK_INDEX_MERGE_SELECT::prepare_unique() DBUG_RETURN(1); for (;;) { while ((result= cur_quick_select->get_next()) == HA_ERR_END_OF_FILE) while ((result= cur_quick->get_next()) == HA_ERR_END_OF_FILE) { cur_quick_select->range_end(); cur_quick_select= cur_quick_it++; if (!cur_quick_select) cur_quick->range_end(); cur_quick= cur_quick_it++; if (!cur_quick) break; if (cur_quick_select->init()) if (cur_quick->file->inited != handler::NONE) cur_quick->file->ha_index_end(); if (cur_quick->init()) DBUG_RETURN(1); /* QUICK_RANGE_SELECT::reset never fails */ cur_quick_select->reset(); cur_quick->reset(); } if (result) { if (result != HA_ERR_END_OF_FILE) { cur_quick->range_end(); DBUG_RETURN(result); } break; } Loading @@ -5620,8 +5662,8 @@ int QUICK_INDEX_MERGE_SELECT::prepare_unique() if (pk_quick_select && pk_quick_select->row_in_ranges()) continue; cur_quick_select->file->position(cur_quick_select->record); result= unique->unique_add((char*)cur_quick_select->file->ref); cur_quick->file->position(cur_quick->record); result= unique->unique_add((char*)cur_quick->file->ref); if (result) DBUG_RETURN(1); Loading @@ -5629,6 +5671,7 @@ int QUICK_INDEX_MERGE_SELECT::prepare_unique() /* ok, all row ids are in Unique */ result= unique->get(head); delete unique; doing_pk_scan= FALSE; /* start table scan */ init_read_record(&read_record, thd, head, (SQL_SELECT*) 0, 1, 1); Loading Loading @@ -5668,6 +5711,7 @@ int QUICK_INDEX_MERGE_SELECT::get_next() doing_pk_scan= TRUE; if ((result= pk_quick_select->init())) DBUG_RETURN(result); pk_quick_select->reset(); DBUG_RETURN(pk_quick_select->get_next()); } } Loading
sql/opt_range.h +10 −8 Original line number Diff line number Diff line Loading @@ -127,7 +127,8 @@ class QUICK_SELECT_I reset() should be called when it is certain that row retrieval will be necessary. This call may do heavyweight initialization like buffering first N records etc. If reset() call fails get_next() must not be called. Note that reset() may be called several times if this quick select executes in a subselect. RETURN 0 OK other Error code Loading Loading @@ -274,6 +275,10 @@ class QUICK_RANGE_SELECT : public QUICK_SELECT_I next=0; range= NULL; cur_range= NULL; /* Note: in opt_range.cc there are places where it is assumed that this function always succeeds */ return 0; } int init(); Loading Loading @@ -388,21 +393,15 @@ class QUICK_INDEX_MERGE_SELECT : public QUICK_SELECT_I /* 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; /* quick select that uses clustered primary key (NULL if none) */ QUICK_RANGE_SELECT* pk_quick_select; /* true if this select is currently doing a clustered PK scan */ bool doing_pk_scan; Unique *unique; MEM_ROOT alloc; THD *thd; int prepare_unique(); int read_keys_and_merge(); /* used to get rows collected in Unique */ READ_RECORD read_record; Loading Loading @@ -465,6 +464,8 @@ class QUICK_ROR_INTERSECT_SELECT : public QUICK_SELECT_I MEM_ROOT alloc; /* Memory pool for this and merged quick selects data. */ THD *thd; /* current thread */ bool need_to_fetch_row; /* if true, do retrieve full table records. */ /* in top-level quick select, true if merged scans where initialized */ bool scans_inited; }; Loading Loading @@ -514,6 +515,7 @@ class QUICK_ROR_UNION_SELECT : public QUICK_SELECT_I uint rowid_length; /* table rowid length */ private: static int queue_cmp(void *arg, byte *val1, byte *val2); bool scans_inited; }; Loading