summaryrefslogtreecommitdiff
path: root/sql/multi_range_read.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/multi_range_read.cc')
-rw-r--r--sql/multi_range_read.cc1308
1 files changed, 1091 insertions, 217 deletions
diff --git a/sql/multi_range_read.cc b/sql/multi_range_read.cc
index b338bc147bc..130ab676e7b 100644
--- a/sql/multi_range_read.cc
+++ b/sql/multi_range_read.cc
@@ -1,5 +1,22 @@
+/* Copyright (C) 2010, 2011 Monty Program Ab
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
#include "sql_parse.h"
+#include <my_bit.h>
#include "sql_select.h"
+#include "key.h"
/****************************************************************************
* Default MRR implementation (MRR to non-MRR converter)
@@ -136,10 +153,16 @@ handler::multi_range_read_info_const(uint keyno, RANGE_SEQ_IF *seq,
*/
ha_rows handler::multi_range_read_info(uint keyno, uint n_ranges, uint n_rows,
- uint *bufsz, uint *flags, COST_VECT *cost)
+ uint key_parts, uint *bufsz,
+ uint *flags, COST_VECT *cost)
{
- *bufsz= 0; /* Default implementation doesn't need a buffer */
+ /*
+ Currently we expect this function to be called only in preparation of scan
+ with HA_MRR_SINGLE_POINT property.
+ */
+ DBUG_ASSERT(*flags | HA_MRR_SINGLE_POINT);
+ *bufsz= 0; /* Default implementation doesn't need a buffer */
*flags |= HA_MRR_USE_DEFAULT_IMPL;
cost->zero();
@@ -207,7 +230,6 @@ handler::multi_range_read_init(RANGE_SEQ_IF *seq_funcs, void *seq_init_param,
DBUG_RETURN(0);
}
-
/**
Get next record in MRR scan
@@ -221,10 +243,10 @@ handler::multi_range_read_init(RANGE_SEQ_IF *seq_funcs, void *seq_init_param,
@retval other Error code
*/
-int handler::multi_range_read_next(char **range_info)
+int handler::multi_range_read_next(range_id_t *range_info)
{
- int UNINIT_VAR(result);
- int range_res;
+ int result= HA_ERR_END_OF_FILE;
+ bool range_res;
DBUG_ENTER("handler::multi_range_read_next");
if (!mrr_have_range)
@@ -246,7 +268,14 @@ int handler::multi_range_read_next(char **range_info)
else
{
if (was_semi_consistent_read())
+ {
+ /*
+ The following assignment is redundant, but for extra safety and to
+ remove the compiler warning:
+ */
+ range_res= FALSE;
goto scan_it_again;
+ }
/*
We need to set this for the last range only, but checking this
condition is more expensive than just setting the result code.
@@ -277,7 +306,459 @@ scan_it_again:
}
/****************************************************************************
- * DS-MRR implementation
+ * Mrr_*_reader classes (building blocks for DS-MRR)
+ ***************************************************************************/
+
+int Mrr_simple_index_reader::init(handler *h_arg, RANGE_SEQ_IF *seq_funcs,
+ void *seq_init_param, uint n_ranges,
+ uint mode, Key_parameters *key_par_arg,
+ Lifo_buffer *key_buffer_arg,
+ Buffer_manager *buf_manager_arg)
+{
+ HANDLER_BUFFER no_buffer = {NULL, NULL, NULL};
+ file= h_arg;
+ return file->handler::multi_range_read_init(seq_funcs, seq_init_param,
+ n_ranges, mode, &no_buffer);
+}
+
+
+int Mrr_simple_index_reader::get_next(range_id_t *range_info)
+{
+ int res;
+ while (!(res= file->handler::multi_range_read_next(range_info)))
+ {
+ KEY_MULTI_RANGE *curr_range= &file->handler::mrr_cur_range;
+ if (!file->mrr_funcs.skip_index_tuple ||
+ !file->mrr_funcs.skip_index_tuple(file->mrr_iter, curr_range->ptr))
+ break;
+ }
+ if (res && res != HA_ERR_END_OF_FILE && res != HA_ERR_KEY_NOT_FOUND)
+ file->print_error(res, MYF(0)); // Fatal error
+ return res;
+}
+
+
+/**
+ @brief Get next index record
+
+ @param range_info OUT identifier of range that the returned record belongs to
+
+ @note
+ We actually iterate over nested sequences:
+ - an ordered sequence of groups of identical keys
+ - each key group has key value, which has multiple matching records
+ - thus, each record matches all members of the key group
+
+ @retval 0 OK, next record was successfully read
+ @retval HA_ERR_END_OF_FILE End of records
+ @retval Other Some other error; Error is printed
+*/
+
+int Mrr_ordered_index_reader::get_next(range_id_t *range_info)
+{
+ int res;
+ DBUG_ENTER("Mrr_ordered_index_reader::get_next");
+
+ for(;;)
+ {
+ if (!scanning_key_val_iter)
+ {
+ while ((res= kv_it.init(this)))
+ {
+ if ((res != HA_ERR_KEY_NOT_FOUND && res != HA_ERR_END_OF_FILE))
+ DBUG_RETURN(res); /* Some fatal error */
+
+ if (key_buffer->is_empty())
+ {
+ DBUG_RETURN(HA_ERR_END_OF_FILE);
+ }
+ }
+ scanning_key_val_iter= TRUE;
+ }
+
+ if ((res= kv_it.get_next(range_info)))
+ {
+ scanning_key_val_iter= FALSE;
+ if ((res != HA_ERR_KEY_NOT_FOUND && res != HA_ERR_END_OF_FILE))
+ DBUG_RETURN(res);
+ kv_it.move_to_next_key_value();
+ continue;
+ }
+ if (!skip_index_tuple(*range_info) &&
+ !skip_record(*range_info, NULL))
+ {
+ break;
+ }
+ /* Go get another (record, range_id) combination */
+ } /* while */
+
+ DBUG_RETURN(0);
+}
+
+
+/*
+ Supply index reader with the O(1)space it needs for scan interrupt/restore
+ operation
+*/
+
+bool Mrr_ordered_index_reader::set_interruption_temp_buffer(uint rowid_length,
+ uint key_len,
+ uint saved_pk_len,
+ uchar **space_start,
+ uchar *space_end)
+{
+ if (space_end - *space_start <= (ptrdiff_t)(rowid_length + key_len + saved_pk_len))
+ return TRUE;
+ support_scan_interruptions= TRUE;
+
+ saved_rowid= *space_start;
+ *space_start += rowid_length;
+
+ if (saved_pk_len)
+ {
+ saved_primary_key= *space_start;
+ *space_start += saved_pk_len;
+ }
+ else
+ saved_primary_key= NULL;
+
+ saved_key_tuple= *space_start;
+ *space_start += key_len;
+
+ have_saved_rowid= FALSE;
+ return FALSE;
+}
+
+void Mrr_ordered_index_reader::set_no_interruption_temp_buffer()
+{
+ support_scan_interruptions= FALSE;
+ saved_key_tuple= saved_rowid= saved_primary_key= NULL; /* safety */
+ have_saved_rowid= FALSE;
+}
+
+void Mrr_ordered_index_reader::interrupt_read()
+{
+ DBUG_ASSERT(support_scan_interruptions);
+ TABLE *table= file->get_table();
+ /* Save the current key value */
+ key_copy(saved_key_tuple, table->record[0],
+ &table->key_info[file->active_index],
+ keypar.key_tuple_length);
+
+ if (saved_primary_key)
+ {
+ key_copy(saved_primary_key, table->record[0],
+ &table->key_info[table->s->primary_key],
+ table->key_info[table->s->primary_key].key_length);
+ }
+
+ /* Save the last rowid */
+ memcpy(saved_rowid, file->ref, file->ref_length);
+ have_saved_rowid= TRUE;
+}
+
+void Mrr_ordered_index_reader::position()
+{
+ if (have_saved_rowid)
+ memcpy(file->ref, saved_rowid, file->ref_length);
+ else
+ Mrr_index_reader::position();
+}
+
+void Mrr_ordered_index_reader::resume_read()
+{
+ TABLE *table= file->get_table();
+ key_restore(table->record[0], saved_key_tuple,
+ &table->key_info[file->active_index],
+ keypar.key_tuple_length);
+ if (saved_primary_key)
+ {
+ key_restore(table->record[0], saved_primary_key,
+ &table->key_info[table->s->primary_key],
+ table->key_info[table->s->primary_key].key_length);
+ }
+}
+
+
+/**
+ Fill the buffer with (lookup_tuple, range_id) pairs and sort
+*/
+
+int Mrr_ordered_index_reader::refill_buffer(bool initial)
+{
+ KEY_MULTI_RANGE cur_range;
+ DBUG_ENTER("Mrr_ordered_index_reader::refill_buffer");
+
+ DBUG_ASSERT(key_buffer->is_empty());
+
+ if (source_exhausted)
+ DBUG_RETURN(HA_ERR_END_OF_FILE);
+
+ buf_manager->reset_buffer_sizes(buf_manager->arg);
+ key_buffer->reset();
+ key_buffer->setup_writing(keypar.key_size_in_keybuf,
+ is_mrr_assoc? sizeof(range_id_t) : 0);
+
+ while (key_buffer->can_write() &&
+ !(source_exhausted= mrr_funcs.next(mrr_iter, &cur_range)))
+ {
+ DBUG_ASSERT(cur_range.range_flag & EQ_RANGE);
+
+ /* Put key, or {key, range_id} pair into the buffer */
+ key_buffer->write_ptr1= keypar.use_key_pointers ?
+ (uchar*)&cur_range.start_key.key :
+ (uchar*)cur_range.start_key.key;
+ key_buffer->write_ptr2= (uchar*)&cur_range.ptr;
+ key_buffer->write();
+ }
+
+ /* Force get_next() to start with kv_it.init() call: */
+ scanning_key_val_iter= FALSE;
+
+ if (source_exhausted && key_buffer->is_empty())
+ DBUG_RETURN(HA_ERR_END_OF_FILE);
+
+ key_buffer->sort((key_buffer->type() == Lifo_buffer::FORWARD)?
+ (qsort2_cmp)Mrr_ordered_index_reader::compare_keys_reverse :
+ (qsort2_cmp)Mrr_ordered_index_reader::compare_keys,
+ this);
+ 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, Key_parameters *key_par_arg,
+ Lifo_buffer *key_buffer_arg,
+ Buffer_manager *buf_manager_arg)
+{
+ file= h_arg;
+ key_buffer= key_buffer_arg;
+ buf_manager= buf_manager_arg;
+ keypar= *key_par_arg;
+
+ KEY *key_info= &file->get_table()->key_info[file->active_index];
+ keypar.index_ranges_unique= test(key_info->flags & HA_NOSAME &&
+ key_info->key_parts ==
+ my_count_bits(keypar.key_tuple_map));
+
+ mrr_iter= seq_funcs->init(seq_init_param, n_ranges, mode);
+ is_mrr_assoc= !test(mode & HA_MRR_NO_ASSOCIATION);
+ mrr_funcs= *seq_funcs;
+ source_exhausted= FALSE;
+ if (support_scan_interruptions)
+ bzero(saved_key_tuple, keypar.key_tuple_length);
+ have_saved_rowid= FALSE;
+ return 0;
+}
+
+
+static int rowid_cmp_reverse(void *file, uchar *a, uchar *b)
+{
+ return - ((handler*)file)->cmp_ref(a, b);
+}
+
+
+int Mrr_ordered_rndpos_reader::init(handler *h_arg,
+ Mrr_index_reader *index_reader_arg,
+ uint mode,
+ Lifo_buffer *buf)
+{
+ file= h_arg;
+ index_reader= index_reader_arg;
+ rowid_buffer= buf;
+ is_mrr_assoc= !test(mode & HA_MRR_NO_ASSOCIATION);
+ index_reader_exhausted= FALSE;
+ index_reader_needs_refill= TRUE;
+ return 0;
+}
+
+
+/**
+ 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.
+
+ @retval 0 OK, the next portion of rowids is in the buffer,
+ properly ordered
+ @retval other Error
+*/
+
+int Mrr_ordered_rndpos_reader::refill_buffer(bool initial)
+{
+ int res;
+ DBUG_ENTER("Mrr_ordered_rndpos_reader::refill_buffer");
+
+ if (index_reader_exhausted)
+ DBUG_RETURN(HA_ERR_END_OF_FILE);
+
+ while (initial || index_reader_needs_refill ||
+ (res= refill_from_index_reader()) == HA_ERR_END_OF_FILE)
+ {
+ if ((res= index_reader->refill_buffer(initial)))
+ {
+ if (res == HA_ERR_END_OF_FILE)
+ index_reader_exhausted= TRUE;
+ break;
+ }
+ initial= FALSE;
+ index_reader_needs_refill= FALSE;
+ }
+ DBUG_RETURN(res);
+}
+
+
+void Mrr_index_reader::position()
+{
+ file->position(file->get_table()->record[0]);
+}
+
+
+/*
+ @brief Try to refill the rowid buffer without calling
+ index_reader->refill_buffer().
+*/
+
+int Mrr_ordered_rndpos_reader::refill_from_index_reader()
+{
+ range_id_t range_info;
+ int res;
+ DBUG_ENTER("Mrr_ordered_rndpos_reader::refill_from_index_reader");
+
+ DBUG_ASSERT(rowid_buffer->is_empty());
+ index_rowid= index_reader->get_rowid_ptr();
+ rowid_buffer->reset();
+ rowid_buffer->setup_writing(file->ref_length,
+ is_mrr_assoc? sizeof(range_id_t) : 0);
+
+ last_identical_rowid= NULL;
+
+ index_reader->resume_read();
+ while (rowid_buffer->can_write())
+ {
+ res= index_reader->get_next(&range_info);
+
+ if (res)
+ {
+ if (res != HA_ERR_END_OF_FILE)
+ DBUG_RETURN(res);
+ index_reader_needs_refill=TRUE;
+ break;
+ }
+
+ index_reader->position();
+
+ /* Put rowid, or {rowid, range_id} pair into the buffer */
+ rowid_buffer->write_ptr1= index_rowid;
+ rowid_buffer->write_ptr2= (uchar*)&range_info;
+ rowid_buffer->write();
+ }
+
+ index_reader->interrupt_read();
+ /* Sort the buffer contents by rowid */
+ rowid_buffer->sort((qsort2_cmp)rowid_cmp_reverse, (void*)file);
+
+ rowid_buffer->setup_reading(file->ref_length,
+ is_mrr_assoc ? sizeof(range_id_t) : 0);
+ DBUG_RETURN(rowid_buffer->is_empty()? HA_ERR_END_OF_FILE : 0);
+}
+
+
+/*
+ Get the next {record, range_id} using ordered array of rowid+range_id pairs
+
+ @note
+ Since we have sorted rowids, we try not to make multiple rnd_pos() calls
+ with the same rowid value.
+*/
+
+int Mrr_ordered_rndpos_reader::get_next(range_id_t *range_info)
+{
+ int res;
+
+ /*
+ First, check if rowid buffer has elements with the same rowid value as
+ the previous.
+ */
+ 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.
+ */
+ (void)rowid_buffer->read();
+
+ if (rowid_buffer->read_ptr1 == last_identical_rowid)
+ last_identical_rowid= NULL; /* reached the last of identical rowids */
+
+ if (!is_mrr_assoc)
+ return 0;
+
+ memcpy(range_info, rowid_buffer->read_ptr2, sizeof(range_id_t));
+ if (!index_reader->skip_record(*range_info, rowid_buffer->read_ptr1))
+ return 0;
+ }
+
+ /*
+ Ok, last_identical_rowid==NULL, it's time to read next different rowid
+ value and get record for it.
+ */
+ for(;;)
+ {
+ /* 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, rowid_buffer->read_ptr2, sizeof(range_id_t));
+ if (index_reader->skip_record(*range_info, rowid_buffer->read_ptr1))
+ continue;
+ }
+
+ res= file->ha_rnd_pos(file->get_table()->record[0],
+ rowid_buffer->read_ptr1);
+
+ if (res == HA_ERR_RECORD_DELETED)
+ {
+ /* not likely to get this code with current storage engines, but still */
+ continue;
+ }
+
+ if (res)
+ return res; /* Some fatal error */
+
+ break; /* Got another record */
+ }
+
+ /*
+ 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.
+
+ 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())
+ {
+ if (file->cmp_ref(it.read_ptr1, rowid_buffer->read_ptr1))
+ break;
+ last_identical_rowid= it.read_ptr1;
+ }
+ return 0;
+}
+
+
+/****************************************************************************
+ * Top-level DS-MRR implementation functions (the ones called by storage engine)
***************************************************************************/
/**
@@ -286,7 +767,7 @@ scan_it_again:
Initialize and start the MRR scan. Depending on the mode parameter, this
may use default or DS-MRR implementation.
- @param h Table handler to be used
+ @param h_arg Table handler to be used
@param key Index to be used
@param seq_funcs Interval sequence enumeration functions
@param seq_init_param Interval sequence enumeration parameter
@@ -302,279 +783,591 @@ 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)
{
- uint elem_size;
- Item *pushed_cond= NULL;
- handler *new_h2= 0;
+ THD *thd= current_thd;
+ int res;
+ Key_parameters keypar;
+ uint key_buff_elem_size;
+ handler *h_idx;
+ Mrr_ordered_rndpos_reader *disk_strategy= NULL;
+ bool do_sort_keys= FALSE;
DBUG_ENTER("DsMrr_impl::dsmrr_init");
-
+ LINT_INIT(key_buff_elem_size); /* set/used when do_sort_keys==TRUE */
/*
index_merge may invoke a scan on an object for which dsmrr_info[_const]
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)
+ primary_file= h_arg;
+ is_mrr_assoc= !test(mode & HA_MRR_NO_ASSOCIATION);
+
+ strategy_exhausted= FALSE;
+
+ /* By default, have do-nothing buffer manager */
+ buf_manager.arg= this;
+ buf_manager.reset_buffer_sizes= do_nothing;
+ buf_manager.redistribute_buffer_space= do_nothing;
+
+ if (mode & (HA_MRR_USE_DEFAULT_IMPL | HA_MRR_SORTED))
+ goto use_default_impl;
+
+ /*
+ Determine whether we'll need to do key sorting and/or rnd_pos() scan
+ */
+ index_strategy= NULL;
+ if ((mode & HA_MRR_SINGLE_POINT) &&
+ optimizer_flag(thd, OPTIMIZER_SWITCH_MRR_SORT_KEYS))
{
- use_default_impl= TRUE;
- const int retval=
- h->handler::multi_range_read_init(seq_funcs, seq_init_param,
- n_ranges, mode, buf);
- DBUG_RETURN(retval);
+ do_sort_keys= TRUE;
+ index_strategy= &reader_factory.ordered_index_reader;
}
- rowids_buf= buf->buffer;
+ else
+ index_strategy= &reader_factory.simple_index_reader;
- is_mrr_assoc= !test(mode & HA_MRR_NO_ASSOCIATION);
+ 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(primary_file->inited == handler::INDEX ||
+ (primary_file->inited == handler::RND &&
+ secondary_file &&
+ secondary_file->inited == handler::INDEX));
+
+ h_idx= (primary_file->inited == handler::INDEX)? primary_file: secondary_file;
+ keyno= h_idx->active_index;
+
+ if (!(keyno == table->s->primary_key && h_idx->primary_key_is_clustered()))
+ {
+ strategy= disk_strategy= &reader_factory.ordered_rndpos_reader;
+ }
if (is_mrr_assoc)
- status_var_increment(table->in_use->status_var.ha_multi_range_read_init_count);
-
- rowids_buf_end= buf->buffer_end;
- elem_size= h->ref_length + (int)is_mrr_assoc * sizeof(void*);
- rowids_buf_last= rowids_buf +
- ((rowids_buf_end - rowids_buf)/ elem_size)*
- elem_size;
- rowids_buf_end= rowids_buf_last;
+ status_var_increment(thd->status_var.ha_multi_range_read_init_count);
- /*
- 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.
+ full_buf= buf->buffer;
+ full_buf_end= buf->buffer_end;
+
+ if (do_sort_keys)
+ {
+ /* Pre-calculate some parameters of key sorting */
+ keypar.use_key_pointers= test(mode & HA_MRR_MATERIALIZED_KEYS);
+ seq_funcs->get_key_info(seq_init_param, &keypar.key_tuple_length,
+ &keypar.key_tuple_map);
+ keypar.key_size_in_keybuf= keypar.use_key_pointers?
+ sizeof(char*) : keypar.key_tuple_length;
+ key_buff_elem_size= keypar.key_size_in_keybuf + (int)is_mrr_assoc * sizeof(void*);
+
+ /* Ordered index reader needs some space to store an index tuple */
+ if (strategy != index_strategy)
+ {
+ uint saved_pk_length=0;
+ if (h_idx->primary_key_is_clustered())
+ {
+ uint pk= h_idx->get_table()->s->primary_key;
+ saved_pk_length= h_idx->get_table()->key_info[pk].key_length;
+ }
+
+ if (reader_factory.ordered_index_reader.
+ set_interruption_temp_buffer(primary_file->ref_length,
+ keypar.key_tuple_length,
+ saved_pk_length,
+ &full_buf, full_buf_end))
+ goto use_default_impl;
+ }
+ else
+ reader_factory.ordered_index_reader.set_no_interruption_temp_buffer();
+ }
+
+ if (strategy == index_strategy)
+ {
+ /*
+ Index strategy alone handles the record retrieval. Give all buffer space
+ to it. Key buffer should have forward orientation so we can return the
+ end of it.
+ */
+ key_buffer= &forward_key_buf;
+ key_buffer->set_buffer_space(full_buf, full_buf_end);
+
+ /* Safety: specify that rowid buffer has zero size: */
+ rowid_buffer.set_buffer_space(full_buf_end, full_buf_end);
+
+ if (do_sort_keys && !key_buffer->have_space_for(key_buff_elem_size))
+ goto use_default_impl;
+
+ if ((res= index_strategy->init(primary_file, seq_funcs, seq_init_param, n_ranges,
+ mode, &keypar, key_buffer, &buf_manager)))
+ goto error;
+ }
+ else
+ {
+ /* We'll have both index and rndpos strategies working together */
+ if (do_sort_keys)
+ {
+ /* Both strategies will need buffer space, share the buffer */
+ if (setup_buffer_sharing(keypar.key_size_in_keybuf, keypar.key_tuple_map))
+ goto use_default_impl;
+
+ buf_manager.reset_buffer_sizes= reset_buffer_sizes;
+ buf_manager.redistribute_buffer_space= redistribute_buffer_space;
+ }
+ else
+ {
+ /* index strategy doesn't need buffer, give all space to rowids*/
+ rowid_buffer.set_buffer_space(full_buf, full_buf_end);
+ if (!rowid_buffer.have_space_for(primary_file->ref_length +
+ (int)is_mrr_assoc * sizeof(range_id_t)))
+ goto use_default_impl;
+ }
+
+ if ((res= setup_two_handlers()))
+ goto error;
+
+ if ((res= index_strategy->init(secondary_file, seq_funcs, seq_init_param,
+ n_ranges, mode, &keypar, key_buffer,
+ &buf_manager)) ||
+ (res= disk_strategy->init(primary_file, index_strategy, mode,
+ &rowid_buffer)))
+ {
+ goto error;
+ }
+ }
+
+ res= strategy->refill_buffer(TRUE);
+ if (res)
+ {
+ if (res != HA_ERR_END_OF_FILE)
+ goto error;
+ strategy_exhausted= TRUE;
+ }
+
+ /*
+ If we have scanned through all intervals in *seq, then adjust *buf to
+ indicate that the remaining buffer space will not be used.
*/
- if (!h2)
+// if (dsmrr_eof)
+// buf->end_of_used_area= rowid_buffer.end_of_space();
+
+
+ DBUG_RETURN(0);
+error:
+ close_second_handler();
+ /* Safety, not really needed but: */
+ strategy= NULL;
+ DBUG_RETURN(res);
+
+use_default_impl:
+ if (primary_file->inited != handler::INDEX)
{
- /* Create a separate handler object to do rndpos() calls. */
- THD *thd= current_thd;
+ /* We can get here when
+ - we've previously successfully done a DS-MRR scan (and so have
+ secondary_file!= NULL, secondary_file->inited= INDEX,
+ primary_file->inited=RND)
+ - for this invocation, we haven't got enough buffer space, and so we
+ have to use the default MRR implementation.
+
+ note: primary_file->ha_index_end() will call dsmrr_close() which will
+ close/destroy the secondary_file, this is intentional.
+ (Yes this is slow, but one can't expect performance with join buffer
+ so small that it can accomodate one rowid and one index tuple)
+ */
+ if ((res= primary_file->ha_rnd_end()) ||
+ (res= primary_file->ha_index_init(keyno, test(mode & HA_MRR_SORTED))))
+ {
+ DBUG_RETURN(res);
+ }
+ }
+ /* Call correct init function and assign to top level object */
+ Mrr_simple_index_reader *s= &reader_factory.simple_index_reader;
+ res= s->init(primary_file, seq_funcs, seq_init_param, n_ranges, mode, NULL,
+ NULL, NULL);
+ strategy= s;
+ DBUG_RETURN(res);
+}
+
+
+/*
+ Whatever the current state is, make it so that we have two handler objects:
+ - primary_file - initialized for rnd_pos() scan
+ - secondary_file - 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= primary_file->get_table()->in_use;
+ DBUG_ENTER("DsMrr_impl::setup_two_handlers");
+ if (!secondary_file)
+ {
+ handler *new_h2;
+ Item *pushed_cond= NULL;
+ DBUG_ASSERT(primary_file->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.
The constant 5 is an empiric result.
*/
if (check_stack_overrun(thd, 5*STACK_MIN_SIZE, (uchar*) &new_h2))
DBUG_RETURN(1);
- DBUG_ASSERT(h->active_index != MAX_KEY);
- uint mrr_keyno= h->active_index;
- /* Create a separate handler object to do rndpos() calls. */
- if (!(new_h2= h->clone(h->table->s->normalized_path.str, thd->mem_root)) ||
+ /* Create a separate handler object to do rnd_pos() calls. */
+ if (!(new_h2= primary_file->clone(primary_file->get_table()->s->
+ normalized_path.str,
+ thd->mem_root)) ||
new_h2->ha_external_lock(thd, F_RDLCK))
{
delete new_h2;
DBUG_RETURN(1);
}
- if (mrr_keyno == h->pushed_idx_cond_keyno)
- pushed_cond= h->pushed_idx_cond;
-
+ if (keyno == primary_file->pushed_idx_cond_keyno)
+ pushed_cond= primary_file->pushed_idx_cond;
+
+ Mrr_reader *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->secondary_file or it
+ will delete it. Also, save the picked strategy
*/
- if (h->ha_index_end())
- {
- h2=new_h2;
+ res= primary_file->ha_index_end();
+
+ strategy= save_strategy;
+ secondary_file= new_h2;
+
+ if (res || (res= (primary_file->ha_rnd_init(FALSE))))
goto error;
- }
- h2= new_h2; /* Ok, now can put it into h2 */
table->prepare_for_position();
- h2->extra(HA_EXTRA_KEYREAD);
-
- if (h2->ha_index_init(mrr_keyno, FALSE))
+ secondary_file->extra(HA_EXTRA_KEYREAD);
+ secondary_file->mrr_iter= primary_file->mrr_iter;
+
+ if ((res= secondary_file->ha_index_init(keyno, FALSE)))
goto error;
- use_default_impl= FALSE;
if (pushed_cond)
- h2->idx_cond_push(mrr_keyno, pushed_cond);
+ secondary_file->idx_cond_push(keyno, pushed_cond);
}
else
{
+ DBUG_ASSERT(secondary_file && secondary_file->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
+ Calling primary_file->index_end() will invoke dsmrr_close() for this object,
+ which will delete secondary_file. We need to keep it, so 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)
+ if (primary_file->inited == handler::INDEX)
+ {
+ handler *save_h2= secondary_file;
+ Mrr_reader *save_strategy= strategy;
+ secondary_file= NULL;
+ strategy= NULL;
+ res= primary_file->ha_index_end();
+ secondary_file= save_h2;
+ strategy= save_strategy;
+ if (res)
+ goto error;
+ }
+ if ((primary_file->inited != handler::RND) &&
+ (res= primary_file->ha_rnd_init(FALSE)))
goto error;
}
+ DBUG_RETURN(0);
- if (h2->handler::multi_range_read_init(seq_funcs, seq_init_param, n_ranges,
- mode, buf) ||
- dsmrr_fill_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= rowids_buf_last;
+error:
+ DBUG_RETURN(res);
+}
- /*
- 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;
- use_default_impl= FALSE;
- 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::close_second_handler()
+{
+ if (secondary_file)
+ {
+ secondary_file->ha_index_or_rnd_end();
+ secondary_file->ha_external_lock(current_thd, F_UNLCK);
+ secondary_file->ha_close();
+ delete secondary_file;
+ secondary_file= NULL;
+ }
}
void DsMrr_impl::dsmrr_close()
{
DBUG_ENTER("DsMrr_impl::dsmrr_close");
- if (h2)
+ close_second_handler();
+ strategy= NULL;
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ my_qsort2-compatible static member function to compare key tuples
+*/
+
+int Mrr_ordered_index_reader::compare_keys(void* arg, uchar* key1_arg,
+ uchar* key2_arg)
+{
+ Mrr_ordered_index_reader *reader= (Mrr_ordered_index_reader*)arg;
+ TABLE *table= reader->file->get_table();
+ KEY_PART_INFO *part= table->key_info[reader->file->active_index].key_part;
+ uchar *key1, *key2;
+
+ if (reader->keypar.use_key_pointers)
{
- h2->ha_index_or_rnd_end();
- h2->ha_external_lock(current_thd, F_UNLCK);
- h2->close();
- delete h2;
- h2= NULL;
+ /* the buffer stores pointers to keys, get to the keys */
+ memcpy(&key1, key1_arg, sizeof(char*));
+ memcpy(&key2, key2_arg, sizeof(char*));
}
- use_default_impl= TRUE;
- DBUG_VOID_RETURN;
+ else
+ {
+ key1= key1_arg;
+ key2= key2_arg;
+ }
+
+ return key_tuple_cmp(part, key1, key2, reader->keypar.key_tuple_length);
}
-static int rowid_cmp(void *h, uchar *a, uchar *b)
+int Mrr_ordered_index_reader::compare_keys_reverse(void* arg, uchar* key1,
+ uchar* key2)
{
- return ((handler*)h)->cmp_ref(a, b);
+ return -compare_keys(arg, key1, key2);
}
/**
- DS-MRR: Fill the buffer with rowids and sort it by rowid
+ Set the buffer space to be shared between rowid and key buffer
+
+ @return FALSE ok
+ @return TRUE There is so little buffer space that we won't be able to use
+ the strategy.
+ This happens when we don't have enough space for one rowid
+ element and one key element so this is mainly targeted at
+ testing.
+*/
- {This is an internal function of DiskSweep MRR implementation}
- 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.
+bool DsMrr_impl::setup_buffer_sharing(uint key_size_in_keybuf,
+ key_part_map key_tuple_map)
+{
+ long key_buff_elem_size= key_size_in_keybuf +
+ (int)is_mrr_assoc * sizeof(range_id_t);
- The function assumes that rowids buffer is empty when it is invoked.
+ KEY *key_info= &primary_file->get_table()->key_info[keyno];
+ /*
+ Ok if we got here we need to allocate one part of the buffer
+ for keys and another part for rowids.
+ */
+ ulonglong rowid_buf_elem_size= primary_file->ref_length +
+ (int)is_mrr_assoc * sizeof(range_id_t);
- @param h Table handler
+ /*
+ Use rec_per_key statistics as a basis to find out how many rowids
+ we'll get for each key value.
+ TODO: what should be the default value to use when there is no
+ statistics?
+ */
+ uint parts= my_count_bits(key_tuple_map);
+ ulong rpc;
+ ulonglong rowids_size= rowid_buf_elem_size;
+ if ((rpc= key_info->rec_per_key[parts - 1]))
+ rowids_size= rowid_buf_elem_size * rpc;
+
+ double fraction_for_rowids=
+ (ulonglong2double(rowids_size) /
+ (ulonglong2double(rowids_size) + key_buff_elem_size));
+
+ ptrdiff_t bytes_for_rowids=
+ (ptrdiff_t)floor(0.5 + fraction_for_rowids * (full_buf_end - full_buf));
+
+ ptrdiff_t bytes_for_keys= (full_buf_end - full_buf) - bytes_for_rowids;
- @retval 0 OK, the next portion of rowids is in the buffer,
- properly ordered
- @retval other Error
-*/
+ if (bytes_for_keys < key_buff_elem_size + 1)
+ {
+ ptrdiff_t add= key_buff_elem_size + 1 - bytes_for_keys;
+ bytes_for_keys= key_buff_elem_size + 1;
+ bytes_for_rowids -= add;
+ }
+
+ if (bytes_for_rowids < (ptrdiff_t)rowid_buf_elem_size + 1)
+ {
+ ptrdiff_t add= (ptrdiff_t)(rowid_buf_elem_size + 1 - bytes_for_rowids);
+ bytes_for_rowids= (ptrdiff_t)rowid_buf_elem_size + 1;
+ bytes_for_keys -= add;
+ }
+
+ rowid_buffer_end= full_buf + bytes_for_rowids;
+ 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);
-int DsMrr_impl::dsmrr_fill_buffer()
+ if (!key_buffer->have_space_for(key_buff_elem_size) ||
+ !rowid_buffer.have_space_for((size_t)rowid_buf_elem_size))
+ return TRUE; /* Failed to provide minimum space for one of the buffers */
+
+ return FALSE;
+}
+
+
+void DsMrr_impl::do_nothing(void *dsmrr_arg)
{
- char *range_info;
- int res;
- DBUG_ENTER("DsMrr_impl::dsmrr_fill_buffer");
+ /* Do nothing */
+}
- rowids_buf_cur= rowids_buf;
- while ((rowids_buf_cur < rowids_buf_end) &&
- !(res= h2->handler::multi_range_read_next(&range_info)))
- {
- KEY_MULTI_RANGE *curr_range= &h2->handler::mrr_cur_range;
- if (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]);
- memcpy(rowids_buf_cur, h2->ref, h2->ref_length);
- rowids_buf_cur += h2->ref_length;
- if (is_mrr_assoc)
- {
- memcpy(rowids_buf_cur, &range_info, sizeof(void*));
- rowids_buf_cur += sizeof(void*);
- }
- }
+void DsMrr_impl::reset_buffer_sizes(void *dsmrr_arg)
+{
+ DsMrr_impl *dsmrr= (DsMrr_impl*)dsmrr_arg;
+ dsmrr->rowid_buffer.set_buffer_space(dsmrr->full_buf,
+ dsmrr->rowid_buffer_end);
+ dsmrr->key_buffer->set_buffer_space(dsmrr->rowid_buffer_end,
+ dsmrr->full_buf_end);
+}
- if (res && res != HA_ERR_END_OF_FILE)
- DBUG_RETURN(res);
- dsmrr_eof= test(res == HA_ERR_END_OF_FILE);
- /* Sort the buffer contents by rowid */
- uint elem_size= h->ref_length + (int)is_mrr_assoc * sizeof(void*);
- uint n_rowids= (rowids_buf_cur - rowids_buf) / elem_size;
-
- my_qsort2(rowids_buf, n_rowids, elem_size, (qsort2_cmp)rowid_cmp,
- (void*)h);
- rowids_buf_last= rowids_buf_cur;
- rowids_buf_cur= rowids_buf;
- DBUG_RETURN(0);
+/*
+ Take unused space from the key buffer and give it to the rowid buffer
+*/
+
+void DsMrr_impl::redistribute_buffer_space(void *dsmrr_arg)
+{
+ DsMrr_impl *dsmrr= (DsMrr_impl*)dsmrr_arg;
+ uchar *unused_start, *unused_end;
+ dsmrr->key_buffer->remove_unused_space(&unused_start, &unused_end);
+ dsmrr->rowid_buffer.grow(unused_start, unused_end);
}
-/**
- DS-MRR implementation: multi_range_read_next() function
+/*
+ @brief Initialize the iterator
+
+ @note
+ Initialize the iterator to produce matches for the key of the first element
+ in owner_arg->key_buffer
+
+ @retval 0 OK
+ @retval HA_ERR_END_OF_FILE Either the owner->key_buffer is empty or
+ no matches for the key we've tried (check
+ key_buffer->is_empty() to tell these apart)
+ @retval other code Fatal error
*/
-int DsMrr_impl::dsmrr_next(char **range_info)
+int Key_value_records_iterator::init(Mrr_ordered_index_reader *owner_arg)
{
int res;
- uchar *cur_range_info= 0;
- uchar *rowid;
+ owner= owner_arg;
+
+ identical_key_it.init(owner->key_buffer);
+ owner->key_buffer->setup_reading(owner->keypar.key_size_in_keybuf,
+ owner->is_mrr_assoc ? sizeof(void*) : 0);
+
+ if (identical_key_it.read())
+ return HA_ERR_END_OF_FILE;
- if (use_default_impl)
- return h->handler::multi_range_read_next(range_info);
+ uchar *key_in_buf= last_identical_key_ptr= identical_key_it.read_ptr1;
+
+ uchar *index_tuple= key_in_buf;
+ if (owner->keypar.use_key_pointers)
+ memcpy(&index_tuple, key_in_buf, sizeof(char*));
- do
+ /* Check out how many more identical keys are following */
+ while (!identical_key_it.read())
+ {
+ if (Mrr_ordered_index_reader::compare_keys(owner, key_in_buf,
+ identical_key_it.read_ptr1))
+ break;
+ last_identical_key_ptr= identical_key_it.read_ptr1;
+ }
+ identical_key_it.init(owner->key_buffer);
+ res= owner->file->ha_index_read_map(owner->file->get_table()->record[0],
+ index_tuple,
+ owner->keypar.key_tuple_map,
+ HA_READ_KEY_EXACT);
+
+ if (res)
+ {
+ /* Failed to find any matching records */
+ move_to_next_key_value();
+ return res;
+ }
+ owner->have_saved_rowid= FALSE;
+ get_next_row= FALSE;
+ return 0;
+}
+
+
+int Key_value_records_iterator::get_next(range_id_t *range_info)
+{
+ int res;
+
+ if (get_next_row)
{
- if (rowids_buf_cur == rowids_buf_last)
+ if (owner->keypar.index_ranges_unique)
{
- if (dsmrr_eof)
- {
- res= HA_ERR_END_OF_FILE;
- goto end;
- }
- res= dsmrr_fill_buffer();
- if (res)
- goto end;
+ /* We're using a full unique key, no point to call index_next_same */
+ return HA_ERR_END_OF_FILE;
}
-
- /* return eof if there are no rowids in the buffer after re-fill attempt */
- if (rowids_buf_cur == rowids_buf_last)
+
+ handler *h= owner->file;
+ if ((res= h->ha_index_next_same(h->get_table()->record[0],
+ identical_key_it.read_ptr1,
+ owner->keypar.key_tuple_length)))
{
- res= HA_ERR_END_OF_FILE;
- goto end;
+ /* It's either HA_ERR_END_OF_FILE or some other error */
+ return res;
}
- rowid= rowids_buf_cur;
+ identical_key_it.init(owner->key_buffer);
+ owner->have_saved_rowid= FALSE;
+ get_next_row= FALSE;
+ }
- if (is_mrr_assoc)
- memcpy(&cur_range_info, rowids_buf_cur + h->ref_length, sizeof(uchar**));
+ identical_key_it.read(); /* This gets us next range_id */
+ memcpy(range_info, identical_key_it.read_ptr2, sizeof(range_id_t));
- rowids_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);
- break;
- } while (true);
-
- if (is_mrr_assoc)
+ if (!last_identical_key_ptr ||
+ (identical_key_it.read_ptr1 == last_identical_key_ptr))
{
- memcpy(range_info, rowid + h->ref_length, sizeof(void*));
+ /*
+ We've reached the last of the identical keys that current record is a
+ match for. Set get_next_row=TRUE so that we read the next index record
+ on the next call to this function.
+ */
+ get_next_row= TRUE;
+ }
+ return 0;
+}
+
+
+void Key_value_records_iterator::move_to_next_key_value()
+{
+ while (!owner->key_buffer->read() &&
+ (owner->key_buffer->read_ptr1 != last_identical_key_ptr)) {}
+}
+
+
+/**
+ DS-MRR implementation: multi_range_read_next() function.
+
+ Calling convention is like multi_range_read_next() has.
+*/
+
+int DsMrr_impl::dsmrr_next(range_id_t *range_info)
+{
+ int res;
+ if (strategy_exhausted)
+ return HA_ERR_END_OF_FILE;
+
+ while ((res= strategy->get_next(range_info)) == HA_ERR_END_OF_FILE)
+ {
+ if ((res= strategy->refill_buffer(FALSE)))
+ break; /* EOF or error */
}
-end:
return res;
}
@@ -582,7 +1375,8 @@ end:
/**
DS-MRR implementation: multi_range_read_info() function
*/
-ha_rows DsMrr_impl::dsmrr_info(uint keyno, uint n_ranges, uint rows,
+ha_rows DsMrr_impl::dsmrr_info(uint keyno, uint n_ranges, uint rows,
+ uint key_parts,
uint *bufsz, uint *flags, COST_VECT *cost)
{
ha_rows res;
@@ -590,12 +1384,13 @@ ha_rows DsMrr_impl::dsmrr_info(uint keyno, uint n_ranges, uint rows,
uint def_bufsz= *bufsz;
/* Get cost/flags/mem_usage of default MRR implementation */
- res= h->handler::multi_range_read_info(keyno, n_ranges, rows, &def_bufsz,
- &def_flags, cost);
+ res= primary_file->handler::multi_range_read_info(keyno, n_ranges, rows,
+ key_parts, &def_bufsz,
+ &def_flags, cost);
DBUG_ASSERT(!res);
if ((*flags & HA_MRR_USE_DEFAULT_IMPL) ||
- choose_mrr_impl(keyno, rows, &def_flags, &def_bufsz, cost))
+ choose_mrr_impl(keyno, rows, flags, bufsz, cost))
{
/* Default implementation is choosen */
DBUG_PRINT("info", ("Default MRR implementation choosen"));
@@ -623,9 +1418,11 @@ ha_rows DsMrr_impl::dsmrr_info_const(uint keyno, RANGE_SEQ_IF *seq,
uint def_flags= *flags;
uint def_bufsz= *bufsz;
/* Get cost/flags/mem_usage of default MRR implementation */
- rows= h->handler::multi_range_read_info_const(keyno, seq, seq_init_param,
- n_ranges, &def_bufsz,
- &def_flags, cost);
+ rows= primary_file->handler::multi_range_read_info_const(keyno, seq,
+ seq_init_param,
+ n_ranges,
+ &def_bufsz,
+ &def_flags, cost);
if (rows == HA_POS_ERROR)
{
/* Default implementation can't perform MRR scan => we can't either */
@@ -635,7 +1432,7 @@ ha_rows DsMrr_impl::dsmrr_info_const(uint keyno, RANGE_SEQ_IF *seq,
/*
If HA_MRR_USE_DEFAULT_IMPL has been passed to us, that is an order to
use the default MRR implementation (we need it for UPDATE/DELETE).
- Otherwise, make a choice based on cost and @@optimizer_use_mrr.
+ Otherwise, make a choice based on cost and @@optimizer_switch settings
*/
if ((*flags & HA_MRR_USE_DEFAULT_IMPL) ||
choose_mrr_impl(keyno, rows, flags, bufsz, cost))
@@ -683,7 +1480,28 @@ bool key_uses_partial_cols(TABLE *table, uint keyno)
return FALSE;
}
-/**
+
+/*
+ Check if key/flags allow DS-MRR/CPK strategy to be used
+
+ @param thd
+ @param keyno Index that will be used
+ @param mrr_flags
+
+ @retval TRUE DS-MRR/CPK should be used
+ @retval FALSE Otherwise
+*/
+
+bool DsMrr_impl::check_cpk_scan(THD *thd, uint keyno, uint mrr_flags)
+{
+ return test((mrr_flags & HA_MRR_SINGLE_POINT) &&
+ keyno == table->s->primary_key &&
+ primary_file->primary_key_is_clustered() &&
+ optimizer_flag(thd, OPTIMIZER_SWITCH_MRR_SORT_KEYS));
+}
+
+
+/*
DS-MRR Internals: Choose between Default MRR implementation and DS-MRR
Make the choice between using Default MRR implementation and DS-MRR.
@@ -706,22 +1524,29 @@ bool key_uses_partial_cols(TABLE *table, uint keyno)
@retval FALSE DS-MRR implementation should be used
*/
+
bool DsMrr_impl::choose_mrr_impl(uint keyno, ha_rows rows, uint *flags,
uint *bufsz, COST_VECT *cost)
{
COST_VECT dsmrr_cost;
bool res;
THD *thd= current_thd;
- if (thd->variables.optimizer_use_mrr == 2 || *flags & HA_MRR_INDEX_ONLY ||
- (keyno == table->s->primary_key && h->primary_key_is_clustered()) ||
- key_uses_partial_cols(table, keyno))
+
+ bool doing_cpk_scan= check_cpk_scan(thd, keyno, *flags);
+ bool using_cpk= test(keyno == table->s->primary_key &&
+ primary_file->primary_key_is_clustered());
+ *flags &= ~HA_MRR_IMPLEMENTATION_FLAGS;
+ if (!optimizer_flag(thd, OPTIMIZER_SWITCH_MRR) ||
+ *flags & HA_MRR_INDEX_ONLY ||
+ (using_cpk && !doing_cpk_scan) || key_uses_partial_cols(table, keyno))
{
/* Use the default implementation */
*flags |= HA_MRR_USE_DEFAULT_IMPL;
+ *flags &= ~HA_MRR_IMPLEMENTATION_FLAGS;
return TRUE;
}
-
- uint add_len= table->key_info[keyno].key_length + h->ref_length;
+
+ uint add_len= table->key_info[keyno].key_length + primary_file->ref_length;
*bufsz -= add_len;
if (get_disk_sweep_mrr_cost(keyno, rows, *flags, bufsz, &dsmrr_cost))
return TRUE;
@@ -729,12 +1554,12 @@ bool DsMrr_impl::choose_mrr_impl(uint keyno, ha_rows rows, uint *flags,
bool force_dsmrr;
/*
- If @@optimizer_use_mrr==force, then set cost of DS-MRR to be minimum of
+ If mrr_cost_based flag is not set, then set cost of DS-MRR to be minimum of
DS-MRR and Default implementations cost. This allows one to force use of
DS-MRR whenever it is applicable without affecting other cost-based
choices.
*/
- if ((force_dsmrr= (thd->variables.optimizer_use_mrr == 1)) &&
+ if ((force_dsmrr= !optimizer_flag(thd, OPTIMIZER_SWITCH_MRR_COST_BASED)) &&
dsmrr_cost.total_cost() > cost->total_cost())
dsmrr_cost= *cost;
@@ -744,6 +1569,25 @@ 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 ((using_cpk && doing_cpk_scan) ||
+ (optimizer_flag(thd, OPTIMIZER_SWITCH_MRR_SORT_KEYS) &&
+ *flags & HA_MRR_SINGLE_POINT))
+ {
+ *flags |= DSMRR_IMPL_SORT_KEYS;
+ }
+
+ if (!(using_cpk && doing_cpk_scan) &&
+ !(*flags & HA_MRR_INDEX_ONLY))
+ {
+ *flags |= DSMRR_IMPL_SORT_ROWIDS;
+ }
+ /*
+ if ((*flags & HA_MRR_SINGLE_POINT) &&
+ optimizer_flag(thd, OPTIMIZER_SWITCH_MRR_SORT_KEYS))
+ *flags |= HA_MRR_MATERIALIZED_KEYS;
+ */
}
else
{
@@ -753,6 +1597,38 @@ bool DsMrr_impl::choose_mrr_impl(uint keyno, ha_rows rows, uint *flags,
return res;
}
+/*
+ Take the flags we've returned previously and print one of
+ - Key-ordered scan
+ - Rowid-ordered scan
+ - Key-ordered Rowid-ordered scan
+*/
+
+int DsMrr_impl::dsmrr_explain_info(uint mrr_mode, char *str, size_t size)
+{
+ const char *key_ordered= "Key-ordered scan";
+ const char *rowid_ordered= "Rowid-ordered scan";
+ const char *both_ordered= "Key-ordered Rowid-ordered scan";
+ const char *used_str="";
+ const uint BOTH_FLAGS= (DSMRR_IMPL_SORT_KEYS | DSMRR_IMPL_SORT_ROWIDS);
+
+ if (!(mrr_mode & HA_MRR_USE_DEFAULT_IMPL))
+ {
+ if ((mrr_mode & BOTH_FLAGS) == BOTH_FLAGS)
+ used_str= both_ordered;
+ else if (mrr_mode & DSMRR_IMPL_SORT_KEYS)
+ used_str= key_ordered;
+ else if (mrr_mode & DSMRR_IMPL_SORT_ROWIDS)
+ used_str= rowid_ordered;
+
+ uint used_str_len= strlen(used_str);
+ uint copy_len= min(used_str_len, size);
+ memcpy(str, used_str, size);
+ return copy_len;
+ }
+ return 0;
+}
+
static void get_sort_and_sweep_cost(TABLE *table, ha_rows nrows, COST_VECT *cost);
@@ -779,7 +1655,8 @@ bool DsMrr_impl::get_disk_sweep_mrr_cost(uint keynr, ha_rows rows, uint flags,
uint n_full_steps;
double index_read_cost;
- elem_size= h->ref_length + sizeof(void*) * (!test(flags & HA_MRR_NO_ASSOCIATION));
+ elem_size= primary_file->ref_length +
+ sizeof(void*) * (!test(flags & HA_MRR_NO_ASSOCIATION));
max_buff_entries = *buffer_size / elem_size;
if (!max_buff_entries)
@@ -807,7 +1684,7 @@ bool DsMrr_impl::get_disk_sweep_mrr_cost(uint keynr, ha_rows rows, uint flags,
cost->zero();
*buffer_size= max(*buffer_size,
(size_t)(1.2*rows_in_last_step) * elem_size +
- h->ref_length + table->key_info[keynr].key_length);
+ primary_file->ref_length + table->key_info[keynr].key_length);
}
COST_VECT last_step_cost;
@@ -820,7 +1697,7 @@ bool DsMrr_impl::get_disk_sweep_mrr_cost(uint keynr, ha_rows rows, uint flags,
cost->mem_cost= (double)rows_in_last_step * elem_size;
/* Total cost of all index accesses */
- index_read_cost= h->keyread_time(keynr, 1, (double)rows);
+ index_read_cost= primary_file->keyread_time(keynr, 1, rows);
cost->add_io(index_read_cost, 1 /* Random seeks */);
return FALSE;
}
@@ -828,17 +1705,14 @@ bool DsMrr_impl::get_disk_sweep_mrr_cost(uint keynr, ha_rows rows, uint flags,
/*
Get cost of one sort-and-sweep step
+
+ It consists of two parts:
+ - sort an array of #nrows ROWIDs using qsort
+ - read #nrows records from table in a sweep.
- SYNOPSIS
- get_sort_and_sweep_cost()
- table Table being accessed
- nrows Number of rows to be sorted and retrieved
- cost OUT The cost
-
- DESCRIPTION
- Get cost of these operations:
- - sort an array of #nrows ROWIDs using qsort
- - read #nrows records from table in a sweep.
+ @param table Table being accessed
+ @param nrows Number of rows to be sorted and retrieved
+ @param cost OUT The cost of scan
*/
static