From e0999cdf7c2222f37573d50ecd7eeb9612d51a49 Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Sat, 17 Jul 2010 18:03:50 +0400 Subject: [PATCH] DS-MRR support improvements (MWL#123, MWL#124, MWL#125) - Lots of TODO comments - add mrr_sort_keys flag to @@optimizer_switch - [from Igor] SQL layer part passes HA_MRR_MATERIALIZED_KEYS flag - Don't call rnd_pos() many times in a row if sorted rowid buffer has the same rowid value for multiple consequive (rowid, range_id) pairs. --- mysql-test/r/optimizer_switch.result | 34 +++--- sql/handler.h | 6 + sql/multi_range_read.cc | 160 +++++++++++++++++++++++---- sql/multi_range_read.h | 6 +- sql/mysql_priv.h | 13 ++- sql/mysqld.cc | 5 +- sql/sql_join_cache.cc | 5 + 7 files changed, 181 insertions(+), 48 deletions(-) diff --git a/mysql-test/r/optimizer_switch.result b/mysql-test/r/optimizer_switch.result index 6bccefe54be..13a5f1ab232 100644 --- a/mysql-test/r/optimizer_switch.result +++ b/mysql-test/r/optimizer_switch.result @@ -4,19 +4,19 @@ # select @@optimizer_switch; @@optimizer_switch -index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on +index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr_sort_keys=on set optimizer_switch='index_merge=off,index_merge_union=off'; select @@optimizer_switch; @@optimizer_switch -index_merge=off,index_merge_union=off,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on +index_merge=off,index_merge_union=off,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr_sort_keys=on set optimizer_switch='index_merge_union=on'; select @@optimizer_switch; @@optimizer_switch -index_merge=off,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on +index_merge=off,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr_sort_keys=on set optimizer_switch='default,index_merge_sort_union=off'; select @@optimizer_switch; @@optimizer_switch -index_merge=on,index_merge_union=on,index_merge_sort_union=off,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on +index_merge=on,index_merge_union=on,index_merge_sort_union=off,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr_sort_keys=on set optimizer_switch=4; ERROR 42000: Variable 'optimizer_switch' can't be set to the value of '4' set optimizer_switch=NULL; @@ -43,57 +43,57 @@ set optimizer_switch=default; set optimizer_switch='index_merge=off,index_merge_union=off,default'; select @@optimizer_switch; @@optimizer_switch -index_merge=off,index_merge_union=off,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on +index_merge=off,index_merge_union=off,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr_sort_keys=on set optimizer_switch=default; select @@global.optimizer_switch; @@global.optimizer_switch -index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on +index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr_sort_keys=on set @@global.optimizer_switch=default; select @@global.optimizer_switch; @@global.optimizer_switch -index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on +index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr_sort_keys=on # # Check index_merge's @@optimizer_switch flags # select @@optimizer_switch; @@optimizer_switch -index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on +index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr_sort_keys=on BUG#37120 optimizer_switch allowable values not according to specification select @@optimizer_switch; @@optimizer_switch -index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on +index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr_sort_keys=on set optimizer_switch='default,materialization=off'; select @@optimizer_switch; @@optimizer_switch -index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=off,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on +index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=off,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr_sort_keys=on set optimizer_switch='default,semijoin=off'; select @@optimizer_switch; @@optimizer_switch -index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=on,semijoin=off,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on +index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=on,semijoin=off,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr_sort_keys=on set optimizer_switch='default,loosescan=off'; select @@optimizer_switch; @@optimizer_switch -index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=off,materialization=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on +index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=off,materialization=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr_sort_keys=on set optimizer_switch='default,semijoin=off,materialization=off'; select @@optimizer_switch; @@optimizer_switch -index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=off,semijoin=off,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on +index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=off,semijoin=off,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr_sort_keys=on set optimizer_switch='default,materialization=off,semijoin=off'; select @@optimizer_switch; @@optimizer_switch -index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=off,semijoin=off,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on +index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=on,materialization=off,semijoin=off,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr_sort_keys=on set optimizer_switch='default,semijoin=off,materialization=off,loosescan=off'; select @@optimizer_switch; @@optimizer_switch -index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=off,materialization=off,semijoin=off,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on +index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=off,materialization=off,semijoin=off,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr_sort_keys=on set optimizer_switch='default,semijoin=off,loosescan=off'; select @@optimizer_switch; @@optimizer_switch -index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=off,materialization=on,semijoin=off,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on +index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=off,materialization=on,semijoin=off,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr_sort_keys=on set optimizer_switch='default,materialization=off,loosescan=off'; select @@optimizer_switch; @@optimizer_switch -index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=off,materialization=off,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on +index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_condition_pushdown=on,firstmatch=on,loosescan=off,materialization=off,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr_sort_keys=on set optimizer_switch=default; diff --git a/sql/handler.h b/sql/handler.h index c471aa6e4d3..2eae66fd741 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -1322,6 +1322,12 @@ void get_sweep_read_cost(TABLE *table, ha_rows nrows, bool interrupted, */ #define HA_MRR_NO_NULL_ENDPOINTS 128 +/* + The MRR user has materialized range keys somewhere in the user's buffer. + This can be used for optimization of the procedure that sorts these keys + since in this case key values don't have to be copied into the MRR buffer. +*/ +#define HA_MRR_MATERIALIZED_KEYS 256 /* diff --git a/sql/multi_range_read.cc b/sql/multi_range_read.cc index 14d7722d2c8..f6417beb786 100644 --- a/sql/multi_range_read.cc +++ b/sql/multi_range_read.cc @@ -327,24 +327,62 @@ int DsMrr_impl::dsmrr_init(handler *h_arg, RANGE_SEQ_IF *seq_funcs, mode, buf); DBUG_RETURN(retval); } - mrr_buf= buf->buffer; - + use_default_impl= FALSE; is_mrr_assoc= !test(mode & HA_MRR_NO_ASSOCIATION); + // psergey2: split the buffer: + /* + + psergey2-note: we can't split the buffer here because we don't know how key + length. we'll only be able to do it when we've got the first range. + + if ((mrr_flags & HA_MRR_SINGLE_POINT) && + optimizer_flag(thd, OPTIMIZER_SWITCH_MRR_SORT_KEYS)) + { + do_sort_keys= TRUE; // will use key buffer to sort keys; + bool use_key_pointers= test(mrr_flags & HA_MRR_MATERIALIZED_KEYS); + } + + do_rowid_fetch= FALSE; + if (!doing_cpk_scan && !index_only_read) + { + do_rowid_fetch= TRUE; //will use rowid buffer to store/sort rowids, etc + } + + + if (do_sort_keys && do_rowid_fetch) + { + split buffer space proportionally + } + else + { + // give all space to one buffer + if (do_sort_keys) + { + //sort_buffer_start= ...; + } + else + { + DBUG_ASSERT(do_rowid_fetch); + //rowid_buffer_start= ...; + } + } + */ + mrr_buf= buf->buffer; + mrr_buf_end= buf->buffer_end; + if (is_mrr_assoc) status_var_increment(table->in_use->status_var.ha_multi_range_read_init_count); - - mrr_buf_end= buf->buffer_end; if ((doing_cpk_scan= check_cpk_scan(h->active_index, mode))) { /* It's a DS-MRR/CPK scan */ cpk_tuple_length= 0; /* dummy value telling it needs to be inited */ cpk_have_range= FALSE; - use_default_impl= FALSE; h->mrr_iter= seq_funcs->init(seq_init_param, n_ranges, mode); h->mrr_funcs= *seq_funcs; - dsmrr_fill_buffer_cpk(); + dsmrr_fill_key_buffer(); + if (dsmrr_eof) buf->end_of_used_area= mrr_buf_last; DBUG_RETURN(0); /* nothing could go wrong while filling the buffer */ @@ -355,6 +393,11 @@ int DsMrr_impl::dsmrr_init(handler *h_arg, RANGE_SEQ_IF *seq_funcs, mrr_buf_last= mrr_buf + ((mrr_buf_end - mrr_buf)/ elem_size)* elem_size; mrr_buf_end= mrr_buf_last; + /* + psergey2: this is only needed when + - doing a rowid-to-row scan + - the buffer wasn't exhausted on the first pass. + */ /* There can be two cases: - This is the first call since index_init(), h2==NULL @@ -365,7 +408,7 @@ int DsMrr_impl::dsmrr_init(handler *h_arg, RANGE_SEQ_IF *seq_funcs, */ if (!h2) { - /* Create a separate handler object to do rndpos() calls. */ + /* Create a separate handler object to do rnd_pos() calls. */ THD *thd= current_thd; /* ::clone() takes up a lot of stack, especially on 64 bit platforms. @@ -376,7 +419,7 @@ int DsMrr_impl::dsmrr_init(handler *h_arg, RANGE_SEQ_IF *seq_funcs, DBUG_ASSERT(h->active_index != MAX_KEY); uint mrr_keyno= h->active_index; - /* Create a separate handler object to do rndpos() calls. */ + /* Create a separate handler object to do rnd_pos() calls. */ if (!(new_h2= h->clone(thd->mem_root)) || new_h2->ha_external_lock(thd, F_RDLCK)) { @@ -397,6 +440,7 @@ int DsMrr_impl::dsmrr_init(handler *h_arg, RANGE_SEQ_IF *seq_funcs, goto error; } + use_default_impl= FALSE; h2= new_h2; /* Ok, now can put it into h2 */ table->prepare_for_position(); h2->extra(HA_EXTRA_KEYREAD); @@ -404,7 +448,6 @@ int DsMrr_impl::dsmrr_init(handler *h_arg, RANGE_SEQ_IF *seq_funcs, if (h2->ha_index_init(mrr_keyno, FALSE)) goto error; - use_default_impl= FALSE; if (pushed_cond) h2->idx_cond_push(mrr_keyno, pushed_cond); } @@ -422,14 +465,13 @@ int DsMrr_impl::dsmrr_init(handler *h_arg, RANGE_SEQ_IF *seq_funcs, h2= NULL; int res= (h->inited == handler::INDEX && h->ha_index_end()); h2= save_h2; - use_default_impl= FALSE; if (res) goto error; } if (h2->handler::multi_range_read_init(seq_funcs, seq_init_param, n_ranges, mode, buf) || - dsmrr_fill_buffer()) + dsmrr_fill_rowid_buffer()) { goto error; } @@ -449,7 +491,6 @@ int DsMrr_impl::dsmrr_init(handler *h_arg, RANGE_SEQ_IF *seq_funcs, (h->ha_rnd_init(FALSE)))) goto error; - use_default_impl= FALSE; h->mrr_funcs= *seq_funcs; DBUG_RETURN(0); @@ -497,6 +538,9 @@ static int rowid_cmp(void *h, uchar *a, uchar *b) dsmrr_eof is set to indicate whether we've exhausted the list of ranges we're scanning. + + psergey2: this func will 'fill the rowid buffer'. If filling the rowid buffer + requires that key buffer is filled/sorted first, will do that, too. @param h Table handler @@ -505,13 +549,27 @@ static int rowid_cmp(void *h, uchar *a, uchar *b) @retval other Error */ -int DsMrr_impl::dsmrr_fill_buffer() +int DsMrr_impl::dsmrr_fill_rowid_buffer() { char *range_info; int res; - DBUG_ENTER("DsMrr_impl::dsmrr_fill_buffer"); - + DBUG_ENTER("DsMrr_impl::dsmrr_fill_rowid_buffer"); + mrr_buf_cur= mrr_buf; + mrr_buf_next_identical= mrr_buf_cur; + /* + psergey2-todo: + - call here fill/sort key buffer, if needed. + + psergey2-todo: then, get keys either from + - multi_range_read_next() + - sorted key buffer + + psergey2-todo: if we're traversing an ordered key sequence, + check if next keys are the same as previous. + (note that it's easy as ordered sequence allows forward/backward + navigation so we don't need to buffer things) + */ while ((mrr_buf_cur < mrr_buf_end) && !(res= h2->handler::multi_range_read_next(&range_info))) { @@ -520,6 +578,7 @@ int DsMrr_impl::dsmrr_fill_buffer() 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]); memcpy(mrr_buf_cur, h2->ref, h2->ref_length); @@ -579,20 +638,26 @@ int DsMrr_impl::key_tuple_cmp(void* arg, uchar* key1, uchar* key2) DS-MRR/CPK: Fill the buffer with (lookup_tuple, range_id) pairs and sort SYNOPSIS - DsMrr_impl::dsmrr_fill_buffer_cpk() + DsMrr_impl::dsmrr_fill_key_buffer() DESCRIPTION DS-MRR/CPK: Fill the buffer with (lookup_tuple, range_id) pairs and sort dsmrr_eof is set to indicate whether we've exhausted the list of ranges we're scanning. + + psergey2-q: can this be used for filling/sorting key buffer in general case? + a: yes. + qq: can we push sequence iteration init down into here? */ -void DsMrr_impl::dsmrr_fill_buffer_cpk() +void DsMrr_impl::dsmrr_fill_key_buffer() { + //psergey2: here, no identicals detection is necessary since we always scan + // the unordered sequence. int res; KEY_MULTI_RANGE cur_range; - DBUG_ENTER("DsMrr_impl::dsmrr_fill_buffer_cpk"); + DBUG_ENTER("DsMrr_impl::dsmrr_fill_key_buffer"); mrr_buf_cur= mrr_buf; while ((mrr_buf_cur < mrr_buf_end) && @@ -611,6 +676,8 @@ void DsMrr_impl::dsmrr_fill_buffer_cpk() mrr_buf_end= mrr_buf_last; } + //psergey2: if keys are materialized, store pointers, not copy keys + /* Put key, or {key, range_id} pair into the buffer */ memcpy(mrr_buf_cur, cur_range.start_key.key, cpk_tuple_length); mrr_buf_cur += cpk_tuple_length; @@ -648,12 +715,14 @@ void DsMrr_impl::dsmrr_fill_buffer_cpk() This is similar to DsMrr_impl::dsmrr_next(), the differences are that - we get records with index_read(), not with rnd_pos() - we may get multiple records for one key (=element of the buffer) - - unlike dsmrr_fill_buffer(), dsmrr_fill_buffer_cpk() never fails. + - unlike dsmrr_fill_rowid_buffer(), dsmrr_fill_key_buffer() never fails. RETURN 0 OK, next record was successfully read HA_ERR_END_OF_FILE End of records Other Some other error + + psergey2-todo: this should detect identical keys. */ int DsMrr_impl::dsmrr_next_cpk(char **range_info) @@ -697,7 +766,7 @@ int DsMrr_impl::dsmrr_next_cpk(char **range_info) res= HA_ERR_END_OF_FILE; goto end; } - dsmrr_fill_buffer_cpk(); + dsmrr_fill_key_buffer(); } if (mrr_buf_cur == mrr_buf_last) { @@ -758,6 +827,9 @@ end: /** DS-MRR implementation: multi_range_read_next() function + + psergey2-todo: put identical rowid detection code here + it should always work because rowid sequences are always sorted */ int DsMrr_impl::dsmrr_next(char **range_info) @@ -772,6 +844,23 @@ int DsMrr_impl::dsmrr_next(char **range_info) if (doing_cpk_scan) return dsmrr_next_cpk(range_info); + if (mrr_buf_next_identical != mrr_buf_cur) + { + /* + There are multiple rowids. Return the record again, now with different + range_id + */ + do + { + if (is_mrr_assoc) + memcpy(range_info, mrr_buf_next_identical + h->ref_length, sizeof(uchar*)); + } while (!h2->mrr_funcs.skip_record || + !h2->mrr_funcs.skip_record(h2->mrr_iter, (char *) range_info, rowid)); + + mrr_buf_next_identical += h->ref_length + sizeof(void*) * test(is_mrr_assoc); + return 0; + } + do { if (mrr_buf_cur == mrr_buf_last) @@ -781,7 +870,7 @@ int DsMrr_impl::dsmrr_next(char **range_info) res= HA_ERR_END_OF_FILE; goto end; } - res= dsmrr_fill_buffer(); + res= dsmrr_fill_rowid_buffer(); if (res) goto end; } @@ -796,13 +885,34 @@ int DsMrr_impl::dsmrr_next(char **range_info) if (is_mrr_assoc) memcpy(&cur_range_info, mrr_buf_cur + h->ref_length, sizeof(uchar**)); + + size_t element_size= h->ref_length + sizeof(void*) * test(is_mrr_assoc); + mrr_buf_cur += element_size; + mrr_buf_next_identical= mrr_buf_cur; - mrr_buf_cur += h->ref_length + sizeof(void*) * test(is_mrr_assoc); if (h2->mrr_funcs.skip_record && h2->mrr_funcs.skip_record(h2->mrr_iter, (char *) cur_range_info, rowid)) continue; res= h->ha_rnd_pos(table->record[0], rowid); + + if (res == HA_ERR_RECORD_DELETED) + continue; + + if (0)//(!res) + { + /* + Note: this implies that SQL layer doesn't touch table->record[0] + between calls. + */ + uchar *current_el= mrr_buf_cur - element_size; + while (mrr_buf_cur != mrr_buf_last && + !h2->cmp_ref(current_el, mrr_buf_cur)) + { + mrr_buf_cur += element_size; + } + } break; + } while (true); if (is_mrr_assoc) @@ -986,7 +1096,7 @@ bool DsMrr_impl::choose_mrr_impl(uint keyno, ha_rows rows, uint *flags, *flags |= HA_MRR_USE_DEFAULT_IMPL; return TRUE; } - + uint add_len= table->key_info[keyno].key_length + h->ref_length; *bufsz -= add_len; if (get_disk_sweep_mrr_cost(keyno, rows, *flags, bufsz, &dsmrr_cost)) @@ -1010,6 +1120,10 @@ bool DsMrr_impl::choose_mrr_impl(uint keyno, ha_rows rows, uint *flags, *flags &= ~HA_MRR_SORTED; /* We will return unordered output */ *cost= dsmrr_cost; res= FALSE; + + if ((*flags & HA_MRR_SINGLE_POINT) && + optimizer_flag(thd, OPTIMIZER_SWITCH_MRR_SORT_KEYS)) + *flags |= HA_MRR_MATERIALIZED_KEYS; } else { diff --git a/sql/multi_range_read.h b/sql/multi_range_read.h index 7a5e57e490e..9d0e7dc096e 100644 --- a/sql/multi_range_read.h +++ b/sql/multi_range_read.h @@ -114,6 +114,8 @@ private: uchar *mrr_buf_last; /* When reading: end of used buffer space */ uchar *mrr_buf_end; /* End of the buffer */ + uchar *mrr_buf_next_identical; + bool dsmrr_eof; /* TRUE <=> We have reached EOF when reading index tuples */ /* TRUE <=> need range association, buffer holds {rowid, range_id} pairs */ @@ -143,8 +145,8 @@ private: uint *buffer_size, COST_VECT *cost); bool check_cpk_scan(uint keyno, uint mrr_flags); static int key_tuple_cmp(void* arg, uchar* key1, uchar* key2); - int dsmrr_fill_buffer(); - void dsmrr_fill_buffer_cpk(); + int dsmrr_fill_rowid_buffer(); + void dsmrr_fill_key_buffer(); int dsmrr_next_cpk(char **range_info); }; diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index ba60bab9b50..925840e49f3 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -571,12 +571,13 @@ protected: #define OPTIMIZER_SWITCH_PARTIAL_MATCH_ROWID_MERGE 512 #define OPTIMIZER_SWITCH_PARTIAL_MATCH_TABLE_SCAN 1024 #define OPTIMIZER_SWITCH_SUBQUERY_CACHE (1<<11) +#define OPTIMIZER_SWITCH_MRR_SORT_KEYS (1<<12) #ifdef DBUG_OFF -# define OPTIMIZER_SWITCH_LAST (1<<12) -#else -# define OPTIMIZER_SWITCH_TABLE_ELIMINATION (1<<12) # define OPTIMIZER_SWITCH_LAST (1<<13) +#else +# define OPTIMIZER_SWITCH_TABLE_ELIMINATION (1<<13) +# define OPTIMIZER_SWITCH_LAST (1<<14) #endif #ifdef DBUG_OFF @@ -592,7 +593,8 @@ protected: OPTIMIZER_SWITCH_SEMIJOIN | \ OPTIMIZER_SWITCH_PARTIAL_MATCH_ROWID_MERGE|\ OPTIMIZER_SWITCH_PARTIAL_MATCH_TABLE_SCAN|\ - OPTIMIZER_SWITCH_SUBQUERY_CACHE) + OPTIMIZER_SWITCH_SUBQUERY_CACHE|\ + OPTIMIZER_SWITCH_MRR_SORT_KEYS) #else # define OPTIMIZER_SWITCH_DEFAULT (OPTIMIZER_SWITCH_INDEX_MERGE | \ OPTIMIZER_SWITCH_INDEX_MERGE_UNION | \ @@ -606,7 +608,8 @@ protected: OPTIMIZER_SWITCH_SEMIJOIN | \ OPTIMIZER_SWITCH_PARTIAL_MATCH_ROWID_MERGE|\ OPTIMIZER_SWITCH_PARTIAL_MATCH_TABLE_SCAN|\ - OPTIMIZER_SWITCH_SUBQUERY_CACHE) + OPTIMIZER_SWITCH_SUBQUERY_CACHE|\ + OPTIMIZER_SWITCH_MRR_SORT_KEYS) #endif /* diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 1593a584454..e53c455c3ca 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -308,6 +308,7 @@ static const char *optimizer_switch_names[]= "partial_match_rowid_merge", "partial_match_table_scan", "subquery_cache", + "mrr_sort_keys", #ifndef DBUG_OFF "table_elimination", #endif @@ -329,6 +330,7 @@ static const unsigned int optimizer_switch_names_len[]= sizeof("partial_match_rowid_merge") - 1, sizeof("partial_match_table_scan") - 1, sizeof("subquery_cache") - 1, + sizeof("mrr_sort_keys") - 1, #ifndef DBUG_OFF sizeof("table_elimination") - 1, #endif @@ -415,7 +417,8 @@ static const char *optimizer_switch_str="index_merge=on,index_merge_union=on," "semijoin=on," "partial_match_rowid_merge=on," "partial_match_table_scan=on," - "subquery_cache=on" + "subquery_cache=on," + "mrr_sort_keys=on" #ifndef DBUG_OFF ",table_elimination=on"; #else diff --git a/sql/sql_join_cache.cc b/sql/sql_join_cache.cc index 96850eb99b8..14730eac8c6 100644 --- a/sql/sql_join_cache.cc +++ b/sql/sql_join_cache.cc @@ -651,6 +651,9 @@ int JOIN_CACHE_BKA::init() use_emb_key= check_emb_key_usage(); + if (use_emb_key) + mrr_mode|= HA_MRR_MATERIALIZED_KEYS; + create_remaining_fields(FALSE); set_constants(); @@ -2631,6 +2634,8 @@ int JOIN_CACHE_BKA_UNIQUE::init() data_fields_offset+= copy->length; } + mrr_mode|= HA_MRR_MATERIALIZED_KEYS; + DBUG_RETURN(rc); }