2498
/* **************************************************************************
2499
* DS-MRR implementation
2500
***************************************************************************/
2503
DS-MRR: Initialize and start MRR scan
2505
Initialize and start the MRR scan. Depending on the mode parameter, this
2506
may use default or DS-MRR implementation.
2508
@param h Table handler to be used
2509
@param key Index to be used
2510
@param seq_funcs Interval sequence enumeration functions
2511
@param seq_init_param Interval sequence enumeration parameter
2512
@param n_ranges Number of ranges in the sequence.
2513
@param mode HA_MRR_* modes to use
2514
@param buf INOUT Buffer to use
2516
@retval 0 Ok, Scan started.
2520
int DsMrr_impl::dsmrr_init(handler *h_in, KEY *key,
2521
RANGE_SEQ_IF *seq_funcs, void *seq_init_param,
2522
uint32_t n_ranges, uint32_t mode, HANDLER_BUFFER *buf)
2526
Item *pushed_cond= NULL;
2528
keyno= h_in->active_index;
2530
if (mode & HA_MRR_USE_DEFAULT_IMPL || mode & HA_MRR_SORTED)
2532
use_default_impl= true;
2533
return(h_in->handler::multi_range_read_init(seq_funcs, seq_init_param,
2534
n_ranges, mode, buf));
2536
rowids_buf= buf->buffer;
2537
//psergey-todo: don't add key_length as it is not needed anymore
2538
rowids_buf += key->key_length + h_in->ref_length;
2540
is_mrr_assoc= !test(mode & HA_MRR_NO_ASSOCIATION);
2541
rowids_buf_end= buf->buffer_end;
2543
elem_size= h_in->ref_length + (int)is_mrr_assoc * sizeof(void*);
2544
rowids_buf_last= rowids_buf +
2545
((rowids_buf_end - rowids_buf)/ elem_size)*
2547
rowids_buf_end= rowids_buf_last;
2549
/* Create a separate handler object to do rndpos() calls. */
2550
Session *session= current_session;
2551
if (!(new_h2= h_in->clone(session->mem_root)) ||
2552
new_h2->ha_external_lock(session, F_RDLCK))
2558
if (keyno == h_in->pushed_idx_cond_keyno)
2559
pushed_cond= h_in->pushed_idx_cond;
2560
if (h_in->ha_index_end())
2567
table->prepare_for_position();
2568
new_h2->extra(HA_EXTRA_KEYREAD);
2570
if (h2->ha_index_init(keyno, false) ||
2571
h2->handler::multi_range_read_init(seq_funcs, seq_init_param, n_ranges,
2574
use_default_impl= false;
2577
h2->idx_cond_push(keyno, pushed_cond);
2578
if (dsmrr_fill_buffer(new_h2))
2582
If the above call has scanned through all intervals in *seq, then
2583
adjust *buf to indicate that the remaining buffer space will not be used.
2586
buf->end_of_used_area= rowids_buf_last;
2588
if (h_in->ha_rnd_init(false))
2593
h2->ha_index_or_rnd_end();
2594
h2->ha_external_lock(session, F_UNLCK);
2601
void DsMrr_impl::dsmrr_close()
2605
h2->ha_external_lock(current_session, F_UNLCK);
2610
use_default_impl= true;
2614
static int rowid_cmp(void *h, unsigned char *a, unsigned char *b)
2616
return ((handler*)h)->cmp_ref(a, b);
2621
DS-MRR: Fill the buffer with rowids and sort it by rowid
2623
{This is an internal function of DiskSweep MRR implementation}
2624
Scan the MRR ranges and collect ROWIDs (or {ROWID, range_id} pairs) into
2625
buffer. When the buffer is full or scan is completed, sort the buffer by
2628
The function assumes that rowids buffer is empty when it is invoked.
2630
@param h Table handler
2632
@retval 0 OK, the next portion of rowids is in the buffer,
2637
int DsMrr_impl::dsmrr_fill_buffer(handler *)
2642
rowids_buf_cur= rowids_buf;
2643
while ((rowids_buf_cur < rowids_buf_end) &&
2644
!(res= h2->handler::multi_range_read_next(&range_info)))
2646
/* Put rowid, or {rowid, range_id} pair into the buffer */
2647
h2->position(table->record[0]);
2648
memcpy(rowids_buf_cur, h2->ref, h2->ref_length);
2649
rowids_buf_cur += h->ref_length;
2653
memcpy(rowids_buf_cur, &range_info, sizeof(void*));
2654
rowids_buf_cur += sizeof(void*);
2658
if (res && res != HA_ERR_END_OF_FILE)
2660
dsmrr_eof= test(res == HA_ERR_END_OF_FILE);
2662
/* Sort the buffer contents by rowid */
2663
uint32_t elem_size= h->ref_length + (int)is_mrr_assoc * sizeof(void*);
2664
uint32_t n_rowids= (rowids_buf_cur - rowids_buf) / elem_size;
2666
my_qsort2(rowids_buf, n_rowids, elem_size, (qsort2_cmp)rowid_cmp,
2668
rowids_buf_last= rowids_buf_cur;
2669
rowids_buf_cur= rowids_buf;
2675
DS-MRR implementation: multi_range_read_next() function
2678
int DsMrr_impl::dsmrr_next(handler *h_in, char **range_info)
2682
if (use_default_impl)
2683
return h_in->handler::multi_range_read_next(range_info);
2685
if (rowids_buf_cur == rowids_buf_last)
2689
res= HA_ERR_END_OF_FILE;
2692
res= dsmrr_fill_buffer(h);
2697
/* Return EOF if there are no rowids in the buffer after re-fill attempt */
2698
if (rowids_buf_cur == rowids_buf_last)
2700
res= HA_ERR_END_OF_FILE;
2704
res= h_in->rnd_pos(table->record[0], rowids_buf_cur);
2705
rowids_buf_cur += h_in->ref_length;
2708
memcpy(range_info, rowids_buf_cur, sizeof(void*));
2709
rowids_buf_cur += sizeof(void*);
2720
DS-MRR implementation: multi_range_read_info() function
2722
int DsMrr_impl::dsmrr_info(uint32_t keyno, uint32_t n_ranges, uint32_t rows, uint32_t *bufsz,
2723
uint32_t *flags, COST_VECT *cost)
2726
uint32_t def_flags= *flags;
2727
uint32_t def_bufsz= *bufsz;
2729
/* Get cost/flags/mem_usage of default MRR implementation */
2730
res= h->handler::multi_range_read_info(keyno, n_ranges, rows, &def_bufsz,
2734
if ((*flags & HA_MRR_USE_DEFAULT_IMPL) ||
2735
choose_mrr_impl(keyno, rows, &def_flags, &def_bufsz, cost))
2737
/* Default implementation is choosen */
2746
DS-MRR Implementation: multi_range_read_info_const() function
2749
ha_rows DsMrr_impl::dsmrr_info_const(uint32_t keyno, RANGE_SEQ_IF *seq,
2750
void *seq_init_param, uint32_t n_ranges,
2751
uint32_t *bufsz, uint32_t *flags, COST_VECT *cost)
2754
uint32_t def_flags= *flags;
2755
uint32_t def_bufsz= *bufsz;
2756
/* Get cost/flags/mem_usage of default MRR implementation */
2757
rows= h->handler::multi_range_read_info_const(keyno, seq, seq_init_param,
2758
n_ranges, &def_bufsz,
2760
if (rows == HA_POS_ERROR)
2762
/* Default implementation can't perform MRR scan => we can't either */
2767
If HA_MRR_USE_DEFAULT_IMPL has been passed to us, that is an order to
2768
use the default MRR implementation (we need it for UPDATE/DELETE).
2769
Otherwise, make a choice based on cost and @@optimizer_use_mrr.
2771
if ((*flags & HA_MRR_USE_DEFAULT_IMPL) ||
2772
choose_mrr_impl(keyno, rows, flags, bufsz, cost))
2779
*flags &= ~HA_MRR_USE_DEFAULT_IMPL;
2786
Check if key has partially-covered columns
2788
We can't use DS-MRR to perform range scans when the ranges are over
2789
partially-covered keys, because we'll not have full key part values
2790
(we'll have their prefixes from the index) and will not be able to check
2791
if we've reached the end the range.
2793
@param keyno Key to check
2796
Allow use of DS-MRR in cases where the index has partially-covered
2797
components but they are not used for scanning.
2803
bool DsMrr_impl::key_uses_partial_cols(uint32_t keyno)
2805
KEY_PART_INFO *kp= table->key_info[keyno].key_part;
2806
KEY_PART_INFO *kp_end= kp + table->key_info[keyno].key_parts;
2807
for (; kp != kp_end; kp++)
2809
if (!kp->field->part_of_key.test(keyno))
2817
DS-MRR Internals: Choose between Default MRR implementation and DS-MRR
2819
Make the choice between using Default MRR implementation and DS-MRR.
2820
This function contains common functionality factored out of dsmrr_info()
2821
and dsmrr_info_const(). The function assumes that the default MRR
2822
implementation's applicability requirements are satisfied.
2824
@param keyno Index number
2825
@param rows E(full rows to be retrieved)
2826
@param flags IN MRR flags provided by the MRR user
2827
OUT If DS-MRR is choosen, flags of DS-MRR implementation
2828
else the value is not modified
2829
@param bufsz IN If DS-MRR is choosen, buffer use of DS-MRR implementation
2830
else the value is not modified
2831
@param cost IN Cost of default MRR implementation
2832
OUT If DS-MRR is choosen, cost of DS-MRR scan
2833
else the value is not modified
2835
@retval true Default MRR implementation should be used
2836
@retval false DS-MRR implementation should be used
2839
bool DsMrr_impl::choose_mrr_impl(uint32_t keyno, ha_rows rows, uint32_t *flags,
2840
uint32_t *bufsz, COST_VECT *cost)
2842
COST_VECT dsmrr_cost;
2844
Session *session= current_session;
2845
if ((session->variables.optimizer_use_mrr == 2) ||
2846
(*flags & HA_MRR_INDEX_ONLY) || (*flags & HA_MRR_SORTED) ||
2847
(keyno == table->s->primary_key &&
2848
h->primary_key_is_clustered()) ||
2849
key_uses_partial_cols(keyno))
2851
/* Use the default implementation */
2852
*flags |= HA_MRR_USE_DEFAULT_IMPL;
2856
uint32_t add_len= table->key_info[keyno].key_length + h->ref_length;
2858
if (get_disk_sweep_mrr_cost(keyno, rows, *flags, bufsz, &dsmrr_cost))
2864
If @@optimizer_use_mrr==force, then set cost of DS-MRR to be minimum of
2865
DS-MRR and Default implementations cost. This allows one to force use of
2866
DS-MRR whenever it is applicable without affecting other cost-based
2869
if ((force_dsmrr= (session->variables.optimizer_use_mrr == 1)) &&
2870
dsmrr_cost.total_cost() > cost->total_cost())
2873
if (force_dsmrr || dsmrr_cost.total_cost() <= cost->total_cost())
2875
*flags &= ~HA_MRR_USE_DEFAULT_IMPL; /* Use the DS-MRR implementation */
2876
*flags &= ~HA_MRR_SORTED; /* We will return unordered output */
2882
/* Use the default MRR implementation */
2889
static void get_sort_and_sweep_cost(Table *table, ha_rows nrows, COST_VECT *cost);
2893
Get cost of DS-MRR scan
2895
@param keynr Index to be used
2896
@param rows E(Number of rows to be scanned)
2897
@param flags Scan parameters (HA_MRR_* flags)
2898
@param buffer_size INOUT Buffer size
2899
@param cost OUT The cost
2902
@retval true Error, DS-MRR cannot be used (the buffer is too small
2906
bool DsMrr_impl::get_disk_sweep_mrr_cost(uint32_t keynr, ha_rows rows, uint32_t flags,
2907
uint32_t *buffer_size, COST_VECT *cost)
2909
uint32_t max_buff_entries, elem_size;
2910
ha_rows rows_in_full_step, rows_in_last_step;
2911
uint32_t n_full_steps;
2912
double index_read_cost;
2914
elem_size= h->ref_length + sizeof(void*) * (!test(flags & HA_MRR_NO_ASSOCIATION));
2915
max_buff_entries = *buffer_size / elem_size;
2917
if (!max_buff_entries)
2918
return true; /* Buffer has not enough space for even 1 rowid */
2920
/* Number of iterations we'll make with full buffer */
2921
n_full_steps= (uint32_t)floor(rows2double(rows) / max_buff_entries);
2924
Get numbers of rows we'll be processing in
2925
- non-last sweep, with full buffer
2926
- last iteration, with non-full buffer
2928
rows_in_full_step= max_buff_entries;
2929
rows_in_last_step= rows % max_buff_entries;
2931
/* Adjust buffer size if we expect to use only part of the buffer */
2934
get_sort_and_sweep_cost(table, rows, cost);
2935
cost->multiply(n_full_steps);
2940
*buffer_size= max(*buffer_size,
2941
(uint32_t)(1.2*rows_in_last_step) * elem_size +
2942
h->ref_length + table->key_info[keynr].key_length);
2945
COST_VECT last_step_cost;
2946
get_sort_and_sweep_cost(table, rows_in_last_step, &last_step_cost);
2947
cost->add(&last_step_cost);
2949
if (n_full_steps != 0)
2950
cost->mem_cost= *buffer_size;
2952
cost->mem_cost= (double)rows_in_last_step * elem_size;
2954
/* Total cost of all index accesses */
2955
index_read_cost= h->index_only_read_time(keynr, (double)rows);
2956
cost->add_io(index_read_cost, 1 /* Random seeks */);
2961
#define log2(x) (log(x) / M_LN2)
2965
Get cost of one sort-and-sweep step
2968
get_sort_and_sweep_cost()
2969
table Table being accessed
2970
nrows Number of rows to be sorted and retrieved
2974
Get cost of these operations:
2975
- sort an array of #nrows ROWIDs using qsort
2976
- read #nrows records from table in a sweep.
2980
void get_sort_and_sweep_cost(Table *table, ha_rows nrows, COST_VECT *cost)
2984
get_sweep_read_cost(table, nrows, false, cost);
2985
/* Add cost of qsort call: n * log2(n) * cost(rowid_comparison) */
2986
double cmp_op= rows2double(nrows) * (1.0 / TIME_FOR_COMPARE_ROWID);
2989
cost->cpu_cost += cmp_op * log2(cmp_op);
2997
2499
Get cost of reading nrows table records in a "disk sweep"