mirror of
https://github.com/MariaDB/server.git
synced 2025-01-16 12:02:42 +01:00
DS-MRR improvements: review feedback
- Switch from one bi-directional buffer class to two virtual inheritance-based forward and backward buffer classes.
This commit is contained in:
parent
51f9097608
commit
3066c37718
4 changed files with 425 additions and 396 deletions
|
@ -865,12 +865,12 @@ LEFT JOIN
|
|||
(t1,t2)
|
||||
ON t3.a=1 AND t3.b=t2.b AND t2.b=t4.b;
|
||||
a b a b a b
|
||||
4 2 1 2 4 2
|
||||
4 2 1 2 3 2
|
||||
4 2 1 2 4 2
|
||||
4 2 1 2 3 2
|
||||
4 2 1 2 4 2
|
||||
4 2 1 2 3 2
|
||||
4 2 1 2 4 2
|
||||
NULL NULL 2 2 3 2
|
||||
NULL NULL 2 2 4 2
|
||||
EXPLAIN EXTENDED
|
||||
|
@ -1105,8 +1105,8 @@ t0.b=t1.b AND
|
|||
(t8.b=t9.b OR t8.c IS NULL) AND
|
||||
(t9.a=1);
|
||||
a b a b a b a b a b a b a b a b a b a b
|
||||
1 2 3 2 4 2 1 2 4 2 2 2 6 2 2 2 0 2 1 2
|
||||
1 2 3 2 4 2 1 2 3 2 2 2 6 2 2 2 0 2 1 2
|
||||
1 2 3 2 4 2 1 2 4 2 2 2 6 2 2 2 0 2 1 2
|
||||
1 2 3 2 4 2 1 2 3 2 3 1 6 2 1 1 NULL NULL 1 1
|
||||
1 2 3 2 4 2 1 2 4 2 3 1 6 2 1 1 NULL NULL 1 1
|
||||
1 2 3 2 4 2 1 2 3 2 3 1 6 2 1 1 NULL NULL 1 2
|
||||
|
@ -1785,8 +1785,8 @@ ON t7.b=t8.b AND t6.b < 10
|
|||
ON t6.b >= 2 AND t5.b=t7.b AND
|
||||
(t8.a > 0 OR t8.c IS NULL);
|
||||
a b a b a b a b
|
||||
2 2 3 2 2 2 1 2
|
||||
2 2 1 2 2 2 1 2
|
||||
2 2 3 2 2 2 1 2
|
||||
1 1 1 2 1 1 NULL NULL
|
||||
1 1 3 2 1 1 NULL NULL
|
||||
3 3 NULL NULL NULL NULL NULL NULL
|
||||
|
|
|
@ -352,14 +352,14 @@ Thimble Smith Happy 3 3
|
|||
Lilliana Angelovska NULL NULL NULL
|
||||
select t1.name, t2.name, t2.id,t3.id from t1 right join t2 on (t1.id = t2.owner) right join t1 as t3 on t3.id=t2.owner;
|
||||
name name id id
|
||||
Antonio Paz Perrito 2 1
|
||||
Antonio Paz El Gato 1 1
|
||||
Antonio Paz Perrito 2 1
|
||||
Thimble Smith Happy 3 3
|
||||
NULL NULL NULL 2
|
||||
select t1.name, t2.name, t2.id, t2.owner, t3.id from t1 left join t2 on (t1.id = t2.owner) right join t1 as t3 on t3.id=t2.owner;
|
||||
name name id owner id
|
||||
Antonio Paz Perrito 2 1 1
|
||||
Antonio Paz El Gato 1 1 1
|
||||
Antonio Paz Perrito 2 1 1
|
||||
Thimble Smith Happy 3 3 3
|
||||
NULL NULL NULL NULL 2
|
||||
drop table t1,t2;
|
||||
|
@ -413,9 +413,9 @@ insert into t2 values (1, 2, 3),(2, 2, 8), (4,3,9),(3,2,10);
|
|||
select t1.*, t2.* from t1 left join t2 on t1.n = t2.n and
|
||||
t1.m = t2.m where t1.n = 1;
|
||||
n m o n m o
|
||||
1 2 9 1 2 3
|
||||
1 2 7 1 2 3
|
||||
1 2 11 1 2 3
|
||||
1 2 7 1 2 3
|
||||
1 2 9 1 2 3
|
||||
1 3 9 NULL NULL NULL
|
||||
select t1.*, t2.* from t1 left join t2 on t1.n = t2.n and
|
||||
t1.m = t2.m where t1.n = 1 order by t1.o;
|
||||
|
|
|
@ -284,131 +284,6 @@ scan_it_again:
|
|||
}
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
* SimpleBuffer class implementation (used by DS-MRR code)
|
||||
***************************************************************************/
|
||||
void SimpleBuffer::setup_writing(uchar **data1, size_t len1,
|
||||
uchar **data2, size_t len2)
|
||||
{
|
||||
write_ptr1= data1;
|
||||
size1= len1;
|
||||
|
||||
write_ptr2= data2;
|
||||
size2= len2;
|
||||
}
|
||||
|
||||
|
||||
void SimpleBuffer::write()
|
||||
{
|
||||
if (is_reverse() && write_ptr2)
|
||||
write(*write_ptr2, size2);
|
||||
|
||||
write(*write_ptr1, size1);
|
||||
|
||||
if (!is_reverse() && write_ptr2)
|
||||
write(*write_ptr2, size2);
|
||||
}
|
||||
|
||||
|
||||
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::can_write()
|
||||
{
|
||||
return have_space_for(size1 + (write_ptr2 ? size2 : 0));
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
void SimpleBuffer::setup_reading(uchar **data1, size_t len1,
|
||||
uchar **data2, size_t len2)
|
||||
{
|
||||
read_ptr1= data1;
|
||||
DBUG_ASSERT(len1 == size1);
|
||||
|
||||
read_ptr2= data2;
|
||||
DBUG_ASSERT(len2 == size2);
|
||||
}
|
||||
|
||||
|
||||
bool SimpleBuffer::read()
|
||||
{
|
||||
if (!have_data(size1 + (read_ptr2 ? size2 : 0)))
|
||||
return TRUE;
|
||||
*read_ptr1= read(size1);
|
||||
if (read_ptr2)
|
||||
*read_ptr2= read(size2);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
uchar *SimpleBuffer::end_of_space()
|
||||
{
|
||||
if (direction == 1)
|
||||
return start;
|
||||
else
|
||||
return end;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* DS-MRR implementation
|
||||
***************************************************************************/
|
||||
|
@ -492,7 +367,7 @@ int DsMrr_impl::dsmrr_init(handler *h_arg, RANGE_SEQ_IF *seq_funcs,
|
|||
*/
|
||||
full_buf= buf->buffer;
|
||||
full_buf_end= buf->buffer_end;
|
||||
rowid_buffer.set_buffer_space(full_buf, full_buf_end, SimpleBuffer::FORWARD);
|
||||
rowid_buffer.set_buffer_space(full_buf, full_buf_end);
|
||||
|
||||
if (do_sort_keys)
|
||||
{
|
||||
|
@ -504,7 +379,7 @@ int DsMrr_impl::dsmrr_init(handler *h_arg, RANGE_SEQ_IF *seq_funcs,
|
|||
dsmrr_fill_key_buffer();
|
||||
|
||||
if (dsmrr_eof && !do_rndpos_scan)
|
||||
buf->end_of_used_area= key_buffer.end_of_space();
|
||||
buf->end_of_used_area= key_buffer->end_of_space();
|
||||
}
|
||||
|
||||
if (!do_rndpos_scan)
|
||||
|
@ -646,9 +521,9 @@ void DsMrr_impl::dsmrr_close()
|
|||
}
|
||||
|
||||
|
||||
static int rowid_cmp(void *h, uchar *a, uchar *b)
|
||||
static int rowid_cmp_reverse(void *h, uchar *a, uchar *b)
|
||||
{
|
||||
return ((handler*)h)->cmp_ref(a, b);
|
||||
return - ((handler*)h)->cmp_ref(a, b);
|
||||
}
|
||||
|
||||
|
||||
|
@ -667,8 +542,6 @@ static int rowid_cmp(void *h, uchar *a, uchar *b)
|
|||
post-condition:
|
||||
rowid buffer is not empty, or key source is exhausted.
|
||||
|
||||
@param h Table handler
|
||||
|
||||
@retval 0 OK, the next portion of rowids is in the buffer,
|
||||
properly ordered
|
||||
@retval other Error
|
||||
|
@ -689,8 +562,8 @@ int DsMrr_impl::dsmrr_fill_rowid_buffer()
|
|||
|
||||
last_identical_rowid= NULL;
|
||||
|
||||
if (do_sort_keys && key_buffer.is_reverse())
|
||||
key_buffer.flip();
|
||||
//if (do_sort_keys && key_buffer.is_reverse())
|
||||
// key_buffer.flip();
|
||||
|
||||
while (rowid_buffer.can_write())
|
||||
{
|
||||
|
@ -721,7 +594,7 @@ int DsMrr_impl::dsmrr_fill_rowid_buffer()
|
|||
dsmrr_eof= test(res == HA_ERR_END_OF_FILE);
|
||||
|
||||
/* Sort the buffer contents by rowid */
|
||||
rowid_buffer.sort((qsort2_cmp)rowid_cmp, (void*)h);
|
||||
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*));
|
||||
|
@ -729,14 +602,6 @@ int DsMrr_impl::dsmrr_fill_rowid_buffer()
|
|||
}
|
||||
|
||||
|
||||
void SimpleBuffer::sort(qsort2_cmp cmp_func, void *cmp_func_arg)
|
||||
{
|
||||
uint elem_size= size1 + (write_ptr2 ? size2 : 0);
|
||||
uint n_elements= used_size() / elem_size;
|
||||
my_qsort2(used_area(), n_elements, elem_size, cmp_func, cmp_func_arg);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
my_qsort2-compatible function to compare key tuples
|
||||
*/
|
||||
|
@ -787,6 +652,10 @@ equals:
|
|||
return 0;
|
||||
}
|
||||
|
||||
int DsMrr_impl::key_tuple_cmp_reverse(void* arg, uchar* key1, uchar* key2)
|
||||
{
|
||||
return -key_tuple_cmp(arg, key1, key2);
|
||||
}
|
||||
|
||||
/*
|
||||
Setup key/rowid buffer sizes based on sample_key
|
||||
|
@ -812,12 +681,13 @@ void DsMrr_impl::setup_buffer_sizes(key_range *sample_key)
|
|||
my_count_bits(sample_key->keypart_map));
|
||||
if (!do_rndpos_scan)
|
||||
{
|
||||
/* Give all space to key buffer. */
|
||||
key_buffer.set_buffer_space(full_buf, full_buf_end, SimpleBuffer::FORWARD);
|
||||
/* Give all space to forward key buffer. */
|
||||
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: */
|
||||
rowid_buffer.set_buffer_space(full_buf_end, full_buf_end,
|
||||
SimpleBuffer::FORWARD);
|
||||
rowid_buffer.set_buffer_space(full_buf_end, full_buf_end);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -860,10 +730,10 @@ 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,
|
||||
SimpleBuffer::FORWARD);
|
||||
key_buffer.set_buffer_space(rowid_buffer_end, full_buf_end,
|
||||
SimpleBuffer::BACKWARD);
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -892,7 +762,7 @@ void DsMrr_impl::dsmrr_fill_key_buffer()
|
|||
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());
|
||||
DBUG_ASSERT(!know_key_tuple_params || key_buffer->is_empty());
|
||||
|
||||
uchar *key_ptr;
|
||||
if (know_key_tuple_params)
|
||||
|
@ -903,18 +773,18 @@ void DsMrr_impl::dsmrr_fill_key_buffer()
|
|||
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,
|
||||
SimpleBuffer::FORWARD);
|
||||
key_buffer.set_buffer_space(rowid_buffer_end, full_buf_end,
|
||||
SimpleBuffer::BACKWARD);
|
||||
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);
|
||||
}
|
||||
key_buffer.reset_for_writing();
|
||||
key_buffer.setup_writing(&key_ptr, key_size_in_keybuf,
|
||||
is_mrr_assoc? (uchar**)&range_info_ptr : NULL,
|
||||
sizeof(uchar*));
|
||||
key_buffer->reset_for_writing();
|
||||
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()) &&
|
||||
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);
|
||||
|
@ -923,10 +793,10 @@ void DsMrr_impl::dsmrr_fill_key_buffer()
|
|||
/* 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,
|
||||
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());
|
||||
DBUG_ASSERT(key_buffer->can_write());
|
||||
}
|
||||
|
||||
/* Put key, or {key, range_id} pair into the buffer */
|
||||
|
@ -935,16 +805,19 @@ void DsMrr_impl::dsmrr_fill_key_buffer()
|
|||
else
|
||||
key_ptr=(uchar*) cur_range.start_key.key;
|
||||
|
||||
key_buffer.write();
|
||||
key_buffer->write();
|
||||
}
|
||||
|
||||
dsmrr_eof= test(res);
|
||||
|
||||
key_buffer.sort((qsort2_cmp)DsMrr_impl::key_tuple_cmp, (void*)this);
|
||||
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*));
|
||||
key_buffer->setup_reading(&cur_index_tuple, key_size_in_keybuf,
|
||||
is_mrr_assoc? (uchar**)&cur_range_info: NULL,
|
||||
sizeof(void*));
|
||||
|
||||
last_identical_key_ptr= NULL;
|
||||
in_identical_keys_range= FALSE;
|
||||
|
@ -959,7 +832,7 @@ void DsMrr_impl::dsmrr_fill_key_buffer()
|
|||
void DsMrr_impl::reallocate_buffer_space()
|
||||
{
|
||||
uchar *unused_start, *unused_end;
|
||||
key_buffer.remove_unused_space(&unused_start, &unused_end);
|
||||
key_buffer->remove_unused_space(&unused_start, &unused_end);
|
||||
rowid_buffer.grow(unused_start, unused_end);
|
||||
}
|
||||
|
||||
|
@ -1000,7 +873,7 @@ int DsMrr_impl::dsmrr_next_from_index(char **range_info_arg)
|
|||
while (in_identical_keys_range)
|
||||
{
|
||||
/* This will read to (cur_index_tuple, cur_range_info): */
|
||||
res2= identical_key_it.read_next();
|
||||
res2= identical_key_it->read_next();
|
||||
DBUG_ASSERT(!res2);
|
||||
|
||||
if (cur_index_tuple == last_identical_key_ptr)
|
||||
|
@ -1038,7 +911,7 @@ check_record:
|
|||
if (last_identical_key_ptr)
|
||||
{
|
||||
in_identical_keys_range= TRUE;
|
||||
identical_key_it.init(&key_buffer);
|
||||
identical_key_it->init(key_buffer);
|
||||
cur_range_info= first_identical_range_info;
|
||||
}
|
||||
|
||||
|
@ -1053,12 +926,12 @@ check_record:
|
|||
if (last_identical_key_ptr)
|
||||
{
|
||||
/* key_buffer.read() reads to (cur_index_tuple, cur_range_info) */
|
||||
while (!key_buffer.read() && (cur_index_tuple != last_identical_key_ptr)) {}
|
||||
while (!key_buffer->read() && (cur_index_tuple != last_identical_key_ptr)) {}
|
||||
last_identical_key_ptr= NULL;
|
||||
}
|
||||
|
||||
/* First, make sure we have a range at start of the buffer */
|
||||
if (key_buffer.is_empty())
|
||||
if (key_buffer->is_empty())
|
||||
{
|
||||
if (dsmrr_eof)
|
||||
{
|
||||
|
@ -1072,7 +945,7 @@ check_record:
|
|||
if (!do_rndpos_scan)
|
||||
dsmrr_fill_key_buffer();
|
||||
|
||||
if (key_buffer.is_empty())
|
||||
if (key_buffer->is_empty())
|
||||
{
|
||||
res= HA_ERR_END_OF_FILE;
|
||||
goto end;
|
||||
|
@ -1088,7 +961,7 @@ check_record:
|
|||
reallocate_buffer_space();
|
||||
|
||||
/* Get the next range to scan */
|
||||
key_buffer.read(); // reads to (cur_index_tuple, cur_range_info)
|
||||
key_buffer->read(); // reads to (cur_index_tuple, cur_range_info)
|
||||
key_in_buf= cur_index_tuple;
|
||||
|
||||
if (use_key_pointers)
|
||||
|
@ -1106,9 +979,9 @@ check_record:
|
|||
/* Check if subsequent keys in the key buffer are the same as this one */
|
||||
{
|
||||
char *save_cur_range_info= cur_range_info;
|
||||
identical_key_it.init(&key_buffer);
|
||||
identical_key_it->init(key_buffer);
|
||||
last_identical_key_ptr= NULL;
|
||||
while (!identical_key_it.read_next())
|
||||
while (!identical_key_it->read_next())
|
||||
{
|
||||
if (key_tuple_cmp(this, key_in_buf, cur_index_tuple))
|
||||
break;
|
||||
|
@ -1119,7 +992,7 @@ check_record:
|
|||
if (last_identical_key_ptr)
|
||||
{
|
||||
in_identical_keys_range= TRUE;
|
||||
identical_key_it.init(&key_buffer);
|
||||
identical_key_it->init(key_buffer);
|
||||
first_identical_range_info= cur_range_info;
|
||||
}
|
||||
}
|
||||
|
@ -1178,7 +1051,7 @@ int DsMrr_impl::dsmrr_next(char **range_info)
|
|||
{
|
||||
if (do_sort_keys)
|
||||
{
|
||||
if (!key_buffer.is_empty() || in_index_range)
|
||||
if (!key_buffer->is_empty() || in_index_range)
|
||||
{
|
||||
/* There are some sorted keys left. Use them to get rowids */
|
||||
if ((res= dsmrr_fill_rowid_buffer()))
|
||||
|
@ -1239,9 +1112,9 @@ int DsMrr_impl::dsmrr_next(char **range_info)
|
|||
Note: this implies that SQL layer doesn't touch table->record[0]
|
||||
between calls.
|
||||
*/
|
||||
SimpleBuffer::PeekIterator identical_rowid_it;
|
||||
identical_rowid_it.init(&rowid_buffer);
|
||||
while (!identical_rowid_it.read_next()) // reads to (rowid, ...)
|
||||
Forward_iterator it;
|
||||
it.init(&rowid_buffer);
|
||||
while (!it.read_next()) // reads to (rowid, ...)
|
||||
{
|
||||
if (h2->cmp_ref(rowid, cur_rowid))
|
||||
break;
|
||||
|
|
|
@ -46,6 +46,356 @@
|
|||
storage and has better performance when reading data in rowid order.
|
||||
*/
|
||||
|
||||
class Forward_lifo_buffer;
|
||||
class Backward_lifo_buffer;
|
||||
|
||||
class Lifo_buffer
|
||||
{
|
||||
protected:
|
||||
/*
|
||||
Data to be written. write() call will assume that (*write_ptr1) points to
|
||||
size1 bytes of data to be written.
|
||||
If write_ptr2 != NULL then the buffer stores pairs, and (*write_ptr2)
|
||||
points to size2 bytes of data that form the second component.
|
||||
*/
|
||||
uchar **write_ptr1;
|
||||
size_t size1;
|
||||
uchar **write_ptr2;
|
||||
size_t size2;
|
||||
|
||||
/*
|
||||
read() will do reading by storing pointer to read data into *read_ptr1 (if
|
||||
the buffer stores atomic elements), or into {*read_ptr1, *read_ptr2} (if
|
||||
the buffer stores pairs).
|
||||
*/
|
||||
uchar **read_ptr1;
|
||||
uchar **read_ptr2;
|
||||
|
||||
uchar *start; /* points to start of buffer space */
|
||||
uchar *end; /* points to just beyond the end of buffer space */
|
||||
public:
|
||||
|
||||
enum enum_direction {
|
||||
BACKWARD=-1, /* buffer is filled/read from bigger to smaller memory addresses */
|
||||
FORWARD=1 /* buffer is filled/read from smaller to bigger memory addresses */
|
||||
};
|
||||
|
||||
virtual enum_direction type() = 0;
|
||||
|
||||
/* Buffer space control functions */
|
||||
void set_buffer_space(uchar *start_arg, uchar *end_arg)
|
||||
{
|
||||
start= start_arg;
|
||||
end= end_arg;
|
||||
TRASH(start, end - start);
|
||||
reset_for_writing();
|
||||
}
|
||||
|
||||
void setup_writing(uchar **data1, size_t len1, uchar **data2, size_t len2)
|
||||
{
|
||||
write_ptr1= data1;
|
||||
size1= len1;
|
||||
write_ptr2= data2;
|
||||
size2= len2;
|
||||
}
|
||||
|
||||
void setup_reading(uchar **data1, size_t len1, uchar **data2, size_t len2)
|
||||
{
|
||||
read_ptr1= data1;
|
||||
DBUG_ASSERT(len1 == size1);
|
||||
read_ptr2= data2;
|
||||
DBUG_ASSERT(len2 == size2);
|
||||
}
|
||||
|
||||
//virtual void write_bytes(const uchar *data, size_t bytes)=0;
|
||||
|
||||
virtual bool read() = 0;
|
||||
virtual void write() = 0;
|
||||
bool can_write()
|
||||
{
|
||||
return have_space_for(size1 + (write_ptr2 ? size2 : 0));
|
||||
}
|
||||
|
||||
bool is_empty() { return used_size() == 0; }
|
||||
virtual size_t used_size() = 0;
|
||||
|
||||
void sort(qsort2_cmp cmp_func, void *cmp_func_arg)
|
||||
{
|
||||
uint elem_size= size1 + (write_ptr2 ? size2 : 0);
|
||||
uint n_elements= used_size() / elem_size;
|
||||
my_qsort2(used_area(), n_elements, elem_size, cmp_func, cmp_func_arg);
|
||||
}
|
||||
|
||||
|
||||
virtual void reset_for_writing() = 0;
|
||||
virtual uchar *end_of_space() = 0;
|
||||
bool have_data(size_t bytes)
|
||||
{
|
||||
return (used_size() >= bytes);
|
||||
}
|
||||
virtual bool have_space_for(size_t bytes) = 0;
|
||||
//virtual uchar *read_bytes(size_t bytes) = 0;
|
||||
|
||||
virtual void remove_unused_space(uchar **unused_start, uchar **unused_end)=0;
|
||||
virtual uchar *used_area() = 0;
|
||||
|
||||
class Iterator
|
||||
{
|
||||
public:
|
||||
virtual void init(Lifo_buffer *buf) = 0;
|
||||
/*
|
||||
Read the next value. The calling convention is the same as buf->read()
|
||||
has.
|
||||
|
||||
RETURN
|
||||
FALSE - Ok
|
||||
TRUE - EOF, reached the end of the buffer
|
||||
*/
|
||||
virtual bool read_next()= 0;
|
||||
virtual ~Iterator() {}
|
||||
protected:
|
||||
Lifo_buffer *buf;
|
||||
virtual uchar *get_next(size_t nbytes)=0;
|
||||
};
|
||||
virtual ~Lifo_buffer() {};
|
||||
|
||||
friend class Forward_iterator;
|
||||
friend class Backward_iterator;
|
||||
};
|
||||
|
||||
|
||||
class Forward_lifo_buffer: public Lifo_buffer
|
||||
{
|
||||
uchar *pos;
|
||||
public:
|
||||
enum_direction type() { return FORWARD; }
|
||||
size_t used_size()
|
||||
{
|
||||
return pos - start;
|
||||
}
|
||||
void reset_for_writing()
|
||||
{
|
||||
pos= start;
|
||||
}
|
||||
uchar *end_of_space() { return pos; }
|
||||
bool have_space_for(size_t bytes)
|
||||
{
|
||||
return (pos + bytes < end);
|
||||
}
|
||||
|
||||
void write()
|
||||
{
|
||||
write_bytes(*write_ptr1, size1);
|
||||
if (write_ptr2)
|
||||
write_bytes(*write_ptr2, size2);
|
||||
}
|
||||
void write_bytes(const uchar *data, size_t bytes)
|
||||
{
|
||||
DBUG_ASSERT(have_space_for(bytes));
|
||||
memcpy(pos, data, bytes);
|
||||
pos += bytes;
|
||||
}
|
||||
uchar *read_bytes(size_t bytes)
|
||||
{
|
||||
DBUG_ASSERT(have_data(bytes));
|
||||
pos= pos - bytes;
|
||||
return pos;
|
||||
}
|
||||
bool read()
|
||||
{
|
||||
if (!have_data(size1 + (read_ptr2 ? size2 : 0)))
|
||||
return TRUE;
|
||||
if (read_ptr2)
|
||||
*read_ptr2= read_bytes(size2);
|
||||
*read_ptr1= read_bytes(size1);
|
||||
return FALSE;
|
||||
}
|
||||
/*
|
||||
Stop using/return the unneded space (the one that we have already wrote
|
||||
to read from).
|
||||
*/
|
||||
void remove_unused_space(uchar **unused_start, uchar **unused_end)
|
||||
{
|
||||
DBUG_ASSERT(0); /* Don't need this yet */
|
||||
}
|
||||
void grow(uchar *unused_start, uchar *unused_end)
|
||||
{
|
||||
/*
|
||||
Passed memory area can be meaningfully used for growing the buffer if:
|
||||
- it is adjacent to buffer space we're using
|
||||
- it is on the end towards which we grow.
|
||||
*/
|
||||
DBUG_ASSERT(unused_end >= unused_start);
|
||||
TRASH(unused_start, unused_end - unused_start);
|
||||
DBUG_ASSERT(end == unused_start);
|
||||
end= unused_end;
|
||||
}
|
||||
/* Return pointer to start of the memory area that is occupied by the data */
|
||||
uchar *used_area() { return start; }
|
||||
friend class Forward_iterator;
|
||||
};
|
||||
|
||||
|
||||
class Forward_iterator : public Lifo_buffer::Iterator
|
||||
{
|
||||
uchar *pos;
|
||||
|
||||
/* Return pointer to next chunk of nbytes bytes and avance over it */
|
||||
uchar *get_next(size_t nbytes)
|
||||
{
|
||||
if (pos - nbytes < ((Forward_lifo_buffer*)buf)->start)
|
||||
return NULL;
|
||||
pos -= nbytes;
|
||||
return pos;
|
||||
}
|
||||
public:
|
||||
bool read_next()
|
||||
{
|
||||
uchar *res;
|
||||
if (buf->read_ptr2)
|
||||
{
|
||||
if ((res= get_next(buf->size2)))
|
||||
{
|
||||
*(buf->read_ptr2)= res;
|
||||
*buf->read_ptr1= get_next(buf->size1);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((res= get_next(buf->size1)))
|
||||
{
|
||||
*(buf->read_ptr1)= res;
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
return TRUE; /* EOF */
|
||||
}
|
||||
|
||||
void init(Lifo_buffer *buf_arg)
|
||||
{
|
||||
DBUG_ASSERT(buf_arg->type() == Lifo_buffer::FORWARD);
|
||||
buf= buf_arg;
|
||||
pos= ((Forward_lifo_buffer*)buf)->pos;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class Backward_lifo_buffer: public Lifo_buffer
|
||||
{
|
||||
uchar *pos;
|
||||
public:
|
||||
enum_direction type() { return BACKWARD; }
|
||||
|
||||
size_t used_size()
|
||||
{
|
||||
return end - pos;
|
||||
}
|
||||
void reset_for_writing()
|
||||
{
|
||||
pos= end;
|
||||
}
|
||||
uchar *end_of_space() { return end; }
|
||||
bool have_space_for(size_t bytes)
|
||||
{
|
||||
return (pos - bytes >= start);
|
||||
}
|
||||
void write()
|
||||
{
|
||||
if (write_ptr2)
|
||||
write_bytes(*write_ptr2, size2);
|
||||
write_bytes(*write_ptr1, size1);
|
||||
}
|
||||
void write_bytes(const uchar *data, size_t bytes)
|
||||
{
|
||||
DBUG_ASSERT(have_space_for(bytes));
|
||||
pos -= bytes;
|
||||
memcpy(pos, data, bytes);
|
||||
}
|
||||
bool read()
|
||||
{
|
||||
if (!have_data(size1 + (read_ptr2 ? size2 : 0)))
|
||||
return TRUE;
|
||||
*read_ptr1= read_bytes(size1);
|
||||
if (read_ptr2)
|
||||
*read_ptr2= read_bytes(size2);
|
||||
return FALSE;
|
||||
}
|
||||
uchar *read_bytes(size_t bytes)
|
||||
{
|
||||
DBUG_ASSERT(have_data(bytes));
|
||||
uchar *ret= pos;
|
||||
pos= pos + bytes;
|
||||
return ret;
|
||||
}
|
||||
/*
|
||||
Stop using/return the unneded space (the one that we have already wrote
|
||||
to and have read from).
|
||||
*/
|
||||
void remove_unused_space(uchar **unused_start, uchar **unused_end)
|
||||
{
|
||||
*unused_start= start;
|
||||
*unused_end= pos;
|
||||
start= pos;
|
||||
}
|
||||
void grow(uchar *unused_start, uchar *unused_end)
|
||||
{
|
||||
/*
|
||||
Passed memory area can be meaningfully used for growing the buffer if:
|
||||
- it is adjacent to buffer space we're using
|
||||
- it is on the end towards which we grow.
|
||||
*/
|
||||
/*
|
||||
DBUG_ASSERT(unused_end >= unused_start);
|
||||
TRASH(unused_start, unused_end - unused_start);
|
||||
DBUG_ASSERT(start == unused_end);
|
||||
start= unused_start;
|
||||
*/
|
||||
DBUG_ASSERT(0); //Not used
|
||||
}
|
||||
/* Return pointer to start of the memory area that is occupied by the data */
|
||||
uchar *used_area() { return pos; }
|
||||
friend class Backward_iterator;
|
||||
};
|
||||
|
||||
|
||||
class Backward_iterator : public Lifo_buffer::Iterator
|
||||
{
|
||||
uchar *pos;
|
||||
/* Return pointer to next chunk of nbytes bytes and advance over it */
|
||||
uchar *get_next(size_t nbytes)
|
||||
{
|
||||
if (pos + nbytes > ((Backward_lifo_buffer*)buf)->end)
|
||||
return NULL;
|
||||
uchar *res= pos;
|
||||
pos += nbytes;
|
||||
return res;
|
||||
}
|
||||
public:
|
||||
bool read_next()
|
||||
{
|
||||
/*
|
||||
Always read the first component first (if the buffer is backwards, we
|
||||
have written the second component first).
|
||||
*/
|
||||
uchar *res;
|
||||
if ((res= get_next(buf->size1)))
|
||||
{
|
||||
*(buf->read_ptr1)= res;
|
||||
if (buf->read_ptr2)
|
||||
*buf->read_ptr2= get_next(buf->size2);
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE; /* EOF */
|
||||
}
|
||||
void init(Lifo_buffer *buf_arg)
|
||||
{
|
||||
DBUG_ASSERT(buf_arg->type() == Lifo_buffer::BACKWARD);
|
||||
buf= buf_arg;
|
||||
pos= ((Backward_lifo_buffer*)buf)->pos;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
An in-memory buffer used by DS-MRR implementation.
|
||||
|
@ -78,211 +428,6 @@
|
|||
writing is done from end to start.
|
||||
*/
|
||||
|
||||
class SimpleBuffer
|
||||
{
|
||||
public:
|
||||
|
||||
enum enum_direction {
|
||||
BACKWARD=-1, /* buffer is filled/read from bigger to smaller memory addresses */
|
||||
FORWARD=1 /* buffer is filled/read from smaller to bigger memory addresses */
|
||||
};
|
||||
|
||||
private:
|
||||
enum_direction direction;
|
||||
|
||||
uchar *start; /* points to start of buffer space */
|
||||
uchar *end; /* points to just beyond the end of buffer space */
|
||||
/*
|
||||
Forward buffer: points to the start of the data that will be read next
|
||||
Backward buffer: points to just beyond the end of the data that will be
|
||||
read next.
|
||||
*/
|
||||
uchar *read_pos;
|
||||
/*
|
||||
Forward buffer: points to just after the end of the used area.
|
||||
Backward buffer: points to the start of used area.
|
||||
*/
|
||||
uchar *write_pos;
|
||||
|
||||
/*
|
||||
Data to be written. write() call will assume that (*write_ptr1) points to
|
||||
size1 bytes of data to be written.
|
||||
If write_ptr2 != NULL then the buffer stores pairs, and (*write_ptr2)
|
||||
points to size2 bytes of data that form the second component.
|
||||
*/
|
||||
uchar **write_ptr1;
|
||||
size_t size1;
|
||||
uchar **write_ptr2;
|
||||
size_t size2;
|
||||
|
||||
/*
|
||||
read() will do reading by storing pointer to read data into *read_ptr1 (if
|
||||
the buffer stores atomic elements), or into {*read_ptr1, *read_ptr2} (if
|
||||
the buffer stores pairs).
|
||||
*/
|
||||
uchar **read_ptr1;
|
||||
uchar **read_ptr2;
|
||||
|
||||
public:
|
||||
/* Write-mode functions */
|
||||
void setup_writing(uchar **data1, size_t len1,
|
||||
uchar **data2, size_t len2);
|
||||
void reset_for_writing();
|
||||
bool can_write();
|
||||
void write();
|
||||
|
||||
/* Read-mode functions */
|
||||
bool is_empty() { return used_size() == 0; }
|
||||
void setup_reading(uchar **data1, size_t len1,
|
||||
uchar **data2, size_t len2);
|
||||
bool read();
|
||||
|
||||
/* Misc functions */
|
||||
void sort(qsort2_cmp cmp_func, void *cmp_func_arg);
|
||||
bool is_reverse() { return direction == BACKWARD; }
|
||||
uchar *end_of_space();
|
||||
|
||||
/* Buffer space control functions */
|
||||
void set_buffer_space(uchar *start_arg, uchar *end_arg, enum_direction direction_arg)
|
||||
{
|
||||
start= start_arg;
|
||||
end= end_arg;
|
||||
direction= direction_arg;
|
||||
TRASH(start, end - start);
|
||||
reset_for_writing();
|
||||
}
|
||||
|
||||
/*
|
||||
Stop using/return the unneded space (the one that we have already wrote
|
||||
to read from).
|
||||
*/
|
||||
void remove_unused_space(uchar **unused_start, uchar **unused_end)
|
||||
{
|
||||
if (direction == 1)
|
||||
{
|
||||
*unused_start= start;
|
||||
*unused_end= read_pos;
|
||||
start= read_pos;
|
||||
}
|
||||
else
|
||||
{
|
||||
*unused_start= read_pos;
|
||||
*unused_end= end;
|
||||
end= read_pos;
|
||||
}
|
||||
}
|
||||
|
||||
void flip()
|
||||
{
|
||||
uchar *tmp= read_pos;
|
||||
read_pos= write_pos;
|
||||
write_pos= tmp;
|
||||
direction= (direction == FORWARD)? BACKWARD: FORWARD;
|
||||
}
|
||||
|
||||
void grow(uchar *unused_start, uchar *unused_end)
|
||||
{
|
||||
/*
|
||||
Passed memory area can be meaningfully used for growing the buffer if:
|
||||
- it is adjacent to buffer space we're using
|
||||
- it is on the end towards which we grow.
|
||||
*/
|
||||
DBUG_ASSERT(unused_end >= unused_start);
|
||||
TRASH(unused_start, unused_end - unused_start);
|
||||
if (direction == 1 && end == unused_start)
|
||||
{
|
||||
end= unused_end;
|
||||
}
|
||||
else if (direction == -1 && start == unused_end)
|
||||
{
|
||||
start= unused_start;
|
||||
}
|
||||
else
|
||||
DBUG_ASSERT(0); /* Attempt to grow buffer in wrong direction */
|
||||
}
|
||||
|
||||
/*
|
||||
An iterator to do look at what we're about to read from the buffer without
|
||||
actually reading it.
|
||||
*/
|
||||
class PeekIterator
|
||||
{
|
||||
SimpleBuffer *buf; /* The buffer we're iterating over*/
|
||||
/*
|
||||
if buf->direction==FORWARD : pointer to what to return next
|
||||
if buf->direction==BACKWARD : pointer to the end of what is to be
|
||||
returned next
|
||||
*/
|
||||
uchar *pos;
|
||||
public:
|
||||
/*
|
||||
Initialize the iterator. After intiialization, the first read_next() call
|
||||
will read what buf_arg->read() would read.
|
||||
*/
|
||||
void init(SimpleBuffer *buf_arg)
|
||||
{
|
||||
buf= buf_arg;
|
||||
pos= buf->read_pos;
|
||||
}
|
||||
|
||||
/*
|
||||
Read the next value. The calling convention is the same as buf->read()
|
||||
has.
|
||||
|
||||
RETURN
|
||||
FALSE - Ok
|
||||
TRUE - EOF, reached the end of the buffer
|
||||
*/
|
||||
bool read_next()
|
||||
{
|
||||
/*
|
||||
Always read the first component first (if the buffer is backwards, we
|
||||
have written the second component first).
|
||||
*/
|
||||
uchar *res;
|
||||
if ((res= get_next(buf->size1)))
|
||||
{
|
||||
*(buf->read_ptr1)= res;
|
||||
if (buf->read_ptr2)
|
||||
*buf->read_ptr2= get_next(buf->size2);
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE; /* EOF */
|
||||
}
|
||||
private:
|
||||
/* Return pointer to next chunk of nbytes bytes and avance over it */
|
||||
uchar *get_next(size_t nbytes)
|
||||
{
|
||||
if (buf->direction == 1)
|
||||
{
|
||||
if (pos + nbytes > buf->write_pos)
|
||||
return NULL;
|
||||
uchar *res= pos;
|
||||
pos += nbytes;
|
||||
return res;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pos - nbytes < buf->write_pos)
|
||||
return NULL;
|
||||
pos -= nbytes;
|
||||
return pos;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
bool have_space_for(size_t bytes);
|
||||
/* Return pointer to start of the memory area that is occupied by the data */
|
||||
uchar *used_area() { return (direction == FORWARD)? read_pos : write_pos; }
|
||||
size_t used_size();
|
||||
|
||||
void write(const uchar *data, size_t bytes);
|
||||
uchar *read(size_t bytes);
|
||||
bool have_data(size_t bytes);
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
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
|
||||
|
@ -439,8 +584,18 @@ private:
|
|||
/* 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
|
||||
buffers.
|
||||
*/
|
||||
Forward_lifo_buffer forward_key_buf;
|
||||
Forward_iterator forward_key_it;
|
||||
Backward_lifo_buffer backward_key_buf;
|
||||
Backward_iterator backward_key_it;
|
||||
|
||||
/* Buffer to store (key, range_id) pairs */
|
||||
SimpleBuffer key_buffer;
|
||||
Lifo_buffer *key_buffer;
|
||||
|
||||
/* key_buffer.read() reads */
|
||||
uchar *cur_index_tuple;
|
||||
|
@ -489,7 +644,7 @@ private:
|
|||
and do that by walking from current buffer read position until we get
|
||||
last_identical_key_ptr.
|
||||
*/
|
||||
SimpleBuffer::PeekIterator identical_key_it;
|
||||
Lifo_buffer::Iterator *identical_key_it;
|
||||
|
||||
|
||||
/** rnd_pos() scan and rowid buffer-related members **/
|
||||
|
@ -498,7 +653,7 @@ private:
|
|||
Buffer to store (rowid, range_id) pairs, or just rowids if
|
||||
is_mrr_assoc==FALSE
|
||||
*/
|
||||
SimpleBuffer rowid_buffer;
|
||||
Forward_lifo_buffer rowid_buffer;
|
||||
|
||||
/* rowid_buffer.read() will set the following: */
|
||||
uchar *rowid;
|
||||
|
@ -522,6 +677,7 @@ private:
|
|||
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);
|
||||
|
|
Loading…
Reference in a new issue