mirror of
https://github.com/MariaDB/server.git
synced 2025-07-30 16:24:05 +03:00
key/rowid buffer overflow fixes for various tricky cases.
This commit is contained in:
@ -865,12 +865,12 @@ LEFT JOIN
|
|||||||
(t1,t2)
|
(t1,t2)
|
||||||
ON t3.a=1 AND t3.b=t2.b AND t2.b=t4.b;
|
ON t3.a=1 AND t3.b=t2.b AND t2.b=t4.b;
|
||||||
a b a b a b
|
a b a b a b
|
||||||
4 2 1 2 3 2
|
|
||||||
4 2 1 2 4 2
|
4 2 1 2 4 2
|
||||||
4 2 1 2 3 2
|
4 2 1 2 3 2
|
||||||
4 2 1 2 4 2
|
4 2 1 2 4 2
|
||||||
4 2 1 2 3 2
|
4 2 1 2 3 2
|
||||||
4 2 1 2 4 2
|
4 2 1 2 4 2
|
||||||
|
4 2 1 2 3 2
|
||||||
NULL NULL 2 2 3 2
|
NULL NULL 2 2 3 2
|
||||||
NULL NULL 2 2 4 2
|
NULL NULL 2 2 4 2
|
||||||
EXPLAIN EXTENDED
|
EXPLAIN EXTENDED
|
||||||
@ -1105,8 +1105,8 @@ t0.b=t1.b AND
|
|||||||
(t8.b=t9.b OR t8.c IS NULL) AND
|
(t8.b=t9.b OR t8.c IS NULL) AND
|
||||||
(t9.a=1);
|
(t9.a=1);
|
||||||
a b a b a b a b a b a b a b a b a b a b
|
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 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 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 3 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 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 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
|
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
|
ON t6.b >= 2 AND t5.b=t7.b AND
|
||||||
(t8.a > 0 OR t8.c IS NULL);
|
(t8.a > 0 OR t8.c IS NULL);
|
||||||
a b a b a b a b
|
a b a b a b a b
|
||||||
2 2 1 2 2 2 1 2
|
|
||||||
2 2 3 2 2 2 1 2
|
2 2 3 2 2 2 1 2
|
||||||
|
2 2 1 2 2 2 1 2
|
||||||
1 1 1 2 1 1 NULL NULL
|
1 1 1 2 1 1 NULL NULL
|
||||||
1 1 3 2 1 1 NULL NULL
|
1 1 3 2 1 1 NULL NULL
|
||||||
3 3 NULL NULL NULL NULL NULL NULL
|
3 3 NULL NULL NULL NULL NULL NULL
|
||||||
|
@ -563,8 +563,8 @@ int DsMrr_impl::dsmrr_init(handler *h_arg, RANGE_SEQ_IF *seq_funcs,
|
|||||||
If the above call has scanned through all intervals in *seq, then
|
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.
|
adjust *buf to indicate that the remaining buffer space will not be used.
|
||||||
*/
|
*/
|
||||||
if (dsmrr_eof)
|
// if (dsmrr_eof)
|
||||||
buf->end_of_used_area= rowid_buffer.end_of_space();
|
// buf->end_of_used_area= rowid_buffer.end_of_space();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
h->inited == INDEX may occur when 'range checked for each record' is
|
h->inited == INDEX may occur when 'range checked for each record' is
|
||||||
@ -619,21 +619,18 @@ static int rowid_cmp(void *h, uchar *a, uchar *b)
|
|||||||
buffer. When the buffer is full or scan is completed, sort the buffer by
|
buffer. When the buffer is full or scan is completed, sort the buffer by
|
||||||
rowid and return.
|
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
|
dsmrr_eof is set to indicate whether we've exhausted the list of ranges we're
|
||||||
scanning.
|
scanning. This function never returns HA_ERR_END_OF_FILE.
|
||||||
|
|
||||||
|
post-condition:
|
||||||
|
rowid buffer is not empty, or key source is exhausted.
|
||||||
|
|
||||||
@param h Table handler
|
@param h Table handler
|
||||||
|
|
||||||
@retval 0 OK, the next portion of rowids is in the buffer,
|
@retval 0 OK, the next portion of rowids is in the buffer,
|
||||||
properly ordered
|
properly ordered
|
||||||
@retval other Error
|
@retval other Error
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int DsMrr_impl::dsmrr_fill_rowid_buffer()
|
int DsMrr_impl::dsmrr_fill_rowid_buffer()
|
||||||
@ -642,9 +639,11 @@ int DsMrr_impl::dsmrr_fill_rowid_buffer()
|
|||||||
int res;
|
int res;
|
||||||
DBUG_ENTER("DsMrr_impl::dsmrr_fill_rowid_buffer");
|
DBUG_ENTER("DsMrr_impl::dsmrr_fill_rowid_buffer");
|
||||||
|
|
||||||
|
DBUG_ASSERT(rowid_buffer.is_empty());
|
||||||
rowid_buffer.reset_for_writing();
|
rowid_buffer.reset_for_writing();
|
||||||
identical_rowid_ptr= NULL;
|
identical_rowid_ptr= NULL;
|
||||||
if (do_sort_keys)
|
|
||||||
|
if (key_buffer.is_reverse())
|
||||||
key_buffer.flip();
|
key_buffer.flip();
|
||||||
|
|
||||||
while (rowid_buffer.have_space_for(rowid_buff_elem_size))
|
while (rowid_buffer.have_space_for(rowid_buff_elem_size))
|
||||||
@ -656,7 +655,6 @@ int DsMrr_impl::dsmrr_fill_rowid_buffer()
|
|||||||
|
|
||||||
if (res)
|
if (res)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
||||||
KEY_MULTI_RANGE *curr_range= &h2->handler::mrr_cur_range;
|
KEY_MULTI_RANGE *curr_range= &h2->handler::mrr_cur_range;
|
||||||
if (!do_sort_keys && /* If keys are sorted then this check is already done */
|
if (!do_sort_keys && /* If keys are sorted then this check is already done */
|
||||||
@ -674,7 +672,9 @@ int DsMrr_impl::dsmrr_fill_rowid_buffer()
|
|||||||
|
|
||||||
if (res && res != HA_ERR_END_OF_FILE)
|
if (res && res != HA_ERR_END_OF_FILE)
|
||||||
DBUG_RETURN(res);
|
DBUG_RETURN(res);
|
||||||
dsmrr_eof= test(res == HA_ERR_END_OF_FILE);
|
|
||||||
|
if (!do_sort_keys)
|
||||||
|
dsmrr_eof= test(res == HA_ERR_END_OF_FILE);
|
||||||
|
|
||||||
/* Sort the buffer contents by rowid */
|
/* Sort the buffer contents by rowid */
|
||||||
uint elem_size= h->ref_length + (int)is_mrr_assoc * sizeof(void*);
|
uint elem_size= h->ref_length + (int)is_mrr_assoc * sizeof(void*);
|
||||||
@ -830,6 +830,10 @@ void DsMrr_impl::setup_buffer_sizes(key_range *sample_key)
|
|||||||
|
|
||||||
dsmrr_eof is set to indicate whether we've exhausted the list of ranges
|
dsmrr_eof is set to indicate whether we've exhausted the list of ranges
|
||||||
we're scanning.
|
we're scanning.
|
||||||
|
|
||||||
|
post-condition:
|
||||||
|
- key buffer is non-empty
|
||||||
|
- key buffer is empty and source range sequence is exhausted
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void DsMrr_impl::dsmrr_fill_key_buffer()
|
void DsMrr_impl::dsmrr_fill_key_buffer()
|
||||||
@ -838,12 +842,16 @@ void DsMrr_impl::dsmrr_fill_key_buffer()
|
|||||||
KEY_MULTI_RANGE cur_range;
|
KEY_MULTI_RANGE cur_range;
|
||||||
DBUG_ENTER("DsMrr_impl::dsmrr_fill_key_buffer");
|
DBUG_ENTER("DsMrr_impl::dsmrr_fill_key_buffer");
|
||||||
|
|
||||||
// reset the buffer for writing.
|
DBUG_ASSERT(!key_tuple_length || key_buffer.is_empty());
|
||||||
|
|
||||||
if (key_tuple_length)
|
if (key_tuple_length)
|
||||||
{
|
{
|
||||||
if (do_rowid_fetch)
|
if (do_rowid_fetch && rowid_buffer.is_empty())
|
||||||
{
|
{
|
||||||
/* Restore original buffer sizes */
|
/*
|
||||||
|
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, 1);
|
rowid_buffer.set_buffer_space(full_buf, rowid_buffer_end, 1);
|
||||||
key_buffer.set_buffer_space(rowid_buffer_end, full_buf_end, -1);
|
key_buffer.set_buffer_space(rowid_buffer_end, full_buf_end, -1);
|
||||||
}
|
}
|
||||||
@ -858,7 +866,6 @@ void DsMrr_impl::dsmrr_fill_key_buffer()
|
|||||||
if (!key_tuple_length)
|
if (!key_tuple_length)
|
||||||
{
|
{
|
||||||
/* This only happens when we've just started filling the buffer */
|
/* 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);
|
setup_buffer_sizes(&cur_range.start_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -991,21 +998,21 @@ check_record:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* First, make sure we have a range at start of the buffer */
|
/* First, make sure we have a range at start of the buffer */
|
||||||
|
if (key_buffer.is_empty())
|
||||||
//psergey-todo: why would we re-fill it here in the case when
|
|
||||||
// we're doing rowid retrieval?
|
|
||||||
// - need to check if this is really happening.
|
|
||||||
|
|
||||||
if (!key_buffer.have_data(key_buff_elem_size))
|
|
||||||
{
|
{
|
||||||
if (dsmrr_eof)
|
if (dsmrr_eof)
|
||||||
{
|
{
|
||||||
res= HA_ERR_END_OF_FILE;
|
res= HA_ERR_END_OF_FILE;
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
When rowid fetching is used, it controls all buffer refills. When we're
|
||||||
|
on our own, try refilling our buffer.
|
||||||
|
*/
|
||||||
if (!do_rowid_fetch)
|
if (!do_rowid_fetch)
|
||||||
dsmrr_fill_key_buffer();
|
dsmrr_fill_key_buffer();
|
||||||
if (!key_buffer.have_data(key_buff_elem_size))
|
|
||||||
|
if (key_buffer.is_empty())
|
||||||
{
|
{
|
||||||
res= HA_ERR_END_OF_FILE;
|
res= HA_ERR_END_OF_FILE;
|
||||||
goto end;
|
goto end;
|
||||||
@ -1015,16 +1022,16 @@ check_record:
|
|||||||
if (do_rowid_fetch)
|
if (do_rowid_fetch)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
At this point we're not using anything beyond what we've read from key
|
At this point we're not using anything what we've read from key
|
||||||
buffer. Shrik the key buffer and grow the rowid buffer.
|
buffer. Cut off unused key buffer space and give it to the rowid
|
||||||
|
buffer.
|
||||||
*/
|
*/
|
||||||
uchar *unused_start;
|
uchar *unused_start, *unused_end;
|
||||||
uchar *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);
|
rowid_buffer.grow(unused_start, unused_end);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get the next range to scan*/
|
/* Get the next range to scan */
|
||||||
cur_index_tuple= key_in_buf= key_buffer.read(key_size_in_keybuf);
|
cur_index_tuple= key_in_buf= key_buffer.read(key_size_in_keybuf);
|
||||||
if (use_key_pointers)
|
if (use_key_pointers)
|
||||||
cur_index_tuple= *((uchar**)cur_index_tuple);
|
cur_index_tuple= *((uchar**)cur_index_tuple);
|
||||||
@ -1119,20 +1126,41 @@ int DsMrr_impl::dsmrr_next(char **range_info)
|
|||||||
|
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
if (!rowid_buffer.have_data(1))
|
if (rowid_buffer.is_empty())
|
||||||
{
|
{
|
||||||
if (dsmrr_eof)
|
if (do_sort_keys)
|
||||||
return HA_ERR_END_OF_FILE;
|
{
|
||||||
|
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()))
|
||||||
|
return res; /* for fatal errors */
|
||||||
|
}
|
||||||
|
if (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 (do_sort_keys && key_buffer.used_size() == 0)
|
if ((res= dsmrr_fill_rowid_buffer()))
|
||||||
dsmrr_fill_key_buffer();
|
return res;
|
||||||
|
}
|
||||||
if ((res= dsmrr_fill_rowid_buffer()))
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Return eof if there are no rowids in the buffer after re-fill attempt */
|
/* Return eof if there are no rowids in the buffer after re-fill attempt */
|
||||||
if (!rowid_buffer.have_data(1))
|
if (rowid_buffer.is_empty())
|
||||||
return HA_ERR_END_OF_FILE;
|
return HA_ERR_END_OF_FILE;
|
||||||
|
|
||||||
rowid= rowid_buffer.read(h->ref_length);
|
rowid= rowid_buffer.read(h->ref_length);
|
||||||
@ -1145,10 +1173,6 @@ int DsMrr_impl::dsmrr_next(char **range_info)
|
|||||||
memcpy(range_info, range_id, sizeof(uchar*));
|
memcpy(range_info, range_id, sizeof(uchar*));
|
||||||
}
|
}
|
||||||
|
|
||||||
//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 &&
|
if (h2->mrr_funcs.skip_record &&
|
||||||
h2->mrr_funcs.skip_record(h2->mrr_iter, (char *) cur_range_info, rowid))
|
h2->mrr_funcs.skip_record(h2->mrr_iter, (char *) cur_range_info, rowid))
|
||||||
continue;
|
continue;
|
||||||
|
@ -75,6 +75,7 @@ public:
|
|||||||
|
|
||||||
uchar *used_area() { return (direction == 1)? read_pos : write_pos; }
|
uchar *used_area() { return (direction == 1)? read_pos : write_pos; }
|
||||||
size_t used_size();
|
size_t used_size();
|
||||||
|
bool is_empty() { return used_size() == 0; }
|
||||||
|
|
||||||
/* Read-mode functions */
|
/* Read-mode functions */
|
||||||
void reset_for_reading();
|
void reset_for_reading();
|
||||||
@ -277,6 +278,13 @@ private:
|
|||||||
bool do_rowid_fetch;
|
bool do_rowid_fetch;
|
||||||
|
|
||||||
bool dsmrr_eof; /* TRUE <=> We have reached EOF when reading index tuples */
|
bool dsmrr_eof; /* TRUE <=> We have reached EOF when reading index tuples */
|
||||||
|
|
||||||
|
/*
|
||||||
|
TRUE <=> key buffer is exhausted (we need this because we may have a situation
|
||||||
|
where we've read everything from the key buffer but haven't finished with
|
||||||
|
scanning the last range)
|
||||||
|
*/
|
||||||
|
bool key_eof;
|
||||||
|
|
||||||
/* TRUE <=> need range association, buffer holds {rowid, range_id} pairs */
|
/* TRUE <=> need range association, buffer holds {rowid, range_id} pairs */
|
||||||
bool is_mrr_assoc;
|
bool is_mrr_assoc;
|
||||||
|
Reference in New Issue
Block a user