1
0
mirror of https://github.com/MariaDB/server.git synced 2025-07-29 05:21:33 +03:00

MWL#121: DS-MRR support for clustered primary keys

- Add testcases
- Code cleanup: garbage removal, better comments, make members private where possible
This commit is contained in:
Sergey Petrunya
2010-06-22 21:24:22 +04:00
parent 82f8ed17e1
commit 16e197f5b1
4 changed files with 577 additions and 189 deletions

View File

@ -139,8 +139,13 @@ ha_rows handler::multi_range_read_info(uint keyno, uint n_ranges, uint n_rows,
uint key_parts, uint *bufsz,
uint *flags, COST_VECT *cost)
{
/*
Currently we expect this function to be called only in preparation of scan
with HA_MRR_SINGLE_POINT property.
*/
DBUG_ASSERT(*flags | HA_MRR_SINGLE_POINT);
*bufsz= 0; /* Default implementation doesn't need a buffer */
//psergey2-todo: assert for singlepoint ranges here?
*flags |= HA_MRR_USE_DEFAULT_IMPL;
cost->zero();
@ -323,22 +328,25 @@ int DsMrr_impl::dsmrr_init(handler *h_arg, RANGE_SEQ_IF *seq_funcs,
n_ranges, key_parts, mode, buf);
DBUG_RETURN(retval);
}
rowids_buf= buf->buffer;
mrr_buf= buf->buffer;
is_mrr_assoc= !test(mode & HA_MRR_NO_ASSOCIATION);
if (is_mrr_assoc)
status_var_increment(table->in_use->status_var.ha_multi_range_read_init_count);
rowids_buf_end= buf->buffer_end;
mrr_buf_end= buf->buffer_end;
doing_cpk_scan= check_cpk_scan(h->active_index, mode);
if (doing_cpk_scan)
{
/*
When doing a scan on CPK, the buffer stores {lookup_tuple, range_id}
pairs
*/
uint keylen=0;
DBUG_ASSERT(key_parts != 0);
//psergey2-todo: new elem_size here
for (uint kp= 0; kp < key_parts; kp++)
keylen += table->key_info[h->active_index].key_part[kp].store_length;
@ -350,12 +358,29 @@ int DsMrr_impl::dsmrr_init(handler *h_arg, RANGE_SEQ_IF *seq_funcs,
use_default_impl= FALSE;
}
else
{
/* In regular DS-MRR, buffer stores {rowid, range_id} pairs */
elem_size= h->ref_length + (int)is_mrr_assoc * sizeof(void*);
}
rowids_buf_last= rowids_buf +
((rowids_buf_end - rowids_buf)/ elem_size)*
mrr_buf_last= mrr_buf +
((mrr_buf_end - mrr_buf)/ elem_size)*
elem_size;
rowids_buf_end= rowids_buf_last;
mrr_buf_end= mrr_buf_last;
if (doing_cpk_scan)
{
/*
DS-MRR/CPK: fill buffer with lookup tuples and sort; also we don't need a
secondary handler object.
*/
h->mrr_iter= seq_funcs->init(seq_init_param, n_ranges, mode);
h->mrr_funcs= *seq_funcs;
dsmrr_fill_buffer_cpk();
if (dsmrr_eof)
buf->end_of_used_area= mrr_buf_last;
DBUG_RETURN(0); /* nothing could go wrong while filling the buffer */
}
/*
There can be two cases:
@ -365,84 +390,68 @@ int DsMrr_impl::dsmrr_init(handler *h_arg, RANGE_SEQ_IF *seq_funcs,
The caller might have called h->index_init(), need to switch h to
rnd_pos calls.
*/
//psergey2-todo: don't create secondary for CPK scan.
if (!doing_cpk_scan)
if (!h2)
{
if (!h2)
/* Create a separate handler object to do rndpos() calls. */
THD *thd= current_thd;
/*
::clone() takes up a lot of stack, especially on 64 bit platforms.
The constant 5 is an empiric result.
*/
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;
/* Create a separate handler object to do rndpos() calls. */
if (!(new_h2= h->clone(thd->mem_root)) ||
new_h2->ha_external_lock(thd, F_RDLCK))
{
/* Create a separate handler object to do rndpos() calls. */
THD *thd= current_thd;
/*
::clone() takes up a lot of stack, especially on 64 bit platforms.
The constant 5 is an empiric result.
*/
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;
/* Create a separate handler object to do rndpos() calls. */
if (!(new_h2= h->clone(thd->mem_root)) ||
new_h2->ha_external_lock(thd, F_RDLCK))
{
delete new_h2;
DBUG_RETURN(1);
}
if (mrr_keyno == h->pushed_idx_cond_keyno)
pushed_cond= h->pushed_idx_cond;
/*
Caution: this call will invoke this->dsmrr_close(). Do not put the
created secondary table handler into this->h2 or it will delete it.
*/
if (h->ha_index_end())
{
h2=new_h2;
goto error;
}
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))
goto error;
use_default_impl= FALSE;
if (pushed_cond)
h2->idx_cond_push(mrr_keyno, pushed_cond);
delete new_h2;
DBUG_RETURN(1);
}
else
if (mrr_keyno == h->pushed_idx_cond_keyno)
pushed_cond= h->pushed_idx_cond;
/*
Caution: this call will invoke this->dsmrr_close(). Do not put the
created secondary table handler into this->h2 or it will delete it.
*/
if (h->ha_index_end())
{
/*
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
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)
goto error;
h2=new_h2;
goto error;
}
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))
goto error;
use_default_impl= FALSE;
if (pushed_cond)
h2->idx_cond_push(mrr_keyno, pushed_cond);
}
else
{
//doing DS-MRR/CPK
// fill-buffer-analog
// eof
h->mrr_iter= seq_funcs->init(seq_init_param, n_ranges, mode);
h->mrr_funcs= *seq_funcs;
dsmrr_fill_buffer_cpk();
if (dsmrr_eof)
buf->end_of_used_area= rowids_buf_last;
DBUG_RETURN(0); // nothing can go wrong while filling the buffer
/*
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
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)
goto error;
}
if (h2->handler::multi_range_read_init(seq_funcs, seq_init_param, n_ranges,
@ -456,7 +465,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= rowids_buf_last;
buf->end_of_used_area= mrr_buf_last;
/*
h->inited == INDEX may occur when 'range checked for each record' is
@ -512,6 +521,9 @@ static int rowid_cmp(void *h, uchar *a, uchar *b)
rowid and return.
The function assumes that rowids buffer is empty when it is invoked.
dsmrr_eof is set to indicate whether we've exhausted the list of ranges we're
scanning.
@param h Table handler
@ -526,8 +538,8 @@ int DsMrr_impl::dsmrr_fill_buffer()
int res;
DBUG_ENTER("DsMrr_impl::dsmrr_fill_buffer");
rowids_buf_cur= rowids_buf;
while ((rowids_buf_cur < rowids_buf_end) &&
mrr_buf_cur= mrr_buf;
while ((mrr_buf_cur < mrr_buf_end) &&
!(res= h2->handler::multi_range_read_next(&range_info)))
{
KEY_MULTI_RANGE *curr_range= &h2->handler::mrr_cur_range;
@ -537,13 +549,13 @@ int DsMrr_impl::dsmrr_fill_buffer()
/* Put rowid, or {rowid, range_id} pair into the buffer */
h2->position(table->record[0]);
memcpy(rowids_buf_cur, h2->ref, h2->ref_length);
rowids_buf_cur += h2->ref_length;
memcpy(mrr_buf_cur, h2->ref, h2->ref_length);
mrr_buf_cur += h2->ref_length;
if (is_mrr_assoc)
{
memcpy(rowids_buf_cur, &range_info, sizeof(void*));
rowids_buf_cur += sizeof(void*);
memcpy(mrr_buf_cur, &range_info, sizeof(void*));
mrr_buf_cur += sizeof(void*);
}
}
@ -553,27 +565,29 @@ int DsMrr_impl::dsmrr_fill_buffer()
/* Sort the buffer contents by rowid */
uint elem_size= h->ref_length + (int)is_mrr_assoc * sizeof(void*);
uint n_rowids= (rowids_buf_cur - rowids_buf) / elem_size;
uint n_rowids= (mrr_buf_cur - mrr_buf) / elem_size;
my_qsort2(rowids_buf, n_rowids, elem_size, (qsort2_cmp)rowid_cmp,
my_qsort2(mrr_buf, n_rowids, elem_size, (qsort2_cmp)rowid_cmp,
(void*)h);
rowids_buf_last= rowids_buf_cur;
rowids_buf_cur= rowids_buf;
mrr_buf_last= mrr_buf_cur;
mrr_buf_cur= mrr_buf;
DBUG_RETURN(0);
}
/* qsort-compatible function to compare key tuples */
/*
my_qsort2-compatible function to compare key tuples
*/
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;
KEY_PART_INFO *part_end= part + dsmrr->cpk_n_parts;
uchar *key1_end= key1 + dsmrr->cpk_tuple_length;
//uint32 *lengths=item->field_lengths;
for (; part < part_end; ++part)
while (key1 < key1_end)
{
Field* f = part->field;
int len = part->store_length;
@ -582,33 +596,43 @@ int DsMrr_impl::key_tuple_cmp(void* arg, uchar* key1, uchar* key2)
return res;
key1 += len;
key2 += len;
part++;
}
return 0;
}
//psergey2:
int DsMrr_impl::dsmrr_fill_buffer_cpk()
/*
DS-MRR/CPK: Fill the buffer with (lookup_tuple, range_id) pairs and sort
DESCRIPTION
DS-MRR/CPK: Fill the buffer with (lookup_tuple, range_id) pairs and sort
dsmrr_eof is set to indicate whether we've exhausted the list of ranges
we're scanning.
*/
void DsMrr_impl::dsmrr_fill_buffer_cpk()
{
int res;
KEY_MULTI_RANGE cur_range;
DBUG_ENTER("DsMrr_impl::dsmrr_fill_buffer_cpk");
rowids_buf_cur= rowids_buf;
while ((rowids_buf_cur < rowids_buf_end) &&
mrr_buf_cur= mrr_buf;
while ((mrr_buf_cur < mrr_buf_end) &&
!(res= h->mrr_funcs.next(h->mrr_iter, &cur_range)))
{
DBUG_ASSERT(cur_range.range_flag & EQ_RANGE);
DBUG_ASSERT(cpk_tuple_length == cur_range.start_key.length);
/* Put key, or {key, range_id} pair into the buffer */
memcpy(rowids_buf_cur, cur_range.start_key.key, cpk_tuple_length);
rowids_buf_cur += cpk_tuple_length;
memcpy(mrr_buf_cur, cur_range.start_key.key, cpk_tuple_length);
mrr_buf_cur += cpk_tuple_length;
if (is_mrr_assoc)
{
memcpy(rowids_buf_cur, &cur_range.ptr, sizeof(void*));
rowids_buf_cur += sizeof(void*);
memcpy(mrr_buf_cur, &cur_range.ptr, sizeof(void*));
mrr_buf_cur += sizeof(void*);
}
}
@ -616,77 +640,82 @@ int DsMrr_impl::dsmrr_fill_buffer_cpk()
/* Sort the buffer contents by rowid */
uint elem_size= cpk_tuple_length + (int)is_mrr_assoc * sizeof(void*);
uint n_rowids= (rowids_buf_cur - rowids_buf) / elem_size;
uint n_rowids= (mrr_buf_cur - mrr_buf) / elem_size;
my_qsort2(rowids_buf, n_rowids, elem_size,
my_qsort2(mrr_buf, n_rowids, elem_size,
(qsort2_cmp)DsMrr_impl::key_tuple_cmp, (void*)this);
rowids_buf_last= rowids_buf_cur;
rowids_buf_cur= rowids_buf;
DBUG_RETURN(0);
mrr_buf_last= mrr_buf_cur;
mrr_buf_cur= mrr_buf;
DBUG_VOID_RETURN;
}
/*
CPK: so, the source is
- buffer exhaustion/re-fill
- advance to next range on "record-not-found" error.
- if scanning on a prefix, enumerate all records for a key.
DS-MRR/CPK: multi_range_read_next() function
DESCRIPTION
DsMrr_impl::dsmrr_next_cpk()
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_buffer(), dsmrr_fill_buffer_cpk() never fails.
RETURN
0 OK, next record was successfully read
HA_ERR_END_OF_FILE End of records
Other Some other error
*/
int DsMrr_impl::dsmrr_next_cpk(char **range_info)
{
int res;
if (cpk_have_range)
{
res= h->index_next_same(table->record[0], rowids_buf_cur, cpk_tuple_length);
res= h->index_next_same(table->record[0], mrr_buf_cur, cpk_tuple_length);
if (res != HA_ERR_END_OF_FILE)
{
// todo
if (is_mrr_assoc)
memcpy(range_info, &cpk_saved_range_info, sizeof(void*));
return res;
}
/*
Ok, we got EOF for records in this range. Fall through to get to another
range.
*/
/* No more records in this range. Fall through to get to another range */
}
do
{
/* First, make sure we have a range at start of the buffer*/
if (rowids_buf_cur == rowids_buf_last)
/* First, make sure we have a range at start of the buffer */
if (mrr_buf_cur == mrr_buf_last)
{
if (dsmrr_eof)
{
res= HA_ERR_END_OF_FILE;
goto end;
}
// TODO: the return values are mix of HA_ERR_ codes and TRUE as "generic
// failure" error. Is this ok?
if ((res= dsmrr_fill_buffer_cpk()))
goto end;
dsmrr_fill_buffer_cpk();
}
if (rowids_buf_cur == rowids_buf_last)
if (mrr_buf_cur == mrr_buf_last)
{
res= HA_ERR_END_OF_FILE;
goto end;
}
//TODO: make skip_index_tuple() calls, too?
//TODO: skip-record calls here?
//psergey2-todo: make skip_index_tuple() calls, too?
//psergey2-todo: skip-record calls here?
//if (h2->mrr_funcs.skip_record &&
// h2->mrr_funcs.skip_record(h2->mrr_iter, (char *) cur_range_info, rowid))
// continue;
/* Ok, got the range. Try making a lookup. */
uchar *lookup_tuple= rowids_buf_cur;
rowids_buf_cur += cpk_tuple_length;
uchar *lookup_tuple= mrr_buf_cur;
mrr_buf_cur += cpk_tuple_length;
if (is_mrr_assoc)
{
memcpy(cpk_saved_range_info, rowids_buf_cur, sizeof(void*));
rowids_buf_cur += sizeof(void*) * test(is_mrr_assoc);
memcpy(cpk_saved_range_info, mrr_buf_cur, sizeof(void*));
mrr_buf_cur += sizeof(void*) * test(is_mrr_assoc);
}
res= h->index_read(table->record[0], lookup_tuple, cpk_tuple_length,
@ -698,6 +727,10 @@ int DsMrr_impl::dsmrr_next_cpk(char **range_info)
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;
}
@ -707,6 +740,7 @@ end:
return res;
}
/**
DS-MRR implementation: multi_range_read_next() function
*/
@ -725,7 +759,7 @@ int DsMrr_impl::dsmrr_next(char **range_info)
do
{
if (rowids_buf_cur == rowids_buf_last)
if (mrr_buf_cur == mrr_buf_last)
{
if (dsmrr_eof)
{
@ -738,17 +772,17 @@ int DsMrr_impl::dsmrr_next(char **range_info)
}
/* return eof if there are no rowids in the buffer after re-fill attempt */
if (rowids_buf_cur == rowids_buf_last)
if (mrr_buf_cur == mrr_buf_last)
{
res= HA_ERR_END_OF_FILE;
goto end;
}
rowid= rowids_buf_cur;
rowid= mrr_buf_cur;
if (is_mrr_assoc)
memcpy(&cur_range_info, rowids_buf_cur + h->ref_length, sizeof(uchar**));
memcpy(&cur_range_info, mrr_buf_cur + h->ref_length, sizeof(uchar**));
rowids_buf_cur += h->ref_length + sizeof(void*) * test(is_mrr_assoc);
mrr_buf_cur += h->ref_length + sizeof(void*) * test(is_mrr_assoc);
if (h2->mrr_funcs.skip_record &&
h2->mrr_funcs.skip_record(h2->mrr_iter, (char *) cur_range_info, rowid))
continue;
@ -870,7 +904,33 @@ bool key_uses_partial_cols(TABLE *table, uint keyno)
return FALSE;
}
/**
/*
Check if key/flags allow DS-MRR/CPK strategy to be used
SYNOPSIS
DsMrr_impl::check_cpk_scan()
keyno Index that will be used
mrr_flags
DESCRIPTION
Check if key/flags allow DS-MRR/CPK strategy to be used.
RETURN
TRUE DS-MRR/CPK should be used
FALSE Otherwise
*/
bool DsMrr_impl::check_cpk_scan(uint keyno, uint mrr_flags)
{
return test((mrr_flags & HA_MRR_SINGLE_POINT) &&
!(mrr_flags & HA_MRR_SORTED) &&
keyno == table->s->primary_key &&
h->primary_key_is_clustered());
}
/*
DS-MRR Internals: Choose between Default MRR implementation and DS-MRR
Make the choice between using Default MRR implementation and DS-MRR.
@ -892,13 +952,7 @@ bool key_uses_partial_cols(TABLE *table, uint keyno)
@retval TRUE Default MRR implementation should be used
@retval FALSE DS-MRR implementation should be used
*/
bool DsMrr_impl::check_cpk_scan(uint keyno, uint mrr_flags)
{
return test((mrr_flags & HA_MRR_SINGLE_POINT) &&
!(mrr_flags & HA_MRR_SORTED) &&
keyno == table->s->primary_key &&
h->primary_key_is_clustered());
}
bool DsMrr_impl::choose_mrr_impl(uint keyno, ha_rows rows, uint *flags,
uint *bufsz, COST_VECT *cost)
@ -906,9 +960,8 @@ bool DsMrr_impl::choose_mrr_impl(uint keyno, ha_rows rows, uint *flags,
COST_VECT dsmrr_cost;
bool res;
THD *thd= current_thd;
//psergey2: check the criteria.
doing_cpk_scan= check_cpk_scan(keyno, *flags);
doing_cpk_scan= check_cpk_scan(keyno, *flags);
if (thd->variables.optimizer_use_mrr == 2 || *flags & HA_MRR_INDEX_ONLY ||
(keyno == table->s->primary_key && h->primary_key_is_clustered() &&
!doing_cpk_scan) ||