summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sql/handler.h4
-rw-r--r--sql/multi_range_read.cc1122
-rw-r--r--sql/multi_range_read.h346
3 files changed, 878 insertions, 594 deletions
diff --git a/sql/handler.h b/sql/handler.h
index 40f2d321241..6ff252632ee 100644
--- a/sql/handler.h
+++ b/sql/handler.h
@@ -2177,7 +2177,8 @@ public:
TRUE if the engine supports virtual columns
*/
virtual bool check_if_supported_virtual_columns(void) { return FALSE;}
-
+
+ TABLE* get_table() { return table; }
protected:
/* deprecated, don't use in new engines */
inline void ha_statistic_increment(ulong SSV::*offset) const { }
@@ -2370,7 +2371,6 @@ private:
virtual int rename_partitions(const char *path)
{ return HA_ERR_WRONG_COMMAND; }
friend class ha_partition;
- friend class DsMrr_impl;
public:
/* XXX to be removed, see ha_partition::partition_ht() */
virtual handlerton *partition_ht() const
diff --git a/sql/multi_range_read.cc b/sql/multi_range_read.cc
index 58b5cd3da50..4df2c4209d9 100644
--- a/sql/multi_range_read.cc
+++ b/sql/multi_range_read.cc
@@ -214,7 +214,6 @@ handler::multi_range_read_init(RANGE_SEQ_IF *seq_funcs, void *seq_init_param,
DBUG_RETURN(0);
}
-
/**
Get next record in MRR scan
@@ -230,7 +229,7 @@ handler::multi_range_read_init(RANGE_SEQ_IF *seq_funcs, void *seq_init_param,
int handler::multi_range_read_next(char **range_info)
{
- int UNINIT_VAR(result);
+ int result= HA_ERR_END_OF_FILE;
int range_res;
DBUG_ENTER("handler::multi_range_read_next");
@@ -284,6 +283,416 @@ scan_it_again:
}
+/***** MRR_impl classes ****************************************************/
+
+int Mrr_simple_index_reader::get_next(char **range_info)
+{
+ while (!(res= h->handler::multi_range_read_next(range_info)))
+ {
+ KEY_MULTI_RANGE *curr_range= &h->handler::mrr_cur_range;
+ if (!h->mrr_funcs.skip_index_tuple ||
+ !h->mrr_funcs.skip_index_tuple(h->mrr_iter, curr_range->ptr))
+ break;
+ }
+ return res;
+}
+
+int Mrr_simple_index_reader::init(handler *h_arg, RANGE_SEQ_IF *seq_funcs,
+ void *seq_init_param, uint n_ranges,
+ uint mode, Buffer_manager *buf_manager_arg)
+{
+ HANDLER_BUFFER no_buffer = {NULL, NULL, NULL};
+ h= h_arg;
+ return h->handler::multi_range_read_init(seq_funcs, seq_init_param, n_ranges,
+ mode, &no_buffer);
+}
+
+/**
+ DS-MRR/CPK: multi_range_read_next() function
+
+ @param range_info OUT identifier of range that the returned record belongs to
+
+ @note
+ This function walks over key buffer and does index reads, i.e. it produces
+ {current_record, range_id} pairs.
+
+ The function has the same call contract like multi_range_read_next()'s.
+
+ We actually iterate over nested sequences:
+ - a disjoint sequence of index ranges
+ - each range has multiple records
+ - each record goes into multiple identical ranges.
+
+ @retval 0 OK, next record was successfully read
+ @retval HA_ERR_END_OF_FILE End of records
+ @retval Other Some other error
+*/
+
+int Mrr_ordered_index_reader::get_next(char **range_info_arg)
+{
+ DBUG_ENTER("Mrr_ordered_index_reader::get_next");
+
+ while (1)
+ {
+ bool have_record= FALSE;
+ if (scanning_key_val_iter)
+ {
+ if (kv_it.get_next())
+ {
+ kv_it.close();
+ scanning_key_val_iter= FALSE;
+ }
+ else
+ have_record= TRUE;
+ }
+ else
+ {
+ while (kv_it.init(this))
+ {
+ if (key_buffer->is_empty())
+ {
+ if (auto_refill)
+ {
+ int res;
+ if ((res= refill_buffer()))
+ return res;
+ if (key_buffer->is_empty())
+ {
+ index_scan_eof= TRUE;
+ DBUG_RETURN(HA_ERR_END_OF_FILE);
+ }
+ }
+ else
+ {
+ /* Buffer refills are managed by somebody else for us */
+ index_scan_eof= TRUE;
+ DBUG_RETURN(HA_ERR_END_OF_FILE);
+ }
+ }
+ }
+ scanning_key_val_iter= TRUE;
+ }
+
+ if (have_record &&
+ (!mrr_funcs.skip_index_tuple ||
+ !mrr_funcs.skip_index_tuple(mrr_iter, *(char**)cur_range_info))
+ &&
+ (!mrr_funcs.skip_record ||
+ !mrr_funcs.skip_record(mrr_iter, *(char**)cur_range_info, NULL)))
+ {
+ break;
+ }
+ /* Go get another (record, range_id) combination */
+ } /* while */
+
+ memcpy(range_info_arg, cur_range_info, sizeof(void*));
+ DBUG_RETURN(0);
+}
+
+
+/**
+ DS-MRR/CPK: Fill the buffer with (lookup_tuple, range_id) pairs and sort
+
+ Enumerate the input range (=key) sequence, fill the key buffer with
+ (lookup_key, range_id) pairs and sort it.
+
+ When this function returns, either
+ - key buffer is non-empty, or
+ - key buffer is empty and source range sequence is exhausted
+
+ @note
+ dsmrr_eof is set to indicate whether we've exhausted the list of ranges
+ we're scanning.
+*/
+
+int Mrr_ordered_index_reader::refill_buffer()
+{
+ int res;
+ KEY_MULTI_RANGE cur_range;
+ uchar **range_info_ptr= (uchar**)&cur_range.ptr;
+ uchar *key_ptr;
+ DBUG_ENTER("Mrr_ordered_index_reader::refill_buffer");
+
+ DBUG_ASSERT(!know_key_tuple_params || key_buffer->is_empty());
+ if (know_key_tuple_params)
+ {
+ buf_manager->reset_buffer_sizes();
+ key_buffer->reset();
+ key_buffer->setup_writing(&key_ptr, keypar.key_size_in_keybuf,
+ is_mrr_assoc? (uchar**)&range_info_ptr : NULL,
+ sizeof(uchar*));
+ }
+#if 0
+
+ if (know_key_tuple_params)
+ {
+ if (do_rndpos_scan && rowid_buffer.is_empty())
+ {
+ /*
+ We're using two buffers and both of them are empty now. Restore the
+ original sizes
+ */
+ rowid_buffer.set_buffer_space(full_buf, rowid_buffer_end);
+ key_buffer= &backward_key_buf;
+ key_buffer->set_buffer_space(rowid_buffer_end, full_buf_end);
+ }
+ }
+ is all of the ifdef-ed stuff is handled above?
+#endif
+ while ((!know_key_tuple_params || key_buffer->can_write()) &&
+ !(res= mrr_funcs.next(mrr_iter, &cur_range)))
+ {
+ DBUG_ASSERT(cur_range.range_flag & EQ_RANGE);
+
+ if (!know_key_tuple_params)
+ {
+ /* This only happens when we've just started filling the buffer */
+ key_range *sample_key= &cur_range.start_key;
+ know_key_tuple_params= TRUE;
+ keypar.key_tuple_length= sample_key->length;
+ keypar.key_tuple_map= sample_key->keypart_map;
+ keypar.key_size_in_keybuf= keypar.use_key_pointers ? sizeof(char*) : keypar.key_tuple_length;
+ KEY *key_info= &h->get_table()->key_info[h->active_index];
+ keypar.index_ranges_unique= test(key_info->flags & HA_NOSAME &&
+ key_info->key_parts ==
+ my_count_bits(sample_key->keypart_map));
+ buf_manager->setup_buffer_sizes(keypar.key_size_in_keybuf, keypar.key_tuple_map);
+ key_buffer= buf_manager->get_key_buffer();
+ key_buffer->setup_writing(&key_ptr, keypar.key_size_in_keybuf,
+ is_mrr_assoc? (uchar**)&range_info_ptr : NULL,
+ sizeof(uchar*));
+ DBUG_ASSERT(key_buffer->can_write());
+ }
+
+ /* Put key, or {key, range_id} pair into the buffer */
+ if (keypar.use_key_pointers)
+ key_ptr=(uchar*) &cur_range.start_key.key;
+ else
+ key_ptr=(uchar*) cur_range.start_key.key;
+
+ key_buffer->write();
+ }
+
+ no_more_keys= test(res);
+
+ key_buffer->sort((key_buffer->type() == Lifo_buffer::FORWARD)?
+ (qsort2_cmp)Mrr_ordered_index_reader::key_tuple_cmp_reverse :
+ (qsort2_cmp)Mrr_ordered_index_reader::key_tuple_cmp,
+ (void*)this);
+
+ scanning_key_val_iter= FALSE;
+ index_scan_eof= FALSE;
+
+ DBUG_RETURN(0);
+}
+
+
+int Mrr_ordered_index_reader::init(handler *h_arg, RANGE_SEQ_IF *seq_funcs,
+ void *seq_init_param, uint n_ranges,
+ uint mode, Buffer_manager *buf_manager_arg)
+{
+ h= h_arg;
+ mrr_iter= seq_funcs->init(seq_init_param, n_ranges, mode);
+ keypar.use_key_pointers= test(mode & HA_MRR_MATERIALIZED_KEYS);
+ is_mrr_assoc= !test(mode & HA_MRR_NO_ASSOCIATION);
+ mrr_funcs= *seq_funcs;
+ know_key_tuple_params= FALSE;
+ buf_manager= buf_manager_arg;
+ return 0;
+}
+
+
+static int rowid_cmp_reverse(void *h, uchar *a, uchar *b)
+{
+ return - ((handler*)h)->cmp_ref(a, b);
+}
+
+
+int Mrr_ordered_rndpos_reader::init(handler *h_arg,
+ Mrr_index_reader *index_reader_arg,
+ uint mode,
+ Lifo_buffer *buf)
+{
+ h= h_arg;
+ index_reader= index_reader_arg;
+ rowid_buffer= buf;
+ is_mrr_assoc= !test(mode & HA_MRR_NO_ASSOCIATION);
+ //rowid_buff_elem_size= h->ref_length;
+ //if (!(mode & HA_MRR_NO_ASSOCIATION))
+ // rowid_buff_elem_size += sizeof(char*);
+
+ return index_reader->refill_buffer();
+}
+
+
+/**
+ DS-MRR: Fill and sort the rowid buffer
+
+ Scan the MRR ranges and collect ROWIDs (or {ROWID, range_id} pairs) into
+ buffer. When the buffer is full or scan is completed, sort the buffer by
+ rowid and return.
+
+ When this function returns, either rowid buffer is not empty, or the source
+ of lookup keys (i.e. ranges) is exhaused.
+
+ dsmrr_eof is set to indicate whether we've exhausted the list of ranges we're
+ scanning. This function never returns HA_ERR_END_OF_FILE.
+
+ @retval 0 OK, the next portion of rowids is in the buffer,
+ properly ordered
+ @retval other Error
+*/
+
+int Mrr_ordered_rndpos_reader::refill_buffer()
+{
+ char *range_info;
+ uchar **range_info_ptr= (uchar**)&range_info;
+ int res;
+ DBUG_ENTER("Mrr_ordered_rndpos_reader::refill_buffer");
+
+ DBUG_ASSERT(rowid_buffer->is_empty());
+ index_rowid= index_reader->get_rowid_ptr();
+ rowid_buffer->reset();
+ rowid_buffer->setup_writing(&index_rowid, h->ref_length,
+ is_mrr_assoc? (uchar**)&range_info_ptr: NULL,
+ sizeof(void*));
+
+ last_identical_rowid= NULL;
+
+ while (rowid_buffer->can_write())
+ {
+ res= index_reader->get_next(&range_info);
+
+ if (res)
+ break;
+
+ /* Put rowid, or {rowid, range_id} pair into the buffer */
+ index_reader->h->position(index_reader->h->get_table()->record[0]);
+
+ rowid_buffer->write();
+ }
+
+ if (res && res != HA_ERR_END_OF_FILE)
+ DBUG_RETURN(res);
+
+
+ /* Sort the buffer contents by rowid */
+ rowid_buffer->sort((qsort2_cmp)rowid_cmp_reverse, (void*)h);
+
+ rowid_buffer->setup_reading(&rowid, h->ref_length,
+ is_mrr_assoc? (uchar**)&rowids_range_id: NULL,
+ sizeof(void*));
+ DBUG_RETURN(0);
+}
+
+
+/**
+ DS-MRR implementation: multi_range_read_next() function.
+
+ Calling convention is like multi_range_read_next() has.
+*/
+
+int Mrr_ordered_rndpos_reader::get_next(char **range_info)
+{
+ int res;
+
+ while (last_identical_rowid)
+ {
+ /*
+ Current record (the one we've returned in previous call) was obtained
+ from a rowid that matched multiple range_ids. Return this record again,
+ with next matching range_id.
+ */
+ bool UNINIT_VAR(bres);
+ bres= rowid_buffer->read();
+ DBUG_ASSERT(!bres);
+
+ if (is_mrr_assoc)
+ memcpy(range_info, rowids_range_id, sizeof(uchar*));
+
+ if (rowid == last_identical_rowid)
+ {
+ last_identical_rowid= NULL; /* reached the last of identical rowids */
+ }
+
+ if (!index_reader->skip_record((char*)*range_info, rowid))
+ {
+ return 0;
+ }
+ }
+
+ while (1)
+ {
+ if (rowid_buffer->is_empty())
+ {
+ /*
+ We're out of rowids. If there are still some sorted keys, use up them
+ first (that is, don't call re-fill for keys when we still have some).
+ */
+ if (!index_reader->eof())
+ {
+ if ((res= refill_buffer()))
+ return res; /* for fatal errors */
+ }
+ else
+ {
+ //TODO: here: redistribute the buffer space, then refill the index
+ //reader, then refill us.
+ }
+ }
+
+ last_identical_rowid= NULL;
+
+ /* Return eof if there are no rowids in the buffer after re-fill attempt */
+ if (rowid_buffer->read())
+ return HA_ERR_END_OF_FILE;
+
+ if (is_mrr_assoc)
+ {
+ memcpy(range_info, rowids_range_id, sizeof(uchar*));
+ }
+
+ if (index_reader->skip_record(*range_info, rowid))
+ continue;
+
+ res= h->ha_rnd_pos(h->get_table()->record[0], rowid);
+
+ if (res == HA_ERR_RECORD_DELETED)
+ continue;
+
+ /*
+ Check if subsequent buffer elements have the same rowid value as this
+ one. If yes, remember this fact so that we don't make any more rnd_pos()
+ calls with this value.
+ */
+ if (!res)
+ {
+ uchar *cur_rowid= rowid;
+ /*
+ Note: this implies that SQL layer doesn't touch table->record[0]
+ between calls.
+ */
+ Lifo_buffer_iterator it;
+ it.init(rowid_buffer);
+ while (!it.read()) // reads to (rowid, ...)
+ {
+ if (h->cmp_ref(rowid, cur_rowid))
+ break;
+ last_identical_rowid= rowid;
+ }
+ }
+ return 0;
+ }
+
+ return res;
+}
+
+
+
+
+/************ MRR_impl classes end *********************************************/
+
+
/****************************************************************************
* DS-MRR implementation
***************************************************************************/
@@ -310,9 +719,8 @@ int DsMrr_impl::dsmrr_init(handler *h_arg, RANGE_SEQ_IF *seq_funcs,
void *seq_init_param, uint n_ranges, uint mode,
HANDLER_BUFFER *buf)
{
- Item *pushed_cond= NULL;
- handler *new_h2= 0;
THD *thd= current_thd;
+ int res;
DBUG_ENTER("DsMrr_impl::dsmrr_init");
/*
@@ -320,88 +728,130 @@ int DsMrr_impl::dsmrr_init(handler *h_arg, RANGE_SEQ_IF *seq_funcs,
has not been called, so set the owner handler here as well.
*/
h= h_arg;
- if (mode & HA_MRR_USE_DEFAULT_IMPL || mode & HA_MRR_SORTED)
+ is_mrr_assoc= !test(mode & HA_MRR_NO_ASSOCIATION);
+
+ if ((mode & HA_MRR_USE_DEFAULT_IMPL) || (mode & HA_MRR_SORTED))
{
- use_default_impl= TRUE;
- const int retval=
- h->handler::multi_range_read_init(seq_funcs, seq_init_param, n_ranges,
- mode, buf);
- DBUG_RETURN(retval);
+ DBUG_ASSERT(h->inited == handler::INDEX);
+ Mrr_simple_index_reader *s= &strategy_factory.simple_index_reader;
+ res= s->init(h, seq_funcs, seq_init_param, n_ranges, mode, this);
+ strategy= s;
+ DBUG_RETURN(res);
}
- use_default_impl= FALSE;
- is_mrr_assoc= !test(mode & HA_MRR_NO_ASSOCIATION);
+ /* Neither of strategies used below can handle sorting */
+ DBUG_ASSERT(!(mode & HA_MRR_SORTED));
+
/*
Determine whether we'll need to do key sorting and/or rnd_pos() scan
*/
- do_sort_keys= FALSE;
- if ((mode & HA_MRR_SINGLE_POINT) &&
- optimizer_flag(thd, OPTIMIZER_SWITCH_MRR_SORT_KEYS))
+ index_strategy= NULL;
+ Mrr_ordered_index_reader *ordered_idx_reader= NULL;
+ if ((mode & HA_MRR_SINGLE_POINT) &&
+ optimizer_flag(thd, OPTIMIZER_SWITCH_MRR_SORT_KEYS))
{
- do_sort_keys= TRUE;
- use_key_pointers= test(mode & HA_MRR_MATERIALIZED_KEYS);
+ index_strategy= ordered_idx_reader= &strategy_factory.ordered_index_reader;
}
+ else
+ index_strategy= &strategy_factory.simple_index_reader;
- do_rndpos_scan= FALSE;
- bool doing_cpk_scan= check_cpk_scan(thd, h->inited == handler::INDEX?
- h->active_index: h2->active_index, mode);
- if (!doing_cpk_scan /* && !index_only_read */)
+ strategy= index_strategy;
+ /*
+ We don't need a rowid-to-rndpos step if
+ - We're doing a scan on clustered primary key
+ - [In the future] We're doing an index_only read
+ */
+ DBUG_ASSERT(h->inited == handler::INDEX ||
+ (h->inited == handler::RND && h2 &&
+ h2->inited == handler::INDEX));
+
+ handler *h_idx= (h->inited == handler::INDEX)? h: h2;
+ keyno= h_idx->active_index;
+
+ Mrr_ordered_rndpos_reader *disk_strategy= NULL;
+ if (!(keyno == table->s->primary_key && h_idx->primary_key_is_clustered()))
{
- /* Will use rowid buffer to store/sort rowids, etc */
- do_rndpos_scan= TRUE;
+ strategy= disk_strategy= &strategy_factory.ordered_rndpos_reader;
}
- /*
- We should either sort keys, or do ordered rnd_pos scan, or both. If we
- decide to do neither, we should have used default MRR implementation.
- */
- DBUG_ASSERT(do_sort_keys || do_rndpos_scan);
-
-
if (is_mrr_assoc)
- status_var_increment(table->in_use->status_var.ha_multi_range_read_init_count);
+ status_var_increment(thd->status_var.ha_multi_range_read_init_count);
- /*
- At start, alloc all of the buffer for rowids. When/if key sorting code
- figures how much buffer space it needs, it will call setup_buffer_sizes()
- to re-distribute the buffer space.
- */
full_buf= buf->buffer;
full_buf_end= buf->buffer_end;
- rowid_buffer.set_buffer_space(full_buf, full_buf_end);
- if (do_sort_keys)
+ if (strategy == index_strategy)
{
- know_key_tuple_params= FALSE;
- h->mrr_iter= seq_funcs->init(seq_init_param, n_ranges, mode);
- h->mrr_funcs= *seq_funcs;
- keyno= (h->inited == handler::INDEX)? h->active_index : h2->active_index;
- dsmrr_fill_key_buffer();
-
- if (dsmrr_eof && !do_rndpos_scan)
- buf->end_of_used_area= key_buffer->end_of_space();
+ if (ordered_idx_reader)
+ ordered_idx_reader->auto_refill= TRUE;
+ /* Index strategy serves it all. We don't need two handlers, etc */
+ /* Give the buffer to index strategy */
+ if ((res= index_strategy->init(h, seq_funcs, seq_init_param, n_ranges,
+ mode, this)))
+ goto error;
}
-
- if (!do_rndpos_scan)
+ else
{
- /*
- We have the keys and won't need to fetch rowids, as key lookup will be
- the last operation, done in multi_range_read_next().
+ /*
+ If we got here the request is served by both index and rndpos strategies
+ working together.
+
*/
- DBUG_RETURN(0);
+ rowid_buffer.set_buffer_space(buf->buffer, buf->buffer_end);
+
+ if ((res= setup_two_handlers()))
+ DBUG_RETURN(res);
+
+ if (ordered_idx_reader)
+ ordered_idx_reader->auto_refill= FALSE;
+
+ if ((res= index_strategy->init(h2, seq_funcs, seq_init_param, n_ranges,
+ mode, this)) ||
+ (res= disk_strategy->init(h, index_strategy, mode, &rowid_buffer)))
+ {
+ goto error;
+ }
}
- rowid_buff_elem_size= h->ref_length + (is_mrr_assoc? sizeof(char*) : 0);
+ if (strategy->refill_buffer())
+ goto error;
+
/*
- There can be two cases:
- - This is the first call since index_init(), h2==NULL
- Need to setup h2 then.
- - This is not the first call, h2 is initalized and set up appropriately.
- The caller might have called h->index_init(), need to switch h to
- rnd_pos calls.
+ If we have scanned through all intervals in *seq, then adjust *buf to
+ indicate that the remaining buffer space will not be used.
*/
+// if (dsmrr_eof)
+// buf->end_of_used_area= rowid_buffer.end_of_space();
+
+
+ DBUG_RETURN(0);
+error:
+ close_second_handler();
+ strategy= NULL;
+ DBUG_RETURN(1);
+}
+
+
+/*
+ Whatever the current state is, make it so that we have two handler objects:
+ - h (the primary) - initialized for rnd_pos() scan
+ - h2 (the secondary) - initialized for scanning the index specified in
+ this->keyno
+ RETURN
+ 0 OK
+ HA_XXX Error code
+*/
+
+int DsMrr_impl::setup_two_handlers()
+{
+ int res;
+ THD *thd= current_thd;
+ DBUG_ENTER("DsMrr_impl::setup_two_handlers");
if (!h2)
{
+ handler *new_h2;
+ Item *pushed_cond= NULL;
+ DBUG_ASSERT(h->inited == handler::INDEX);
/* Create a separate handler object to do rnd_pos() calls. */
/*
::clone() takes up a lot of stack, especially on 64 bit platforms.
@@ -409,8 +859,6 @@ int DsMrr_impl::dsmrr_init(handler *h_arg, RANGE_SEQ_IF *seq_funcs,
*/
if (check_stack_overrun(thd, 5*STACK_MIN_SIZE, (uchar*) &new_h2))
DBUG_RETURN(1);
- DBUG_ASSERT(h->active_index != MAX_KEY);
- keyno= h->active_index;
/* Create a separate handler object to do rnd_pos() calls. */
if (!(new_h2= h->clone(thd->mem_root)) ||
@@ -422,25 +870,27 @@ int DsMrr_impl::dsmrr_init(handler *h_arg, RANGE_SEQ_IF *seq_funcs,
if (keyno == h->pushed_idx_cond_keyno)
pushed_cond= h->pushed_idx_cond;
-
+
+ Mrr_strategy *save_strategy= strategy;
+ strategy= NULL;
/*
Caution: this call will invoke this->dsmrr_close(). Do not put the
- created secondary table handler into this->h2 or it will delete it.
+ created secondary table handler new_h2 into this->h2 or it will delete
+ it. Also, save the picked strategy
*/
- if (h->ha_index_end())
- {
- h2=new_h2;
- goto error;
- }
+ res= h->ha_index_end();
- use_default_impl= FALSE;
+ strategy= save_strategy;
h2= new_h2; /* Ok, now can put it into h2 */
+
+ if (res || (res= (h->ha_rnd_init(FALSE))))
+ goto error;
+
table->prepare_for_position();
h2->extra(HA_EXTRA_KEYREAD);
- h2->mrr_funcs= *seq_funcs; //psergey3-todo: sort out where to store
h2->mrr_iter= h->mrr_iter;
- if (h2->ha_index_init(keyno, FALSE))
+ if ((res= h2->ha_index_init(keyno, FALSE)))
goto error;
if (pushed_cond)
@@ -448,66 +898,39 @@ int DsMrr_impl::dsmrr_init(handler *h_arg, RANGE_SEQ_IF *seq_funcs,
}
else
{
+ DBUG_ASSERT(h2 && h2->inited==handler::INDEX);
/*
We get here when the access alternates betwen MRR scan(s) and non-MRR
scans.
Calling h->index_end() will invoke dsmrr_close() for this object,
- which will delete h2. We need to keep it, so save put it away and dont
+ which will delete h2. We need to keep it, so put it away and dont
let it be deleted:
*/
- handler *save_h2= h2;
- h2= NULL;
- int res= (h->inited == handler::INDEX && h->ha_index_end());
- h2= save_h2;
- use_default_impl= FALSE;
- if (res)
+ if (h->inited == handler::INDEX)
+ {
+ handler *save_h2= h2;
+ Mrr_strategy *save_strategy= strategy;
+ h2= NULL;
+ strategy= NULL;
+ res= h->ha_index_end();
+ h2= save_h2;
+ strategy= save_strategy;
+ if (res)
+ goto error;
+ }
+ if ((h->inited == handler::RND) && h->ha_rnd_init(FALSE))
goto error;
}
-
- if (!do_sort_keys &&
- h2->handler::multi_range_read_init(seq_funcs, seq_init_param, n_ranges,
- mode, buf))
- {
- goto error;
- }
-
- if (dsmrr_fill_rowid_buffer())
- {
- goto error;
- }
- /*
- If the above call has scanned through all intervals in *seq, then
- adjust *buf to indicate that the remaining buffer space will not be used.
- */
-// if (dsmrr_eof)
-// buf->end_of_used_area= rowid_buffer.end_of_space();
-
- /*
- h->inited == INDEX may occur when 'range checked for each record' is
- used.
- */
- if ((h->inited != handler::RND) &&
- ((h->inited==handler::INDEX? h->ha_index_end(): FALSE) ||
- (h->ha_rnd_init(FALSE))))
- goto error;
-
- h->mrr_funcs= *seq_funcs;
-
DBUG_RETURN(0);
error:
- h2->ha_index_or_rnd_end();
- h2->ha_external_lock(current_thd, F_UNLCK);
- h2->close();
- delete h2;
- h2= NULL;
- DBUG_RETURN(1);
+ //close_second_handler(); -- caller does that
+ DBUG_RETURN(res);
}
-void DsMrr_impl::dsmrr_close()
+void DsMrr_impl::close_second_handler()
{
- DBUG_ENTER("DsMrr_impl::dsmrr_close");
if (h2)
{
h2->ha_index_or_rnd_end();
@@ -516,84 +939,15 @@ void DsMrr_impl::dsmrr_close()
delete h2;
h2= NULL;
}
- use_default_impl= TRUE;
- DBUG_VOID_RETURN;
}
-static int rowid_cmp_reverse(void *h, uchar *a, uchar *b)
-{
- return - ((handler*)h)->cmp_ref(a, b);
-}
-
-
-/**
- DS-MRR: Fill and sort the rowid buffer
-
- Scan the MRR ranges and collect ROWIDs (or {ROWID, range_id} pairs) into
- buffer. When the buffer is full or scan is completed, sort the buffer by
- rowid and return.
-
- When this function returns, either rowid buffer is not empty, or the source
- of lookup keys (i.e. ranges) is exhaused.
-
- dsmrr_eof is set to indicate whether we've exhausted the list of ranges we're
- scanning. This function never returns HA_ERR_END_OF_FILE.
-
- @retval 0 OK, the next portion of rowids is in the buffer,
- properly ordered
- @retval other Error
-*/
-
-int DsMrr_impl::dsmrr_fill_rowid_buffer()
+void DsMrr_impl::dsmrr_close()
{
- char *range_info;
- uchar **range_info_ptr= (uchar**)&range_info;
- int res;
- DBUG_ENTER("DsMrr_impl::dsmrr_fill_rowid_buffer");
-
- DBUG_ASSERT(rowid_buffer.is_empty());
- rowid_buffer.reset();
- rowid_buffer.setup_writing(&h2->ref, h2->ref_length,
- is_mrr_assoc? (uchar**)&range_info_ptr: NULL,
- sizeof(void*));
-
- last_identical_rowid= NULL;
-
- while (rowid_buffer.can_write())
- {
- if (do_sort_keys)
- res= dsmrr_next_from_index(&range_info);
- else
- res= h2->handler::multi_range_read_next(&range_info);
-
- if (res)
- break;
-
- KEY_MULTI_RANGE *curr_range= &h2->handler::mrr_cur_range;
- if (!do_sort_keys && /* If keys are sorted then this check is already done */
- h2->mrr_funcs.skip_index_tuple &&
- h2->mrr_funcs.skip_index_tuple(h2->mrr_iter, curr_range->ptr))
- continue;
-
- /* Put rowid, or {rowid, range_id} pair into the buffer */
- h2->position(table->record[0]);
-
- rowid_buffer.write();
- }
-
- if (res && res != HA_ERR_END_OF_FILE)
- DBUG_RETURN(res);
-
- if (!do_sort_keys)
- dsmrr_eof= test(res == HA_ERR_END_OF_FILE);
-
- /* Sort the buffer contents by rowid */
- rowid_buffer.sort((qsort2_cmp)rowid_cmp_reverse, (void*)h);
-
- rowid_buffer.setup_reading(&rowid, h->ref_length,
- is_mrr_assoc? (uchar**)&rowids_range_id: NULL, sizeof(void*));
- DBUG_RETURN(0);
+ DBUG_ENTER("DsMrr_impl::dsmrr_close");
+ close_second_handler();
+ strategy= NULL;
+ DBUG_VOID_RETURN;
}
@@ -601,21 +955,21 @@ int DsMrr_impl::dsmrr_fill_rowid_buffer()
my_qsort2-compatible function to compare key tuples
*/
-int DsMrr_impl::key_tuple_cmp(void* arg, uchar* key1, uchar* key2)
+int Mrr_ordered_index_reader::key_tuple_cmp(void* arg, uchar* key1, uchar* key2)
{
- DsMrr_impl *dsmrr= (DsMrr_impl*)arg;
- TABLE *table= dsmrr->h->table;
+ Mrr_ordered_index_reader *this_= (Mrr_ordered_index_reader*)arg;
+ TABLE *table= this_->h->get_table();
int res;
- KEY_PART_INFO *part= table->key_info[dsmrr->keyno].key_part;
+ KEY_PART_INFO *part= table->key_info[this_->h->active_index].key_part;
- if (dsmrr->use_key_pointers)
+ if (this_->keypar.use_key_pointers)
{
/* the buffer stores pointers to keys, get to the keys */
key1= *((uchar**)key1);
key2= *((uchar**)key2); // todo is this alignment-safe?
}
- uchar *key1_end= key1 + dsmrr->key_tuple_length;
+ uchar *key1_end= key1 + this_->keypar.key_tuple_length;
while (key1 < key1_end)
{
@@ -648,7 +1002,7 @@ equals:
}
-int DsMrr_impl::key_tuple_cmp_reverse(void* arg, uchar* key1, uchar* key2)
+int Mrr_ordered_index_reader::key_tuple_cmp_reverse(void* arg, uchar* key1, uchar* key2)
{
return -key_tuple_cmp(arg, key1, key2);
}
@@ -664,24 +1018,17 @@ int DsMrr_impl::key_tuple_cmp_reverse(void* arg, uchar* key1, uchar* key2)
This function must be called when all buffers are empty
*/
-void DsMrr_impl::setup_buffer_sizes(key_range *sample_key)
+void DsMrr_impl::setup_buffer_sizes(uint key_size_in_keybuf,
+ key_part_map key_tuple_map)
{
- key_tuple_length= sample_key->length;
- key_tuple_map= sample_key->keypart_map;
- key_size_in_keybuf= use_key_pointers ? sizeof(char*) :
- key_tuple_length;
- key_buff_elem_size= key_size_in_keybuf +
- (int)is_mrr_assoc * sizeof(void*);
+ uint key_buff_elem_size= key_size_in_keybuf +
+ (int)is_mrr_assoc * sizeof(void*);
- KEY *key_info= &h->table->key_info[keyno];
- index_ranges_unique= test(key_info->flags & HA_NOSAME &&
- key_info->key_parts ==
- my_count_bits(sample_key->keypart_map));
- if (!do_rndpos_scan)
+ KEY *key_info= &h->get_table()->key_info[keyno];
+ if (strategy == index_strategy)
{
- /* Give all space to forward key buffer. */
+ /* Give all space to the key buffer, key buffer must be forward */
key_buffer= &forward_key_buf;
- //identical_key_it= &forward_key_it;
key_buffer->set_buffer_space(full_buf, full_buf_end);
/* Just in case, tell rowid buffer that it has zero size: */
@@ -730,100 +1077,21 @@ void DsMrr_impl::setup_buffer_sizes(key_range *sample_key)
rowid_buffer_end= full_buf + bytes_for_rowids;
rowid_buffer.set_buffer_space(full_buf, rowid_buffer_end);
key_buffer= &backward_key_buf;
- //identical_key_it= &backward_key_it;
key_buffer->set_buffer_space(rowid_buffer_end, full_buf_end);
}
-/**
- DS-MRR/CPK: Fill the buffer with (lookup_tuple, range_id) pairs and sort
-
- Enumerate the input range (=key) sequence, fill the key buffer with
- (lookup_key, range_id) pairs and sort it.
-
- When this function returns, either
- - key buffer is non-empty, or
- - key buffer is empty and source range sequence is exhausted
-
- @note
- dsmrr_eof is set to indicate whether we've exhausted the list of ranges
- we're scanning.
-*/
-
-void DsMrr_impl::dsmrr_fill_key_buffer()
+void DsMrr_impl::reset_buffer_sizes()
{
- int res;
- KEY_MULTI_RANGE cur_range;
- uchar **range_info_ptr= (uchar**)&cur_range.ptr;
- DBUG_ENTER("DsMrr_impl::dsmrr_fill_key_buffer");
-
- DBUG_ASSERT(!know_key_tuple_params || key_buffer->is_empty());
-
- uchar *key_ptr;
- if (know_key_tuple_params)
- {
- if (do_rndpos_scan && rowid_buffer.is_empty())
- {
- /*
- We're using two buffers and both of them are empty now. Restore the
- original sizes
- */
- rowid_buffer.set_buffer_space(full_buf, rowid_buffer_end);
- key_buffer= &backward_key_buf;
- key_buffer->set_buffer_space(rowid_buffer_end, full_buf_end);
- }
- key_buffer->reset();
- key_buffer->setup_writing(&key_ptr, key_size_in_keybuf,
- is_mrr_assoc? (uchar**)&range_info_ptr : NULL,
- sizeof(uchar*));
- }
-
- while ((!know_key_tuple_params || key_buffer->can_write()) &&
- !(res= h->mrr_funcs.next(h->mrr_iter, &cur_range)))
- {
- DBUG_ASSERT(cur_range.range_flag & EQ_RANGE);
- if (!know_key_tuple_params)
- {
- /* This only happens when we've just started filling the buffer */
- setup_buffer_sizes(&cur_range.start_key);
- know_key_tuple_params= TRUE;
- key_buffer->setup_writing(&key_ptr, key_size_in_keybuf,
- is_mrr_assoc? (uchar**)&range_info_ptr : NULL,
- sizeof(uchar*));
- DBUG_ASSERT(key_buffer->can_write());
- }
-
- /* Put key, or {key, range_id} pair into the buffer */
- if (use_key_pointers)
- key_ptr=(uchar*) &cur_range.start_key.key;
- else
- key_ptr=(uchar*) cur_range.start_key.key;
-
- key_buffer->write();
- }
-
- dsmrr_eof= test(res);
-
- key_buffer->sort((key_buffer->type() == Lifo_buffer::FORWARD)?
- (qsort2_cmp)DsMrr_impl::key_tuple_cmp_reverse :
- (qsort2_cmp)DsMrr_impl::key_tuple_cmp,
- (void*)this);
-
- key_buffer->setup_reading(&cur_index_tuple, key_size_in_keybuf,
- is_mrr_assoc? (uchar**)&cur_range_info: NULL,
- sizeof(void*));
-
- scanning_key_val_iter= FALSE;
- index_scan_eof= FALSE;
-
- DBUG_VOID_RETURN;
+ rowid_buffer.set_buffer_space(full_buf, rowid_buffer_end);
+ key_buffer= &backward_key_buf;
+ key_buffer->set_buffer_space(rowid_buffer_end, full_buf_end);
}
-
/**
Take unused space from the key buffer and give it to the rowid buffer
*/
-
+//psergey-todo: do invoke this function.
void DsMrr_impl::reallocate_buffer_space()
{
uchar *unused_start, *unused_end;
@@ -834,37 +1102,43 @@ void DsMrr_impl::reallocate_buffer_space()
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
-bool Key_value_records_iterator::init(DsMrr_impl *dsmrr_arg)
+
+bool Key_value_records_iterator::init(Mrr_ordered_index_reader *owner_arg)
{
int res;
- dsmrr= dsmrr_arg;
- handler *file= dsmrr->do_rndpos_scan? dsmrr->h2 : dsmrr->h;
+ //h= h_arg;
+ //param= param_arg;
+ owner= owner_arg;
- identical_key_it.init(dsmrr->key_buffer);
+ identical_key_it.init(owner->key_buffer);
/* Get the first pair into (cur_index_tuple, cur_range_info) */
+ owner->key_buffer->setup_reading(&cur_index_tuple, owner->keypar.key_size_in_keybuf,
+ owner->is_mrr_assoc? (uchar**)&owner->cur_range_info: NULL,
+ sizeof(void*));
+
if (identical_key_it.read())
return TRUE;
- uchar *key_in_buf= dsmrr->cur_index_tuple;
+ uchar *key_in_buf= cur_index_tuple;
- last_identical_key_ptr= dsmrr->cur_index_tuple;
- if (dsmrr->use_key_pointers)
- dsmrr->cur_index_tuple= *((uchar**)dsmrr->cur_index_tuple);
+ last_identical_key_ptr= cur_index_tuple;
+ if (owner->keypar.use_key_pointers)
+ cur_index_tuple= *((uchar**)cur_index_tuple);
/* Check out how many more identical keys are following */
- uchar *save_cur_index_tuple= dsmrr->cur_index_tuple;
+ uchar *save_cur_index_tuple= cur_index_tuple;
while (!identical_key_it.read())
{
- if (DsMrr_impl::key_tuple_cmp(dsmrr, key_in_buf, dsmrr->cur_index_tuple))
+ if (Mrr_ordered_index_reader::key_tuple_cmp(owner, key_in_buf, cur_index_tuple))
break;
- last_identical_key_ptr= dsmrr->cur_index_tuple;
+ last_identical_key_ptr= cur_index_tuple;
}
- identical_key_it.init(dsmrr->key_buffer);
- dsmrr->cur_index_tuple= save_cur_index_tuple;
- res= file->ha_index_read_map(dsmrr->table->record[0],
- dsmrr->cur_index_tuple,
- dsmrr->key_tuple_map,
- HA_READ_KEY_EXACT);
+ identical_key_it.init(owner->key_buffer);
+ cur_index_tuple= save_cur_index_tuple;
+ res= owner->h->ha_index_read_map(owner->h->get_table()->record[0],
+ cur_index_tuple,
+ owner->keypar.key_tuple_map,
+ HA_READ_KEY_EXACT);
if (res)
{
@@ -878,27 +1152,27 @@ bool Key_value_records_iterator::init(DsMrr_impl *dsmrr_arg)
int Key_value_records_iterator::get_next()
{
- handler *file= dsmrr->do_rndpos_scan? dsmrr->h2 : dsmrr->h;
int res;
if (get_next_row)
{
- if (dsmrr->index_ranges_unique)
+ if (owner->keypar.index_ranges_unique)
return HA_ERR_END_OF_FILE; /* Max one match */
-
- if ((res= file->ha_index_next_same(dsmrr->table->record[0],
- dsmrr->cur_index_tuple,
- dsmrr->key_tuple_length)))
+
+ handler *h= owner->h;
+ if ((res= h->ha_index_next_same(h->get_table()->record[0],
+ cur_index_tuple,
+ owner->keypar.key_tuple_length)))
{
/* EOF is EOF for iterator, also, any error means EOF on the iterator */
return res;
}
- identical_key_it.init(dsmrr->key_buffer);
+ identical_key_it.init(owner->key_buffer);
get_next_row= FALSE;
}
identical_key_it.read(); // This gets us next range_id.
- if (!last_identical_key_ptr || (dsmrr->cur_index_tuple == last_identical_key_ptr))
+ if (!last_identical_key_ptr || (cur_index_tuple == last_identical_key_ptr))
{
get_next_row= TRUE;
}
@@ -907,92 +1181,8 @@ int Key_value_records_iterator::get_next()
void Key_value_records_iterator::close()
{
- while (!dsmrr->key_buffer->read() &&
- (dsmrr->cur_index_tuple != last_identical_key_ptr)) {}
-}
-
-
-/**
- DS-MRR/CPK: multi_range_read_next() function
-
- @param range_info OUT identifier of range that the returned record belongs to
-
- @note
- This function walks over key buffer and does index reads, i.e. it produces
- {current_record, range_id} pairs.
-
- The function has the same call contract like multi_range_read_next()'s.
-
- We actually iterate over nested sequences:
- - a disjoint sequence of index ranges
- - each range has multiple records
- - each record goes into multiple identical ranges.
-
- @retval 0 OK, next record was successfully read
- @retval HA_ERR_END_OF_FILE End of records
- @retval Other Some other error
-*/
-
-int DsMrr_impl::dsmrr_next_from_index(char **range_info_arg)
-{
- DBUG_ENTER("DsMrr_impl::dsmrr_next_from_index");
-
- while (1)
- {
- bool have_record= FALSE;
- if (scanning_key_val_iter)
- {
- if (kv_it.get_next())
- {
- kv_it.close();
- scanning_key_val_iter= FALSE;
- }
- else
- have_record= TRUE;
- }
- else
- {
- while (kv_it.init(this))
- {
- if (key_buffer->is_empty())
- {
- if (dsmrr_eof)
- {
- index_scan_eof= TRUE;
- DBUG_RETURN(HA_ERR_END_OF_FILE);
- }
-
- /*
- When rowid fetching is used, it controls all buffer refills. When we're
- on our own, try refilling our buffer.
- */
- if (!do_rndpos_scan)
- dsmrr_fill_key_buffer();
-
- if (key_buffer->is_empty())
- {
- index_scan_eof= TRUE;
- DBUG_RETURN(HA_ERR_END_OF_FILE);
- }
- }
- }
- scanning_key_val_iter= TRUE;
- }
-
- if (have_record &&
- (!h->mrr_funcs.skip_index_tuple ||
- !h->mrr_funcs.skip_index_tuple(h->mrr_iter, *(char**)cur_range_info))
- &&
- (!h->mrr_funcs.skip_record ||
- !h->mrr_funcs.skip_record(h->mrr_iter, *(char**)cur_range_info, NULL)))
- {
- break;
- }
- /* Go get another (record, range_id) combination */
- } /* while */
-
- memcpy(range_info_arg, cur_range_info, sizeof(void*));
- DBUG_RETURN(0);
+ while (!owner->key_buffer->read() &&
+ (cur_index_tuple != last_identical_key_ptr)) {}
}
@@ -1004,119 +1194,7 @@ int DsMrr_impl::dsmrr_next_from_index(char **range_info_arg)
int DsMrr_impl::dsmrr_next(char **range_info)
{
- int res;
-
- if (use_default_impl)
- return h->handler::multi_range_read_next(range_info);
-
- if (!do_rndpos_scan)
- return dsmrr_next_from_index(range_info);
-
- while (last_identical_rowid)
- {
- /*
- Current record (the one we've returned in previous call) was obtained
- from a rowid that matched multiple range_ids. Return this record again,
- with next matching range_id.
- */
- bool bres= rowid_buffer.read();
- DBUG_ASSERT(!bres);
-
- if (is_mrr_assoc)
- memcpy(range_info, rowids_range_id, sizeof(uchar*));
-
- if (rowid == last_identical_rowid)
- {
- last_identical_rowid= NULL; /* reached the last of identical rowids */
- }
-
- if (!h2->mrr_funcs.skip_record ||
- !h2->mrr_funcs.skip_record(h2->mrr_iter, (char *) *range_info, rowid))
- {
- return 0;
- }
- }
-
- while (1)
- {
- if (rowid_buffer.is_empty())
- {
- if (do_sort_keys)
- {
- if (!index_scan_eof)
- {
- /* There are some sorted keys left. Use them to get rowids */
- if ((res= dsmrr_fill_rowid_buffer()))
- return res; /* for fatal errors */
- }
- while (rowid_buffer.is_empty())
- {
- if (dsmrr_eof)
- return HA_ERR_END_OF_FILE;
- dsmrr_fill_key_buffer();
- if ((res= dsmrr_fill_rowid_buffer()))
- return res;
- }
- }
- else
- {
- /*
- There is no buffer with sorted keys. If fill_rowid_buffer() haven't
- reached eof condition before, try refilling the buffer.
- */
- if (dsmrr_eof)
- return HA_ERR_END_OF_FILE;
-
- if ((res= dsmrr_fill_rowid_buffer()))
- return res;
- }
- }
-
- last_identical_rowid= NULL;
-
- /* Return eof if there are no rowids in the buffer after re-fill attempt */
- if (rowid_buffer.read())
- return HA_ERR_END_OF_FILE;
-
- if (is_mrr_assoc)
- {
- memcpy(range_info, rowids_range_id, sizeof(uchar*));
- }
-
- if (h2->mrr_funcs.skip_record &&
- h2->mrr_funcs.skip_record(h2->mrr_iter, *range_info, rowid))
- continue;
-
- res= h->ha_rnd_pos(table->record[0], rowid);
-
- if (res == HA_ERR_RECORD_DELETED)
- continue;
-
- /*
- Check if subsequent buffer elements have the same rowid value as this
- one. If yes, remember this fact so that we don't make any more rnd_pos()
- calls with this value.
- */
- if (!res)
- {
- uchar *cur_rowid= rowid;
- /*
- Note: this implies that SQL layer doesn't touch table->record[0]
- between calls.
- */
- Lifo_buffer_iterator it;
- it.init(&rowid_buffer);
- while (!it.read()) // reads to (rowid, ...)
- {
- if (h2->cmp_ref(rowid, cur_rowid))
- break;
- last_identical_rowid= rowid;
- }
- }
- return 0;
- }
-
- return res;
+ return strategy->get_next(range_info);
}
@@ -1239,11 +1317,11 @@ bool key_uses_partial_cols(TABLE *table, uint keyno)
bool DsMrr_impl::check_cpk_scan(THD *thd, uint keyno, uint mrr_flags)
{
- return test((mrr_flags & HA_MRR_SINGLE_POINT) &&
- !(mrr_flags & HA_MRR_SORTED) &&
+ return test((mrr_flags & HA_MRR_SINGLE_POINT) && // check
+ // !(mrr_flags & HA_MRR_SORTED) &&
keyno == table->s->primary_key &&
h->primary_key_is_clustered() &&
- optimizer_flag(thd, OPTIMIZER_SWITCH_MRR_SORT_KEYS));
+ optimizer_flag(thd, OPTIMIZER_SWITCH_MRR_SORT_KEYS)); //check
}
diff --git a/sql/multi_range_read.h b/sql/multi_range_read.h
index 3c92dcd2950..fb2a67b6af1 100644
--- a/sql/multi_range_read.h
+++ b/sql/multi_range_read.h
@@ -50,6 +50,26 @@
class DsMrr_impl;
+class Key_parameters
+{
+public:
+ /* TRUE <=> We can get at most one index tuple for a lookup key */
+ bool index_ranges_unique;
+
+ uint key_tuple_length; /* Length of index lookup tuple, in bytes */
+ key_part_map key_tuple_map; /* keyparts used in index lookup tuples */
+
+ /*
+ This is
+ = key_tuple_length if we copy keys to buffer
+ = sizeof(void*) if we're using pointers to materialized keys.
+ */
+ uint key_size_in_keybuf;
+
+ /* TRUE <=> don't copy key values, use pointers to them instead. */
+ bool use_key_pointers;
+};
+
/**
Iterator over (record, range_id) pairs that match given key value.
@@ -57,16 +77,23 @@ class DsMrr_impl;
key value. A key value may have multiple matching records, so we'll need to
produce a cross-product of sets of matching records and range_id-s.
*/
-
+class Mrr_ordered_index_reader;
class Key_value_records_iterator
{
/* Scan parameters */
- DsMrr_impl *dsmrr;
+ Key_parameters *param;
Lifo_buffer_iterator identical_key_it;
uchar *last_identical_key_ptr;
bool get_next_row;
+ //handler *h;
+ /* TRUE <=> We can get at most one index tuple for a lookup key */
+ //bool index_ranges_unique;
+
+ Mrr_ordered_index_reader *owner;
+ /* key_buffer.read() reads to here */
+ uchar *cur_index_tuple;
public:
- bool init(DsMrr_impl *dsmrr);
+ bool init(Mrr_ordered_index_reader *owner_arg);
/*
Get next (key_val, range_id) pair.
@@ -74,10 +101,185 @@ public:
int get_next();
void close();
+ friend class Mrr_ordered_index_reader;
};
/*
+ Something that will manage buffers for those that call it
+*/
+class Buffer_manager
+{
+public:
+ virtual void reset_buffer_sizes()= 0;
+ virtual void setup_buffer_sizes(uint key_size_in_keybuf,
+ key_part_map key_tuple_map)=0;
+ virtual Lifo_buffer* get_key_buffer()= 0;
+ virtual ~Buffer_manager(){}
+};
+
+
+/*
+ Abstract MRR execution strategy
+
+ An object of this class produces (R, range_info) pairs where R can be an
+ index tuple or a table record.
+
+ Getting HA_ERR_END_OF_FILE from get_next() means that the source should be
+ re-filled. if eof() returns true after refill attempt, then end of stream has
+ been reached and get_next() must not be called anymore.
+*/
+
+class Mrr_strategy
+{
+public:
+ virtual int get_next(char **range_info) = 0;
+ virtual int refill_buffer()=0;
+
+ virtual ~Mrr_strategy() {};
+};
+
+
+/* A common base for strategies that do index scans and produce index tuples */
+class Mrr_index_reader : public Mrr_strategy
+{
+public:
+ handler *h;
+
+ virtual int init(handler *h_arg, RANGE_SEQ_IF *seq_funcs,
+ void *seq_init_param, uint n_ranges,
+ uint mode, Buffer_manager *buf_manager_arg) = 0;
+ virtual bool eof() = 0;
+ virtual uchar *get_rowid_ptr()= 0;
+ virtual bool skip_record(char *range_id, uchar *rowid)=0;
+};
+
+
+/*
+ A "bypass" strategy that uses default MRR implementation (i.e.
+ handler::multi_range_read_XXX() calls) to produce rows.
+*/
+
+class Mrr_simple_index_reader : public Mrr_index_reader
+{
+ int res;
+public:
+ int init(handler *h_arg, RANGE_SEQ_IF *seq_funcs,
+ void *seq_init_param, uint n_ranges,
+ uint mode, Buffer_manager *buf_manager_arg);
+ int get_next(char **range_info);
+ int refill_buffer() { return 0; }
+ bool eof() { return test(res); }
+ uchar *get_rowid_ptr() { return h->ref; }
+ bool skip_record(char *range_id, uchar *rowid)
+ {
+ return (h->mrr_funcs.skip_record &&
+ h->mrr_funcs.skip_record(h->mrr_iter, range_id, rowid));
+ }
+};
+
+
+
+/*
+ A strategy that sorts index lookup keys before scanning the index
+*/
+
+class Mrr_ordered_index_reader : public Mrr_index_reader
+{
+public:
+ int init(handler *h_arg, RANGE_SEQ_IF *seq_funcs,
+ void *seq_init_param, uint n_ranges,
+ uint mode, Buffer_manager *buf_manager_arg);
+ int get_next(char **range_info);
+ int refill_buffer();
+ bool eof() { return index_scan_eof; }
+ uchar *get_rowid_ptr() { return h->ref; }
+
+ bool skip_record(char *range_info, uchar *rowid)
+ {
+ return (mrr_funcs.skip_record &&
+ mrr_funcs.skip_record(mrr_iter, range_info, rowid));
+ }
+private:
+ Key_value_records_iterator kv_it;
+
+ bool scanning_key_val_iter;
+
+ char *cur_range_info;
+
+ /* Buffer to store (key, range_id) pairs */
+ Lifo_buffer *key_buffer;
+
+ Buffer_manager *buf_manager;
+
+ /* Initially FALSE, becomes TRUE when we've set key_tuple_xxx members */
+ bool know_key_tuple_params;
+
+ // bool use_key_pointers;
+
+ Key_parameters keypar;
+ /* TRUE <=> need range association, buffers hold {rowid, range_id} pairs */
+ bool is_mrr_assoc;
+
+ bool no_more_keys;
+ RANGE_SEQ_IF mrr_funcs;
+ range_seq_t mrr_iter;
+
+ bool auto_refill;
+
+ bool index_scan_eof;
+
+ static int key_tuple_cmp(void* arg, uchar* key1, uchar* key2);
+ static int key_tuple_cmp_reverse(void* arg, uchar* key1, uchar* key2);
+ //void cleanup();
+
+ friend class Key_value_records_iterator;
+ friend class DsMrr_impl;
+ friend class Mrr_ordered_rndpos_reader;
+};
+
+
+/* MRR strategy that fetches rowids */
+
+class Mrr_ordered_rndpos_reader : public Mrr_strategy
+{
+public:
+ int init(handler *h, Mrr_index_reader *index_reader, uint mode,
+ Lifo_buffer *buf);
+ int get_next(char **range_info);
+ int refill_buffer();
+ void cleanup();
+private:
+ handler *h;
+
+ DsMrr_impl *dsmrr;
+ /* This what we get (rowid, range_info) pairs from */
+ Mrr_index_reader *index_reader;
+ uchar *index_rowid;
+
+ /* TRUE <=> need range association, buffers hold {rowid, range_id} pairs */
+ bool is_mrr_assoc;
+
+ uchar *last_identical_rowid;
+ Lifo_buffer *rowid_buffer;
+
+ /* = h->ref_length [ + sizeof(range_assoc_info) ] */
+ //uint rowid_buff_elem_size;
+
+ /* rowid_buffer.read() will set the following: */
+ uchar *rowid;
+ uchar *rowids_range_id;
+};
+
+class Mrr_strategy_factory
+{
+public:
+ Mrr_ordered_rndpos_reader ordered_rndpos_reader;
+ Mrr_ordered_index_reader ordered_index_reader;
+ Mrr_simple_index_reader simple_index_reader;
+};
+
+/*
DS-MRR implementation for one table. Create/use one object of this class for
each ha_{myisam/innobase/etc} object. That object will be further referred to
as "the handler"
@@ -154,9 +356,58 @@ public:
get record by rowid and return the {record, range_id} pair
4. Repeat the above steps until we've exhausted the list of ranges we're
scanning.
+
+ Buffer space management considerations
+ --------------------------------------
+ With regards to buffer/memory management, MRR interface specifies that
+ - SQL layer provides multi_range_read_init() with buffer of certain size.
+ - MRR implementation may use (i.e. have at its disposal till the end of
+ the MRR scan) all of the buffer, or return the unused end of the buffer
+ to SQL layer.
+
+ DS-MRR needs buffer in order to accumulate and sort rowids and/or keys. When
+ we need to accumulate/sort only keys (or only rowids), it is fairly trivial.
+
+ When we need to accumulate/sort both keys and rowids, efficient buffer use
+ gets complicated. We need to:
+ - First, accumulate keys and sort them
+ - Then use the keys (smaller values go first) to obtain rowids. A key is not
+ needed after we've got matching rowids for it.
+ - Make sure that rowids are accumulated at the front of the buffer, so that we
+ can return the end part of the buffer to SQL layer, should there be too
+ few rowid values to occupy the buffer.
+
+ All of these goals are achieved by using the following scheme:
+
+ | | We get an empty buffer from SQL layer.
+
+ | *-|
+ | *----| First, we fill the buffer with keys. Key_buffer
+ | *-------| part grows from end of the buffer space to start
+ | *----------| (In this picture, the buffer is big enough to
+ | *-------------| accomodate all keys and even have some space left)
+
+ | *=============| We want to do key-ordered index scan, so we sort
+ the keys
+
+ |-x *===========| Then we use the keys get rowids. Rowids are
+ |----x *========| stored from start of buffer space towards the end.
+ |--------x *=====| The part of the buffer occupied with keys
+ |------------x *===| gradually frees up space for rowids. In this
+ |--------------x *=| picture we run out of keys before we've ran out
+ |----------------x | of buffer space (it can be other way as well).
+
+ |================x | Then we sort the rowids.
+
+ | |~~~| The unused part of the buffer is at the end, so
+ we can return it to the SQL layer.
+
+ |================* Sorted rowids are then used to read table records
+ in disk order
+
*/
-class DsMrr_impl
+class DsMrr_impl : public Buffer_manager
{
public:
typedef void (handler::*range_check_toggle_func_t)(bool on);
@@ -181,6 +432,9 @@ public:
void *seq_init_param, uint n_ranges, uint *bufsz,
uint *flags, COST_VECT *cost);
private:
+ /* Buffer to store (key, range_id) pairs */
+ Lifo_buffer *key_buffer;
+
/*
The "owner" handler object (the one that is expected to "own" this object
and call its functions).
@@ -197,20 +451,16 @@ private:
/** Properties of current MRR scan **/
uint keyno; /* index we're running the scan on */
- bool use_default_impl; /* TRUE <=> shortcut all calls to default MRR impl */
/* TRUE <=> need range association, buffers hold {rowid, range_id} pairs */
bool is_mrr_assoc;
/* TRUE <=> sort the keys before making index lookups */
- bool do_sort_keys;
+ //bool do_sort_keys;
/* TRUE <=> sort rowids and use rnd_pos() to get and return full records */
- bool do_rndpos_scan;
-
- /*
- (if do_sort_keys==TRUE) don't copy key values, use pointers to them
- instead.
- */
- bool use_key_pointers;
+ //bool do_rndpos_scan;
+ Mrr_strategy_factory strategy_factory;
+ Mrr_strategy *strategy;
+ Mrr_index_reader *index_strategy;
/* The whole buffer space that we're using */
uchar *full_buf;
@@ -226,12 +476,6 @@ private:
/** Index scaning and key buffer-related members **/
- /* TRUE <=> We can get at most one index tuple for a lookup key */
- bool index_ranges_unique;
-
- /* TRUE<=> we're in a middle of enumerating records for a key range */
- //bool in_index_range;
-
/*
One of the following two is used for key buffer: forward is used when
we only need key buffer, backward is used when we need both key and rowid
@@ -240,39 +484,10 @@ private:
Forward_lifo_buffer forward_key_buf;
Backward_lifo_buffer backward_key_buf;
- /* Buffer to store (key, range_id) pairs */
- Lifo_buffer *key_buffer;
-
- /* Index scan state */
- bool scanning_key_val_iter;
- /*
- TRUE <=> we've got index tuples/rowids for all keys (need this flag because
- we may have a situation where we've read everything from the key buffer but
- haven't finished with getting index tuples for the last key)
- */
- bool index_scan_eof;
- Key_value_records_iterator kv_it;
-
- /* key_buffer.read() reads to here */
- uchar *cur_index_tuple;
-
- /* if in_index_range==TRUE: range_id of the range we're enumerating */
- char *cur_range_info;
-
- /* Initially FALSE, becomes TRUE when we've set key_tuple_xxx members */
- bool know_key_tuple_params;
- uint key_tuple_length; /* Length of index lookup tuple, in bytes */
- key_part_map key_tuple_map; /* keyparts used in index lookup tuples */
-
- /*
- This is
- = key_tuple_length if we copy keys to buffer
- = sizeof(void*) if we're using pointers to materialized keys.
- */
- uint key_size_in_keybuf;
+ Forward_lifo_buffer rowid_buffer;
/* = key_size_in_keybuf [ + sizeof(range_assoc_info) ] */
- uint key_buff_elem_size;
+ //uint key_buff_elem_size_;
/** rnd_pos() scan and rowid buffer-related members **/
@@ -280,36 +495,27 @@ private:
Buffer to store (rowid, range_id) pairs, or just rowids if
is_mrr_assoc==FALSE
*/
- Forward_lifo_buffer rowid_buffer;
-
- /* rowid_buffer.read() will set the following: */
- uchar *rowid;
- uchar *rowids_range_id;
-
- uchar *last_identical_rowid;
-
- bool dsmrr_eof; /* TRUE <=> We have reached EOF when reading index tuples */
-
- /* = h->ref_length [ + sizeof(range_assoc_info) ] */
- uint rowid_buff_elem_size;
+ //Forward_lifo_buffer rowid_buffer;
bool choose_mrr_impl(uint keyno, ha_rows rows, uint *flags, uint *bufsz,
COST_VECT *cost);
bool get_disk_sweep_mrr_cost(uint keynr, ha_rows rows, uint flags,
uint *buffer_size, COST_VECT *cost);
bool check_cpk_scan(THD *thd, uint keyno, uint mrr_flags);
- static int key_tuple_cmp(void* arg, uchar* key1, uchar* key2);
- static int key_tuple_cmp_reverse(void* arg, uchar* key1, uchar* key2);
- int dsmrr_fill_rowid_buffer();
- void dsmrr_fill_key_buffer();
- int dsmrr_next_from_index(char **range_info);
- void setup_buffer_sizes(key_range *sample_key);
void reallocate_buffer_space();
- static range_seq_t key_buf_seq_init(void *init_param, uint n_ranges, uint flags);
- static uint key_buf_seq_next(range_seq_t rseq, KEY_MULTI_RANGE *range);
+ /* Buffer_manager implementation */
+ void setup_buffer_sizes(uint key_size_in_keybuf, key_part_map key_tuple_map);
+ void reset_buffer_sizes();
+ Lifo_buffer* get_key_buffer() { return key_buffer; }
+
friend class Key_value_records_iterator;
+ friend class Mrr_ordered_index_reader;
+ friend class Mrr_ordered_rndpos_reader;
+
+ int setup_two_handlers();
+ void close_second_handler();
};
/**