Commit e25db795 authored by Sergey Petrunya's avatar Sergey Petrunya

DS-MRR, key-ordered retrievals: commit for buildbot

parent 7391175d
......@@ -287,6 +287,84 @@ scan_it_again:
* DS-MRR implementation
***************************************************************************/
void SimpleBuffer::write(const uchar *data, size_t bytes)
{
DBUG_ASSERT(have_space_for(bytes));
if (direction == -1)
write_pos -= bytes;
memcpy(write_pos, data, bytes);
if (direction == 1)
write_pos += bytes;
}
bool SimpleBuffer::have_space_for(size_t bytes)
{
if (direction == 1)
return (write_pos + bytes < end);
else
return (write_pos - bytes >= start);
}
size_t SimpleBuffer::used_size()
{
return (direction == 1)? write_pos - read_pos : read_pos - write_pos;
}
uchar *SimpleBuffer::read(size_t bytes)
{
DBUG_ASSERT(have_data(bytes));
uchar *res;
if (direction == 1)
{
res= read_pos;
read_pos += bytes;
return res;
}
else
{
read_pos= read_pos - bytes;
return read_pos;
}
}
bool SimpleBuffer::have_data(size_t bytes)
{
return (direction == 1)? (write_pos - read_pos >= (ptrdiff_t)bytes) :
(read_pos - write_pos >= (ptrdiff_t)bytes);
}
void SimpleBuffer::reset_for_writing()
{
if (direction == 1)
write_pos= read_pos= start;
else
write_pos= read_pos= end;
}
void SimpleBuffer::reset_for_reading()
{
/*
Do we need this at all?
if (direction == 1)
pos= start;
else
pos= end;
//end?
*/
}
uchar *SimpleBuffer::end_of_space()
{
if (direction == 1)
return start;
else
return end;
//TODO: check this.
}
/**
DS-MRR: Initialize and start MRR scan
......@@ -309,7 +387,6 @@ 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)
{
uint elem_size;
Item *pushed_cond= NULL;
handler *new_h2= 0;
THD *thd= current_thd;
......@@ -330,69 +407,69 @@ int DsMrr_impl::dsmrr_init(handler *h_arg, RANGE_SEQ_IF *seq_funcs,
}
use_default_impl= FALSE;
is_mrr_assoc= !test(mode & HA_MRR_NO_ASSOCIATION);
// psergey2: split the buffer:
/*
psergey2-note: we can't split the buffer here because we don't know how key
length. we'll only be able to do it when we've got the first range.
Figure out what steps we'll need to do
*/
do_sort_keys= FALSE;
if ((mode & HA_MRR_SINGLE_POINT) &&
optimizer_flag(thd, OPTIMIZER_SWITCH_MRR_SORT_KEYS))
{
//do_sort_keys= TRUE; // will use key buffer to sort keys;
do_sort_keys= TRUE;
use_key_pointers= test(mode & HA_MRR_MATERIALIZED_KEYS);
}
/*
do_rowid_fetch= FALSE;
if (!doing_cpk_scan && !index_only_read)
doing_cpk_scan= check_cpk_scan(h->active_index, mode);
if (!doing_cpk_scan /* && !index_only_read */)
{
do_rowid_fetch= TRUE; //will use rowid buffer to store/sort rowids, etc
/* Will use rowid buffer to store/sort rowids, etc */
do_rowid_fetch= TRUE;
}
DBUG_ASSERT(do_sort_keys || do_rowid_fetch);
if (do_sort_keys && do_rowid_fetch)
{
split buffer space proportionally
}
else
{
// give all space to one buffer
if (do_sort_keys)
{
//sort_buffer_start= ...;
}
else
{
DBUG_ASSERT(do_rowid_fetch);
//rowid_buffer_start= ...;
}
}
full_buf= buf->buffer;
full_buf_end= buf->buffer_end;
/*
At start, alloc all of the buffer for rowids. Key sorting code will grab a
piece if necessary.
*/
mrr_buf= buf->buffer;
mrr_buf_end= buf->buffer_end;
rowid_buffer.set_buffer_space(full_buf, full_buf_end, 1);
if (is_mrr_assoc)
status_var_increment(table->in_use->status_var.ha_multi_range_read_init_count);
if ((doing_cpk_scan= check_cpk_scan(h->active_index, mode)))
/*
psergey2-todo: for CPK scans:
- use MRR irrespectively of @@mrr_sort_keys setting,
- dont do rowid retrieval.
*/
if (do_sort_keys)
{
/* It's a DS-MRR/CPK scan */
cpk_tuple_length= 0; /* dummy value telling it needs to be inited */
cpk_have_range= FALSE;
key_tuple_length= 0; /* dummy value telling it needs to be inited */
key_buff_elem_size= 0;
in_index_range= FALSE;
h->mrr_iter= seq_funcs->init(seq_init_param, n_ranges, mode);
h->mrr_funcs= *seq_funcs;
keyno= h->active_index != MAX_KEY? h->active_index : h2->active_index;
dsmrr_fill_key_buffer();
if (dsmrr_eof)
buf->end_of_used_area= mrr_buf_last;
DBUG_RETURN(0); /* nothing could go wrong while filling the buffer */
if (dsmrr_eof && !do_rowid_fetch)
buf->end_of_used_area= key_buffer.end_of_space();
}
/* In regular DS-MRR, buffer stores {rowid, range_id} pairs */
elem_size= h->ref_length + (int)is_mrr_assoc * sizeof(void*);
mrr_buf_last= mrr_buf + ((mrr_buf_end - mrr_buf)/ elem_size)* elem_size;
mrr_buf_end= mrr_buf_last;
if (!do_rowid_fetch)
{
/*
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().
*/
DBUG_RETURN(0);
}
rowid_buff_elem_size= h->ref_length + (is_mrr_assoc? sizeof(char*) : 0);
/*
psergey2: this is only needed when
- doing a rowid-to-row scan
......@@ -416,7 +493,7 @@ 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);
uint mrr_keyno= h->active_index;
keyno= h->active_index;
/* Create a separate handler object to do rnd_pos() calls. */
if (!(new_h2= h->clone(thd->mem_root)) ||
......@@ -426,7 +503,7 @@ int DsMrr_impl::dsmrr_init(handler *h_arg, RANGE_SEQ_IF *seq_funcs,
DBUG_RETURN(1);
}
if (mrr_keyno == h->pushed_idx_cond_keyno)
if (keyno == h->pushed_idx_cond_keyno)
pushed_cond= h->pushed_idx_cond;
/*
......@@ -443,12 +520,14 @@ int DsMrr_impl::dsmrr_init(handler *h_arg, RANGE_SEQ_IF *seq_funcs,
h2= new_h2; /* Ok, now can put it into h2 */
table->prepare_for_position();
h2->extra(HA_EXTRA_KEYREAD);
if (h2->ha_index_init(mrr_keyno, FALSE))
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))
goto error;
if (pushed_cond)
h2->idx_cond_push(mrr_keyno, pushed_cond);
h2->idx_cond_push(keyno, pushed_cond);
}
else
{
......@@ -467,10 +546,15 @@ int DsMrr_impl::dsmrr_init(handler *h_arg, RANGE_SEQ_IF *seq_funcs,
if (res)
goto error;
}
if (!do_sort_keys &&
h2->handler::multi_range_read_init(seq_funcs, seq_init_param, n_ranges,
mode, buf))
{
goto error;
}
if (h2->handler::multi_range_read_init(seq_funcs, seq_init_param, n_ranges,
mode, buf) ||
dsmrr_fill_rowid_buffer())
if (dsmrr_fill_rowid_buffer())
{
goto error;
}
......@@ -479,7 +563,7 @@ int DsMrr_impl::dsmrr_init(handler *h_arg, RANGE_SEQ_IF *seq_funcs,
adjust *buf to indicate that the remaining buffer space will not be used.
*/
if (dsmrr_eof)
buf->end_of_used_area= mrr_buf_last;
buf->end_of_used_area= rowid_buffer.end_of_space();
/*
h->inited == INDEX may occur when 'range checked for each record' is
......@@ -526,21 +610,24 @@ static int rowid_cmp(void *h, uchar *a, uchar *b)
/**
DS-MRR: Fill the buffer with rowids and sort it by rowid
DS-MRR: Fill and sort the rowid buffer
{This is an internal function of DiskSweep MRR implementation}
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.
The function assumes that rowids buffer is empty when it is invoked.
New2:
we will need to scan either
- the source sequence getting records
- use dsmrr_next_from_index..
dsmrr_eof is set to indicate whether we've exhausted the list of ranges we're
scanning.
psergey2: this func will 'fill the rowid buffer'. If filling the rowid buffer
requires that key buffer is filled/sorted first, will do that, too.
@param h Table handler
@retval 0 OK, the next portion of rowids is in the buffer,
......@@ -554,40 +641,32 @@ int DsMrr_impl::dsmrr_fill_rowid_buffer()
int res;
DBUG_ENTER("DsMrr_impl::dsmrr_fill_rowid_buffer");
mrr_buf_cur= mrr_buf;
mrr_buf_next_identical= mrr_buf_cur;
/*
psergey2-todo:
- call here fill/sort key buffer, if needed.
rowid_buffer.reset_for_writing();
identical_rowid_ptr= NULL;
psergey2-todo: then, get keys either from
- multi_range_read_next()
- sorted key buffer
psergey2-todo: if we're traversing an ordered key sequence,
check if next keys are the same as previous.
(note that it's easy as ordered sequence allows forward/backward
navigation so we don't need to buffer things)
*/
while ((mrr_buf_cur < mrr_buf_end) &&
!(res= h2->handler::multi_range_read_next(&range_info)))
while (rowid_buffer.have_space_for(rowid_buff_elem_size))
{
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 (h2->mrr_funcs.skip_index_tuple &&
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]);
memcpy(mrr_buf_cur, h2->ref, h2->ref_length);
mrr_buf_cur += h2->ref_length;
rowid_buffer.write(h2->ref, h2->ref_length);
if (is_mrr_assoc)
{
memcpy(mrr_buf_cur, &range_info, sizeof(void*));
mrr_buf_cur += sizeof(void*);
}
rowid_buffer.write((uchar*)&range_info, sizeof(void*));
}
if (res && res != HA_ERR_END_OF_FILE)
......@@ -596,12 +675,11 @@ int DsMrr_impl::dsmrr_fill_rowid_buffer()
/* Sort the buffer contents by rowid */
uint elem_size= h->ref_length + (int)is_mrr_assoc * sizeof(void*);
uint n_rowids= (mrr_buf_cur - mrr_buf) / elem_size;
uint n_rowids= rowid_buffer.used_size() / elem_size;
my_qsort2(mrr_buf, n_rowids, elem_size, (qsort2_cmp)rowid_cmp,
(void*)h);
mrr_buf_last= mrr_buf_cur;
mrr_buf_cur= mrr_buf;
my_qsort2(rowid_buffer.used_area(), n_rowids, elem_size,
(qsort2_cmp)rowid_cmp, (void*)h);
DBUG_RETURN(0);
}
......@@ -616,8 +694,8 @@ int DsMrr_impl::key_tuple_cmp(void* arg, uchar* key1, uchar* key2)
{
DsMrr_impl *dsmrr= (DsMrr_impl*)arg;
TABLE *table= dsmrr->h->table;
KEY_PART_INFO *part= table->key_info[table->s->primary_key].key_part;
int res;
KEY_PART_INFO *part= table->key_info[dsmrr->keyno].key_part;
if (dsmrr->use_key_pointers)
{
......@@ -626,15 +704,31 @@ int DsMrr_impl::key_tuple_cmp(void* arg, uchar* key1, uchar* key2)
key2= *((uchar**)key2); // todo is this alignment-safe?
}
uchar *key1_end= key1 + dsmrr->cpk_tuple_length;
uchar *key1_end= key1 + dsmrr->key_tuple_length;
while (key1 < key1_end)
{
Field* f = part->field;
int len = part->store_length;
int res = f->cmp(key1, key2);
if (res)
if (part->null_bit)
{
if (*key1) // key1 == NULL
{
if (!*key2) // key1(NULL) < key2(notNULL)
return -1;
goto equals;
}
else if (*key2) // key1(notNULL) > key2 (NULL)
return 1;
// Step over NULL byte for f->cmp().
key1++;
key2++;
len--;
}
if ((res= f->key_cmp(key1, key2)))
return res;
equals:
key1 += len;
key2 += len;
part++;
......@@ -643,6 +737,68 @@ int DsMrr_impl::key_tuple_cmp(void* arg, uchar* key1, uchar* key2)
}
/*
Setup key/rowid buffer sizes based on sample_key
DESCRIPTION
Setup key/rowid buffer sizes based on sample_key and its length.
This function must be called when all buffer space is empty.
*/
void DsMrr_impl::setup_buffer_sizes(key_range *sample_key)
{
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 rowid_buf_elem_size= h->ref_length +
(int)is_mrr_assoc * sizeof(char*);
KEY *key_info= &h->table->key_info[keyno];
/*
Use rec_per_key statistics as a basis to find out how many rowids
we'll get for each key value.
TODO: are we guaranteed to get r_p_c==1 for unique keys?
TODO: what should be the default value to use when there is no
statistics?
*/
uint parts= my_count_bits(key_tuple_map);
ulong rpc;
if ((rpc= key_info->rec_per_key[parts - 1]))
{
rowid_buf_elem_size *= rpc;
}
double fraction_for_rowids=
((double) rowid_buf_elem_size /
((double)rowid_buf_elem_size + key_buff_elem_size));
uint bytes_for_rowids=
round(fraction_for_rowids * (full_buf_end - full_buf));
uint bytes_for_keys= (full_buf_end - full_buf) - bytes_for_rowids;
if (bytes_for_keys < key_buff_elem_size + 1)
{
uint add= key_buff_elem_size + 1 - bytes_for_keys;
bytes_for_rowids -= add;
DBUG_ASSERT(bytes_for_rowids >=
(h->ref_length + (int)is_mrr_assoc * sizeof(char*) + 1));
}
rowid_buffer.set_buffer_space(full_buf, full_buf + bytes_for_rowids, 1);
key_buffer.set_buffer_space(full_buf + bytes_for_rowids, full_buf_end, 1);
index_ranges_unique= test(key_info->flags & HA_NOSAME &&
key_info->key_parts ==
my_count_bits(sample_key->keypart_map));
}
/*
DS-MRR/CPK: Fill the buffer with (lookup_tuple, range_id) pairs and sort
......@@ -650,69 +806,55 @@ int DsMrr_impl::key_tuple_cmp(void* arg, uchar* key1, uchar* key2)
DsMrr_impl::dsmrr_fill_key_buffer()
DESCRIPTION
DS-MRR/CPK: Fill the buffer with (lookup_tuple, range_id) pairs and sort
DS-MRR/CPK: Enumerate the input range (=key) sequence, fill the key buffer
(lookup_key, range_id) pairs and sort.
dsmrr_eof is set to indicate whether we've exhausted the list of ranges
we're scanning.
psergey2-q: can this be used for filling/sorting key buffer in general case?
a: yes.
qq: can we push sequence iteration init down into here?
*/
void DsMrr_impl::dsmrr_fill_key_buffer()
{
//psergey2: here, no identicals detection is necessary since we always scan
// the unordered sequence.
int res;
KEY_MULTI_RANGE cur_range;
DBUG_ENTER("DsMrr_impl::dsmrr_fill_key_buffer");
mrr_buf_cur= mrr_buf;
while ((mrr_buf_cur < mrr_buf_end) &&
// reset the buffer for writing.
key_buffer.reset_for_writing();
while ((key_buffer.have_space_for(key_buff_elem_size)) &&
!(res= h->mrr_funcs.next(h->mrr_iter, &cur_range)))
{
DBUG_ASSERT(cur_range.range_flag & EQ_RANGE);
if (!cpk_tuple_length)
if (!key_tuple_length)
{
cpk_tuple_length= cur_range.start_key.length;
key_buf_element_size= use_key_pointers ? sizeof(char*) :
cpk_tuple_length;
cpk_is_unique_scan= test(table->key_info[h->active_index].key_parts ==
my_count_bits(cur_range.start_key.keypart_map));
uint elem_size= key_buf_element_size + (int)is_mrr_assoc * sizeof(void*);
mrr_buf_last= mrr_buf + ((mrr_buf_end - mrr_buf)/elem_size) * elem_size;
mrr_buf_end= mrr_buf_last;
/* This only happens when we've just started filling the buffer */
DBUG_ASSERT(key_buffer.used_size() == 0);
setup_buffer_sizes(&cur_range.start_key);
}
//psergey2: if keys are materialized, store pointers, not copy keys
/* Put key, or {key, range_id} pair into the buffer */
if (use_key_pointers)
memcpy(mrr_buf_cur, &cur_range.start_key.key, sizeof(char*));
key_buffer.write((uchar*)&cur_range.start_key.key, sizeof(char*));
else
memcpy(mrr_buf_cur, cur_range.start_key.key, cpk_tuple_length);
mrr_buf_cur += key_buf_element_size;
key_buffer.write(cur_range.start_key.key, key_tuple_length);
if (is_mrr_assoc)
{
memcpy(mrr_buf_cur, &cur_range.ptr, sizeof(void*));
mrr_buf_cur += sizeof(void*);
}
key_buffer.write((uchar*)&cur_range.ptr, sizeof(void*));
}
dsmrr_eof= test(res);
/* Sort the buffer contents by rowid */
uint elem_size= key_buf_element_size + (int)is_mrr_assoc * sizeof(void*);
uint n_rowids= (mrr_buf_cur - mrr_buf) / elem_size;
uint key_elem_size= key_size_in_keybuf + (int)is_mrr_assoc * sizeof(void*);
uint n_keys= key_buffer.used_size() / key_elem_size;
my_qsort2(mrr_buf, n_rowids, elem_size,
my_qsort2(key_buffer.used_area(), n_keys, key_elem_size,
(qsort2_cmp)DsMrr_impl::key_tuple_cmp, (void*)this);
mrr_buf_last= mrr_buf_cur;
mrr_buf_cur= mrr_buf;
last_identical_key_ptr= NULL;
in_identical_keys_range= FALSE;
DBUG_VOID_RETURN;
}
......@@ -721,60 +863,105 @@ void DsMrr_impl::dsmrr_fill_key_buffer()
DS-MRR/CPK: multi_range_read_next() function
DESCRIPTION
DsMrr_impl::dsmrr_next_cpk()
DsMrr_impl::dsmrr_next_from_index()
range_info OUT identifier of range that the returned record belongs to
DESCRIPTION
DS-MRR/CPK: multi_range_read_next() function.
This is similar to DsMrr_impl::dsmrr_next(), the differences are that
- we get records with index_read(), not with rnd_pos()
- we may get multiple records for one key (=element of the buffer)
- unlike dsmrr_fill_rowid_buffer(), dsmrr_fill_key_buffer() never fails.
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 nested sequences:
- a disjoint sequence of index ranges
- each range has multiple records
- each record goes into multiple identical ranges.
RETURN
0 OK, next record was successfully read
HA_ERR_END_OF_FILE End of records
Other Some other error
psergey2-todo: this should detect identical keys.
*/
int DsMrr_impl::dsmrr_next_cpk(char **range_info)
int DsMrr_impl::dsmrr_next_from_index(char **range_info_arg)
{
int res;
uchar *key_in_buf;
handler *file= do_rowid_fetch? h2: h;
while (cpk_have_range)
while (in_identical_keys_range)
{
//read_and_check:
/* Read record/key pointer from the buffer */
key_in_buf= identical_key_it.get_next(key_size_in_keybuf);
if (is_mrr_assoc)
cur_range_info= (char*)identical_key_it.get_next(sizeof(void*));
if (h->mrr_funcs.skip_record &&
h->mrr_funcs.skip_record(h->mrr_iter, cpk_saved_range_info, NULL))
if (key_in_buf == last_identical_key_ptr)
{
cpk_have_range= FALSE;
break;
/* We're looking at the last of the identical keys */
in_identical_keys_range= FALSE;
}
uchar *lookup_tuple= use_key_pointers? (*((uchar**)mrr_buf_cur)) : mrr_buf_cur;
res= h->index_next_same(table->record[0], lookup_tuple, cpk_tuple_length);
if (h->mrr_funcs.skip_index_tuple &&
h->mrr_funcs.skip_index_tuple(h->mrr_iter, cpk_saved_range_info))
check_record:
if ((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)))
{
continue;
}
memcpy(range_info_arg, cur_range_info, sizeof(void*));
return 0;
}
/* Try returrning next record from the current range */
while (in_index_range)
{
res= file->ha_index_next_same(table->record[0], cur_index_tuple,
key_tuple_length);
if (res)
{
if (res != HA_ERR_END_OF_FILE && res != HA_ERR_KEY_NOT_FOUND)
return res; /* Fatal error */
if (res != HA_ERR_END_OF_FILE)
in_index_range= FALSE; /* no more records here */
break;
}
if (last_identical_key_ptr)
{
if (is_mrr_assoc)
memcpy(range_info, &cpk_saved_range_info, sizeof(void*));
return res;
in_identical_keys_range= TRUE;
identical_key_it.init(&key_buffer);
cur_range_info= first_identical_range_info;
}
/* No more records in this range. Exit this loop and go get another range */
cpk_have_range= FALSE;
goto check_record;
// goto read_and_check;
}
do
while(1)
{
DBUG_ASSERT(!in_identical_keys_range && !in_index_range);
/* Jump over the keys that were handled by identical key processing */
if (last_identical_key_ptr)
{
while (key_buffer.read(key_size_in_keybuf) != last_identical_key_ptr)
{
if (is_mrr_assoc)
key_buffer.read(sizeof(void*));
}
if (is_mrr_assoc)
key_buffer.read(sizeof(void*));
last_identical_key_ptr= NULL;
}
/* First, make sure we have a range at start of the buffer */
if (mrr_buf_cur == mrr_buf_last)
if (!key_buffer.have_data(key_buff_elem_size))
{
if (dsmrr_eof)
{
......@@ -782,58 +969,56 @@ int DsMrr_impl::dsmrr_next_cpk(char **range_info)
goto end;
}
dsmrr_fill_key_buffer();
}
if (mrr_buf_cur == mrr_buf_last)
{
res= HA_ERR_END_OF_FILE;
goto end;
if (!key_buffer.have_data(key_buff_elem_size))
{
res= HA_ERR_END_OF_FILE;
goto end;
}
}
/* Ok, got the range. Try making a lookup. */
uchar *lookup_tuple= use_key_pointers? (*((uchar**)mrr_buf_cur)) : mrr_buf_cur;
mrr_buf_cur += key_buf_element_size;
/* Get the next range to scan*/
cur_index_tuple= key_in_buf= key_buffer.read(key_size_in_keybuf);
if (use_key_pointers)
cur_index_tuple= *((uchar**)cur_index_tuple);
if (is_mrr_assoc)
cur_range_info= (char*)key_buffer.read(sizeof(void*));
/* Do index lookup */
if ((res= file->ha_index_read_map(table->record[0], cur_index_tuple,
key_tuple_map, HA_READ_KEY_EXACT)))
{
memcpy(&cpk_saved_range_info, mrr_buf_cur, sizeof(void*));
mrr_buf_cur += sizeof(void*) * test(is_mrr_assoc);
if (res != HA_ERR_END_OF_FILE && res != HA_ERR_KEY_NOT_FOUND)
return res;
continue; /* to next key and make another lookup */
}
if (h->mrr_funcs.skip_record &&
h->mrr_funcs.skip_record(h->mrr_iter, cpk_saved_range_info, NULL))
continue;
res= h->index_read(table->record[0], lookup_tuple, cpk_tuple_length,
HA_READ_KEY_EXACT);
/*
Check pushed index condition. Performance-wise, it does not make any
sense to put this call here (the above call has already accessed the full
record). That's the best I could do, though, because:
- ha_innobase doesn't support IndexConditionPushdown on clustered PK
- MRR interface doesn't allow the storage engine to refuse a pushed index
condition.
Having this call here is not fully harmless: EXPLAIN shows "pushed index
condition", which is technically true but doesn't bring the benefits that
one might expect.
*/
if (h->mrr_funcs.skip_index_tuple &&
h->mrr_funcs.skip_index_tuple(h->mrr_iter, cpk_saved_range_info))
continue;
/* Check if subsequent keys in the key buffer are the same as this one */
{
uchar *ptr;
identical_key_it.init(&key_buffer);
last_identical_key_ptr= NULL;
while ((ptr= identical_key_it.get_next(key_size_in_keybuf)))
{
if (is_mrr_assoc)
identical_key_it.get_next(sizeof(void*));
if (res && res != HA_ERR_END_OF_FILE)
goto end;
if (key_tuple_cmp(this, key_in_buf, ptr))
break;
if (!res)
{
memcpy(range_info, &cpk_saved_range_info, sizeof(void*));
/*
Attempt reading more rows from this range only if there actually can
be multiple matches:
*/
cpk_have_range= !cpk_is_unique_scan;
break;
last_identical_key_ptr= ptr;
}
if (last_identical_key_ptr)
{
in_identical_keys_range= TRUE;
identical_key_it.init(&key_buffer);
first_identical_range_info= cur_range_info;
}
}
} while (true);
in_index_range= !index_ranges_unique;
goto check_record;
}
end:
return res;
......@@ -842,9 +1027,6 @@ end:
/**
DS-MRR implementation: multi_range_read_next() function
psergey2-todo: put identical rowid detection code here
it should always work because rowid sequences are always sorted
*/
int DsMrr_impl::dsmrr_next(char **range_info)
......@@ -852,89 +1034,108 @@ int DsMrr_impl::dsmrr_next(char **range_info)
int res;
uchar *cur_range_info= 0;
uchar *rowid;
uchar *range_id;
if (use_default_impl)
return h->handler::multi_range_read_next(range_info);
if (doing_cpk_scan)
return dsmrr_next_cpk(range_info);
if (!do_rowid_fetch)
return dsmrr_next_from_index(range_info);
if (mrr_buf_next_identical != mrr_buf_cur)
while (identical_rowid_ptr)
{
/*
There are multiple rowids. Return the record again, now with different
range_id
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.
*/
do
rowid= rowid_buffer.read(h->ref_length);
if (is_mrr_assoc)
{
if (is_mrr_assoc)
memcpy(range_info, mrr_buf_next_identical + h->ref_length, sizeof(uchar*));
} while (!h2->mrr_funcs.skip_record ||
!h2->mrr_funcs.skip_record(h2->mrr_iter, (char *) range_info, rowid));
uchar *range_ptr= rowid_buffer.read(sizeof(uchar*));
memcpy(range_info, range_ptr, sizeof(uchar*));
}
mrr_buf_next_identical += h->ref_length + sizeof(void*) * test(is_mrr_assoc);
return 0;
if (rowid == identical_rowid_ptr)
{
identical_rowid_ptr= 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;
}
}
do
while (1)
{
if (mrr_buf_cur == mrr_buf_last)
if (!rowid_buffer.have_data(1))
{
if (dsmrr_eof)
{
res= HA_ERR_END_OF_FILE;
goto end;
}
res= dsmrr_fill_rowid_buffer();
if (res)
goto end;
return HA_ERR_END_OF_FILE;
if (do_sort_keys && key_buffer.used_size() == 0)
dsmrr_fill_key_buffer();
if ((res= dsmrr_fill_rowid_buffer()))
return res;
}
/* return eof if there are no rowids in the buffer after re-fill attempt */
if (mrr_buf_cur == mrr_buf_last)
{
res= HA_ERR_END_OF_FILE;
goto end;
}
rowid= mrr_buf_cur;
/* Return eof if there are no rowids in the buffer after re-fill attempt */
if (!rowid_buffer.have_data(1))
return HA_ERR_END_OF_FILE;
rowid= rowid_buffer.read(h->ref_length);
identical_rowid_ptr= NULL;
if (is_mrr_assoc)
memcpy(&cur_range_info, mrr_buf_cur + h->ref_length, sizeof(uchar**));
{
range_id= rowid_buffer.read(sizeof(uchar*));
memcpy(&cur_range_info, range_id, sizeof(uchar*));
memcpy(range_info, range_id, sizeof(uchar*));
}
size_t element_size= h->ref_length + sizeof(void*) * test(is_mrr_assoc);
mrr_buf_cur += element_size;
mrr_buf_next_identical= mrr_buf_cur;
//psergey2-note: the below isn't right- we won't want to skip over this
// rowid because this (rowid, range_id) pair has nothing.. the next
// identical rowids might have something.. (but we set identicals later,
// dont we?)
if (h2->mrr_funcs.skip_record &&
h2->mrr_funcs.skip_record(h2->mrr_iter, (char *) cur_range_info, rowid))
continue;
res= h->ha_rnd_pos(table->record[0], rowid);
if (res == HA_ERR_RECORD_DELETED)
continue;
if (0)//(!res)
/*
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)
{
/*
Note: this implies that SQL layer doesn't touch table->record[0]
between calls.
*/
uchar *current_el= mrr_buf_cur - element_size;
while (mrr_buf_cur != mrr_buf_last &&
!h2->cmp_ref(current_el, mrr_buf_cur))
uchar *ptr;
SimpleBuffer::PeekIterator identical_rowid_it;
identical_rowid_it.init(&rowid_buffer);
while ((ptr= identical_rowid_it.get_next(h->ref_length)))
{
mrr_buf_cur += element_size;
if (is_mrr_assoc)
identical_rowid_it.get_next(sizeof(void*));
if (h2->cmp_ref(rowid, ptr))
break;
identical_rowid_ptr= ptr;
}
}
break;
} while (true);
if (is_mrr_assoc)
{
memcpy(range_info, rowid + h->ref_length, sizeof(void*));
return 0;
}
end:
return res;
}
......
......@@ -37,6 +37,96 @@
*/
/*
A simple memory buffer for reading and writing.
when writing, there is no user-visible "current" position, although
internally 'pos' points to just after the end of used area (or at the
start of it for reverse buffer).
When reading, there is current position pointing at start (for reverse
buffer, end) of the element that will be read next.
^^ why end for reverse? it's more logical to point at start
One can peek at what's behind that element by using advance_ptr function.
TODO: will the reverse buffer store {tuple; rowid} or {rowid; tuple} pairs?
(why does it matter??? Read and write in the same order and then it
shouldn't matter.)
*/
class SimpleBuffer
{
uchar *start;
uchar *end;
uchar *read_pos;
uchar *write_pos;
/*
1 <=> buffer grows/is filled/is read from start to end
-1 <=> everthing is done from end to start instead.
*/
int direction;
public:
/* Write-mode functions */
void reset_for_writing();
void write(const uchar *data, size_t bytes);
bool have_space_for(size_t bytes);
uchar *used_area() { return (direction == 1)? read_pos : write_pos; }
size_t used_size();
/* Read-mode functions */
void reset_for_reading();
uchar *read(size_t bytes);
bool have_data(size_t bytes);
uchar *end_of_space();
/* Control functions */
void set_buffer_space(uchar *start_arg, uchar *end_arg, int direction_arg)
{
start= start_arg;
end= end_arg;
direction= direction_arg;
reset_for_writing();
}
friend class PeekIterator;
class PeekIterator
{
// if direction==1 : pointer to what to return next
// if direction==-1: pointer to the end of what is to be returned next
uchar *pos;
SimpleBuffer *sb;
public:
void init(SimpleBuffer *sb_arg)
{
sb= sb_arg;
pos= sb->read_pos;
}
/* Return pointer to next chunk of nbytes bytes and avance over it */
uchar *get_next(size_t nbytes)
{
if (sb->direction == 1)
{
if (pos + nbytes > sb->write_pos)
return NULL;
uchar *res= pos;
pos += nbytes;
return res;
}
else
{
if (pos - nbytes <= sb->write_pos)
return NULL;
pos -= nbytes;
return pos;
}
}
};
};
/*
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
......@@ -73,6 +163,8 @@
scanning.
*/
class DsMrr_impl
{
public:
......@@ -108,14 +200,27 @@ private:
/* Secondary handler object. It is used for scanning the index */
handler *h2;
uchar *full_buf;
uchar *full_buf_end;
/* Buffer to store rowids, or (rowid, range_id) pairs */
uchar *mrr_buf;
uchar *mrr_buf_cur; /* Current position when reading/writing */
uchar *mrr_buf_last; /* When reading: end of used buffer space */
uchar *mrr_buf_end; /* End of the buffer */
SimpleBuffer rowid_buffer;
uchar *identical_rowid_ptr;
/* Identical keys */
bool in_identical_keys_range;
uchar *last_identical_key_ptr;
SimpleBuffer::PeekIterator identical_key_it;
uchar *mrr_buf_next_identical;
SimpleBuffer key_buffer;
uint keyno;
/* Execution control */
bool do_sort_keys;
bool use_key_pointers;
bool do_rowid_fetch;
bool dsmrr_eof; /* TRUE <=> We have reached EOF when reading index tuples */
......@@ -129,18 +234,33 @@ private:
/** DS-MRR/CPK variables start */
/* Length of lookup tuple being used, in bytes */
uint cpk_tuple_length;
uint key_tuple_length;
key_part_map key_tuple_map;
/*
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;
/* = key_size_in_keybuf [ + sizeof(range_assoc_info) ] */
uint key_buff_elem_size;
/* = h->ref_length [ + sizeof(range_assoc_info) ] */
uint rowid_buff_elem_size;
uint key_buf_element_size;
/*
TRUE <=> We're scanning on a full primary key (and not on prefix), and so
can get max. one match for each key
*/
bool cpk_is_unique_scan;
bool index_ranges_unique;
/* TRUE<=> we're in a middle of enumerating records from a range */
bool cpk_have_range;
/* Valid if cpk_have_range==TRUE: range_id of the range we're enumerating */
char *cpk_saved_range_info;
bool in_index_range;
uchar *cur_index_tuple;
/* if in_index_range==TRUE: range_id of the range we're enumerating */
char *cur_range_info;
char *first_identical_range_info;
bool choose_mrr_impl(uint keyno, ha_rows rows, uint *flags, uint *bufsz,
COST_VECT *cost);
......@@ -150,6 +270,11 @@ private:
static int key_tuple_cmp(void* arg, uchar* key1, uchar* key2);
int dsmrr_fill_rowid_buffer();
void dsmrr_fill_key_buffer();
int dsmrr_next_cpk(char **range_info);
int dsmrr_next_from_index(char **range_info);
void setup_buffer_sizes(key_range *sample_key);
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);
};
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment