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