summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorunknown <sergefp@mysql.com>2003-12-09 23:40:02 +0300
committerunknown <sergefp@mysql.com>2003-12-09 23:40:02 +0300
commite7526d3a209160a5fb6ba02208dcfac515c858b2 (patch)
tree993bf61744309dc8fb141dc11bc16af4c0a8f26f /sql
parentd8010263a5abc731affd3482f117ef219af75d06 (diff)
downloadmariadb-git-e7526d3a209160a5fb6ba02208dcfac515c858b2.tar.gz
Two-sweeps read index_merge plus several small index_merge fixes and improvements
Diffstat (limited to 'sql')
-rw-r--r--sql/filesort.cc17
-rw-r--r--sql/ha_berkeley.h1
-rw-r--r--sql/ha_innodb.cc10
-rw-r--r--sql/ha_innodb.h1
-rw-r--r--sql/handler.h7
-rw-r--r--sql/opt_range.cc223
-rw-r--r--sql/opt_range.h85
-rw-r--r--sql/records.cc4
8 files changed, 290 insertions, 58 deletions
diff --git a/sql/filesort.cc b/sql/filesort.cc
index 1967c089622..356afdf748c 100644
--- a/sql/filesort.cc
+++ b/sql/filesort.cc
@@ -87,9 +87,15 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
DBUG_PUSH(""); /* No DBUG here */
#endif
FILESORT_INFO table_sort;
- bzero(&table_sort, sizeof(FILESORT_INFO));
+ /*
+ don't use table->sort in filesort as it is also used by
+ QUICK_INDEX_MERGE_SELECT. work with a copy of it and put it back at the
+ end when index_merge select has finished with it.
+ */
+ memcpy(&table_sort, &table->sort, sizeof(FILESORT_INFO));
+ table->sort.io_cache= NULL;
- outfile= table->sort.io_cache;
+ outfile= table_sort.io_cache;
my_b_clear(&tempfile);
my_b_clear(&buffpek_pointers);
buffpek=0;
@@ -261,7 +267,6 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
DBUG_POP(); /* Ok to DBUG */
#endif
memcpy(&table->sort, &table_sort, sizeof(FILESORT_INFO));
- table->sort.io_cache= outfile;
DBUG_PRINT("exit",("records: %ld",records));
DBUG_RETURN(error ? HA_POS_ERROR : records);
} /* filesort */
@@ -445,7 +450,13 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select,
file->unlock_row();
}
if (quick_select)
+ {
+ /*
+ index_merge quick select uses table->sort when retrieving rows, so free
+ resoures it has allocated.
+ */
end_read_record(&read_record_info);
+ }
else
{
(void) file->extra(HA_EXTRA_NO_CACHE); /* End cacheing of records */
diff --git a/sql/ha_berkeley.h b/sql/ha_berkeley.h
index 39293717c27..582a79906a7 100644
--- a/sql/ha_berkeley.h
+++ b/sql/ha_berkeley.h
@@ -167,6 +167,7 @@ class ha_berkeley: public handler
longlong get_auto_increment();
void print_error(int error, myf errflag);
uint8 table_cache_type() { return HA_CACHE_TBL_TRANSACT; }
+ bool primary_key_is_clustered_covering() { return true; }
};
extern bool berkeley_skip, berkeley_shared_data;
diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc
index c10eec73274..6b0bfdfc8e2 100644
--- a/sql/ha_innodb.cc
+++ b/sql/ha_innodb.cc
@@ -2001,10 +2001,12 @@ build_template(
update field->query_id so that the formula
thd->query_id == field->query_id did not work. */
- if (templ_type == ROW_MYSQL_REC_FIELDS
- && !(fetch_all_in_key
- && dict_index_contains_col_or_prefix(index, i))
- && thd->query_id != field->query_id) {
+ ibool index_contains_field = dict_index_contains_col_or_prefix(index, i);
+
+ if (templ_type == ROW_MYSQL_REC_FIELDS &&
+ ((prebuilt->read_just_key && !index_contains_field) ||
+ (!(fetch_all_in_key && index_contains_field)
+ && thd->query_id != field->query_id))) {
/* This field is not needed in the query, skip it */
diff --git a/sql/ha_innodb.h b/sql/ha_innodb.h
index 2fa70f27128..6fa66377cd6 100644
--- a/sql/ha_innodb.h
+++ b/sql/ha_innodb.h
@@ -187,6 +187,7 @@ class ha_innobase: public handler
void init_table_handle_for_HANDLER();
longlong get_auto_increment();
uint8 table_cache_type() { return HA_CACHE_TBL_ASKTRANSACT; }
+ bool primary_key_is_clustered_covering() { return true; }
};
extern bool innodb_skip;
diff --git a/sql/handler.h b/sql/handler.h
index f4ae45fafe3..ccb8d795927 100644
--- a/sql/handler.h
+++ b/sql/handler.h
@@ -368,6 +368,13 @@ public:
*/
static bool caching_allowed(THD* thd, char* table_key,
uint key_length, uint8 cahe_type);
+
+ /*
+ RETURN
+ true primary key (if there is one) is clustered key covering all fields
+ false otherwise
+ */
+ virtual bool primary_key_is_clustered_covering() { return false; }
};
/* Some extern variables used with handlers */
diff --git a/sql/opt_range.cc b/sql/opt_range.cc
index 5f282695494..5f6b93bb437 100644
--- a/sql/opt_range.cc
+++ b/sql/opt_range.cc
@@ -482,7 +482,7 @@ int SEL_IMERGE::or_sel_imerge_with_checks(PARAM *param, SEL_IMERGE* imerge)
/*
- Perform AND operation on two index_merge lists, storing result in *im1.
+ Perform AND operation on two index_merge lists and store result in *im1.
*/
@@ -617,10 +617,11 @@ QUICK_SELECT_I::QUICK_SELECT_I()
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)
+ :dont_free(0),error(0),range(0),cur_range(NULL)
{
index= key_nr;
head= table;
+ my_init_dynamic_array(&ranges, sizeof(QUICK_RANGE*), 16, 16);
if (!no_alloc && !parent_alloc)
{
@@ -640,21 +641,24 @@ int QUICK_RANGE_SELECT::init()
}
QUICK_RANGE_SELECT::~QUICK_RANGE_SELECT()
-{
+{
if (!dont_free)
{
file->index_end();
+ delete_dynamic(&ranges); /* ranges are allocated in alloc */
free_root(&alloc,MYF(0));
}
}
QUICK_INDEX_MERGE_SELECT::QUICK_INDEX_MERGE_SELECT(THD *thd_param, TABLE *table)
- :cur_quick_it(quick_selects), thd(thd_param), unique(NULL)
+ :cur_quick_it(quick_selects), thd(thd_param), unique(NULL),
+ pk_quick_select(NULL)
{
index= MAX_KEY;
head= table;
reset_called= false;
+ bzero(&read_record, sizeof(read_record));
init_sql_alloc(&alloc,1024,0);
}
@@ -662,7 +666,7 @@ int QUICK_INDEX_MERGE_SELECT::init()
{
cur_quick_it.rewind();
cur_quick_select= cur_quick_it++;
- return cur_quick_select->init();
+ return 0;
}
int QUICK_INDEX_MERGE_SELECT::reset()
@@ -673,19 +677,30 @@ int QUICK_INDEX_MERGE_SELECT::reset()
DBUG_RETURN(0);
reset_called= true;
- result = cur_quick_select->reset() && prepare_unique();
+ result = cur_quick_select->reset() || prepare_unique();
DBUG_RETURN(result);
}
bool
QUICK_INDEX_MERGE_SELECT::push_quick_back(QUICK_RANGE_SELECT *quick_sel_range)
{
- return quick_selects.push_back(quick_sel_range);
+ /*
+ Save quick_select that does scan on clustered covering primary key as
+ it will be processed separately
+ */
+ if (head->file->primary_key_is_clustered_covering() &&
+ quick_sel_range->index == head->primary_key)
+ pk_quick_select= quick_sel_range;
+ else
+ return quick_selects.push_back(quick_sel_range);
+ return 0;
}
QUICK_INDEX_MERGE_SELECT::~QUICK_INDEX_MERGE_SELECT()
{
+ delete unique;
quick_selects.delete_elements();
+ delete pk_quick_select;
free_root(&alloc,MYF(0));
}
@@ -1021,6 +1036,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
ha_rows min_imerge_records;
List_iterator_fast<SEL_IMERGE> it(tree->merges);
+ /* find index_merge with minimal cost */
while ((imerge= it++))
{
double imerge_cost= 0;
@@ -1031,6 +1047,13 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
(SEL_ARG***)alloc_root(&alloc,
(imerge->trees_next - imerge->trees)*
sizeof(void*));
+ /*
+ It may be possible to use different keys for index_merge, e.g for
+ queries like
+ ...WHERE (key1 < c2 AND key2 < c2) OR (key3 < c3 AND key4 < c4)
+ We assume we get the best index_merge if we choose the best key
+ read inside each of the conjuncts.
+ */
for (SEL_TREE **ptree= imerge->trees;
ptree != imerge->trees_next;
ptree++)
@@ -1062,7 +1085,11 @@ imerge_fail:;
goto end_free;
records= min_imerge_records;
- /* ok, got minimal imerge, *min_imerge, with cost min_imerge_cost */
+ /*
+ Ok, got minimal index merge, *min_imerge, with cost min_imerge_cost
+ Compare its cost with "all" scan cost (or "all+using index" if
+ it is possible) and choose the best.
+ */
if (!head->used_keys.is_clear_all())
{
@@ -1184,8 +1211,8 @@ end:
/*
- Calculate quick select read time, # of records, and best key to use
- without constructing QUICK_SELECT
+ Calculate quick range select read time, # of records, and best key to use
+ without constructing QUICK_RANGE_SELECT object.
*/
static int get_quick_select_params(SEL_TREE *tree, PARAM& param,
@@ -1448,6 +1475,7 @@ get_mm_parts(PARAM *param, Field *field, Item_func::Functype type,
if (tree2)
tree= tree_or(param,tree,tree2);
}
+
DBUG_RETURN(tree);
}
@@ -1751,14 +1779,16 @@ tree_and(PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2)
/*
- Check if two SEL_TREES can be combined into one without using index_merge
+ Check if two SEL_TREES can be combined into one (i.e. a single key range
+ read can be constructed for "cond_of_tree1 OR cond_of_tree2" ) 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;
- common_keys.intersect(tree2->keys_map);
DBUG_ENTER("sel_trees_can_be_ored");
+ common_keys.intersect(tree2->keys_map);
if (common_keys.is_clear_all())
DBUG_RETURN(false);
@@ -2869,7 +2899,7 @@ get_quick_select(PARAM *param,uint idx,SEL_ARG *key_tree,
sizeof(KEY_PART)*
param->table->key_info[param->real_keynr[idx]].key_parts);
}
- }
+ }
DBUG_RETURN(quick);
}
@@ -2973,7 +3003,9 @@ get_quick_keys(PARAM *param,QUICK_RANGE_SELECT *quick,KEY_PART *key,
set_if_bigger(quick->max_used_key_length,range->min_length);
set_if_bigger(quick->max_used_key_length,range->max_length);
set_if_bigger(quick->used_key_parts, (uint) key_tree->part+1);
- quick->ranges.push_back(range);
+ if (insert_dynamic(&quick->ranges, (gptr)&range))
+ return 1;
+
end:
if (key_tree->right != &null_element)
@@ -2991,8 +3023,8 @@ bool QUICK_RANGE_SELECT::unique_key_range()
{
if (ranges.elements == 1)
{
- QUICK_RANGE *tmp;
- if (((tmp=ranges.head())->flag & (EQ_RANGE | NULL_RANGE)) == EQ_RANGE)
+ QUICK_RANGE *tmp= *((QUICK_RANGE**)ranges.buffer);
+ if ((tmp->flag & (EQ_RANGE | NULL_RANGE)) == EQ_RANGE)
{
KEY *key=head->key_info+index;
return ((key->flags & HA_NOSAME) &&
@@ -3045,7 +3077,6 @@ QUICK_RANGE_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table,
{
if (thd->is_fatal_error)
return 0; // out of memory
- return quick; // empty range
}
QUICK_RANGE *range= new QUICK_RANGE();
@@ -3070,7 +3101,7 @@ QUICK_RANGE_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table,
key_part->part_length+=HA_KEY_BLOB_LENGTH;
key_part->null_bit= key_info->key_part[part].null_bit;
}
- if (!quick->ranges.push_back(range))
+ if (!insert_dynamic(&quick->ranges,(gptr)&range))
return quick;
err:
@@ -3079,33 +3110,59 @@ err:
}
-#define MEM_STRIP_BUF_SIZE current_thd->variables.sortbuff_size
+#define MEM_STRIP_BUF_SIZE thd->variables.sortbuff_size
+
/*
Fetch all row ids into unique.
+
+ If table has a clustered primary key(PK) that contains all rows (bdb and
+ innodb currently) and one of the index_merge scans is a scan on primary key,
+ 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
+
+ RETURN
+ 0 OK
+ other error
*/
+
int QUICK_INDEX_MERGE_SELECT::prepare_unique()
{
int result;
DBUG_ENTER("QUICK_INDEX_MERGE_SELECT::prepare_unique");
- /* we're going to just read rowids */
+ /* we're going to just read rowids. */
head->file->extra(HA_EXTRA_KEYREAD);
+ /*
+ Make innodb retrieve all PK member fields, so
+ * ha_innobase::position (which uses them) call works.
+ * we filter out rows retrieved by CCPK.
+ (This also creates a deficiency - it is possible that we will retrieve
+ parts of key that are not used by current query at all)
+ */
+ head->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
+
+ cur_quick_select->init();
+
unique= new Unique(refposcmp2, (void *) &head->file->ref_length,
head->file->ref_length,
- MEM_STRIP_BUF_SIZE);
+ MEM_STRIP_BUF_SIZE);
if (!unique)
DBUG_RETURN(1);
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();
- if (cur_quick_select->reset())
+
+ if (cur_quick_select->init())
DBUG_RETURN(1);
+
+ /* QUICK_RANGE_SELECT::reset never fails */
+ cur_quick_select->reset();
}
if (result)
@@ -3124,29 +3181,62 @@ int QUICK_INDEX_MERGE_SELECT::prepare_unique()
if (thd->killed)
DBUG_RETURN(1);
+
+ /* skip row if it will be retrieved by clustered covering PK scan */
+ if (pk_quick_select && pk_quick_select->row_in_ranges())
+ continue;
cur_quick_select->file->position(cur_quick_select->record);
- if (unique->unique_add((char*)cur_quick_select->file->ref))
+ result= unique->unique_add((char*)cur_quick_select->file->ref);
+
+ if (result)
DBUG_RETURN(1);
}while(true);
/* ok, all row ids are in Unique */
result= unique->get(head);
-
+ doing_pk_scan= false;
+ init_read_record(&read_record, thd, head, NULL, 1, 1);
/* index_merge currently doesn't support "using index" at all */
head->file->extra(HA_EXTRA_NO_KEYREAD);
+
DBUG_RETURN(result);
}
+/*
+ Get next row for index_merge.
+ NOTES
+ The rows are read from
+ 1. rowids stored in Unique.
+ 2. QUICK_RANGE_SELECT with clustered primary key (if any).
+ the sets of rows retrieved in 1) and 2) are guaranteed to be disjoint.
+*/
int QUICK_INDEX_MERGE_SELECT::get_next()
{
+ int result;
DBUG_ENTER("QUICK_INDEX_MERGE_SELECT::get_next");
- DBUG_PRINT("QUICK_INDEX_MERGE_SELECT",
- ("ERROR: index merge error: get_next should not be called "));
- DBUG_ASSERT(0);
- DBUG_RETURN(HA_ERR_END_OF_FILE);
+ if (doing_pk_scan)
+ DBUG_RETURN(pk_quick_select->get_next());
+
+ result= read_record.read_record(&read_record);
+
+ if (result == -1)
+ {
+ result= HA_ERR_END_OF_FILE;
+ /* All rows from Unique have been retrieved, do a CCPK scan */
+ end_read_record(&read_record);
+ if(pk_quick_select)
+ {
+ doing_pk_scan= true;
+ if ((result= pk_quick_select->init()))
+ DBUG_RETURN(result);
+ DBUG_RETURN(pk_quick_select->get_next());
+ }
+ }
+
+ DBUG_RETURN(result);
}
/* get next possible record using quick-struct */
@@ -3172,16 +3262,21 @@ int QUICK_RANGE_SELECT::get_next()
if (!result)
{
- if ((range->flag & GEOM_FLAG) || !cmp_next(*it.ref()))
+ if ((range->flag & GEOM_FLAG) || !cmp_next(*cur_range))
DBUG_RETURN(0);
}
else if (result != HA_ERR_END_OF_FILE)
DBUG_RETURN(result);
}
+
+ if (!cur_range)
+ range= *(cur_range= (QUICK_RANGE**)ranges.buffer);
+ else
+ range= (cur_range == ((QUICK_RANGE**)ranges.buffer + ranges.elements - 1))?
+ NULL: *(++cur_range);
- if (!(range=it++))
+ if (!range)
DBUG_RETURN(HA_ERR_END_OF_FILE); // All ranges used
-
if (range->flag & GEOM_FLAG)
{
if ((result = file->index_read(record,
@@ -3272,6 +3367,45 @@ int QUICK_RANGE_SELECT::cmp_next(QUICK_RANGE *range_arg)
/*
+ Check if current row will be retrieved by this QUICK_RANGE_SELECT
+ (this is used to filter out CCPK scan rows in index_merge).
+
+ NOTES
+ It is assumed that currently a scan is being done on another index
+ which reads all necessary parts of the index that is scanned by this
+ quick select.
+
+ The implementation does a binary search on sorted array of disjoint
+ ranges, without taking size of range into account.
+
+ RETURN
+ true if current row will be retrieved by this quick select
+ false if not
+*/
+
+bool QUICK_RANGE_SELECT::row_in_ranges()
+{
+ QUICK_RANGE *range;
+ uint min= 0;
+ uint max= ranges.elements - 1;
+ uint mid= (max + min)/2;
+
+ while (min != max)
+ {
+ if (cmp_next(*(QUICK_RANGE**)dynamic_array_ptr(&ranges, mid)))
+ {
+ /* current row value > mid->max */
+ min= mid + 1;
+ }
+ else
+ max= mid;
+ mid= (min + max) / 2;
+ }
+ range= *(QUICK_RANGE**)dynamic_array_ptr(&ranges, mid);
+ return (!cmp_next(range) && !cmp_prev(range));
+}
+
+/*
This is a hack: we inherit from QUICK_SELECT so that we can use the
get_next() interface, but we have to hold a pointer to the original
QUICK_SELECT because its data are used all over the place. What
@@ -3287,14 +3421,15 @@ QUICK_SELECT_DESC::QUICK_SELECT_DESC(QUICK_RANGE_SELECT *q,
{
bool not_read_after_key = file->table_flags() & HA_NOT_READ_AFTER_KEY;
QUICK_RANGE *r;
-
- it.rewind();
- for (r = it++; r; r = it++)
+
+ QUICK_RANGE **pr= (QUICK_RANGE**)ranges.buffer;
+ QUICK_RANGE **last_range= pr + ranges.elements;
+ for (; pr!=last_range; ++pr)
{
+ r= *pr;
rev_ranges.push_front(r);
if (not_read_after_key && range_reads_after_key(r))
{
- it.rewind(); // Reset range
error = HA_ERR_UNSUPPORTED;
dont_free=1; // Don't free memory from 'q'
return;
@@ -3412,7 +3547,7 @@ int QUICK_SELECT_DESC::get_next()
Returns 0 if found key is inside range (found key >= range->min_key).
*/
-int QUICK_SELECT_DESC::cmp_prev(QUICK_RANGE *range_arg)
+int QUICK_RANGE_SELECT::cmp_prev(QUICK_RANGE *range_arg)
{
if (range_arg->flag & NO_MIN_RANGE)
return 0; /* key can't be to small */
@@ -3563,6 +3698,9 @@ static void print_quick_sel_imerge(QUICK_INDEX_MERGE_SELECT *quick,
{
print_quick_sel_range(quick_range_sel, needed_reg);
}
+ if (quick->pk_quick_select)
+ print_quick_sel_range(quick->pk_quick_select, needed_reg);
+
DBUG_VOID_RETURN;
}
@@ -3574,12 +3712,15 @@ void print_quick_sel_range(QUICK_RANGE_SELECT *quick,const key_map *needed_reg)
if (! _db_on_ || !quick)
DBUG_VOID_RETURN;
- List_iterator<QUICK_RANGE> li(quick->ranges);
DBUG_LOCK_FILE;
fprintf(DBUG_FILE,"Used quick_range on key: %d (other_keys: 0x%s):\n",
quick->index, needed_reg->print(buf));
- while ((range=li++))
+
+ QUICK_RANGE **pr= (QUICK_RANGE**)quick->ranges.buffer;
+ QUICK_RANGE **last_range= pr + quick->ranges.elements;
+ for (; pr!=last_range; ++pr)
{
+ range= *pr;
if (!(range->flag & NO_MIN_RANGE))
{
print_key(quick->key_parts,range->min_key,range->min_length);
diff --git a/sql/opt_range.h b/sql/opt_range.h
index 9e392104818..35a0cb5df88 100644
--- a/sql/opt_range.h
+++ b/sql/opt_range.h
@@ -68,7 +68,7 @@ class QUICK_RANGE :public Sql_alloc {
/*
Quick select interface.
- This class is parent for all QUICK_*_SELECT and FT_SELECT classes.
+ This class is a parent for all QUICK_*_SELECT and FT_SELECT classes.
*/
class QUICK_SELECT_I
@@ -128,19 +128,29 @@ protected:
SEL_ARG *key_tree,
MEM_ROOT *alloc);
friend class QUICK_SELECT_DESC;
+ friend class QUICK_INDEX_MERGE_SELECT;
+
+ DYNAMIC_ARRAY ranges; /* ordered array of range ptrs */
+ QUICK_RANGE **cur_range; /* current element in ranges */
- List<QUICK_RANGE> ranges;
- List_iterator<QUICK_RANGE> it;
QUICK_RANGE *range;
MEM_ROOT alloc;
KEY_PART *key_parts;
int cmp_next(QUICK_RANGE *range);
+ int cmp_prev(QUICK_RANGE *range);
+ bool row_in_ranges();
public:
QUICK_RANGE_SELECT(THD *thd, TABLE *table,uint index_arg,bool no_alloc=0,
MEM_ROOT *parent_alloc=NULL);
~QUICK_RANGE_SELECT();
- int reset(void) { next=0; it.rewind(); return 0; }
+ int reset(void)
+ {
+ next=0;
+ range= NULL;
+ cur_range= NULL;
+ return 0;
+ }
int init();
int get_next();
bool reverse_sorted() { return 0; }
@@ -148,9 +158,60 @@ public:
int get_type() { return QS_TYPE_RANGE; }
};
+
/*
- Index merge quick select.
- It is implemented as a container for several QUICK_RANGE_SELECTs.
+QUICK_INDEX_MERGE_SELECT - index_merge acces method quick select.
+
+ QUICK_INDEX_MERGE_SELECT uses
+ * QUICK_RANGE_SELECTs to get rows
+ * Unique class to remove duplicate rows
+
+INDEX MERGE OPTIMIZER
+ Current implementation doesn't detect all cases where index_merge could be
+ used, in particular:
+ * index_merge will never be used if range scan is possible (even if range
+ scan is more expensive)
+
+ * index_merge+'using index' is not supported (this the consequence of the
+ above restriction)
+
+ * If WHERE part contains complex nested AND and OR conditions, some ways to
+ retrieve rows using index_merge will not be considered. The choice of
+ read plan may depend on the order of conjuncts/disjuncts in WHERE part of
+ the query, see comments near SEL_IMERGE::or_sel_tree_with_checks and
+ imerge_list_or_list function for details.
+
+ * there is no "index_merge_ref" method (but index_merge on non-first table
+ in join is possible with 'range checked for each record').
+
+ See comments around SEL_IMERGE class and test_quick_select for more details.
+
+ROW RETRIEVAL ALGORITHM
+
+ index_merge uses Unique class for duplicates removal. Index merge takes
+ advantage of clustered covering primary key (CCPK) if the table has one.
+ The algorithm is as follows:
+
+ prepare() //implemented in QUICK_INDEX_MERGE_SELECT::prepare_unique
+ {
+ activate 'index only';
+ while(retrieve next row for non-CCPK scan)
+ {
+ if (there is a CCPK scan and row will be retrieved by it)
+ skip this row;
+ else
+ put rowid into Unique;
+ }
+ deactivate 'index only';
+ }
+
+ fetch() //implemented as sequence of QUICK_INDEX_MERGE_SELECT::get_next calls
+ {
+ retrieve all rows from row pointers stored in Unique;
+ free Unique;
+ retrieve all rows for CCPK scan;
+ }
+
*/
class QUICK_INDEX_MERGE_SELECT : public QUICK_SELECT_I
@@ -175,8 +236,14 @@ public:
List_iterator_fast<QUICK_RANGE_SELECT> cur_quick_it;
QUICK_RANGE_SELECT* cur_quick_select;
- /* last element in quick_selects list. */
+ /* last element in quick_selects list */
QUICK_RANGE_SELECT* last_quick_select;
+
+ /* quick select that uses Covering Clustered Primary Key (NULL if none) */
+ QUICK_RANGE_SELECT* pk_quick_select;
+
+ /* true if this select is currently doing a CCPK scan */
+ bool doing_pk_scan;
Unique *unique;
MEM_ROOT alloc;
@@ -184,6 +251,9 @@ public:
THD *thd;
int prepare_unique();
bool reset_called;
+
+ /* used to get rows collected in Unique */
+ READ_RECORD read_record;
};
class QUICK_SELECT_DESC: public QUICK_RANGE_SELECT
@@ -194,7 +264,6 @@ public:
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);
#ifdef NOT_USED
bool test_if_null_range(QUICK_RANGE *range, uint used_key_parts);
diff --git a/sql/records.cc b/sql/records.cc
index 02c0cc8cba9..f8fbfe62187 100644
--- a/sql/records.cc
+++ b/sql/records.cc
@@ -97,8 +97,8 @@ void init_read_record(READ_RECORD *info,THD *thd, TABLE *table,
}
}
}
- else if (select && select->quick &&
- (select->quick->get_type() != QUICK_SELECT_I::QS_TYPE_INDEX_MERGE))
+ else if (select && select->quick)
+ //&& (select->quick->get_type() != QUICK_SELECT_I::QS_TYPE_INDEX_MERGE))
{
DBUG_PRINT("info",("using rr_quick"));
info->read_record=rr_quick;