Below is the list of changes that have just been committed into a local
5.1 repository of psergey. When psergey does a push these changes will
be propagated to the main repository and, within 24 hours after the
push, to the public repository.
For information on how to access the public repository
see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html
ChangeSet@stripped, 2007-04-04 22:51:51+04:00, sergefp@stripped +50 -0
WL#2475: Manual merge
MERGE: 1.2409.71.24
BitKeeper/triggers/post-commit@stripped, 2007-04-04 22:51:42+04:00, sergefp@stripped +0 -0
WL#2475: Manual merge
MERGE: 1.36.1.1
include/config-win.h@stripped, 2007-04-04 22:51:42+04:00, sergefp@stripped +0 -0
WL#2475: Manual merge
MERGE: 1.97.1.2
include/my_base.h@stripped, 2007-04-04 22:51:42+04:00, sergefp@stripped +0 -0
WL#2475: Manual merge
MERGE: 1.95.1.2
include/myisam.h@stripped, 2007-04-04 22:51:42+04:00, sergefp@stripped +0 -0
WL#2475: Manual merge
MERGE: 1.80.1.1
mysql-test/r/compress.result@stripped, 2007-04-04 22:51:42+04:00, sergefp@stripped +0 -0
WL#2475: Manual merge
MERGE: 1.7.1.1
mysql-test/r/create.result@stripped, 2007-04-04 22:51:42+04:00, sergefp@stripped +0 -0
WL#2475: Manual merge
MERGE: 1.132.1.1
mysql-test/r/explain.result@stripped, 2007-04-04 22:51:42+04:00, sergefp@stripped +0 -0
WL#2475: Manual merge
MERGE: 1.19.1.1
mysql-test/r/func_in.result@stripped, 2007-04-04 22:51:42+04:00, sergefp@stripped +0 -0
WL#2475: Manual merge
MERGE: 1.37.1.1
mysql-test/r/heap.result@stripped, 2007-04-04 22:51:42+04:00, sergefp@stripped +0 -0
WL#2475: Manual merge
MERGE: 1.51.1.1
mysql-test/r/innodb.result@stripped, 2007-04-04 22:51:42+04:00, sergefp@stripped +0 -0
WL#2475: Manual merge
MERGE: 1.185.1.2
mysql-test/r/join.result@stripped, 2007-04-04 22:51:42+04:00, sergefp@stripped +0 -0
WL#2475: Manual merge
MERGE: 1.43.1.1
mysql-test/r/merge.result@stripped, 2007-04-04 22:51:42+04:00, sergefp@stripped +0 -0
WL#2475: Manual merge
MERGE: 1.60.1.1
mysql-test/r/mix2_myisam.result@stripped, 2007-04-04 22:51:42+04:00, sergefp@stripped +0 -0
WL#2475: Manual merge
MERGE: 1.4.1.2
mysql-test/r/myisam.result@stripped, 2007-04-04 22:51:42+04:00, sergefp@stripped +0 -0
WL#2475: Manual merge
MERGE: 1.108.1.1
mysql-test/r/order_by.result@stripped, 2007-04-04 22:51:42+04:00, sergefp@stripped +0 -0
WL#2475: Manual merge
MERGE: 1.61.2.1
mysql-test/r/ps_1general.result@stripped, 2007-04-04 22:51:42+04:00, sergefp@stripped +0 -0
WL#2475: Manual merge
MERGE: 1.63.1.1
mysql-test/r/ps_2myisam.result@stripped, 2007-04-04 22:51:42+04:00, sergefp@stripped +0 -0
WL#2475: Manual merge
MERGE: 1.51.1.1
mysql-test/r/range.result@stripped, 2007-04-04 22:51:43+04:00, sergefp@stripped +0 -0
WL#2475: Manual merge
MERGE: 1.59.1.2
mysql-test/r/select.result@stripped, 2007-04-04 22:51:43+04:00, sergefp@stripped +0 -0
WL#2475: Manual merge
MERGE: 1.153.1.1
mysql-test/r/subselect.result@stripped, 2007-04-04 22:51:43+04:00, sergefp@stripped +0 -0
WL#2475: Manual merge
MERGE: 1.164.2.1
mysql-test/r/subselect3.result@stripped, 2007-04-04 22:51:43+04:00, sergefp@stripped +0 -0
WL#2475: Manual merge
MERGE: 1.6.1.2
mysql-test/r/union.result@stripped, 2007-04-04 22:51:43+04:00, sergefp@stripped +0 -0
WL#2475: Manual merge
MERGE: 1.93.1.1
mysql-test/r/view.result@stripped, 2007-04-04 22:51:43+04:00, sergefp@stripped +0 -0
WL#2475: Manual merge
MERGE: 1.205.2.1
mysql-test/t/innodb.test@stripped, 2007-04-04 22:51:43+04:00, sergefp@stripped +0 -0
WL#2475: Manual merge
MERGE: 1.155.1.1
sql/ha_ndbcluster.cc@stripped, 2007-04-04 22:51:43+04:00, sergefp@stripped +0 -0
WL#2475: Manual merge
MERGE: 1.411.4.2
sql/ha_ndbcluster.h@stripped, 2007-04-04 22:51:43+04:00, sergefp@stripped +0 -0
WL#2475: Manual merge
MERGE: 1.165.1.6
sql/ha_partition.h@stripped, 2007-04-04 22:51:43+04:00, sergefp@stripped +0 -0
WL#2475: Manual merge
MERGE: 1.32.1.2
sql/handler.cc@stripped, 2007-04-04 22:51:43+04:00, sergefp@stripped +4 -3
WL#2475: Manual merge
MERGE: 1.295.4.3
sql/handler.h@stripped, 2007-04-04 22:51:43+04:00, sergefp@stripped +0 -0
WL#2475: Manual merge
MERGE: 1.252.2.2
sql/item_cmpfunc.h@stripped, 2007-04-04 22:51:43+04:00, sergefp@stripped +0 -0
WL#2475: Manual merge
MERGE: 1.151.3.1
sql/key.cc@stripped, 2007-04-04 22:51:43+04:00, sergefp@stripped +0 -0
WL#2475: Manual merge
MERGE: 1.48.1.1
sql/log_event.cc@stripped, 2007-04-04 22:51:43+04:00, sergefp@stripped +0 -0
WL#2475: Manual merge
MERGE: 1.266.5.2
sql/mysql_priv.h@stripped, 2007-04-04 22:51:43+04:00, sergefp@stripped +0 -0
WL#2475: Manual merge
MERGE: 1.470.16.1
sql/mysqld.cc@stripped, 2007-04-04 22:51:43+04:00, sergefp@stripped +0 -0
WL#2475: Manual merge
MERGE: 1.601.22.1
sql/opt_range.cc@stripped, 2007-04-04 22:51:43+04:00, sergefp@stripped +7 -30
WL#2475: Manual merge
MERGE: 1.264.2.3
sql/opt_range.h@stripped, 2007-04-04 22:51:43+04:00, sergefp@stripped +0 -0
WL#2475: Manual merge
MERGE: 1.75.1.2
sql/set_var.cc@stripped, 2007-04-04 22:51:43+04:00, sergefp@stripped +0 -0
WL#2475: Manual merge
MERGE: 1.215.2.3
sql/sql_class.h@stripped, 2007-04-04 22:51:44+04:00, sergefp@stripped +0 -0
WL#2475: Manual merge
MERGE: 1.339.2.1
sql/sql_select.cc@stripped, 2007-04-04 22:51:44+04:00, sergefp@stripped +0 -0
WL#2475: Manual merge
MERGE: 1.492.4.2
sql/sql_select.h@stripped, 2007-04-04 22:51:44+04:00, sergefp@stripped +0 -0
WL#2475: Manual merge
MERGE: 1.117.2.1
sql/table.cc@stripped, 2007-04-04 22:51:44+04:00, sergefp@stripped +0 -0
WL#2475: Manual merge
MERGE: 1.275.4.1
sql/table.h@stripped, 2007-04-04 22:51:44+04:00, sergefp@stripped +0 -2
WL#2475: Manual merge
MERGE: 1.158.4.1
storage/innobase/handler/ha_innodb.cc@stripped, 2007-04-04 22:51:44+04:00, sergefp@stripped +0 -0
WL#2475: Manual merge
MERGE: 1.320.2.3
storage/innobase/handler/ha_innodb.h@stripped, 2007-04-04 22:51:44+04:00, sergefp@stripped +0 -0
WL#2475: Manual merge
MERGE: 1.133.1.1
storage/myisam/ha_myisam.cc@stripped, 2007-04-04 22:51:44+04:00, sergefp@stripped +0 -1
WL#2475: Manual merge
MERGE: 1.209.3.3
storage/myisam/ha_myisam.h@stripped, 2007-04-04 22:51:44+04:00, sergefp@stripped +0 -0
WL#2475: Manual merge
MERGE: 1.81.2.1
storage/myisam/mi_extra.c@stripped, 2007-04-04 22:51:44+04:00, sergefp@stripped +0 -0
WL#2475: Manual merge
MERGE: 1.53.1.1
storage/myisam/mi_key.c@stripped, 2007-04-04 22:51:44+04:00, sergefp@stripped +3 -5
WL#2475: Manual merge
MERGE: 1.55.1.2
storage/myisam/mi_rkey.c@stripped, 2007-04-04 22:51:44+04:00, sergefp@stripped +3 -2
WL#2475: Manual merge
MERGE: 1.30.1.1
storage/myisam/myisamdef.h@stripped, 2007-04-04 22:51:44+04:00, sergefp@stripped +0 -0
WL#2475: Manual merge
MERGE: 1.97.1.1
# This is a BitKeeper patch. What follows are the unified diffs for the
# set of deltas contained in the patch. The rest of the patch, the part
# that BitKeeper cares about, is below these diffs.
# User: sergefp
# Host: pylon64.mylan
# Root: /home/psergey/mysql-5.1-merge-to-r2/RESYNC
--- 1.98/include/config-win.h 2007-04-04 22:52:01 +04:00
+++ 1.99/include/config-win.h 2007-04-04 22:52:01 +04:00
@@ -172,6 +172,7 @@
#endif
#define VOID_SIGHANDLER
#define SIZEOF_CHAR 1
+#define SIZEOF_INT 4
#define SIZEOF_LONG 4
#define SIZEOF_LONG_LONG 8
#define SIZEOF_OFF_T 8
@@ -426,14 +427,8 @@
#define shared_memory_buffer_length 16000
#define default_shared_memory_base_name "MYSQL"
-#ifdef CYBOZU
-#define MYSQL_DEFAULT_CHARSET_NAME "utf8"
-#define MYSQL_DEFAULT_COLLATION_NAME "utf8_general_cs"
-#define HAVE_UTF8_GENERAL_CS 1
-#else
#define MYSQL_DEFAULT_CHARSET_NAME "latin1"
#define MYSQL_DEFAULT_COLLATION_NAME "latin1_swedish_ci"
-#endif
#define HAVE_SPATIAL 1
#define HAVE_RTREE_KEYS 1
@@ -444,10 +439,8 @@
/* Define charsets you want */
/* #undef HAVE_CHARSET_armscii8 */
/* #undef HAVE_CHARSET_ascii */
-#ifndef CYBOZU
#define HAVE_CHARSET_big5 1
#define HAVE_CHARSET_cp1250 1
-#endif
/* #undef HAVE_CHARSET_cp1251 */
/* #undef HAVE_CHARSET_cp1256 */
/* #undef HAVE_CHARSET_cp1257 */
@@ -456,33 +449,27 @@
/* #undef HAVE_CHARSET_cp866 */
#define HAVE_CHARSET_cp932 1
/* #undef HAVE_CHARSET_dec8 */
-#ifndef CYBOZU
#define HAVE_CHARSET_eucjpms 1
#define HAVE_CHARSET_euckr 1
#define HAVE_CHARSET_gb2312 1
#define HAVE_CHARSET_gbk 1
-#endif
/* #undef HAVE_CHARSET_greek */
/* #undef HAVE_CHARSET_hebrew */
/* #undef HAVE_CHARSET_hp8 */
/* #undef HAVE_CHARSET_keybcs2 */
/* #undef HAVE_CHARSET_koi8r */
/* #undef HAVE_CHARSET_koi8u */
-#ifndef CYBOZU
#define HAVE_CHARSET_latin1 1
#define HAVE_CHARSET_latin2 1
-#endif
/* #undef HAVE_CHARSET_latin5 */
/* #undef HAVE_CHARSET_latin7 */
/* #undef HAVE_CHARSET_macce */
/* #undef HAVE_CHARSET_macroman */
#define HAVE_CHARSET_sjis 1
/* #undef HAVE_CHARSET_swe7 */
-#ifndef CYBOZU
#define HAVE_CHARSET_tis620 1
#define HAVE_CHARSET_ucs2 1
#define HAVE_CHARSET_ujis 1
-#endif
#define HAVE_CHARSET_utf8 1
#define HAVE_UCA_COLLATIONS 1
--- 1.98/include/my_base.h 2007-04-04 22:52:01 +04:00
+++ 1.99/include/my_base.h 2007-04-04 22:52:01 +04:00
@@ -458,14 +458,40 @@
/* For key ranges */
+/* from -inf */
#define NO_MIN_RANGE 1
+
+/* to +inf */
#define NO_MAX_RANGE 2
+
+/* X < key, i.e. not including the left endpoint */
#define NEAR_MIN 4
+
+/* X > key, i.e. not including the right endpoint */
#define NEAR_MAX 8
+
+/*
+ This flag means that index is a unique index, and the interval is
+ equivalent to "AND(keypart_i = const_i)", where all of const_i are not NULLs.
+*/
#define UNIQUE_RANGE 16
+
+/*
+ This flag means that the interval is equivalent to
+ "AND(keypart_i = const_i)", where not all key parts may be used but all of
+ const_i are not NULLs.
+*/
#define EQ_RANGE 32
+
+/*
+ This flag has the same meaning as UNIQUE_RANGE, except that for at least
+ one keypart the condition is "keypart IS NULL".
+*/
#define NULL_RANGE 64
+
#define GEOM_FLAG 128
+
+/* Deprecated, currently used only by NDB at row retrieval */
#define SKIP_RANGE 256
typedef struct st_key_range
--- 1.82/include/myisam.h 2007-04-04 22:52:01 +04:00
+++ 1.83/include/myisam.h 2007-04-04 22:52:01 +04:00
@@ -128,7 +128,7 @@
(_to_)= (mi_get_mask_all_keys_active(_maxkeys_) & \
(_from_))
- /* Param to/from mi_info */
+ /* Param to/from mi_status */
typedef struct st_mi_isaminfo /* Struct from h_info */
{
--- 1.54/storage/myisam/mi_extra.c 2007-04-04 22:52:01 +04:00
+++ 1.55/storage/myisam/mi_extra.c 2007-04-04 22:52:01 +04:00
@@ -350,11 +350,13 @@
#ifdef HAVE_MMAP
pthread_mutex_lock(&share->intern_lock);
/*
- Memory map the data file if it is not already mapped and if there
- are no other threads using this table. intern_lock prevents other
- threads from starting to use the table while we are mapping it.
+ Memory map the data file if it is not already mapped. It is safe
+ to memory map a file while other threads are using file I/O on it.
+ Assigning a new address to a function pointer is an atomic
+ operation. intern_lock prevents that two or more mappings are done
+ at the same time.
*/
- if (!share->file_map && (share->tot_locks == 1))
+ if (!share->file_map)
{
if (mi_dynmap_file(info, share->state.state.data_file_length))
{
--- 1.57/storage/myisam/mi_key.c 2007-04-04 22:52:01 +04:00
+++ 1.58/storage/myisam/mi_key.c 2007-04-04 22:52:01 +04:00
@@ -238,7 +238,34 @@
uint char_length;
uchar *pos;
CHARSET_INFO *cs=keyseg->charset;
-
+ /* psergey: we probably don't need all this as we will be cloning handlers instead
+ if (!type)
+ {
+ /*
+ Decode the ROWID value back to to file pointer. The if-check below is
+ needed to distinguish between the cases of
+ a) when this function is called with k_length=USE_WHOLE_KEY (the
+ packed tuple doesn't have rowid), and
+ b) when this function is called with tuple+rowid
+
+ The right check ought to be "k_length == length" but sergefp has
+ discovered that rowid key segment length may be inequal to
+ ha_myisam->ref_length, which forces us to use the below ugly check:
+ */
+ if (k_length > 0 && k_length < MI_MAX_KEY_LENGTH)
+ {
+ my_off_t rowid;
+ pos=old;
+ rowid= my_get_ptr(pos, k_length);
+ _mi_dpointer(info, key, rowid);
+ pos+=length;
+ key+= keyseg->length;
+ k_length= 0;
+ keyseg++;
+ }
+ break;
+ }
+*/
keypart_map>>= 1;
if (keyseg->null_bit)
{
@@ -482,6 +509,35 @@
my_errno=HA_ERR_WRONG_INDEX;
}
return(-1); /* Wrong data to read */
+}
+
+
+/*
+ Save current key tuple to record and call index condition check function
+
+ SYNOPSIS
+ mi_check_index_cond()
+ info MyISAM handler
+ keynr Index we're running a scan on
+ record Record buffer to use (it is assumed that index check function
+ will look for column values there)
+
+ RETURN
+ -1 Error
+ 0 Index condition is not satisfied, continue scanning
+ 1 Index condition is satisfied
+ 2 Index condition is not satisfied, end the scan.
+*/
+
+int mi_check_index_cond(register MI_INFO *info, uint keynr, byte *record)
+{
+ if (_mi_put_key_in_record(info, keynr, record))
+ {
+ mi_print_error(info->s, HA_ERR_CRASHED);
+ my_errno=HA_ERR_CRASHED;
+ return -1;
+ }
+ return info->index_cond_func(info->index_cond_func_arg);
}
--- 1.32/storage/myisam/mi_rkey.c 2007-04-04 22:52:01 +04:00
+++ 1.33/storage/myisam/mi_rkey.c 2007-04-04 22:52:01 +04:00
@@ -29,6 +29,9 @@
MI_KEYDEF *keyinfo;
HA_KEYSEG *last_used_keyseg;
uint pack_key_length, use_key_length, nextflag;
+ uint myisam_search_flag;
+ my_bool key_tuple_has_rowid= FALSE;
+ int res= 0;
DBUG_ENTER("mi_rkey");
DBUG_PRINT("enter", ("base: 0x%lx buf: 0x%lx inx: %d search_flag: %d",
(long) info, (long) buf, inx, search_flag));
@@ -58,9 +61,16 @@
key_buff=info->lastkey+info->s->base.max_key_length;
pack_key_length=_mi_pack_key(info,(uint) inx, key_buff, (uchar*) key,
keypart_map, &last_used_keyseg);
+ /* psergey-merge: this is not needed:
+ if (last_used_keyseg > (info->s->keyinfo[inx].seg +
+ info->s->keyinfo[inx].keysegs))
+ {
+ key_tuple_has_rowid= TRUE;
+ last_used_keyseg--;
+ } */
/* Save packed_key_length for use by the MERGE engine. */
info->pack_key_length= pack_key_length;
- DBUG_EXECUTE("key",_mi_print_key(DBUG_FILE, keyinfo->seg,
+ DBUG_EXECUTE("info",_mi_print_key(DBUG_FILE, keyinfo->seg,
key_buff, pack_key_length););
}
@@ -88,47 +98,60 @@
#endif
case HA_KEY_ALG_BTREE:
default:
+ myisam_search_flag= myisam_read_vec[search_flag];
+ if (key_tuple_has_rowid)
+ {
+ /* Fiddle with flags so ha_key_cmp compares the rowid, too */
+ myisam_search_flag &= ~(SEARCH_FIND | SEARCH_NO_FIND);
+ }
if (!_mi_search(info, keyinfo, key_buff, use_key_length,
- myisam_read_vec[search_flag], info->s->state.key_root[inx]))
+ myisam_search_flag, info->s->state.key_root[inx]))
{
/*
If we searching for a partial key (or using >, >=, < or <=) and
the data is outside of the data file, we need to continue searching
for the first key inside the data file
*/
- if (info->lastpos >= info->state->data_file_length &&
- (search_flag != HA_READ_KEY_EXACT ||
- last_used_keyseg != keyinfo->seg + keyinfo->keysegs))
+ while ((info->lastpos >= info->state->data_file_length &&
+ (search_flag != HA_READ_KEY_EXACT ||
+ last_used_keyseg != keyinfo->seg + keyinfo->keysegs)) ||
+ (info->index_cond_func &&
+ !(res= mi_check_index_cond(info, inx, buf))))
{
- do
+ uint not_used[2];
+ /*
+ Skip rows that are inserted by other threads since we got a lock
+ Note that this can only happen if we are not searching after an
+ full length exact key, because the keys are sorted
+ according to position
+ */
+ if (_mi_search_next(info, keyinfo, info->lastkey,
+ info->lastkey_length,
+ myisam_readnext_vec[search_flag],
+ info->s->state.key_root[inx]))
+ break;
+ /*
+ Check that the found key does still match the search.
+ _mi_search_next() delivers the next key regardless of its
+ value.
+ */
+ if (search_flag == HA_READ_KEY_EXACT &&
+ ha_key_cmp(keyinfo->seg, key_buff, info->lastkey, use_key_length,
+ SEARCH_FIND, not_used))
{
- uint not_used[2];
- /*
- Skip rows that are inserted by other threads since we got a lock
- Note that this can only happen if we are not searching after an
- full length exact key, because the keys are sorted
- according to position
- */
- if (_mi_search_next(info, keyinfo, info->lastkey,
- info->lastkey_length,
- myisam_readnext_vec[search_flag],
- info->s->state.key_root[inx]))
- break;
- /*
- Check that the found key does still match the search.
- _mi_search_next() delivers the next key regardless of its
- value.
- */
- if (search_flag == HA_READ_KEY_EXACT &&
- ha_key_cmp(keyinfo->seg, key_buff, info->lastkey, use_key_length,
- SEARCH_FIND, not_used))
- {
- my_errno= HA_ERR_KEY_NOT_FOUND;
- info->lastpos= HA_OFFSET_ERROR;
- break;
- }
- } while (info->lastpos >= info->state->data_file_length);
+ my_errno= HA_ERR_KEY_NOT_FOUND;
+ info->lastpos= HA_OFFSET_ERROR;
+ break;
+ }
+ }
+ if (res == 2)
+ {
+ info->lastpos= HA_OFFSET_ERROR;
+ if (share->concurrent_insert)
+ rw_unlock(&share->key_root_lock[inx]);
+ DBUG_RETURN((my_errno= HA_ERR_KEY_NOT_FOUND));
}
+
}
}
if (share->concurrent_insert)
--- 1.99/storage/myisam/myisamdef.h 2007-04-04 22:52:01 +04:00
+++ 1.100/storage/myisam/myisamdef.h 2007-04-04 22:52:01 +04:00
@@ -231,6 +231,9 @@
uint error;
} MI_BIT_BUFF;
+
+typedef my_bool (*index_cond_func_t)(void *param);
+
struct st_myisam_info {
MYISAM_SHARE *s; /* Shared between open:s */
MI_STATUS_INFO *state,save_state;
@@ -292,6 +295,9 @@
my_bool page_changed; /* If info->buff can't be used for rnext */
my_bool buff_used; /* If info->buff has to be reread for rnext */
my_bool once_flags; /* For MYISAMMRG */
+
+ index_cond_func_t index_cond_func; /* Index condition function */
+ void *index_cond_func_arg; /* parameter for the func */
#ifdef __WIN__
my_bool owned_by_merge; /* This MyISAM table is part of a merge union */
#endif
@@ -767,6 +773,8 @@
my_bool mi_dynmap_file(MI_INFO *info, my_off_t size);
void mi_remap_file(MI_INFO *info, my_off_t size);
+int mi_check_index_cond(register MI_INFO *info, uint keynr, byte *record);
+
/* Functions needed by mi_check */
volatile int *killed_ptr(MI_CHECK *param);
void mi_check_print_error _VARARGS((MI_CHECK *param, const char *fmt,...));
@@ -783,6 +791,8 @@
int sort_write_record(MI_SORT_PARAM *sort_param);
int _create_index_by_sort(MI_SORT_PARAM *info,my_bool no_messages, ulong);
+extern void mi_set_index_cond_func(MI_INFO *info, index_cond_func_t func,
+ void *func_arg);
#ifdef __cplusplus
}
#endif
--- 1.217/storage/myisam/ha_myisam.cc 2007-04-04 22:52:01 +04:00
+++ 1.218/storage/myisam/ha_myisam.cc 2007-04-04 22:52:01 +04:00
@@ -477,9 +477,14 @@
HA_DUPLICATE_POS | HA_CAN_INDEX_BLOBS | HA_AUTO_PART_KEY |
HA_FILE_BASED | HA_CAN_GEOMETRY | HA_NO_TRANSACTIONS |
HA_CAN_INSERT_DELAYED | HA_CAN_BIT_FIELD | HA_CAN_RTREEKEYS |
- HA_HAS_RECORDS | HA_STATS_RECORDS_IS_EXACT),
- can_enable_indexes(1)
-{}
+ HA_HAS_RECORDS | HA_STATS_RECORDS_IS_EXACT |
+ HA_NEED_READ_RANGE_BUFFER | HA_MRR_CANT_SORT),
+ can_enable_indexes(1),
+ cond_keyno(MAX_KEY),
+ ds_mrr((DsMrr_impl::range_check_toggle_func_t)&ha_myisam::toggle_range_check)
+{
+ ds_mrr.h= this;
+}
handler *ha_myisam::clone(MEM_ROOT *mem_root)
{
@@ -691,7 +696,8 @@
int_table_flags|=HA_REC_NOT_IN_SEQ;
if (file->s->options & (HA_OPTION_CHECKSUM | HA_OPTION_COMPRESS_RECORD))
int_table_flags|=HA_HAS_CHECKSUM;
-
+
+ keys_with_parts.clear_all();
for (i= 0; i < table->s->keys; i++)
{
struct st_plugin_int *parser= table->key_info[i].parser;
@@ -699,6 +705,17 @@
file->s->keyinfo[i].parser=
(struct st_mysql_ftparser *)parser->plugin->info;
table->key_info[i].block_size= file->s->keyinfo[i].block_length;
+
+ KEY_PART_INFO *kp= table->key_info[i].key_part;
+ KEY_PART_INFO *kp_end= kp + table->key_info[i].key_parts;
+ for (; kp != kp_end; kp++)
+ {
+ if (!kp->field->part_of_key.is_set(i))
+ {
+ keys_with_parts.set_bit(i);
+ break;
+ }
+ }
}
my_errno= 0;
goto end;
@@ -1608,6 +1625,41 @@
return mi_delete(file,buf);
}
+C_MODE_START
+
+my_bool index_cond_func_myisam(void *arg)
+{
+ ha_myisam *h= (ha_myisam*)arg;
+ if (h->in_range_read)
+ {
+ if (h->compare_key(h->end_range) > 0)
+ return 2; /* caller should return HA_ERR_END_OF_FILE already */
+ }
+ return (my_bool)h->idx_cond->val_int();
+}
+
+C_MODE_END
+
+
+int ha_myisam::index_init(uint idx, bool sorted)
+{
+ active_index=idx;
+ in_range_read= FALSE;
+ if (cond_keyno == idx)
+ mi_set_index_cond_func(file, index_cond_func_myisam, this);
+ return 0;
+}
+
+
+int ha_myisam::index_end()
+{
+ active_index=MAX_KEY;
+ mi_set_index_cond_func(file, NULL, 0);
+ in_range_check_pushed_down= FALSE;
+ return 0;
+}
+
+
int ha_myisam::index_read(byte *buf, const byte *key, key_part_map keypart_map,
enum ha_rkey_function find_flag)
{
@@ -1695,6 +1747,31 @@
return error;
}
+int ha_myisam::read_range_first(const key_range *start_key,
+ const key_range *end_key,
+ bool eq_range_arg,
+ bool sorted /* ignored */)
+{
+ int res;
+ if (!eq_range_arg)
+ in_range_read= TRUE;
+
+ res= handler::read_range_first(start_key, end_key, eq_range_arg, sorted);
+
+ if (res)
+ in_range_read= FALSE;
+ return res;
+}
+
+
+int ha_myisam::read_range_next()
+{
+ int res= handler::read_range_next();
+ if (res)
+ in_range_read= FALSE;
+ return res;
+}
+
int ha_myisam::rnd_init(bool scan)
{
@@ -1809,6 +1886,8 @@
int ha_myisam::reset(void)
{
+ cond_keyno= MAX_KEY;
+ mi_set_index_cond_func(file, NULL, 0);
return mi_reset(file);
}
@@ -2062,6 +2141,62 @@
myisam_hton->flags= HTON_CAN_RECREATE | HTON_SUPPORT_LOG_TABLES;
return 0;
}
+
+
+/****************************************************************************
+ * MyISAM MRR implementation: use DS-MRR
+ ***************************************************************************/
+
+int ha_myisam::multi_range_read_init(RANGE_SEQ_IF *seq, void *seq_init_param,
+ uint n_ranges, uint mode,
+ HANDLER_BUFFER *buf)
+{
+ return ds_mrr.dsmrr_init(this, &table->key_info[active_index],
+ seq, seq_init_param, n_ranges, mode, buf);
+}
+
+int ha_myisam::multi_range_read_next(char **range_info)
+{
+ return ds_mrr.dsmrr_next(this, range_info);
+}
+
+ha_rows ha_myisam::multi_range_read_info_const(uint keyno, RANGE_SEQ_IF *seq,
+ void *seq_init_param,
+ uint n_ranges, uint *bufsz,
+ uint *flags, COST_VECT *cost)
+{
+ return ds_mrr.dsmrr_info_const(keyno, seq, seq_init_param, n_ranges, bufsz,
+ flags, cost);
+}
+
+int ha_myisam::multi_range_read_info(uint keyno, uint n_ranges, uint keys,
+ uint *bufsz, uint *flags, COST_VECT *cost)
+{
+ return ds_mrr.dsmrr_info(keyno, n_ranges, keys, bufsz, flags, cost);
+}
+
+/* MyISAM MRR implementation ends */
+
+
+/* Index condition pushdown implementation*/
+
+
+Item *ha_myisam::idx_cond_push(uint keyno_arg, Item* idx_cond_arg)
+{
+ cond_keyno= keyno_arg;
+ idx_cond= idx_cond_arg;
+ in_range_check_pushed_down= TRUE;
+ if (active_index == cond_keyno)
+ mi_set_index_cond_func(file, index_cond_func_myisam, this);
+ return NULL;
+}
+
+void ha_myisam::add_explain_extra_info(uint keyno, String *extra)
+{
+ if (cond_keyno != MAX_KEY && idx_cond && keyno==cond_keyno)
+ extra->append(STRING_WITH_LEN("; Using index condition"));
+}
+
struct st_mysql_storage_engine myisam_storage_engine=
{ MYSQL_HANDLERTON_INTERFACE_VERSION };
--- 1.84/storage/myisam/ha_myisam.h 2007-04-04 22:52:01 +04:00
+++ 1.85/storage/myisam/ha_myisam.h 2007-04-04 22:52:01 +04:00
@@ -33,6 +33,10 @@
extern TYPELIB myisam_recover_typelib;
extern ulong myisam_recover_options;
+C_MODE_START
+my_bool index_cond_func_myisam(void *arg);
+C_MODE_END
+
class ha_myisam: public handler
{
MI_INFO *file;
@@ -49,11 +53,14 @@
const char *index_type(uint key_number);
const char **bas_ext() const;
ulonglong table_flags() const { return int_table_flags; }
+ int index_init(uint idx, bool sorted);
+ int index_end();
ulong index_flags(uint inx, uint part, bool all_parts) const
{
return ((table_share->key_info[inx].algorithm == HA_KEY_ALG_FULLTEXT) ?
0 : HA_READ_NEXT | HA_READ_PREV | HA_READ_RANGE |
- HA_READ_ORDER | HA_KEYREAD_ONLY);
+ HA_READ_ORDER | HA_KEYREAD_ONLY |
+ (keys_with_parts.is_set(inx)?0:HA_DO_INDEX_COND_PUSHDOWN));
}
uint max_supported_keys() const { return MI_MAX_KEY; }
uint max_supported_key_length() const { return MI_MAX_KEY_LENGTH; }
@@ -137,4 +144,37 @@
int dump(THD* thd, int fd);
int net_read_dump(NET* net);
#endif
+ int read_range_first(const key_range *start_key, const key_range *end_key,
+ bool eq_range_arg, bool sorted);
+ int read_range_next();
+public:
+ /**
+ * Multi Range Read interface
+ */
+ int multi_range_read_init(RANGE_SEQ_IF *seq, void *seq_init_param,
+ uint n_ranges, uint mode, HANDLER_BUFFER *buf);
+ int multi_range_read_next(char **range_info);
+ ha_rows multi_range_read_info_const(uint keyno, RANGE_SEQ_IF *seq,
+ void *seq_init_param,
+ uint n_ranges, uint *bufsz,
+ uint *flags, COST_VECT *cost);
+ int multi_range_read_info(uint keyno, uint n_ranges, uint keys,
+ uint *bufsz, uint *flags, COST_VECT *cost);
+
+ /* Index condition pushdown implementation */
+ Item *idx_cond_push(uint keyno, Item* idx_cond);
+
+ void add_explain_extra_info(uint keyno, String *extra);
+private:
+ uint cond_keyno;
+ Item *idx_cond;
+ DsMrr_impl ds_mrr;
+ key_map keys_with_parts;
+ bool in_range_read;
+ void toggle_range_check(bool on)
+ {
+ in_range_read= on;
+ }
+ friend my_bool index_cond_func_myisam(void *arg);
};
+
--- 1.301/sql/handler.cc 2007-04-04 22:52:01 +04:00
+++ 1.302/sql/handler.cc 2007-04-04 22:52:01 +04:00
@@ -3037,94 +3037,310 @@
}
#endif
-/** @brief
- Read the first row of a multi-range set.
+/*
+ Calculate cost of 'index only' scan for given index and number of records
SYNOPSIS
- read_multi_range_first()
- found_range_p Returns a pointer to the element in 'ranges' that
- corresponds to the returned row.
- ranges An array of KEY_MULTI_RANGE range descriptions.
- range_count Number of ranges in 'ranges'.
- sorted If result should be sorted per key.
- buffer A HANDLER_BUFFER for internal handler usage.
+ index_only_read_time()
+ keynr Index number
+ records Estimated number of records to be retrieved
NOTES
- Record is read into table->record[0].
- *found_range_p returns a valid value only if read_multi_range_first()
- returns 0.
- Sorting is done within each range. If you want an overall sort, enter
- 'ranges' with sorted ranges.
+ It is assumed that we will read trough the whole key range and that all
+ key blocks are half full (normally things are much better). It is also
+ assumed that each time we read the next key from the index, the handler
+ performs a random seek, thus the cost is proportional to the number of
+ blocks read.
+
+ TODO
+ Consider joining this function and handler::read_time() into one
+ handler::read_time(keynr, records, ranges, bool index_only) function.
RETURN
- 0 OK, found a row
- HA_ERR_END_OF_FILE No rows in range
- # Error code
+ Estimated cost of 'index only' scan
*/
-int handler::read_multi_range_first(KEY_MULTI_RANGE **found_range_p,
- KEY_MULTI_RANGE *ranges, uint range_count,
- bool sorted, HANDLER_BUFFER *buffer)
-{
- int result= HA_ERR_END_OF_FILE;
- DBUG_ENTER("handler::read_multi_range_first");
- multi_range_sorted= sorted;
- multi_range_buffer= buffer;
-
- table->mark_columns_used_by_index_no_reset(active_index, table->read_set);
- table->column_bitmaps_set(table->read_set, table->write_set);
-
- for (multi_range_curr= ranges, multi_range_end= ranges + range_count;
- multi_range_curr < multi_range_end;
- multi_range_curr++)
- {
- result= read_range_first(multi_range_curr->start_key.keypart_map ?
- &multi_range_curr->start_key : 0,
- multi_range_curr->end_key.keypart_map ?
- &multi_range_curr->end_key : 0,
- test(multi_range_curr->range_flag & EQ_RANGE),
- multi_range_sorted);
- if (result != HA_ERR_END_OF_FILE)
- break;
- }
- *found_range_p= multi_range_curr;
- DBUG_PRINT("exit",("result %d", result));
- DBUG_RETURN(result);
+
+double handler::index_only_read_time(uint keynr, double records)
+{
+ double read_time;
+ uint keys_per_block= (stats.block_size/2/
+ (table->key_info[keynr].key_length + ref_length) + 1);
+ read_time=((double) (records + keys_per_block-1) /
+ (double) keys_per_block);
+ return read_time;
}
-/** @brief
- Read the next row of a multi-range set.
+/****************************************************************************
+ * Default MRR implementation (MRR to non-MRR converter)
+ ***************************************************************************/
+
+/*
+ Get cost and other information about MRR scan over a known list of ranges
SYNOPSIS
- read_multi_range_next()
- found_range_p Returns a pointer to the element in 'ranges' that
- corresponds to the returned row.
+ multi_range_read_info_const()
+ keyno Index number
+ seq Range sequence to be traversed
+ seq_init_param First parameter for seq->init()
+ n_ranges_arg Number of ranges in the sequence, or 0 if the caller
+ can't efficiently determine it
+ bufsz INOUT IN: Size of the buffer available for use
+ OUT: Size of the buffer that is expected to be actually
+ used, or 0 if buffer is not needed.
+ flags INOUT A combination of HA_MRR_* flags
+ cost OUT Estimated cost of MRR access
+
+ DESCRIPTION
+ Calculate estimated cost and other information about an MRR scan for given
+ sequence of ranges.
+
+ RETURN
+ HA_POS_ERROR - Error or the engine is unable to perform the requested
+ scan. Values of OUT parameters are undefined.
+ other - OK, *cost contains cost of the scan, *bufsz and *flags
+ contain scan parameters.
+*/
+
+ha_rows
+handler::multi_range_read_info_const(uint keyno, RANGE_SEQ_IF *seq,
+ void *seq_init_param, uint n_ranges_arg,
+ uint *bufsz, uint *flags, COST_VECT *cost)
+{
+ KEY_MULTI_RANGE range;
+ range_seq_t seq_it;
+ ha_rows rows, total_rows= 0;
+ uint n_ranges=0;
+
+ /* Default MRR implementation doesn't need buffer */
+ *bufsz= 0;
+
+ seq_it= seq->init(seq_init_param, n_ranges, *flags);
+ while (!seq->next(seq_it, &range))
+ {
+ n_ranges++;
+ key_range *min_endp, *max_endp;
+ if (range.range_flag & GEOM_FLAG)
+ {
+ /* In this case tmp_min_flag contains the handler-read-function */
+ range.start_key.flag= (ha_rkey_function) (range.range_flag ^ GEOM_FLAG);
+ min_endp= &range.start_key;
+ max_endp= NULL;
+ }
+ else
+ {
+ min_endp= range.start_key.length? &range.start_key : NULL;
+ max_endp= range.end_key.length? &range.end_key : NULL;
+ }
+ if ((range.range_flag & UNIQUE_RANGE) && !(range.range_flag & NULL_RANGE))
+ rows= 1; /* there can be at most one row */
+ else
+ {
+ if (HA_POS_ERROR == (rows= this->records_in_range(keyno, min_endp,
+ max_endp)))
+ {
+ /* Can't scan one range => can't do MRR scan at all */
+ total_rows= HA_POS_ERROR;
+ break;
+ }
+ }
+ total_rows += rows;
+ }
+
+ if (total_rows != HA_POS_ERROR)
+ {
+ /* The following calculation is the same as in multi_range_read_info(): */
+ *flags |= HA_MRR_USE_DEFAULT_IMPL;
+ cost->zero();
+ cost->avg_io_cost= 1; /* assume random seeks */
+ if ((*flags & HA_MRR_INDEX_ONLY) && total_rows > 2)
+ cost->io_count= index_only_read_time(keyno, total_rows);
+ else
+ cost->io_count= read_time(keyno, n_ranges, total_rows);
+ cost->cpu_cost= (double) total_rows / TIME_FOR_COMPARE + 0.01;
+ }
+ return total_rows;
+}
+
+
+/*
+ Get cost and other information about MRR scan over some sequence of ranges
+
+ SYNOPSIS
+ multi_range_read_info()
+ keyno Index number
+ n_ranges Estimated number of ranges (i.e. intervals) in the
+ range sequence.
+ n_rows Estimated total number of records contained within all
+ of the ranges
+ bufsz INOUT IN: Size of the buffer available for use
+ OUT: Size of the buffer that will be actually used, or
+ 0 if buffer is not needed.
+ flags INOUT A combination of HA_MRR_* flags
+ cost OUT Estimated cost of MRR access
+
+ DESCRIPTION
+ Calculate estimated cost and other information about an MRR scan for some
+ sequence of ranges.
+
+ The ranges themselves will be known only at execution phase. When this
+ function is called we only know number of ranges and a (rough) E(#records)
+ within those ranges.
+
+ Currently this function is only called for "n-keypart singlepoint" ranges,
+ i.e. each range is "keypart1=someconst1 AND ... AND keypartN=someconstN"
+
+ The flags parameter is a combination of those flags: HA_MRR_SORTED,
+ HA_MRR_INDEX_ONLY, HA_MRR_NO_ASSOCIATION, HA_MRR_LIMITS.
+
+ RETURN
+ 0 - OK, *cost contains cost of the scan, *bufsz and *flags contain scan
+ parameters.
+ other - Error or can't perform the requested scan
+*/
+
+int handler::multi_range_read_info(uint keyno, uint n_ranges, uint n_rows,
+ uint *bufsz, uint *flags, COST_VECT *cost)
+{
+ *bufsz= 0; /* Default implementation doesn't need a buffer */
+
+ *flags |= HA_MRR_USE_DEFAULT_IMPL;
+
+ cost->zero();
+ cost->avg_io_cost= 1; /* assume random seeks */
+
+ /* Produce the same cost as non-MRR code does */
+ if (*flags & HA_MRR_INDEX_ONLY)
+ cost->io_count= index_only_read_time(keyno, n_rows);
+ else
+ cost->io_count= read_time(keyno, n_ranges, n_rows);
+ return 0;
+
+
+
+/*
+ Initialize the MRR scan
+
+ SYNOPSIS
+ multi_range_read_init()
+ seq Range sequence to be traversed
+ seq_init_param First parameter for seq->init()
+ n_ranges Number of ranges in the sequence
+ mode Flags, see the description section for the details
+ buf INOUT: memory buffer to be used
+
+ DESCRIPTION
+ Initialize the MRR scan. This function may do heavyweight scan
+ initialization like row prefetching/sorting/etc (NOTE: but better not do
+ it here as we may not need it, e.g. if we never satisfy WHERE clause on
+ previous tables. For many implementations it would be natural to do such
+ initializations in the first multi_read_range_next() call)
+
+ mode is a combination of the following flags: HA_MRR_SORTED,
+ HA_MRR_INDEX_ONLY, HA_MRR_NO_ASSOCIATION
NOTES
- Record is read into table->record[0].
- *found_range_p returns a valid value only if read_multi_range_next()
- returns 0.
+ One must have called index_init() before calling this function. Several
+ multi_range_read_init() calls may be made in course of one query.
+
+ Until WL#2623 is done (see its text, section 3.2), the following will
+ also hold:
+ The caller will guarantee that if "seq->init == mrr_ranges_array_init"
+ then seq_init_param is an array of n_ranges KEY_MULTI_RANGE structures.
+ This property will only be used by NDB handler until WL#2623 is done.
+
+ Buffer memory management is done according to the following scenario:
+ The caller allocates the buffer and provides it to the callee by filling
+ the members of HANDLER_BUFFER structure.
+ The callee consumes all or some fraction of the provided buffer space, and
+ sets the HANDLER_BUFFER members accordingly.
+ The callee may use the buffer memory until the next multi_range_read_init()
+ call is made, all records have been read, or until index_end() call is
+ made, whichever comes first.
RETURN
- 0 OK, found a row
- HA_ERR_END_OF_FILE No (more) rows in range
- # Error code
+ 0 - OK
+ 1 - Error
*/
-int handler::read_multi_range_next(KEY_MULTI_RANGE **found_range_p)
+
+int
+handler::multi_range_read_init(RANGE_SEQ_IF *seq_funcs, void *seq_init_param,
+ uint n_ranges, uint mode, HANDLER_BUFFER *buf)
+{
+ DBUG_ENTER("handler::multi_range_read_init");
+ mrr_iter= seq_funcs->init(seq_init_param, n_ranges, mode);
+ mrr_funcs= *seq_funcs;
+ mrr_is_output_sorted= test(mode & HA_MRR_SORTED);
+ mrr_have_range= FALSE;
+ DBUG_RETURN(0);
+}
+
+
+/*
+ Get next record in MRR scan
+
+ SYNOPSIS
+ multi_range_read_next()
+ range_info OUT Undefined if HA_MRR_NO_ASSOCIATION flag is in effect
+ Otherwise, the opaque value associated with the range
+ that contains the returned record.
+ DESCRIPTION
+ Default MRR implementation: read the next record
+
+ RETURN
+ 0 OK
+ other Error code
+*/
+
+int handler::multi_range_read_next(char **range_info)
{
int result;
- DBUG_ENTER("handler::read_multi_range_next");
+ int range_res;
+ DBUG_ENTER("handler::multi_range_read_next");
+ LINT_INIT(result);
- /* We should not be called after the last call returned EOF. */
- DBUG_ASSERT(multi_range_curr < multi_range_end);
+ if (!mrr_have_range)
+ {
+ mrr_have_range= TRUE;
+ goto start;
+ }
do
{
/* Save a call if there can be only one row in range. */
- if (multi_range_curr->range_flag != (UNIQUE_RANGE | EQ_RANGE))
+ if (mrr_cur_range.range_flag != (UNIQUE_RANGE | EQ_RANGE))
{
- result= read_range_next();
+ if (mrr_restore_scan)
+ {
+ result= mrr_restore_scan_res;
+ if (!result)
+ result= (compare_key(end_range) <= 0 ? 0 : HA_ERR_END_OF_FILE);
+ else
+ {
+ if (result == HA_ERR_KEY_NOT_FOUND)
+ result= HA_ERR_END_OF_FILE;
+ }
+ mrr_restore_scan= FALSE;
+ if (!result)
+ {
+ /*
+ Continue as if we're scanning a generic (i.e. not necessarily
+ equality) range. The point is that if we're in the middle of
+ equality range, we can't continue scanning it as equality range.
+ The call sequence is:
+ file->index_read( ... , HA_READ_KEY_EXACT);
+ file->index_next_same(...)
+ ...
+ file->index_read(..., HA_READ_AFTER_KEY) // restore the scan
+ That is, the "restore the scan" call has interrupted the
+ equality range and we can't use index_next_same() anymore.
+ */
+ eq_range= FALSE;
+ }
+ }
+ else
+ result= read_range_next();
/* On success or non-EOF errors jump to the end. */
if (result != HA_ERR_END_OF_FILE)
@@ -3139,33 +3355,660 @@
result= HA_ERR_END_OF_FILE;
}
+start:
/* Try the next range(s) until one matches a record. */
- for (multi_range_curr++;
- multi_range_curr < multi_range_end;
- multi_range_curr++)
- {
- result= read_range_first(multi_range_curr->start_key.keypart_map ?
- &multi_range_curr->start_key : 0,
- multi_range_curr->end_key.keypart_map ?
- &multi_range_curr->end_key : 0,
- test(multi_range_curr->range_flag & EQ_RANGE),
- multi_range_sorted);
+ while (!(range_res= mrr_funcs.next(mrr_iter, &mrr_cur_range)))
+ {
+ result= read_range_first(mrr_cur_range.start_key.keypart_map ?
+ &mrr_cur_range.start_key : 0,
+ mrr_cur_range.end_key.keypart_map ?
+ &mrr_cur_range.end_key : 0,
+ test(mrr_cur_range.range_flag & EQ_RANGE),
+ mrr_is_output_sorted);
if (result != HA_ERR_END_OF_FILE)
break;
}
}
- while ((result == HA_ERR_END_OF_FILE) &&
- (multi_range_curr < multi_range_end));
+ while ((result == HA_ERR_END_OF_FILE) && !range_res);
- *found_range_p= multi_range_curr;
- DBUG_PRINT("exit",("handler::read_multi_range_next: result %d", result));
+ *range_info= mrr_cur_range.ptr;
+ DBUG_PRINT("exit",("handler::multi_range_read_next result %d", result));
DBUG_RETURN(result);
}
+/****************************************************************************
+ * DS-MRR implementation
+ ***************************************************************************/
+
+/*
+ DS-MRR: Initialize and start MRR scan
+
+ SYNOPSIS
+ DsMrr_impl::dsmrr_init()
+ h Table handler to be used
+ key Index to be used
+ seq_funcs Interval sequence enumeration functions
+ seq_init_param Interval sequence enumeration parameter
+ n_ranges Number of ranges in the sequence.
+ mode HA_MRR_* modes to use
+ buf INOUT Buffer to use
+
+ DESCRIPTION
+ Initialize and start the MRR scan. Depending on the mode parameter, this
+ may use default or DS-MRR implementation.
+
+ RETURN
+ 0 - Ok, Scan started.
+ other - Error
+*/
+
+int DsMrr_impl::dsmrr_init(handler *h, KEY *key,
+ RANGE_SEQ_IF *seq_funcs, void *seq_init_param,
+ uint n_ranges, uint mode, HANDLER_BUFFER *buf)
+{
+ int res;
+ uint elem_size;
+
+ if (mode & HA_MRR_USE_DEFAULT_IMPL || mode & HA_MRR_SORTED)
+ {
+ use_default_impl= TRUE;
+ return h->handler::multi_range_read_init(seq_funcs, seq_init_param,
+ n_ranges, mode, buf);
+ }
+ use_default_impl= FALSE;
+
+ rowids_buf= (byte*)buf->buffer;
+ last_idx_tuple= rowids_buf;
+ rowids_buf += key->key_length + h->ref_length;
+
+ is_mrr_assoc= !test(mode & HA_MRR_NO_ASSOCIATION);
+ rowids_buf_end= (byte*)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;
+ res= h->handler::multi_range_read_init(seq_funcs, seq_init_param, n_ranges,
+ mode, buf);
+ DBUG_ASSERT(!res);
+
+ mrr_key= key;
+ mrr_keyno= h->active_index;
+
+ if (h->index_end() || h->extra(HA_EXTRA_KEYREAD))
+ return 1;
+
+ h->table->key_read= TRUE;
+ h->table->prepare_for_position();
+ h->table->mark_columns_used_by_index_no_reset(mrr_keyno, h->table->read_set);
+
+ if (h->index_init(mrr_keyno, FALSE))
+ return 1;
+
+
+ h->mrr_restore_scan= FALSE;
+ /* h->mrr_restore_scan_res needs no initialization */
+
+ if ((res= dsmrr_fill_buffer(h)))
+ return res;
+
+ if (h->index_end() || h->rnd_init(FALSE))
+ return 1;
+
+ /*
+ 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;
+ return 0;
+}
+
+
+static int rowid_cmp(void *h, byte *a, byte *b)
+{
+ return ((handler*)h)->cmp_ref(a, b);
+}
+
+
+/*
+ DS-MRR: Fill the buffer with rowids and sort it by rowid
+
+ SYNOPSIS
+ DsMrr_impl::dsmrr_fill_buffer()
+ h Table handler
+
+ DESCRIPTION
+ {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.
+
+ The function assumes that rowids buffer is empty when it is invoked.
+
+ RETURN
+ 0 - OK, the next portion of rowids is in the buffer, properly ordered
+ other - Error
+*/
+
+int DsMrr_impl::dsmrr_fill_buffer(handler *h)
+{
+ char *range_info;
+ int res;
+ DBUG_ENTER("DsMrr_impl::dsmrr_fill_buffer");
+
+ rowids_buf_cur= rowids_buf;
+ while ((rowids_buf_cur < rowids_buf_end) &&
+ !(res= h->handler::multi_range_read_next(&range_info)))
+ {
+ /* Put rowid, or {rowid, range_id} pair into the buffer */
+ h->position((const byte*)(h->table->record[0]));
+ memcpy(rowids_buf_cur, h->ref, h->ref_length);
+ rowids_buf_cur += h->ref_length;
+
+ if (is_mrr_assoc)
+ {
+ memcpy(rowids_buf_cur, &range_info, sizeof(void*));
+ rowids_buf_cur += sizeof(void*);
+ }
+ }
+
+ if (res && res != HA_ERR_END_OF_FILE)
+ DBUG_RETURN(res);
+ dsmrr_eof= test(res == HA_ERR_END_OF_FILE);
+
+ if (!res && (h->mrr_cur_range.range_flag != (UNIQUE_RANGE | EQ_RANGE)))
+ {
+ /* Save the index position: search tuple + rowid */
+ key_copy(last_idx_tuple, h->table->record[0], mrr_key,
+ mrr_key->key_length);
+ key_zero_nulls(last_idx_tuple, mrr_key);
+
+ memcpy(last_idx_tuple + mrr_key->key_length, h->ref, h->ref_length);
+ h->mrr_restore_scan= TRUE;
+ }
+
+ /* 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;
+
+ 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);
+}
+
+
+/*
+ DS-MRR implementation: multi_range_read_next() function
+*/
+
+int DsMrr_impl::dsmrr_next(handler *h, char **range_info)
+{
+ int res;
+
+ if (use_default_impl)
+ return h->handler::multi_range_read_next(range_info);
+
+ if ((rowids_buf_cur == rowids_buf_last))
+ {
+ h->rnd_end();
+ if (h->extra(HA_EXTRA_KEYREAD))
+ return HA_ERR_KEY_NOT_FOUND;
+ h->table->prepare_for_position();
+ h->table->mark_columns_used_by_index_no_reset(mrr_keyno, h->table->read_set);
+
+ if (h->index_init(mrr_keyno, FALSE))
+ return HA_ERR_KEY_NOT_FOUND;
+ /*
+ The following check is made here after index_init call so that we
+ leave the table handler in "scanning the index" state after returning
+ EOF. Join de-initialization code depends on this.
+ */
+ if (dsmrr_eof)
+ return HA_ERR_END_OF_FILE;
+
+ if (h->mrr_restore_scan)
+ {
+ if (h->eq_range)
+ {
+ /*
+ When scanning equality ranges, the index condition pushdown
+ function doesn't check if we've ran out of the range; this is done
+ by the implementation of engine's index_next_same() function.
+
+ When we continue such scan to need to tell the handler that
+ 1. We need a row that is 'after' than {last_idx_tuple, rowid}
+ 2. But the first N key components (N depends on what range we're
+ scanning) must be equal to those of previously returned tuples.
+
+ The index_read() call contract allows to request only #1. We need
+ to pass the #2 down, too: if we don't, the index_read() call may
+ run out of the range we're scanning and will continue walking forward
+ until it finds an index tuple that satisfies the index condition.
+
+ To prevent this overrun, we enable the range bound check for the
+ time of the call.
+ */
+ (h->*range_check_toggle_func)(TRUE);
+ }
+ h->mrr_restore_scan_res=
+ h->index_read(h->table->record[0], last_idx_tuple,
+ mrr_key->key_length + h->ref_length,
+ HA_READ_AFTER_KEY);
+ //if (h->eq_range)
+ // (h->*range_check_toggle_func)(FALSE);
+ }
+ res= dsmrr_fill_buffer(h);
+
+ h->index_end();
+ h->rnd_init(FALSE);
+
+ if (res)
+ return res;
+ }
+
+ /* Return EOF if there are no rowids in the buffer after re-fill attempt */
+ if (rowids_buf_cur == rowids_buf_last)
+ {
+ return HA_ERR_END_OF_FILE;
+ }
+
+ res= h->rnd_pos((byte*)h->table->record[0], rowids_buf_cur);
+ rowids_buf_cur += h->ref_length;
+
+ if (is_mrr_assoc)
+ {
+ memcpy(range_info, rowids_buf_cur, sizeof(void*));
+ rowids_buf_cur += sizeof(void*);
+ }
+ return res;
+}
+
+
+/*
+ DS-MRR implementation: multi_range_read_info() function
+*/
+int DsMrr_impl::dsmrr_info(uint keyno, uint n_ranges, uint rows, uint *bufsz,
+ uint *flags, COST_VECT *cost)
+{
+ int res;
+ uint def_flags= *flags;
+ 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);
+ DBUG_ASSERT(!res);
+
+ if ((*flags & HA_MRR_USE_DEFAULT_IMPL) ||
+ choose_mrr_impl(keyno, rows, &def_flags, &def_bufsz, cost))
+ {
+ /* Default implementation is choosen */
+ DBUG_PRINT("info", ("Default MRR implementation choosen"));
+ *flags= def_flags;
+ *bufsz= def_bufsz;
+ }
+ else
+ {
+ DBUG_PRINT("info", ("DS-MRR implementation choosen"));
+ }
+
+ return 0;
+}
+
+
+/*
+ DS-MRR Implementation: multi_range_read_info_const() function
+*/
+
+ha_rows DsMrr_impl::dsmrr_info_const(uint keyno, RANGE_SEQ_IF *seq,
+ void *seq_init_param, uint n_ranges,
+ uint *bufsz, uint *flags, COST_VECT *cost)
+{
+ ha_rows rows;
+ 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);
+ if (rows == HA_POS_ERROR)
+ {
+ /* Default implementation can't perform MRR scan => we can't either */
+ return rows;
+ }
+
+ /*
+ 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.
+ */
+ if ((*flags & HA_MRR_USE_DEFAULT_IMPL) ||
+ choose_mrr_impl(keyno, rows, flags, bufsz, cost))
+ {
+ DBUG_PRINT("info", ("Default MRR implementation choosen"));
+ *flags= def_flags;
+ *bufsz= def_bufsz;
+ }
+ else
+ {
+ *flags &= ~HA_MRR_USE_DEFAULT_IMPL;
+ DBUG_PRINT("info", ("DS-MRR implementation choosen"));
+ }
+ return rows;
+}
+
+
+/*
+ Check if key has partially-covered columns
+
+ SYNOPSIS
+ DsMrr_impl::key_uses_partial_cols()
+ keyno Key to check
+
+ DESCRIPTION
+ We can't use DS-MRR to perform range scans when the ranges are over
+ partially-covered keys, because we'll not have full key part values
+ (we'll have their prefixes from the index) and will not be able to check
+ if we've reached the end the range.
+
+ TODO: Allow use of DS-MRR in cases where the index has partially-covered
+ components but they are not used for scanning.
+
+ RETURN
+ TRUE - Yes
+ FALSE - No
+*/
+
+bool DsMrr_impl::key_uses_partial_cols(uint keyno)
+{
+ KEY_PART_INFO *kp= h->table->key_info[keyno].key_part;
+ KEY_PART_INFO *kp_end= kp + h->table->key_info[keyno].key_parts;
+ for (; kp != kp_end; kp++)
+ {
+ if (!kp->field->part_of_key.is_set(keyno))
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+/*
+ DS-MRR Internals: Choose between Default MRR implementation and DS-MRR
+
+ SYNOPSIS
+ choose_mrr_impl()
+ keyno Index number
+ rows E(full rows to be retrieved)
+ flags IN MRR flags provided by the MRR user
+ OUT If DS-MRR is choosen, flags of DS-MRR implementation
+ else the value is not modified
+ bufsz IN If DS-MRR is choosen, buffer use of DS-MRR implementation
+ else the value is not modified
+ cost IN Cost of default MRR implementation
+ OUT If DS-MRR is choosen, cost of DS-MRR scan
+ else the value is not modified
+
+ DESCRIPTION
+ Make the choice between using Default MRR implementation and DS-MRR.
+ This function contains common functionality factored out of dsmrr_info()
+ and dsmrr_info_const(). The function assumes that the default MRR
+ implementation's applicability requirements are satisfied.
+
+ RETURN
+ TRUE Default MRR implementation should be used
+ 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) || (*flags & HA_MRR_SORTED) ||
+ (keyno == h->table_share->primary_key &&
+ h->primary_key_is_clustered()) ||
+ key_uses_partial_cols(keyno))
+ {
+ /* Use the default implementation */
+ *flags |= HA_MRR_USE_DEFAULT_IMPL;
+ return TRUE;
+ }
+
+ uint add_len= h->table->key_info[keyno].key_length + h->ref_length;
+ *bufsz -= add_len;
+ if (get_disk_sweep_mrr_cost(keyno, rows, *flags, bufsz, &dsmrr_cost))
+ return TRUE;
+ *bufsz += add_len;
+
+ bool force_dsmrr;
+ /*
+ If @@optimizer_use_mrr==force, 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)) &&
+ dsmrr_cost.total_cost() > cost->total_cost())
+ dsmrr_cost= *cost;
+
+ if (force_dsmrr || dsmrr_cost.total_cost() <= cost->total_cost())
+ {
+ *flags &= ~HA_MRR_USE_DEFAULT_IMPL; /* Use the DS-MRR implementation */
+ *flags &= ~HA_MRR_SORTED; /* We will return unordered output */
+ *cost= dsmrr_cost;
+ res= FALSE;
+ }
+ else
+ {
+ /* Use the default MRR implementation */
+ res= TRUE;
+ }
+ return res;
+}
+
+
+static void get_sort_and_sweep_cost(TABLE *table, ha_rows nrows, COST_VECT *cost);
+
+
+/*
+ Get cost of DS-MRR scan
+
+ SYNOPSIS
+ get_disk_sweep_mrr_cost()
+ keynr Index to be used
+ rows E(Number of rows to be scanned)
+ flags Scan parameters (HA_MRR_* flags)
+ buffer_size INOUT Buffer size
+ cost OUT The cost
+
+ RETURN
+ FALSE OK
+ TRUE Error, DS-MRR cannot be used (the buffer is too small for even 1
+ rowid)
+*/
+
+bool DsMrr_impl::get_disk_sweep_mrr_cost(uint keynr, ha_rows rows, uint flags,
+ uint *buffer_size, COST_VECT *cost)
+{
+ ulong max_buff_entries, elem_size;
+ ha_rows rows_in_full_step, rows_in_last_step;
+ uint n_full_steps;
+ double index_read_cost;
+
+ elem_size= h->ref_length + sizeof(void*) * (!test(flags & HA_MRR_NO_ASSOCIATION));
+ max_buff_entries = *buffer_size / elem_size;
+
+ if (!max_buff_entries)
+ return TRUE; /* Buffer has not enough space for even 1 rowid */
+
+ /* Number of iterations we'll make with full buffer */
+ n_full_steps= (uint)floor(rows2double(rows) / max_buff_entries);
+
+ /*
+ Get numbers of rows we'll be processing in
+ - non-last sweep, with full buffer
+ - last iteration, with non-full buffer
+ */
+ rows_in_full_step= max_buff_entries;
+ rows_in_last_step= rows % max_buff_entries;
+
+ /* Adjust buffer size if we expect to use only part of the buffer */
+ if (n_full_steps)
+ {
+ get_sort_and_sweep_cost(h->table, rows, cost);
+ cost->multiply(n_full_steps);
+ }
+ else
+ {
+ cost->zero();
+ *buffer_size= rows_in_last_step * elem_size;
+ }
+
+ COST_VECT last_step_cost;
+ get_sort_and_sweep_cost(h->table, rows_in_last_step, &last_step_cost);
+ cost->add(&last_step_cost);
+
+ if (n_full_steps != 0)
+ cost->mem_cost= *buffer_size;
+ else
+ cost->mem_cost= rows_in_last_step * elem_size;
+
+ /* Total cost of all index accesses */
+ index_read_cost= h->index_only_read_time(keynr, rows);
+ cost->add_io(index_read_cost, 1 /* Random seeks */);
+ return FALSE;
+}
+
+
+/*
+ Get cost of one sort-and-sweep step
+
+ 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.
+*/
+
+static
+void get_sort_and_sweep_cost(TABLE *table, ha_rows nrows, COST_VECT *cost)
+{
+ if (nrows)
+ {
+ get_sweep_read_cost(table, nrows, FALSE, cost);
+ /* Add cost of qsort call: n * log2(n) * cost(rowid_comparison) */
+ double cmp_op= rows2double(nrows) * (1.0 / TIME_FOR_COMPARE_ROWID);
+ if (cmp_op < 3)
+ cmp_op= 3;
+ cost->cpu_cost += cmp_op * log2(cmp_op);
+ }
+ else
+ cost->zero();
+}
+
+
+/*
+ Get cost of reading nrows table records in a "disk sweep"
+
+ SYNOPSIS
+ get_sweep_read_cost()
+ table Table to be accessed
+ nrows Number of rows to retrieve
+ interrupted TRUE <=> Assume that the disk sweep will be
+ interrupted by other disk IO. FALSE - otherwise.
+ cost OUT The cost.
+
+ DESCRIPTION
+ Get cost of reading nrows table records in a "disk sweep".
+
+ A disk sweep read is a sequence of handler->rnd_pos(rowid) calls that made
+ for an ordered sequence of rowids.
+
+ We assume hard disk IO. The read is performed as follows:
+
+ 1. The disk head is moved to the needed cylinder
+ 2. The controller waits for the plate to rotate
+ 3. The data is transferred
+
+ Time to do #3 is insignificant compared to #2+#1.
+
+ Time to move the disk head is proportional to head travel distance.
+
+ Time to wait for the plate to rotate depends on whether the disk head
+ was moved or not.
+
+ If disk head wasn't moved, the wait time is proportional to distance
+ between the previous block and the block we're reading.
+
+ If the head was moved, we don't know how much we'll need to wait for the
+ plate to rotate. We assume the wait time to be a variate with a mean of
+ 0.5 of full rotation time.
+
+ Our cost units are "random disk seeks". The cost of random disk seek is
+ actually not a constant, it depends one range of cylinders we're going
+ to access. We make it constant by introducing a fuzzy concept of "typical
+ datafile length" (it's fuzzy as it's hard to tell whether it should
+ include index file, temp.tables etc). Then random seek cost is:
+
+ 1 = half_rotation_cost + move_cost * 1/3 * typical_data_file_length
+
+ We define half_rotation_cost as DISK_SEEK_BASE_COST=0.9.
+*/
+
+void get_sweep_read_cost(TABLE *table, ha_rows nrows, bool interrupted,
+ COST_VECT *cost)
+{
+ DBUG_ENTER("get_sweep_read_cost");
+
+ cost->zero();
+ if (table->file->primary_key_is_clustered())
+ {
+ cost->io_count= table->file->read_time(table->s->primary_key, nrows,
+ nrows);
+ }
+ else
+ {
+ double n_blocks=
+ ceil(ulonglong2double(table->file->stats.data_file_length) / IO_SIZE);
+ double busy_blocks=
+ n_blocks * (1.0 - pow(1.0 - 1.0/n_blocks, rows2double(nrows)));
+ if (busy_blocks < 1.0)
+ busy_blocks= 1.0;
+
+ DBUG_PRINT("info",("sweep: nblocks=%g, busy_blocks=%g", n_blocks,
+ busy_blocks));
+ cost->io_count= busy_blocks;
+
+ if (!interrupted)
+ {
+ /* Assume reading is done in one 'sweep' */
+ cost->avg_io_cost= (DISK_SEEK_BASE_COST +
+ DISK_SEEK_PROP_COST*n_blocks/busy_blocks);
+ }
+ }
+ DBUG_PRINT("info",("returning cost=%g", cost->total_cost()));
+ DBUG_VOID_RETURN;
+}
+
+
+/****************************************************************************
+ * DS-MRR implementation ends
+ ***************************************************************************/
+
/** @brief
Read first row between two ranges.
- Store ranges for future calls to read_range_next
SYNOPSIS
read_range_first()
@@ -3184,7 +4027,8 @@
*/
int handler::read_range_first(const key_range *start_key,
const key_range *end_key,
- bool eq_range_arg, bool sorted)
+ bool eq_range_arg,
+ bool sorted /* ignored */)
{
int result;
DBUG_ENTER("handler::read_range_first");
@@ -3217,7 +4061,7 @@
/** @brief
- Read next row between two ranges.
+ Read next row between two endpoints.
SYNOPSIS
read_range_next()
@@ -3269,7 +4113,7 @@
int handler::compare_key(key_range *range)
{
int cmp;
- if (!range)
+ if (!range || in_range_check_pushed_down)
return 0; // No max range
cmp= key_cmp(range_key_part, range->key, range->length);
if (!cmp)
@@ -3692,7 +4536,6 @@
DBUG_PRINT("enter", ("logfile '%s'", log_file));
DBUG_VOID_RETURN;
}
-
#ifdef TRANS_LOG_MGM_EXAMPLE_CODE
/*
--- 1.255/sql/handler.h 2007-04-04 22:52:01 +04:00
+++ 1.256/sql/handler.h 2007-04-04 22:52:01 +04:00
@@ -117,6 +117,8 @@
#define HA_HAS_RECORDS (LL(1) << 32) /* records() gives exact count*/
/* Has it's own method of binlog logging */
#define HA_HAS_OWN_BINLOGGING (LL(1) << 33)
+#define HA_MRR_CANT_SORT (LL(1) << 34)
+
/* bits in index_flags(index_number) for what you can do with index */
#define HA_READ_NEXT 1 /* TODO really use this flag */
@@ -125,6 +127,14 @@
#define HA_READ_RANGE 8 /* can find all records in a range */
#define HA_ONLY_WHOLE_INDEX 16 /* Can't use part key searches */
#define HA_KEYREAD_ONLY 64 /* Support HA_EXTRA_KEYREAD */
+/*
+ Index scan will not return records in rowid order. Not guaranteed to be
+ set for unordered (e.g. HASH) indexes.
+*/
+#define HA_KEY_SCAN_NOT_ROR 128
+#define HA_DO_INDEX_COND_PUSHDOWN 256 /* Supports Index Condition Pushdown */
+
+
/*
bits in alter_table_flags:
@@ -179,12 +189,6 @@
#define HA_FAST_CHANGE_PARTITION (1L << 13)
#define HA_PARTITION_ONE_PHASE (1L << 14)
-/*
- Index scan will not return records in rowid order. Not guaranteed to be
- set for unordered (e.g. HASH) indexes.
-*/
-#define HA_KEY_SCAN_NOT_ROR 128
-
/* operations for disable/enable indexes */
#define HA_KEY_SWITCH_NONUNIQ 0
#define HA_KEY_SWITCH_ALL 1
@@ -842,6 +846,143 @@
typedef struct system_status_var SSV;
+
+typedef void *range_seq_t;
+
+typedef struct st_range_seq_if
+{
+ /*
+ Initialize the traversal of range sequence
+
+ SYNOPSIS
+ init()
+ init_params The seq_init_param parameter
+ n_ranges The number of ranges obtained
+ flags A combination of HA_MRR_SINGLE_POINT, HA_MRR_FIXED_KEY
+
+ RETURN
+ An opaque value to be used as RANGE_SEQ_IF::next() parameter
+ */
+ range_seq_t (*init)(void *init_params, uint n_ranges, uint flags);
+
+
+ /*
+ Get the next range in the range sequence
+
+ SYNOPSIS
+ next()
+ seq The value returned by RANGE_SEQ_IF::init()
+ range OUT Information about the next range
+
+ RETURN
+ 0 - Ok, the range structure filled with info about the next range
+ 1 - No more ranges
+ */
+ uint (*next) (range_seq_t seq, KEY_MULTI_RANGE *range);
+} RANGE_SEQ_IF;
+
+uint16 &mrr_persistent_flag_storage(range_seq_t seq, uint idx);
+char* &mrr_get_ptr_by_idx(range_seq_t seq, uint idx);
+
+class COST_VECT
+{
+public:
+ double io_count; /* number of I/O */
+ double avg_io_cost; /* cost of an average I/O oper. */
+ double cpu_cost; /* cost of operations in CPU */
+ double mem_cost; /* cost of used memory */
+ double import_cost; /* cost of remote operations */
+
+ enum { IO_COEFF=1 };
+ enum { CPU_COEFF=1 };
+ enum { MEM_COEFF=1 };
+ enum { IMPORT_COEFF=1 };
+
+ double total_cost()
+ {
+ return IO_COEFF*io_count*avg_io_cost + CPU_COEFF * cpu_cost +
+ MEM_COEFF*mem_cost + IMPORT_COEFF*import_cost;
+ }
+
+ void zero()
+ {
+ avg_io_cost= 1.0;
+ io_count= cpu_cost= mem_cost= import_cost= 0.0;
+ }
+
+ void multiply(double m)
+ {
+ io_count *= m;
+ cpu_cost *= m;
+ import_cost *= m;
+ /* Don't multiply mem_cost */
+ }
+
+ void add(const COST_VECT* cost)
+ {
+ double io_count_sum= io_count + cost->io_count;
+ add_io(cost->io_count, cost->avg_io_cost);
+ io_count= io_count_sum;
+ cpu_cost += cost->cpu_cost;
+ }
+ void add_io(double add_io_cnt, double add_avg_cost)
+ {
+ double io_count_sum= io_count + add_io_cnt;
+ avg_io_cost= (io_count * avg_io_cost +
+ add_io_cnt * add_avg_cost) / io_count_sum;
+ io_count= io_count_sum;
+ }
+};
+
+void get_sweep_read_cost(TABLE *table, ha_rows nrows, bool interrupted,
+ COST_VECT *cost);
+
+/*
+ The below two are not used (and not handled) in this milestone of this WL
+ entry because there seems to be no use for them at this stage of
+ implementation.
+*/
+#define HA_MRR_SINGLE_POINT 1
+#define HA_MRR_FIXED_KEY 2
+
+/*
+ Indicates that RANGE_SEQ_IF::next(&range) doesn't need to fill in the
+ 'range' parameter.
+*/
+#define HA_MRR_NO_ASSOCIATION 4
+
+/*
+ The MRR user will provide ranges in key order, and MRR implementation
+ must return rows in key order.
+*/
+#define HA_MRR_SORTED 8
+
+/* MRR implementation doesn't have to retrieve full records */
+#define HA_MRR_INDEX_ONLY 16
+
+/*
+ The passed memory buffer is of maximum possible size, the caller can't
+ assume larger buffer.
+*/
+#define HA_MRR_LIMITS 32
+
+
+/*
+ Flag set <=> default MRR implementation is used
+ (The choice is made by **_info[_const]() function which may set this
+ flag. SQL layer remembers the flag value and then passes it to
+ multi_read_range_init().
+*/
+#define HA_MRR_USE_DEFAULT_IMPL 64
+
+/*
+ Used only as parameter to multi_range_read_info():
+ Flag set <=> the caller guarantees that the bounds of the scanned ranges
+ will not have NULL values.
+*/
+#define HA_MRR_NO_NULL_ENDPOINTS 128
+
+
class ha_statistics
{
public:
@@ -888,6 +1029,7 @@
class handler :public Sql_alloc
{
friend class ha_partition;
+ friend class DsMrr_impl;
protected:
struct st_table_share *table_share; /* The table definition */
@@ -917,18 +1059,34 @@
byte *dup_ref; /* Pointer to duplicate row */
ha_statistics stats;
-
- /* The following are for read_multi_range */
- bool multi_range_sorted;
- KEY_MULTI_RANGE *multi_range_curr;
- KEY_MULTI_RANGE *multi_range_end;
- HANDLER_BUFFER *multi_range_buffer;
+
+ /* Multi range read implementation-used members: */
+ range_seq_t mrr_iter; /* MRR range sequence iterator being traversed */
+ RANGE_SEQ_IF mrr_funcs; /* Saved MRR range sequence traversal functions */
+ HANDLER_BUFFER *multi_range_buffer; /* Saved MRR buffer info */
+ uint ranges_in_seq; /* Total number of ranges in the traversed sequence */
+ /* TRUE <=> source MRR ranges and the output are ordered */
+ bool mrr_is_output_sorted;
+
+ /* TRUE <=> we're currently traversing a range in mrr_cur_range. */
+ bool mrr_have_range;
+ /* Current range (the one we're now returning rows from) */
+ KEY_MULTI_RANGE mrr_cur_range;
+
+ /* Default MRR implementation: */
+ bool mrr_restore_scan; /* TRUE <=> we're restoring the scan */
+ int mrr_restore_scan_res; /* iff mrr_restore_scan: return value of next call */
/* The following are for read_range() */
key_range save_end_range, *end_range;
KEY_PART_INFO *range_key_part;
int key_compare_result_on_equal;
bool eq_range;
+ /*
+ TRUE <=> the engine checks that returned records are within the range
+ being scanned.
+ */
+ bool in_range_check_pushed_down;
uint errkey; /* Last dup key */
uint key_used_on_scan;
@@ -964,7 +1122,8 @@
handler(handlerton *ht_arg, TABLE_SHARE *share_arg)
:table_share(share_arg), estimation_rows_to_insert(0), ht(ht_arg),
- ref(0), key_used_on_scan(MAX_KEY), active_index(MAX_KEY),
+ ref(0), mrr_restore_scan(FALSE), in_range_check_pushed_down(FALSE),
+ key_used_on_scan(MAX_KEY), active_index(MAX_KEY),
ref_length(sizeof(my_off_t)),
ft_handler(0), inited(NONE), implicit_emptied(0),
pushed_cond(NULL), next_insert_id(0), insert_id_for_cur_row(0)
@@ -1029,10 +1188,26 @@
table= table_arg;
table_share= share;
}
+ /* Estimates calculation */
virtual double scan_time()
{ return ulonglong2double(stats.data_file_length) / IO_SIZE + 2; }
virtual double read_time(uint index, uint ranges, ha_rows rows)
{ return rows2double(ranges+rows); }
+
+ virtual double index_only_read_time(uint keynr, double records);
+
+ virtual ha_rows multi_range_read_info_const(uint keyno, RANGE_SEQ_IF *seq,
+ void *seq_init_param,
+ uint n_ranges, uint *bufsz,
+ uint *flags, COST_VECT *cost);
+ virtual int multi_range_read_info(uint keyno, uint n_ranges, uint keys,
+ uint *bufsz, uint *flags, COST_VECT *cost);
+ virtual int multi_range_read_init(RANGE_SEQ_IF *seq, void *seq_init_param,
+ uint n_ranges, uint mode,
+ HANDLER_BUFFER *buf);
+ virtual int multi_range_read_next(char **range_info);
+
+
virtual const key_map *keys_to_use_for_scanning() { return &key_map_empty; }
bool has_transactions()
{ return (ha_table_flags() & HA_NO_TRANSACTIONS) == 0; }
@@ -1269,10 +1444,6 @@
uint key_len= calculate_key_len(table, active_index, key, keypart_map);
return index_read_last(buf, key, key_len);
}
- virtual int read_multi_range_first(KEY_MULTI_RANGE **found_range_p,
- KEY_MULTI_RANGE *ranges, uint range_count,
- bool sorted, HANDLER_BUFFER *buffer);
- virtual int read_multi_range_next(KEY_MULTI_RANGE **found_range_p);
virtual int read_range_first(const key_range *start_key,
const key_range *end_key,
bool eq_range, bool sorted);
@@ -1613,14 +1784,18 @@
Calls to rnd_init/rnd_end, index_init/index_end etc do not affect the
condition stack.
*/
- virtual const COND *cond_push(const COND *cond) { return cond; };
+ virtual const COND *cond_push(const COND *cond) { return cond; }
+
/*
Pop the top condition from the condition stack of the handler instance.
SYNOPSIS
cond_pop()
Pops the top if condition stack, if stack is not empty
*/
- virtual void cond_pop() { return; };
+ virtual void cond_pop() { return; }
+
+ virtual Item *idx_cond_push(uint keyno, Item* idx_cond) { return idx_cond; }
+
virtual bool check_if_incompatible_data(HA_CREATE_INFO *create_info,
uint table_changes)
{ return COMPATIBLE_DATA_NO; }
@@ -1648,6 +1823,7 @@
*/
virtual void use_hidden_primary_key();
+ virtual void add_explain_extra_info(uint keyno, String *extra) {}
private:
/*
Row-level primitives for storage engines. These should be
@@ -1661,7 +1837,62 @@
}
};
- /* Some extern variables used with handlers */
+
+/*
+ A Disk-Sweep MRR interface implementation
+
+ This implementation makes range (and, in the future, 'ref') scans to read
+ table rows in disk sweeps.
+
+ Currently it is used by MyISAM and InnoDB. Potentially it can be used with
+ any table handler that has non-clustered indexes and on-disk rows.
+*/
+
+class DsMrr_impl
+{
+public:
+ typedef void (handler::*range_check_toggle_func_t)(bool on);
+
+ DsMrr_impl(range_check_toggle_func_t func)
+ : last_idx_tuple(NULL), range_check_toggle_func(func) {};
+ byte *rowids_buf; // rows buffer
+
+ byte *rowids_buf_cur; // current position in rowids buffer
+ byte *rowids_buf_last; // just-after-the-end position in rowids buffer
+ byte *rowids_buf_end;
+
+ KEY *mrr_key;
+ byte *last_idx_tuple;
+ uint mrr_keyno;
+ bool dsmrr_eof;
+
+ bool is_mrr_assoc; // TRUE <=> need mrr association (and the buffer holds
+ // {rowid, range_id} pairs
+ /* Some extern variables used with handlers */
+ handler *h;
+ bool use_default_impl;
+ range_check_toggle_func_t range_check_toggle_func;
+
+
+ int dsmrr_init(handler *h, KEY *key, RANGE_SEQ_IF *seq_funcs,
+ void *seq_init_param, uint n_ranges, uint mode,
+ HANDLER_BUFFER *buf);
+ int dsmrr_fill_buffer(handler *h);
+ int dsmrr_next(handler *h, char **range_info);
+
+ int dsmrr_info(uint keyno, uint n_ranges, uint keys, uint *bufsz,
+ uint *flags, COST_VECT *cost);
+
+ ha_rows dsmrr_info_const(uint keyno, RANGE_SEQ_IF *seq,
+ void *seq_init_param, uint n_ranges, uint *bufsz,
+ uint *flags, COST_VECT *cost);
+private:
+ bool key_uses_partial_cols(uint keyno);
+ 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);
+};
extern const char *ha_row_type[];
extern TYPELIB tx_isolation_typelib;
--- 1.157/sql/item_cmpfunc.h 2007-04-04 22:52:01 +04:00
+++ 1.158/sql/item_cmpfunc.h 2007-04-04 22:52:01 +04:00
@@ -404,6 +404,8 @@
const char *func_name() const { return "trigcond"; };
bool const_item() const { return FALSE; }
bool *get_trig_var() { return trig_var; }
+ /* The following is needed for ICP: */
+ table_map used_tables() const { return args[0]->used_tables(); }
};
class Item_func_not_all :public Item_func_not
--- 1.49/sql/key.cc 2007-04-04 22:52:01 +04:00
+++ 1.50/sql/key.cc 2007-04-04 22:52:01 +04:00
@@ -170,6 +170,29 @@
/*
+ Zero the null components of key tuple
+ SYNOPSIS
+ key_zero_nulls()
+ tuple
+ key_info
+
+ DESCRIPTION
+*/
+
+void key_zero_nulls(byte *tuple, KEY *key_info)
+{
+ KEY_PART_INFO *key_part= key_info->key_part;
+ KEY_PART_INFO *key_part_end= key_part + key_info->key_parts;
+ for (; key_part != key_part_end; key_part++)
+ {
+ if (key_part->null_bit && *tuple)
+ bzero(tuple+1, key_part->store_length-1);
+ tuple+= key_part->store_length;
+ }
+}
+
+
+/*
Restore a key from some buffer to record.
SYNOPSIS
--- 1.275/sql/log_event.cc 2007-04-04 22:52:01 +04:00
+++ 1.276/sql/log_event.cc 2007-04-04 22:52:01 +04:00
@@ -7129,7 +7129,13 @@
*/
if (table->file->ha_table_flags() & HA_DUPLICATE_POS)
{
+ if (table->file->inited && (error= table->file->ha_index_end()))
+ DBUG_RETURN(error);
+ if ((error= table->file->ha_rnd_init(FALSE)))
+ DBUG_RETURN(error);
+
error= table->file->rnd_pos(table->record[1], table->file->dup_ref);
+ table->file->ha_rnd_end();
if (error)
{
table->file->print_error(error, MYF(0));
@@ -7365,7 +7371,14 @@
*/
table->file->position(table->record[0]);
- int error= table->file->rnd_pos(table->record[0], table->file->ref);
+ int error;
+ if (table->file->inited && (error= table->file->ha_index_end()))
+ DBUG_RETURN(error);
+ if ((error= table->file->ha_rnd_init(FALSE)))
+ DBUG_RETURN(error);
+
+ error= table->file->rnd_pos(table->record[0], table->file->ref);
+ table->file->ha_rnd_end();
/*
rnd_pos() returns the record in table->record[0], so we have to
move it to table->record[1].
--- 1.495/sql/mysql_priv.h 2007-04-04 22:52:01 +04:00
+++ 1.496/sql/mysql_priv.h 2007-04-04 22:52:01 +04:00
@@ -222,11 +222,11 @@
The cost of average seek
DISK_SEEK_BASE_COST + DISK_SEEK_PROP_COST*BLOCKS_IN_AVG_SEEK =1.0.
*/
-#define DISK_SEEK_BASE_COST ((double)0.5)
+#define DISK_SEEK_BASE_COST ((double)0.9)
#define BLOCKS_IN_AVG_SEEK 128
-#define DISK_SEEK_PROP_COST ((double)0.5/BLOCKS_IN_AVG_SEEK)
+#define DISK_SEEK_PROP_COST ((double)0.1/BLOCKS_IN_AVG_SEEK)
/*
@@ -1515,6 +1515,7 @@
int find_ref_key(KEY *key, uint key_count, byte *record, Field *field,
uint *key_length, uint *keypart);
void key_copy(byte *to_key, byte *from_record, KEY *key_info, uint key_length);
+void key_zero_nulls(byte *tuple, KEY *key_info);
void key_restore(byte *to_record, byte *from_key, KEY *key_info,
uint key_length);
bool key_cmp_if_same(TABLE *form,const byte *key,uint index,uint key_length);
--- 1.626/sql/mysqld.cc 2007-04-04 22:52:01 +04:00
+++ 1.627/sql/mysqld.cc 2007-04-04 22:52:01 +04:00
@@ -2907,6 +2907,8 @@
global_system_variables.character_set_client= default_charset_info;
global_system_variables.collation_connection= default_charset_info;
+ global_system_variables.optimizer_use_mrr= 1;
+
if (!(character_set_filesystem=
get_charset_by_csname(character_set_filesystem_name,
MY_CS_PRIMARY, MYF(MY_WME))))
@@ -6202,11 +6204,6 @@
"After this many write locks, allow some read locks to run in between.",
(gptr*) &max_write_lock_count, (gptr*) &max_write_lock_count, 0, GET_ULONG,
REQUIRED_ARG, ~0L, 1, ~0L, 0, 1, 0},
- {"multi_range_count", OPT_MULTI_RANGE_COUNT,
- "Number of key ranges to request at once.",
- (gptr*) &global_system_variables.multi_range_count,
- (gptr*) &max_system_variables.multi_range_count, 0,
- GET_ULONG, REQUIRED_ARG, 256, 1, ~0L, 0, 1, 0},
{"myisam_block_size", OPT_MYISAM_BLOCK_SIZE,
"Block size to be used for MyISAM index pages.",
(gptr*) &opt_myisam_block_size,
@@ -6356,8 +6353,8 @@
"When reading rows in sorted order after a sort, the rows are read through this buffer to avoid a disk seeks. If not set, then it's set to the value of record_buffer.",
(gptr*) &global_system_variables.read_rnd_buff_size,
(gptr*) &max_system_variables.read_rnd_buff_size, 0,
- GET_ULONG, REQUIRED_ARG, 256*1024L, IO_SIZE*2+MALLOC_OVERHEAD,
- SSIZE_MAX, MALLOC_OVERHEAD, IO_SIZE, 0},
+ GET_ULONG, REQUIRED_ARG, 256*1024L, 64 /*IO_SIZE*2+MALLOC_OVERHEAD*/ ,
+ SSIZE_MAX, MALLOC_OVERHEAD, 1 /* Small lower limit to be able to test MRR */, 0},
{"record_buffer", OPT_RECORD_BUFFER,
"Alias for read_buffer_size",
(gptr*) &global_system_variables.read_buff_size,
--- 1.272/sql/opt_range.cc 2007-04-04 22:52:01 +04:00
+++ 1.273/sql/opt_range.cc 2007-04-04 22:52:01 +04:00
@@ -34,7 +34,7 @@
The lists are returned in form of complicated structure of interlinked
SEL_TREE/SEL_IMERGE/SEL_ARG objects.
- See check_quick_keys, find_used_partitions for examples of how to walk
+ See quick_range_seq_next, find_used_partitions for examples of how to walk
this structure.
All direct "users" of this module are located within this file, too.
@@ -59,6 +59,48 @@
Record retrieval code for range/index_merge/groupby-min-max.
Implementations of QUICK_*_SELECT classes.
+
+ KeyTupleFormat
+ ~~~~~~~~~~~~~~
+ The code in this file (and elsewhere) makes operations on key value tuples.
+ Those tuples are stored in the following format:
+
+ The tuple is a sequence of key part values. The length of key part value
+ depends only on its type (and not depends on the what value is stored)
+
+ KeyTuple: keypart1-data, keypart2-data, ...
+
+ The value of each keypart is stored in the following format:
+
+ keypart_data: [isnull_byte] keypart-value-bytes
+
+ If a keypart may have a NULL value (key_part->field->real_maybe_null() can
+ be used to check this), then the first byte is a NULL indicator with the
+ following valid values:
+ 1 - keypart has NULL value.
+ 0 - keypart has non-NULL value.
+
+ <questionable-statement> If isnull_byte==1 (NULL value), then the following
+ keypart->length bytes must be 0.
+ </questionable-statement>
+
+ keypart-value-bytes holds the value. Its format depends on the field type.
+ The length of keypart-value-bytes may or may not depend on the value being
+ stored. The default is that length is static and equal to
+ KEY_PART_INFO::length.
+
+ Key parts with (key_part_flag & HA_BLOB_PART) have length depending of the
+ value:
+
+ keypart-value-bytes: value_length value_bytes
+
+ The value_length part itself occupies HA_KEY_BLOB_LENGTH=2 bytes.
+
+ See key_copy() and key_restore() for code to move data between index tuple
+ and table record
+
+ CAUTION: the above description is only sergefp's understanding of the
+ subject and may omit some details.
*/
#ifdef USE_PRAGMA_IMPLEMENTATION
@@ -400,6 +442,7 @@
/* returns a number of keypart values (0 or 1) appended to the key buffer */
int store_min(uint length,char **min_key,uint min_key_flag)
{
+ /* "(kp1 > c1) AND (kp2 OP c2) AND ..." -> (kp1 > c1) */
if ((min_flag & GEOM_FLAG) ||
(!(min_flag & NO_MIN_RANGE) &&
!(min_key_flag & (NO_MIN_RANGE | NEAR_MIN))))
@@ -417,7 +460,7 @@
return 0;
}
/* returns a number of keypart values (0 or 1) appended to the key buffer */
- int store_max(uint length,char **max_key, uint max_key_flag)
+ int store_max(uint length, char **max_key, uint max_key_flag)
{
if (!(max_flag & NO_MAX_RANGE) &&
!(max_key_flag & (NO_MAX_RANGE | NEAR_MAX)))
@@ -442,6 +485,7 @@
uint res= key_tree->store_min(key[key_tree->part].store_length,
range_key, *range_key_flag);
*range_key_flag|= key_tree->min_flag;
+
if (key_tree->next_key_part &&
key_tree->next_key_part->part == key_tree->part+1 &&
!(*range_key_flag & (NO_MIN_RANGE | NEAR_MIN)) &&
@@ -632,6 +676,7 @@
uint real_keynr[MAX_KEY];
/* Number of SEL_ARG objects allocated by SEL_ARG::clone_tree operations */
uint alloced_sel_args;
+ bool force_default_mrr;
};
class PARAM : public RANGE_OPT_PARAM
@@ -639,8 +684,9 @@
public:
KEY_PART *key[MAX_KEY]; /* First key parts of keys used in the query */
longlong baseflag;
- uint max_key_part, range_count;
-
+ uint max_key_part;
+ /* Number of ranges in the last checked tree->key */
+ uint range_count;
char min_key[MAX_KEY_LENGTH+MAX_FIELD_WIDTH],
max_key[MAX_KEY_LENGTH+MAX_FIELD_WIDTH];
@@ -659,7 +705,6 @@
bool is_ror_scan;
/* Number of ranges in the last checked tree->key */
uint n_ranges;
- uint8 first_null_comp; /* first null component if any, 0 - otherwise */
};
class TABLE_READ_PLAN;
@@ -680,15 +725,18 @@
static SEL_TREE *get_mm_tree(RANGE_OPT_PARAM *param,COND *cond);
static bool is_key_scan_ror(PARAM *param, uint keynr, uint8 nparts);
-static ha_rows check_quick_select(PARAM *param,uint index,SEL_ARG *key_tree,
+static ha_rows check_quick_select(PARAM *param, uint idx, bool index_only,
+ SEL_ARG *tree, bool update_tbl_stats,
+ uint *mrr_flags, uint *bufsize,
+ COST_VECT *cost);
bool update_tbl_stats);
static ha_rows check_quick_keys(PARAM *param,uint index,SEL_ARG *key_tree,
char *min_key, uint min_key_flag, int,
char *max_key, uint max_key_flag, int);
QUICK_RANGE_SELECT *get_quick_select(PARAM *param,uint index,
- SEL_ARG *key_tree,
- MEM_ROOT *alloc = NULL);
+ SEL_ARG *key_tree, uint mrr_flags,
+ uint mrr_buf_size, MEM_ROOT *alloc);
static TRP_RANGE *get_key_scans_params(PARAM *param, SEL_TREE *tree,
bool index_read_must_be_used,
bool update_tbl_stats,
@@ -706,8 +754,6 @@
double read_time);
static
TRP_GROUP_MIN_MAX *get_best_group_min_max(PARAM *param, SEL_TREE *tree);
-static double get_index_only_read_time(const PARAM* param, ha_rows records,
- int keynr);
#ifndef DBUG_OFF
static void print_sel_tree(PARAM *param, SEL_TREE *tree, key_map *tree_map,
@@ -1027,8 +1073,9 @@
{}
QUICK_RANGE_SELECT::QUICK_RANGE_SELECT(THD *thd, TABLE *table, uint key_nr,
- bool no_alloc, MEM_ROOT *parent_alloc)
- :dont_free(0),error(0),free_file(0),in_range(0),cur_range(NULL),last_range(0)
+ bool no_alloc, MEM_ROOT *parent_alloc,
+ bool *create_error)
+ :dont_free(0),free_file(0),cur_range(NULL),last_range(0)
{
my_bitmap_map *bitmap;
DBUG_ENTER("QUICK_RANGE_SELECT::QUICK_RANGE_SELECT");
@@ -1041,11 +1088,8 @@
my_init_dynamic_array(&ranges, sizeof(QUICK_RANGE*), 16, 16);
/* 'thd' is not accessible in QUICK_RANGE_SELECT::reset(). */
- multi_range_bufsiz= thd->variables.read_rnd_buff_size;
- multi_range_count= thd->variables.multi_range_count;
- multi_range_length= 0;
- multi_range= NULL;
- multi_range_buff= NULL;
+ mrr_buf_size= thd->variables.read_rnd_buff_size;
+ mrr_buf_desc= NULL;
if (!no_alloc && !parent_alloc)
{
@@ -1060,12 +1104,12 @@
save_read_set= head->read_set;
save_write_set= head->write_set;
- /* Allocate a bitmap for used columns */
+ /* Allocate a bitmap for used columns (Q: why not on MEM_ROOT?) */
if (!(bitmap= (my_bitmap_map*) my_malloc(head->s->column_bitmap_size,
MYF(MY_WME))))
{
column_bitmap.bitmap= 0;
- error= 1;
+ *create_error= 1;
}
else
bitmap_init(&column_bitmap, bitmap, head->s->fields, FALSE);
@@ -1079,7 +1123,7 @@
if (file->inited != handler::NONE)
file->ha_index_or_rnd_end();
- DBUG_RETURN(error= file->ha_index_init(index, 1));
+ DBUG_RETURN(file->ha_index_init(index, 1));
}
@@ -1118,8 +1162,7 @@
my_free((char*) column_bitmap.bitmap, MYF(MY_ALLOW_ZERO_PTR));
}
head->column_bitmaps_set(save_read_set, save_write_set);
- x_free(multi_range);
- x_free(multi_range_buff);
+ x_free(mrr_buf_desc);
DBUG_VOID_RETURN;
}
@@ -1879,9 +1922,11 @@
public:
SEL_ARG *key; /* set of intervals to be used in "range" method retrieval */
uint key_idx; /* key number in PARAM::key */
+ uint mrr_flags;
+ uint mrr_buf_size;
- TRP_RANGE(SEL_ARG *key_arg, uint idx_arg)
- : key(key_arg), key_idx(idx_arg)
+ TRP_RANGE(SEL_ARG *key_arg, uint idx_arg, uint mrr_flags_arg)
+ : key(key_arg), key_idx(idx_arg), mrr_flags(mrr_flags_arg)
{}
virtual ~TRP_RANGE() {} /* Remove gcc warning */
@@ -1890,7 +1935,8 @@
{
DBUG_ENTER("TRP_RANGE::make_quick");
QUICK_RANGE_SELECT *quick;
- if ((quick= get_quick_select(param, key_idx, key, parent_alloc)))
+ if ((quick= get_quick_select(param, key_idx, key, mrr_flags, mrr_buf_size,
+ parent_alloc)))
{
quick->records= records;
quick->read_time= read_cost;
@@ -2115,7 +2161,8 @@
int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
table_map prev_tables,
- ha_rows limit, bool force_quick_range)
+ ha_rows limit, bool force_quick_range,
+ bool ordered_output)
{
uint idx;
double scan_time;
@@ -2170,6 +2217,7 @@
param.imerge_cost_buff_size= 0;
param.using_real_indexes= TRUE;
param.remove_jump_scans= TRUE;
+ param.force_default_mrr= ordered_output;
thd->no_errors=1; // Don't warn about NULL
init_sql_alloc(&alloc, thd->variables.range_alloc_block_size, 0);
@@ -2223,9 +2271,10 @@
if (!head->covering_keys.is_clear_all())
{
int key_for_use= find_shortest_key(head, &head->covering_keys);
- double key_read_time= (get_index_only_read_time(¶m, records,
- key_for_use) +
- (double) records / TIME_FOR_COMPARE);
+ double key_read_time=
+ param.table->file->index_only_read_time(key_for_use,
+ rows2double(records)) +
+ (double) records / TIME_FOR_COMPARE;
DBUG_PRINT("info", ("'all'+'using index' scan will be using key %d, "
"read time %g", key_for_use, key_read_time));
if (key_read_time < read_time)
@@ -3016,7 +3065,7 @@
{
/*
Partitioning is done by RANGE|INTERVAL(monotonic_expr(fieldX)), and
- we got "const1 CMP fieldX CMP const2" interval <-- psergey-todo: change
+ we got "const1 CMP fieldX CMP const2" interval
*/
DBUG_EXECUTE("info", dbug_print_segment_range(key_tree,
ppar->range_param.
@@ -3335,7 +3384,7 @@
key_part->part= part;
key_part->length= (uint16) (*field)->pack_length_in_rec();
/*
- psergey-todo: check yet again if this is correct for tricky field types,
+ sergefp-todo: check yet again if this is correct for tricky field types,
e.g. see "Fix a fatal error in decimal key handling" in open_binary_frm()
*/
key_part->store_length= (uint16) (*field)->pack_length();
@@ -3479,63 +3528,6 @@
/*
- Get cost of 'sweep' full records retrieval.
- SYNOPSIS
- get_sweep_read_cost()
- param Parameter from test_quick_select
- records # of records to be retrieved
- RETURN
- cost of sweep
-*/
-
-double get_sweep_read_cost(const PARAM *param, ha_rows records)
-{
- double result;
- DBUG_ENTER("get_sweep_read_cost");
- if (param->table->file->primary_key_is_clustered())
- {
- result= param->table->file->read_time(param->table->s->primary_key,
- records, records);
- }
- else
- {
- double n_blocks=
- ceil(ulonglong2double(param->table->file->stats.data_file_length) /
- IO_SIZE);
- double busy_blocks=
- n_blocks * (1.0 - pow(1.0 - 1.0/n_blocks, rows2double(records)));
- if (busy_blocks < 1.0)
- busy_blocks= 1.0;
- DBUG_PRINT("info",("sweep: nblocks: %g, busy_blocks: %g", n_blocks,
- busy_blocks));
- /*
- Disabled: Bail out if # of blocks to read is bigger than # of blocks in
- table data file.
- if (max_cost != DBL_MAX && (busy_blocks+index_reads_cost) >= n_blocks)
- return 1;
- */
- JOIN *join= param->thd->lex->select_lex.join;
- if (!join || join->tables == 1)
- {
- /* No join, assume reading is done in one 'sweep' */
- result= busy_blocks*(DISK_SEEK_BASE_COST +
- DISK_SEEK_PROP_COST*n_blocks/busy_blocks);
- }
- else
- {
- /*
- Possibly this is a join with source table being non-last table, so
- assume that disk seeks are random here.
- */
- result= busy_blocks;
- }
- }
- DBUG_PRINT("return",("cost: %g", result));
- DBUG_RETURN(result);
-}
-
-
-/*
Get best plan for a SEL_IMERGE disjunctive expression.
SYNOPSIS
get_best_disjunct_quick()
@@ -3696,7 +3688,14 @@
}
/* Calculate cost(rowid_to_row_scan) */
- imerge_cost += get_sweep_read_cost(param, non_cpk_scan_records);
+ {
+ COST_VECT sweep_cost;
+ JOIN *join= param->thd->lex->select_lex.join;
+ bool is_interrupted= test(join && join->tables == 1);
+ get_sweep_read_cost(param->table, non_cpk_scan_records, is_interrupted,
+ &sweep_cost);
+ imerge_cost += sweep_cost.total_cost();
+ }
DBUG_PRINT("info",("index_merge cost with rowid-to-row scan: %g",
imerge_cost));
if (imerge_cost > read_time)
@@ -3808,12 +3807,18 @@
cost_of_row_retrieval
See get_merge_buffers_cost function for queue_use_cost formula derivation.
*/
-
double roru_total_cost;
- roru_total_cost= roru_index_costs +
- rows2double(roru_total_records)*log((double)n_child_scans) /
- (TIME_FOR_COMPARE_ROWID * M_LN2) +
- get_sweep_read_cost(param, roru_total_records);
+ {
+ COST_VECT sweep_cost;
+ JOIN *join= param->thd->lex->select_lex.join;
+ bool is_interrupted= test(join && join->tables == 1);
+ get_sweep_read_cost(param->table, roru_total_records, is_interrupted,
+ &sweep_cost);
+ roru_total_cost= roru_index_costs +
+ rows2double(roru_total_records)*log((double)n_child_scans) /
+ (TIME_FOR_COMPARE_ROWID * M_LN2) +
+ sweep_cost.total_cost();
+ }
DBUG_PRINT("info", ("ROR-union: cost %g, %d members", roru_total_cost,
n_child_scans));
@@ -3833,42 +3838,6 @@
}
-/*
- Calculate cost of 'index only' scan for given index and number of records.
-
- SYNOPSIS
- get_index_only_read_time()
- param parameters structure
- records #of records to read
- keynr key to read
-
- NOTES
- It is assumed that we will read trough the whole key range and that all
- key blocks are half full (normally things are much better). It is also
- assumed that each time we read the next key from the index, the handler
- performs a random seek, thus the cost is proportional to the number of
- blocks read.
-
- TODO:
- Move this to handler->read_time() by adding a flag 'index-only-read' to
- this call. The reason for doing this is that the current function doesn't
- handle the case when the row is stored in the b-tree (like in innodb
- clustered index)
-*/
-
-static double get_index_only_read_time(const PARAM* param, ha_rows records,
- int keynr)
-{
- double read_time;
- uint keys_per_block= (param->table->file->stats.block_size/2/
- (param->table->key_info[keynr].key_length+
- param->table->file->ref_length) + 1);
- read_time=((double) (records+keys_per_block-1)/
- (double) keys_per_block);
- return read_time;
-}
-
-
typedef struct st_ror_scan_info
{
uint idx; /* # of used key in param->keys */
@@ -3944,9 +3913,9 @@
if (bitmap_is_set(¶m->needed_fields, key_part->fieldnr-1))
bitmap_set_bit(&ror_scan->covered_fields, key_part->fieldnr-1);
}
+ double rows= rows2double(param->table->quick_rows[ror_scan->keynr]);
ror_scan->index_read_cost=
- get_index_only_read_time(param, param->table->quick_rows[ror_scan->keynr],
- ror_scan->keynr);
+ param->table->file->index_only_read_time(ror_scan->keynr, rows);
DBUG_RETURN(ror_scan);
}
@@ -4326,8 +4295,12 @@
DBUG_PRINT("info", ("info->total_cost: %g", info->total_cost));
if (!info->is_covering)
{
- info->total_cost +=
- get_sweep_read_cost(info->param, double2rows(info->out_rows));
+ COST_VECT sweep_cost;
+ JOIN *join= info->param->thd->lex->select_lex.join;
+ bool is_interrupted= test(join && join->tables == 1);
+ get_sweep_read_cost(info->param->table, double2rows(info->out_rows),
+ is_interrupted, &sweep_cost);
+ info->total_cost += sweep_cost.total_cost();
DBUG_PRINT("info", ("info->total_cost= %g", info->total_cost));
}
DBUG_PRINT("info", ("New out_rows: %g", info->out_rows));
@@ -4711,15 +4684,26 @@
/*
- Get best "range" table read plan for given SEL_TREE.
- Also update PARAM members and store ROR scans info in the SEL_TREE.
+ Get best "range" table read plan for given SEL_TREE, also update some info
+
SYNOPSIS
- get_key_scans_params
- param parameters from test_quick_select
- tree make range select for this SEL_TREE
- index_read_must_be_used if TRUE, assume 'index only' option will be set
- (except for clustered PK indexes)
- read_time don't create read plans with cost > read_time.
+ get_key_scans_params()
+ param Parameters from test_quick_select
+ tree Make range select for this SEL_TREE
+ index_read_must_be_used TRUE <=> assume 'index only' option will be set
+ (except for clustered PK indexes)
+ update_tbl_stats TRUE <=> update table->quick_* with information
+ about range scans we've evaluated.
+ read_time Maximum cost. i.e. don't create read plans with
+ cost > read_time.
+
+ DESCRIPTION
+ Find the best "range" table read plan for given SEL_TREE.
+ The side effects are
+ - tree->ror_scans is updated to indicate which scans are ROR scans.
+ - if update_tbl_stats=TRUE then table->quick_* is updated with info
+ about every possible range scan.
+
RETURN
Best range read plan
NULL if no plan found or error occurred
@@ -4730,13 +4714,15 @@
bool update_tbl_stats,
double read_time)
{
- int idx;
+ uint idx;
SEL_ARG **key,**end, **key_to_read= NULL;
ha_rows best_records;
+ uint best_mrr_flags, best_buf_size;
TRP_RANGE* read_plan= NULL;
- bool pk_is_clustered= param->table->file->primary_key_is_clustered();
DBUG_ENTER("get_key_scans_params");
LINT_INIT(best_records); /* protected by key_to_read */
+ LINT_INIT(best_mrr_flags); /* protected by key_to_read */
+ LINT_INIT(best_buf_size); /* protected by key_to_read */
/*
Note that there may be trees that have type SEL_TREE::KEY but contain no
key reads at all, e.g. tree for expression "key1 is not null" where key1
@@ -4746,64 +4732,39 @@
"tree scans"););
tree->ror_scans_map.clear_all();
tree->n_ror_scans= 0;
- for (idx= 0,key=tree->keys, end=key+param->keys;
- key != end ;
- key++,idx++)
+ for (idx= 0,key=tree->keys, end=key+param->keys; key != end; key++,idx++)
{
- ha_rows found_records;
- double found_read_time;
if (*key)
{
+ ha_rows found_records;
+ COST_VECT cost;
+ double found_read_time;
+ uint mrr_flags, buf_size;
uint keynr= param->real_keynr[idx];
if ((*key)->type == SEL_ARG::MAYBE_KEY ||
(*key)->maybe_flag)
param->needed_reg->set_bit(keynr);
- bool read_index_only= index_read_must_be_used ? TRUE :
- (bool) param->table->covering_keys.is_set(keynr);
+ bool read_index_only= index_read_must_be_used ||
+ param->table->covering_keys.is_set(keynr);
- found_records= check_quick_select(param, idx, *key, update_tbl_stats);
- if (param->is_ror_scan)
+ found_records= check_quick_select(param, idx, read_index_only, *key,
+ update_tbl_stats, &mrr_flags,
+ &buf_size, &cost);
+ found_read_time= cost.total_cost();
+ if ((found_records != HA_POS_ERROR) && param->is_ror_scan)
{
tree->n_ror_scans++;
tree->ror_scans_map.set_bit(idx);
}
- double cpu_cost= (double) found_records / TIME_FOR_COMPARE;
- if (found_records != HA_POS_ERROR && found_records > 2 &&
- read_index_only &&
- (param->table->file->index_flags(keynr, param->max_key_part,1) &
- HA_KEYREAD_ONLY) &&
- !(pk_is_clustered && keynr == param->table->s->primary_key))
- {
- /*
- We can resolve this by only reading through this key.
- 0.01 is added to avoid races between range and 'index' scan.
- */
- found_read_time= get_index_only_read_time(param,found_records,keynr) +
- cpu_cost + 0.01;
- }
- else
- {
- /*
- cost(read_through_index) = cost(disk_io) + cost(row_in_range_checks)
- The row_in_range check is in QUICK_RANGE_SELECT::cmp_next function.
- */
- found_read_time= param->table->file->read_time(keynr,
- param->range_count,
- found_records) +
- cpu_cost + 0.01;
- }
- DBUG_PRINT("info",("key %s: found_read_time: %g (cur. read_time: %g)",
- param->table->key_info[keynr].name, found_read_time,
- read_time));
-
if (read_time > found_read_time && found_records != HA_POS_ERROR)
{
read_time= found_read_time;
best_records= found_records;
key_to_read= key;
+ best_mrr_flags= mrr_flags;
+ best_buf_size= buf_size;
}
-
}
}
@@ -4812,11 +4773,13 @@
if (key_to_read)
{
idx= key_to_read - tree->keys;
- if ((read_plan= new (param->mem_root) TRP_RANGE(*key_to_read, idx)))
+ if ((read_plan= new (param->mem_root) TRP_RANGE(*key_to_read, idx,
+ best_mrr_flags)))
{
read_plan->records= best_records;
read_plan->is_ror= tree->ror_scans_map.is_set(idx);
read_plan->read_cost= read_time;
+ read_plan->mrr_buf_size= best_buf_size;
DBUG_PRINT("info",
("Returning range plan for key %s, cost %g, records %lu",
param->table->key_info[param->real_keynr[idx]].name,
@@ -4879,7 +4842,9 @@
for (; first_scan != last_scan;++first_scan)
{
if (!(quick= get_quick_select(param, (*first_scan)->idx,
- (*first_scan)->sel_arg, alloc)) ||
+ (*first_scan)->sel_arg,
+ HA_MRR_USE_DEFAULT_IMPL | HA_MRR_SORTED,
+ 0, alloc)) ||
quick_intrsect->push_quick_back(quick))
{
delete quick_intrsect;
@@ -4889,7 +4854,9 @@
if (cpk_scan)
{
if (!(quick= get_quick_select(param, cpk_scan->idx,
- cpk_scan->sel_arg, alloc)))
+ cpk_scan->sel_arg,
+ HA_MRR_USE_DEFAULT_IMPL | HA_MRR_SORTED,
+ 0, alloc)))
{
delete quick_intrsect;
DBUG_RETURN(NULL);
@@ -7113,242 +7080,242 @@
#endif
+/****************************************************************************
+ MRR Range Sequence Interface implementation that walks a SEL_ARG* tree.
+ ****************************************************************************/
+
+/* MRR range sequence, SEL_ARG* implementation: stack entry */
+typedef struct st_range_seq_entry
+{
+ /*
+ Pointers in min and max keys. They point to right-after-end of key
+ images. The 0-th entry has these pointing to key tuple start.
+ */
+ char *min_key, *max_key;
+
+ /*
+ Flags, for {keypart0, keypart1, ... this_keypart} subtuple.
+ min_key_flag may have NULL_RANGE set.
+ */
+ uint min_key_flag, max_key_flag;
+ SEL_ARG *key_tree;
+} RANGE_SEQ_ENTRY;
+
/*
- Calculate estimate of number records that will be retrieved by a range
- scan on given index using given SEL_ARG intervals tree.
- SYNOPSIS
- check_quick_select
- param Parameter from test_quick_select
- idx Number of index to use in tree->keys
- tree Transformed selection condition, tree->keys[idx]
- holds the range tree to be used for scanning.
- update_tbl_stats If true, update table->quick_keys with information
- about range scan we've evaluated.
+ MRR range sequence, SEL_ARG* implementation: SEL_ARG graph traversal context
+*/
+typedef struct st_sel_arg_range_seq
+{
+ uint keyno; /* index of used tree in SEL_TREE structure */
+ uint real_keyno; /* Number of the index in tables */
+ PARAM *param;
+ SEL_ARG *start; /* Root node of the traversed SEL_ARG* graph */
+
+ RANGE_SEQ_ENTRY stack[MAX_REF_PARTS];
+ int i; /* Index of last used element in the above array */
+
+ bool at_start; /* TRUE <=> The traversal has just started */
+} SEL_ARG_RANGE_SEQ;
- NOTES
- param->is_ror_scan is set to reflect if the key scan is a ROR (see
- is_key_scan_ror function for more info)
- param->table->quick_*, param->range_count (and maybe others) are
- updated with data of given key scan, see check_quick_keys for details.
- RETURN
- Estimate # of records to be retrieved.
- HA_POS_ERROR if estimate calculation failed due to table handler problems.
+/*
+ Range sequence interface, SEL_ARG* implementation: Initialize the traversal
+ SYNOPSIS
+ init()
+ init_params SEL_ARG tree traversal context
+ n_ranges [ignored] The number of ranges obtained
+ flags [ignored] HA_MRR_SINGLE_POINT, HA_MRR_FIXED_KEY
+
+ RETURN
+ Value of init_param
*/
-static ha_rows
-check_quick_select(PARAM *param,uint idx,SEL_ARG *tree, bool update_tbl_stats)
+range_seq_t sel_arg_range_seq_init(void *init_param, uint n_ranges, uint flags)
{
- ha_rows records;
- bool cpk_scan;
- uint key;
- DBUG_ENTER("check_quick_select");
-
- param->is_ror_scan= FALSE;
- param->first_null_comp= 0;
+ SEL_ARG_RANGE_SEQ *seq= (SEL_ARG_RANGE_SEQ*)init_param;
+ seq->at_start= TRUE;
+ seq->stack[0].key_tree= NULL;
+ seq->stack[0].min_key= seq->param->min_key;
+ seq->stack[0].min_key_flag= 0;
+ seq->stack[0].max_key= seq->param->max_key;
+ seq->stack[0].max_key_flag= 0;
+ seq->i= 0;
+ return init_param;
+}
- if (!tree)
- DBUG_RETURN(HA_POS_ERROR); // Can't use it
- param->max_key_part=0;
- param->range_count=0;
- key= param->real_keynr[idx];
- if (tree->type == SEL_ARG::IMPOSSIBLE)
- DBUG_RETURN(0L); // Impossible select. return
- if (tree->type != SEL_ARG::KEY_RANGE || tree->part != 0)
- DBUG_RETURN(HA_POS_ERROR); // Don't use tree
+static void step_down_to(SEL_ARG_RANGE_SEQ *arg, SEL_ARG *key_tree)
+{
+ RANGE_SEQ_ENTRY *cur= &arg->stack[arg->i+1];
+ RANGE_SEQ_ENTRY *prev= &arg->stack[arg->i];
+
+ cur->key_tree= key_tree;
+ cur->min_key= prev->min_key;
+ cur->max_key= prev->max_key;
+
+ key_tree->store(arg->param->key[arg->keyno][key_tree->part].store_length,
+ &cur->min_key, prev->min_key_flag,
+ &cur->max_key, prev->max_key_flag);
+ cur->min_key_flag= prev->min_key_flag | key_tree->min_flag;
+ cur->max_key_flag= prev->max_key_flag | key_tree->max_flag;
- enum ha_key_alg key_alg= param->table->key_info[key].algorithm;
- if ((key_alg != HA_KEY_ALG_BTREE) && (key_alg!= HA_KEY_ALG_UNDEF))
- {
- /* Records are not ordered by rowid for other types of indexes. */
- cpk_scan= FALSE;
- }
- else
- {
- /*
- Clustered PK scan is a special case, check_quick_keys doesn't recognize
- CPK scans as ROR scans (while actually any CPK scan is a ROR scan).
- */
- cpk_scan= ((param->table->s->primary_key == param->real_keynr[idx]) &&
- param->table->file->primary_key_is_clustered());
- param->is_ror_scan= !cpk_scan;
- }
- param->n_ranges= 0;
-
- records= check_quick_keys(param, idx, tree,
- param->min_key, 0, -1,
- param->max_key, 0, -1);
- if (records != HA_POS_ERROR)
- {
- if (update_tbl_stats)
- {
- param->table->quick_keys.set_bit(key);
- param->table->quick_key_parts[key]=param->max_key_part+1;
- param->table->quick_n_ranges[key]= param->n_ranges;
- param->table->quick_condition_rows=
- min(param->table->quick_condition_rows, records);
- }
- /*
- Need to save quick_rows in any case as it is used when calculating
- cost of ROR intersection:
- */
- param->table->quick_rows[key]=records;
- if (cpk_scan)
- param->is_ror_scan= TRUE;
- }
- if (param->table->file->index_flags(key, 0, TRUE) & HA_KEY_SCAN_NOT_ROR)
- param->is_ror_scan= FALSE;
- DBUG_PRINT("exit", ("Records: %lu", (ulong) records));
- DBUG_RETURN(records);
+ if (key_tree->is_null_interval())
+ cur->min_key_flag |= NULL_RANGE;
+ (arg->i)++;
}
/*
- Recursively calculate estimate of # rows that will be retrieved by
- key scan on key idx.
+ Range sequence interface, SEL_ARG* implementation: get the next interval
+
SYNOPSIS
- check_quick_keys()
- param Parameter from test_quick select function.
- idx Number of key to use in PARAM::keys in list of used keys
- (param->real_keynr[idx] holds the key number in table)
- key_tree SEL_ARG tree being examined.
- min_key Buffer with partial min key value tuple
- min_key_flag
- max_key Buffer with partial max key value tuple
- max_key_flag
-
- NOTES
- The function does the recursive descent on the tree via SEL_ARG::left,
- SEL_ARG::right, and SEL_ARG::next_key_part edges. The #rows estimates
- are calculated using records_in_range calls at the leaf nodes and then
- summed.
-
- param->min_key and param->max_key are used to hold prefixes of key value
- tuples.
+ sel_arg_range_seq_next()
+ rseq Value returned from sel_arg_range_seq_init
+ range OUT Store information about the range here
- The side effects are:
-
- param->max_key_part is updated to hold the maximum number of key parts used
- in scan minus 1.
+ DESCRIPTION
+ This is "get_next" function for Range sequence interface implementation
+ for SEL_ARG* tree.
- param->range_count is incremented if the function finds a range that
- wasn't counted by the caller.
+ IMPLEMENTATION
+ The traversal also updates those param members:
+ - is_ror_scan
+ - range_count
+ - max_key_part
- param->is_ror_scan is cleared if the function detects that the key scan is
- not a Rowid-Ordered Retrieval scan ( see comments for is_key_scan_ror
- function for description of which key scans are ROR scans)
+ RETURN
+ 0 Ok
+ 1 No more ranges in the sequence
*/
-static ha_rows
-check_quick_keys(PARAM *param, uint idx, SEL_ARG *key_tree,
- char *min_key, uint min_key_flag, int min_keypart,
- char *max_key, uint max_key_flag, int max_keypart)
+//psergey-merge-todo: support check_quick_keys:max_keypart
+uint sel_arg_range_seq_next(range_seq_t rseq, KEY_MULTI_RANGE *range)
{
- ha_rows records=0, tmp;
- uint tmp_min_flag, tmp_max_flag, keynr, min_key_length, max_key_length;
- uint tmp_min_keypart= min_keypart, tmp_max_keypart= max_keypart;
- char *tmp_min_key, *tmp_max_key;
- uint8 save_first_null_comp= param->first_null_comp;
-
- param->max_key_part=max(param->max_key_part,key_tree->part);
- if (key_tree->left != &null_element)
+ SEL_ARG *key_tree;
+ SEL_ARG_RANGE_SEQ *seq= (SEL_ARG_RANGE_SEQ*)rseq;
+ if (seq->at_start)
{
- /*
- There are at least two intervals for current key part, i.e. condition
- was converted to something like
- (keyXpartY less/equals c1) OR (keyXpartY more/equals c2).
- This is not a ROR scan if the key is not Clustered Primary Key.
- */
- param->is_ror_scan= FALSE;
- records=check_quick_keys(param, idx, key_tree->left,
- min_key, min_key_flag, min_keypart,
- max_key, max_key_flag, max_keypart);
- if (records == HA_POS_ERROR) // Impossible
- return records;
- }
-
- tmp_min_key= min_key;
- tmp_max_key= max_key;
- tmp_min_keypart+= key_tree->store_min(param->key[idx][key_tree->part].store_length,
- &tmp_min_key, min_key_flag);
- tmp_max_keypart+= key_tree->store_max(param->key[idx][key_tree->part].store_length,
- &tmp_max_key, max_key_flag);
- min_key_length= (uint) (tmp_min_key - param->min_key);
- max_key_length= (uint) (tmp_max_key - param->max_key);
+ key_tree= seq->start;
+ seq->at_start= FALSE;
+ goto walk_up_right;
+ }
- if (param->is_ror_scan)
+ key_tree= seq->stack[seq->i].key_tree;
+ /* Ok, we're at some "full tuple" position in the tree */
+
+ /* Step down if we can */
+ if (key_tree->next && key_tree->next != &null_element)
{
- /*
- If the index doesn't cover entire key, mark the scan as non-ROR scan.
- Actually we're cutting off some ROR scans here.
- */
- uint16 fieldnr= param->table->key_info[param->real_keynr[idx]].
- key_part[key_tree->part].fieldnr - 1;
- if (param->table->field[fieldnr]->key_length() !=
- param->key[idx][key_tree->part].length)
- param->is_ror_scan= FALSE;
+ //step down; (update the tuple, we'll step right and stay there)
+ seq->i--;
+ step_down_to(seq, key_tree->next);
+ key_tree= key_tree->next;
+ seq->param->is_ror_scan= FALSE;
+ goto walk_right_up;
}
- if (!param->first_null_comp && key_tree->is_null_interval())
- param->first_null_comp= key_tree->part+1;
-
- if (key_tree->next_key_part &&
- key_tree->next_key_part->part == key_tree->part+1 &&
- key_tree->next_key_part->type == SEL_ARG::KEY_RANGE)
- { // const key as prefix
- if (min_key_length == max_key_length &&
- !memcmp(min_key, max_key, (uint) (tmp_max_key - max_key)) &&
- !key_tree->min_flag && !key_tree->max_flag)
- {
- tmp=check_quick_keys(param,idx,key_tree->next_key_part, tmp_min_key,
- min_key_flag | key_tree->min_flag, tmp_min_keypart,
- tmp_max_key, max_key_flag | key_tree->max_flag,
- tmp_max_keypart);
- goto end; // Ugly, but efficient
+ /* Ok, can't step down, walk left until we can step down */
+ while (1)
+ {
+ if (seq->i == 1) // can't step left
+ return 1;
+ /* Step left */
+ seq->i--;
+ key_tree= seq->stack[seq->i].key_tree;
+
+ /* Step down if we can */
+ if (key_tree->next && key_tree->next != &null_element)
+ {
+ // Step down; update the tuple
+ seq->i--;
+ step_down_to(seq, key_tree->next);
+ key_tree= key_tree->next;
+ break;
}
- else
+ }
+
+ /*
+ Ok, we've stepped down from the path to previous tuple.
+ Walk right-up while we can
+ */
+walk_right_up:
+ while (key_tree->next_key_part && key_tree->next_key_part != &null_element &&
+ key_tree->next_key_part->part == key_tree->part + 1)
+ {
{
- /* The interval for current key part is not c1 <= keyXpartY <= c1 */
- param->is_ror_scan= FALSE;
+ uint min_key_length= seq->stack[seq->i].min_key - seq->param->min_key;
+ uint max_key_length= seq->stack[seq->i].max_key - seq->param->max_key;
+ uint len= seq->stack[seq->i].min_key - seq->stack[seq->i-1].min_key;
+ if (!(min_key_length == max_key_length &&
+ !memcmp(seq->stack[seq->i-1].min_key, seq->stack[seq->i-1].max_key, len) &&
+ !key_tree->min_flag && !key_tree->max_flag))
+ {
+ seq->param->is_ror_scan= FALSE;
+ if (!key_tree->min_flag)
+ key_tree->next_key_part->store_min_key(seq->param->key[seq->keyno],
+ &seq->stack[seq->i].min_key,
+ &seq->stack[seq->i].min_key_flag);
+ if (!key_tree->max_flag)
+ key_tree->next_key_part->store_max_key(seq->param->key[seq->keyno],
+ &seq->stack[seq->i].max_key,
+ &seq->stack[seq->i].max_key_flag);
+ break;
+ }
}
+
+ /*
+ Ok, current atomic interval is in form "t.field=const" and there is
+ next_key_part interval. Step right, and walk up from there.
+ */
+ key_tree= key_tree->next_key_part;
- tmp_min_flag=key_tree->min_flag;
- tmp_max_flag=key_tree->max_flag;
- if (!tmp_min_flag)
- tmp_min_keypart+=
- key_tree->next_key_part->store_min_key(param->key[idx], &tmp_min_key,
- &tmp_min_flag);
- if (!tmp_max_flag)
- tmp_max_keypart+=
- key_tree->next_key_part->store_max_key(param->key[idx], &tmp_max_key,
- &tmp_max_flag);
- min_key_length= (uint) (tmp_min_key - param->min_key);
- max_key_length= (uint) (tmp_max_key - param->max_key);
- }
- else
- {
- tmp_min_flag= min_key_flag | key_tree->min_flag;
- tmp_max_flag= max_key_flag | key_tree->max_flag;
+walk_up_right:
+ while (key_tree->prev && key_tree->prev != &null_element)
+ {
+ /* Step up */
+ key_tree= key_tree->prev;
+ }
+ step_down_to(seq, key_tree);
}
- keynr=param->real_keynr[idx];
- param->range_count++;
- if (!tmp_min_flag && ! tmp_max_flag &&
- (uint) key_tree->part+1 == param->table->key_info[keynr].key_parts &&
- (param->table->key_info[keynr].flags & (HA_NOSAME | HA_END_SPACE_KEY)) ==
- HA_NOSAME && min_key_length == max_key_length &&
- !memcmp(param->min_key, param->max_key, min_key_length) &&
- !param->first_null_comp)
+ /* Ok got a tuple */
+ RANGE_SEQ_ENTRY *cur= &seq->stack[seq->i];
+ uint min_key_length= cur->min_key - seq->param->min_key;
+
+ range->ptr= (char*)(int)(key_tree->part);
+ if (cur->min_key_flag & GEOM_FLAG)
{
- tmp=1; // Max one record
- param->n_ranges++;
+ range->range_flag= cur->min_key_flag;
+
+ /* Here minimum contains also function code bits, and maximum is +inf */
+ range->start_key.key= (byte*)seq->param->min_key;
+ range->start_key.length= min_key_length;
+ range->start_key.flag= (ha_rkey_function) (cur->min_key_flag ^ GEOM_FLAG);
}
else
{
- if (param->is_ror_scan)
+ range->range_flag= cur->min_key_flag | cur->max_key_flag;
+
+ range->start_key.key= (byte*)seq->param->min_key;
+ range->start_key.length= cur->min_key - seq->param->min_key;
+ range->start_key.flag= (cur->min_key_flag & NEAR_MIN ? HA_READ_AFTER_KEY :
+ HA_READ_KEY_EXACT);
+
+ range->end_key.key= (byte*)seq->param->max_key;
+ range->end_key.length= cur->max_key - seq->param->max_key;
+ range->end_key.flag= (cur->max_key_flag & NEAR_MAX ? HA_READ_BEFORE_KEY :
+ HA_READ_AFTER_KEY);
+ if (!(cur->min_key_flag & ~NULL_RANGE) && !cur->max_key_flag &&
+ (uint)key_tree->part+1 == seq->param->table->key_info[seq->real_keyno].key_parts &&
+ (seq->param->table->key_info[seq->real_keyno].flags & (HA_NOSAME | HA_END_SPACE_KEY)) ==
+ HA_NOSAME &&
+ range->start_key.length == range->end_key.length &&
+ !memcmp(seq->param->min_key,seq->param->max_key,range->start_key.length))
+ range->range_flag= UNIQUE_RANGE | (cur->min_key_flag & NULL_RANGE);
+
+ if (seq->param->is_ror_scan)
{
/*
If we get here, the condition on the key was converted to form
@@ -7359,69 +7326,122 @@
uncovered "tail" of KeyX parts is either empty or is identical to
first members of clustered primary key.
*/
- if (!(min_key_length == max_key_length &&
- !memcmp(min_key, max_key, (uint) (tmp_max_key - max_key)) &&
- !key_tree->min_flag && !key_tree->max_flag &&
- is_key_scan_ror(param, keynr, key_tree->part + 1)))
- param->is_ror_scan= FALSE;
- }
- param->n_ranges++;
-
- if (tmp_min_flag & GEOM_FLAG)
- {
- key_range min_range;
- min_range.key= (byte*) param->min_key;
- min_range.length= min_key_length;
- min_range.keypart_map= make_keypart_map(tmp_min_keypart);
- /* In this case tmp_min_flag contains the handler-read-function */
- min_range.flag= (ha_rkey_function) (tmp_min_flag ^ GEOM_FLAG);
-
- tmp= param->table->file->records_in_range(keynr,
- &min_range, (key_range*) 0);
- }
- else
+ if (!(!(cur->min_key_flag & ~NULL_RANGE) && !cur->max_key_flag &&
+ (range->start_key.length == range->end_key.length) &&
+ !memcmp(range->start_key.key, range->end_key.key, range->start_key.length) &&
+ is_key_scan_ror(seq->param, seq->real_keyno, key_tree->part + 1)))
+ seq->param->is_ror_scan= FALSE;
+ }
+ seq->param->range_count++;
+ seq->param->max_key_part=max(seq->param->max_key_part,key_tree->part);
+ return 0;
+}
+
+
+/*
+ Calculate cost and E(#rows) for a given index and intervals tree
+
+ SYNOPSIS
+ check_quick_select()
+ param Parameter from test_quick_select
+ idx Number of index to use in PARAM::key SEL_TREE::key
+ index_only TRUE - assume only index tuples will be accessed
+ FALSE - assume full table rows will be read
+ tree Transformed selection condition, tree->key[idx] holds
+ the intervals for the given index.
+ update_tbl_stats TRUE <=> update table->quick_* with information
+ about range scan we've evaluated.
+ mrr_flags INOUT MRR access flags
+ cost OUT Scan cost
+
+ NOTES
+ param->is_ror_scan is set to reflect if the key scan is a ROR (see
+ is_key_scan_ror function for more info)
+ param->table->quick_*, param->range_count (and maybe others) are
+ updated with data of given key scan, see quick_range_seq_next for details.
+
+ RETURN
+ Estimate # of records to be retrieved.
+ HA_POS_ERROR if estimate calculation failed due to table handler problems.
+*/
+
+static
+ha_rows check_quick_select(PARAM *param, uint idx, bool index_only,
+ SEL_ARG *tree, bool update_tbl_stats,
+ uint *mrr_flags, uint *bufsize, COST_VECT *cost)
+{
+ SEL_ARG_RANGE_SEQ seq;
+ RANGE_SEQ_IF seq_if = {sel_arg_range_seq_init, sel_arg_range_seq_next};
+ handler *file= param->table->file;
+ ha_rows rows;
+ uint keynr= param->real_keynr[idx];
+ DBUG_ENTER("check_quick_select");
+
+ /* Handle cases when we don't have a valid non-empty list of range */
+ if (tree->type == SEL_ARG::IMPOSSIBLE)
+ DBUG_RETURN(0L);
+ if (tree->type != SEL_ARG::KEY_RANGE || tree->part != 0)
+ DBUG_RETURN(HA_POS_ERROR);
+
+ seq.keyno= idx;
+ seq.real_keyno= keynr;
+ seq.param= param;
+ seq.start= tree;
+
+ param->range_count=0;
+ param->max_key_part=0;
+
+ param->is_ror_scan= TRUE;
+ if (file->index_flags(keynr, 0, TRUE) & HA_KEY_SCAN_NOT_ROR)
+ param->is_ror_scan= FALSE;
+
+ *mrr_flags= param->force_default_mrr? HA_MRR_USE_DEFAULT_IMPL: 0;
+ *mrr_flags= HA_MRR_NO_ASSOCIATION;
+
+ bool pk_is_clustered= file->primary_key_is_clustered();
+ if (index_only &&
+ (file->index_flags(keynr, param->max_key_part, 1) & HA_KEYREAD_ONLY) &&
+ !(pk_is_clustered && keynr == param->table->s->primary_key))
+ *mrr_flags |= HA_MRR_INDEX_ONLY;
+
+ if (current_thd->lex->sql_command != SQLCOM_SELECT)
+ *mrr_flags |= HA_MRR_USE_DEFAULT_IMPL;
+
+ *bufsize= param->thd->variables.read_rnd_buff_size;
+ rows= file->multi_range_read_info_const(keynr, &seq_if, (void*)&seq, 0,
+ bufsize, mrr_flags, cost);
+ if (rows != HA_POS_ERROR)
+ {
+ param->table->quick_rows[keynr]=rows;
+ if (update_tbl_stats)
{
- key_range min_range, max_range;
-
- min_range.key= (byte*) param->min_key;
- min_range.length= min_key_length;
- min_range.flag= (tmp_min_flag & NEAR_MIN ? HA_READ_AFTER_KEY :
- HA_READ_KEY_EXACT);
- min_range.keypart_map= make_keypart_map(tmp_min_keypart);
- max_range.key= (byte*) param->max_key;
- max_range.length= max_key_length;
- max_range.flag= (tmp_max_flag & NEAR_MAX ?
- HA_READ_BEFORE_KEY : HA_READ_AFTER_KEY);
- max_range.keypart_map= make_keypart_map(tmp_max_keypart);
- tmp=param->table->file->records_in_range(keynr,
- (min_key_length ? &min_range :
- (key_range*) 0),
- (max_key_length ? &max_range :
- (key_range*) 0));
+ param->table->quick_keys.set_bit(keynr);
+ param->table->quick_key_parts[keynr]=param->max_key_part+1;
+ param->table->quick_n_ranges[keynr]= param->range_count;
+ param->table->quick_condition_rows=
+ min(param->table->quick_condition_rows, rows);
}
}
- end:
- if (tmp == HA_POS_ERROR) // Impossible range
- return tmp;
- records+=tmp;
- if (key_tree->right != &null_element)
+ /* Figure out if the key scan is ROR (returns rows in ROWID order) or not */
+ enum ha_key_alg key_alg= param->table->key_info[seq.real_keyno].algorithm;
+ if ((key_alg != HA_KEY_ALG_BTREE) && (key_alg!= HA_KEY_ALG_UNDEF))
{
- /*
- There are at least two intervals for current key part, i.e. condition
- was converted to something like
- (keyXpartY less/equals c1) OR (keyXpartY more/equals c2).
- This is not a ROR scan if the key is not Clustered Primary Key.
+ /*
+ All scans are non-ROR scans for those index types.
+ TODO: Don't have this logic here, make table engines return
+ appropriate flags instead.
*/
param->is_ror_scan= FALSE;
- tmp=check_quick_keys(param, idx, key_tree->right,
- min_key, min_key_flag, min_keypart,
- max_key, max_key_flag, max_keypart);
- if (tmp == HA_POS_ERROR)
- return tmp;
- records+=tmp;
}
- param->first_null_comp= save_first_null_comp;
- return records;
+ else
+ {
+ /* Clustered PK scan is always a ROR scan (TODO: same as above) */
+ if (param->table->s->primary_key == keynr && pk_is_clustered)
+ param->is_ror_scan= TRUE;
+ }
+
+ DBUG_PRINT("exit", ("Records: %lu", (ulong) rows));
+ DBUG_RETURN(rows); //psergey-merge:todo: maintain first_null_comp.
}
@@ -7455,7 +7475,7 @@
Scans on HASH indexes are not ROR scans,
any range scan on clustered primary key is ROR scan (3)
- Check (1) is made in check_quick_keys()
+ Check (1) is made in quick_range_seq_next()
Check (3) is made check_quick_select()
Check (2) is made by this function.
@@ -7471,9 +7491,19 @@
KEY_PART_INFO *key_part_end= (table_key->key_part +
table_key->key_parts);
uint pk_number;
+
+ for (KEY_PART_INFO *kp= table_key->key_part; kp < key_part; kp++)
+ {
+ uint16 fieldnr= param->table->key_info[keynr].
+ key_part[kp - table_key->key_part].fieldnr - 1;
+ if (param->table->field[fieldnr]->key_length() != kp->length)
+ return FALSE;
+ }
if (key_part == key_part_end)
return TRUE;
+
+ key_part= table_key->key_part + nparts;
pk_number= param->table->s->primary_key;
if (!param->table->file->primary_key_is_clustered() || pk_number == MAX_KEY)
return FALSE;
@@ -7498,12 +7528,14 @@
SYNOPSIS
get_quick_select()
param
- idx Index of used key in param->key.
- key_tree SEL_ARG tree for the used key
- parent_alloc If not NULL, use it to allocate memory for
- quick select data. Otherwise use quick->alloc.
+ idx Index of used key in param->key.
+ key_tree SEL_ARG tree for the used key
+ mrr_flags MRR parameter for quick select
+ mrr_buf_size MRR parameter for quick select
+ parent_alloc If not NULL, use it to allocate memory for
+ quick select data. Otherwise use quick->alloc.
NOTES
- The caller must call QUICK_SELECT::init for returned quick select
+ The caller must call QUICK_SELECT::init for returned quick select.
CAUTION! This function may change thd->mem_root to a MEM_ROOT which will be
deallocated when the returned quick select is deleted.
@@ -7514,25 +7546,26 @@
*/
QUICK_RANGE_SELECT *
-get_quick_select(PARAM *param,uint idx,SEL_ARG *key_tree,
- MEM_ROOT *parent_alloc)
+get_quick_select(PARAM *param,uint idx,SEL_ARG *key_tree, uint mrr_flags,
+ uint mrr_buf_size, MEM_ROOT *parent_alloc)
{
QUICK_RANGE_SELECT *quick;
+ bool create_err= FALSE;
DBUG_ENTER("get_quick_select");
if (param->table->key_info[param->real_keynr[idx]].flags & HA_SPATIAL)
quick=new QUICK_RANGE_SELECT_GEOM(param->thd, param->table,
param->real_keynr[idx],
test(parent_alloc),
- parent_alloc);
+ parent_alloc, &create_err);
else
quick=new QUICK_RANGE_SELECT(param->thd, param->table,
param->real_keynr[idx],
- test(parent_alloc));
+ test(parent_alloc), NULL, &create_err);
if (quick)
{
- if (quick->error ||
+ if (create_err ||
get_quick_keys(param,quick,param->key[idx],key_tree,param->min_key,0,
param->max_key,0))
{
@@ -7541,6 +7574,8 @@
}
else
{
+ quick->mrr_flags= mrr_flags;
+ quick->mrr_buf_size= mrr_buf_size;
quick->key_parts=(KEY_PART*)
memdup_root(parent_alloc? parent_alloc : &quick->alloc,
(char*) param->key[idx],
@@ -7689,7 +7724,20 @@
}
-/* Returns TRUE if any part of the key is NULL */
+
+/*
+ Return TRUE if any part of the key is NULL
+
+ SYNOPSIS
+ null_part_in_key()
+ key_part Array of key parts (index description)
+ key Key values tuple
+ length Length of key values tuple in bytes.
+
+ RETURN
+ TRUE The tuple has at least one "keypartX is NULL"
+ FALSE Otherwise
+*/
static bool null_part_in_key(KEY_PART *key_part, const char *key, uint length)
{
@@ -7746,6 +7794,37 @@
}
+FT_SELECT *get_ft_select(THD *thd, TABLE *table, uint key)
+{
+ bool create_err= FALSE;
+ FT_SELECT *fts= new FT_SELECT(thd, table, key, &create_err);
+ if (create_err)
+ {
+ delete fts;
+ return NULL;
+ }
+ else
+ return fts;
+}
+
+static bool
+key_has_nulls(const KEY* key_info, const byte *key, uint key_len)
+{
+ KEY_PART_INFO *curr_part, *end_part;
+ const byte* end_ptr= key + key_len;
+ curr_part= key_info->key_part;
+ end_part= curr_part + key_info->key_parts;
+
+ for (; curr_part != end_part && key < end_ptr; curr_part++)
+ {
+ if (curr_part->null_bit && *key)
+ return TRUE;
+
+ key += curr_part->store_length;
+ }
+ return FALSE;
+}
+
/*
Create quick select from ref/ref_or_null scan.
@@ -7774,10 +7853,11 @@
KEY_PART *key_part;
QUICK_RANGE *range;
uint part;
+ bool create_err= FALSE;
old_root= thd->mem_root;
/* The following call may change thd->mem_root */
- quick= new QUICK_RANGE_SELECT(thd, table, ref->key, 0);
+ quick= new QUICK_RANGE_SELECT(thd, table, ref->key, 0, 0, &create_err);
/* save mem_root set by QUICK_RANGE_SELECT constructor */
alloc= thd->mem_root;
/*
@@ -7786,7 +7866,7 @@
*/
thd->mem_root= old_root;
- if (!quick)
+ if (!quick || create_err)
return 0; /* no ranges found */
if (quick->init())
goto err;
@@ -7842,8 +7922,25 @@
goto err;
}
- return quick;
+ /* Call multi_range_read_info() to get the MRR flags and buffer size */
+ quick->mrr_flags= HA_MRR_NO_ASSOCIATION |
+ (table->key_read ? HA_MRR_INDEX_ONLY : 0);
+ if (thd->lex->sql_command != SQLCOM_SELECT)
+ quick->mrr_flags |= HA_MRR_USE_DEFAULT_IMPL;
+#ifdef WITH_NDBCLUSTER_STORAGE_ENGINE
+ if (!ref->null_ref_key && !key_has_nulls(key_info, range->min_key,
+ ref->key_length))
+ quick->mrr_flags |= HA_MRR_NO_NULL_ENDPOINTS;
+#endif
+ quick->mrr_buf_size= thd->variables.read_rnd_buff_size;
+ COST_VECT cost;
+ if (table->file->multi_range_read_info(quick->index, 1, records,
+ &quick->mrr_buf_size,
+ &quick->mrr_flags, &cost))
+ goto err;
+
+ return quick;
err:
delete quick;
return 0;
@@ -8142,79 +8239,182 @@
int QUICK_RANGE_SELECT::reset()
{
- uint mrange_bufsiz;
+ uint buf_size;
byte *mrange_buff;
+ int error;
+ HANDLER_BUFFER empty_buf;
DBUG_ENTER("QUICK_RANGE_SELECT::reset");
- next=0;
last_range= NULL;
- in_range= FALSE;
cur_range= (QUICK_RANGE**) ranges.buffer;
if (file->inited == handler::NONE && (error= file->ha_index_init(index,1)))
DBUG_RETURN(error);
- /* Do not allocate the buffers twice. */
- if (multi_range_length)
+ /* Allocate buffer if we need one but haven't allocated it yet */
+ if (mrr_buf_size && !mrr_buf_desc)
{
- DBUG_ASSERT(multi_range_length == min(multi_range_count, ranges.elements));
- DBUG_RETURN(0);
- }
-
- /* Allocate the ranges array. */
- DBUG_ASSERT(ranges.elements);
- multi_range_length= min(multi_range_count, ranges.elements);
- DBUG_ASSERT(multi_range_length > 0);
- while (multi_range_length && ! (multi_range= (KEY_MULTI_RANGE*)
- my_malloc(multi_range_length *
- sizeof(KEY_MULTI_RANGE),
- MYF(MY_WME))))
- {
- /* Try to shrink the buffers until it is 0. */
- multi_range_length/= 2;
- }
- if (! multi_range)
- {
- multi_range_length= 0;
- DBUG_RETURN(HA_ERR_OUT_OF_MEM);
- }
-
- /* Allocate the handler buffer if necessary. */
- if (file->ha_table_flags() & HA_NEED_READ_RANGE_BUFFER)
- {
- mrange_bufsiz= min(multi_range_bufsiz,
- (QUICK_SELECT_I::records + 1)* head->s->reclength);
-
- while (mrange_bufsiz &&
- ! my_multi_malloc(MYF(MY_WME),
- &multi_range_buff, sizeof(*multi_range_buff),
- &mrange_buff, mrange_bufsiz,
- NullS))
+ buf_size= mrr_buf_size;
+ while (buf_size && !my_multi_malloc(MYF(MY_WME),
+ &mrr_buf_desc, sizeof(*mrr_buf_desc),
+ &mrange_buff, buf_size,
+ NullS))
{
/* Try to shrink the buffers until both are 0. */
- mrange_bufsiz/= 2;
+ buf_size/= 2;
}
- if (! multi_range_buff)
- {
- my_free((char*) multi_range, MYF(0));
- multi_range= NULL;
- multi_range_length= 0;
+ if (!mrr_buf_desc)
DBUG_RETURN(HA_ERR_OUT_OF_MEM);
- }
/* Initialize the handler buffer. */
- multi_range_buff->buffer= mrange_buff;
- multi_range_buff->buffer_end= mrange_buff + mrange_bufsiz;
- multi_range_buff->end_of_used_area= mrange_buff;
+ mrr_buf_desc->buffer= mrange_buff;
+ mrr_buf_desc->buffer_end= mrange_buff + buf_size;
+ mrr_buf_desc->end_of_used_area= mrange_buff;
#ifdef HAVE_purify
/*
We need this until ndb will use the buffer efficiently
(Now ndb stores complete row in here, instead of only the used fields
which gives us valgrind warnings in compare_record[])
*/
- bzero((char*) mrange_buff, mrange_bufsiz);
+ bzero((char*) mrange_buff, buf_size);
#endif
}
- DBUG_RETURN(0);
+
+ if (!mrr_buf_desc)
+ empty_buf.buffer= empty_buf.buffer_end= empty_buf.end_of_used_area= NULL;
+
+ if (sorted)
+ mrr_flags |= HA_MRR_SORTED;
+ RANGE_SEQ_IF seq_funcs= {quick_range_seq_init, quick_range_seq_next};
+ error= file->multi_range_read_init(&seq_funcs, (void*)this, ranges.elements,
+ mrr_flags, mrr_buf_desc? mrr_buf_desc:
+ &empty_buf);
+ DBUG_RETURN(error);
+}
+
+
+/*
+ Range sequence interface implementation for array<QUICK_RANGE>: initialize
+
+ SYNOPSIS
+ quick_range_seq_init()
+ init_param Caller-opaque paramenter: QUICK_RANGE_SELECT* pointer
+ n_ranges Number of ranges in the sequence (ignored)
+ flags MRR flags (currently not used)
+
+ RETURN
+ Opaque value to be passed to quick_range_seq_next
+*/
+
+range_seq_t quick_range_seq_init(void *init_param, uint n_ranges, uint flags)
+{
+ QUICK_RANGE_SELECT *quick= (QUICK_RANGE_SELECT*)init_param;
+ quick->qr_traversal_ctx.first= (QUICK_RANGE**)quick->ranges.buffer;
+ quick->qr_traversal_ctx.cur= (QUICK_RANGE**)quick->ranges.buffer;
+ quick->qr_traversal_ctx.last= quick->qr_traversal_ctx.cur +
+ quick->ranges.elements;
+ return &quick->qr_traversal_ctx;
+}
+
+
+/*
+ Range sequence interface implementation for array<QUICK_RANGE>: get next
+
+ SYNOPSIS
+ quick_range_seq_next()
+ rseq Value returned from quick_range_seq_init
+ range OUT Store information about the range here
+
+ RETURN
+ 0 Ok
+ 1 No more ranges in the sequence
+*/
+
+uint quick_range_seq_next(range_seq_t rseq, KEY_MULTI_RANGE *range)
+{
+ QUICK_RANGE_SEQ_CTX *ctx= (QUICK_RANGE_SEQ_CTX*)rseq;
+
+ if (ctx->cur == ctx->last)
+ return 1; /* no more ranges */
+
+ QUICK_RANGE *cur= *(ctx->cur);
+ key_range *start_key= &range->start_key;
+ key_range *end_key= &range->end_key;
+
+ start_key->key= (const byte*)cur->min_key;
+ start_key->length= cur->min_length;
+ start_key->flag= ((cur->flag & NEAR_MIN) ? HA_READ_AFTER_KEY :
+ (cur->flag & EQ_RANGE) ?
+ HA_READ_KEY_EXACT : HA_READ_KEY_OR_NEXT);
+ end_key->key= (const byte*)cur->max_key;
+ end_key->length= cur->max_length;
+ /*
+ We use HA_READ_AFTER_KEY here because if we are reading on a key
+ prefix. We want to find all keys with this prefix.
+ */
+ end_key->flag= (cur->flag & NEAR_MAX ? HA_READ_BEFORE_KEY :
+ HA_READ_AFTER_KEY);
+ range->range_flag= cur->flag;
+ ctx->cur++;
+ return 0;
+}
+
+
+/*
+ MRR range sequence interface: array<QUICK_RANGE> impl: utility func for NDB
+
+ SYNOPSIS
+ mrr_persistent_flag_storage()
+ seq Range sequence being traversed
+ idx Number of range
+
+ DESCRIPTION
+ MRR/NDB implementation needs to store some bits for each range. This
+ function returns a reference to the "range_flag" associated with the
+ range number idx.
+
+ This function should be removed when we get a proper MRR/NDB
+ implementation.
+
+ RETURN
+ Reference to range_flag associated with range number #idx
+*/
+
+uint16 &mrr_persistent_flag_storage(range_seq_t seq, uint idx)
+{
+ QUICK_RANGE_SEQ_CTX *ctx= (QUICK_RANGE_SEQ_CTX*)seq;
+ return ctx->first[idx]->flag;
+}
+
+
+/*
+ MRR range sequence interface: array<QUICK_RANGE> impl: utility func for NDB
+
+ SYNOPSIS
+ mrr_get_ptr_by_idx()
+ seq Range sequence bening traversed
+ idx Number of the range
+
+ DESCRIPTION
+ An extension of MRR range sequence interface needed by NDB: return the
+ data associated with the given range.
+
+ A proper MRR interface implementer is supposed to store and return
+ range-associated data. NDB stores number of the range instead. So this
+ is a helper function that translates range number to range associated
+ data.
+
+ This function does nothing, as currrently there is only one user of the
+ MRR interface - the quick range select code, and this user doesn't need
+ to use range-associated data.
+
+ RETURN
+ Reference to range-associated data
+*/
+
+char* &mrr_get_ptr_by_idx(range_seq_t seq, uint idx)
+{
+ static char *dummy;
+ return dummy;
}
@@ -8235,15 +8435,8 @@
int QUICK_RANGE_SELECT::get_next()
{
- int result;
- KEY_MULTI_RANGE *mrange;
- key_range *start_key;
- key_range *end_key;
+ char *dummy;
DBUG_ENTER("QUICK_RANGE_SELECT::get_next");
- DBUG_ASSERT(multi_range_length && multi_range &&
- (cur_range >= (QUICK_RANGE**) ranges.buffer) &&
- (cur_range <= (QUICK_RANGE**) ranges.buffer + ranges.elements));
-
if (in_ror_merged_scan)
{
/*
@@ -8253,63 +8446,8 @@
head->column_bitmaps_set_no_signal(&column_bitmap, &column_bitmap);
}
- for (;;)
- {
- if (in_range)
- {
- /* We did already start to read this key. */
- result= file->read_multi_range_next(&mrange);
- if (result != HA_ERR_END_OF_FILE)
- goto end;
- }
-
- uint count= min(multi_range_length, ranges.elements -
- (cur_range - (QUICK_RANGE**) ranges.buffer));
- if (count == 0)
- {
- /* Ranges have already been used up before. None is left for read. */
- in_range= FALSE;
- if (in_ror_merged_scan)
- head->column_bitmaps_set_no_signal(save_read_set, save_write_set);
- DBUG_RETURN(HA_ERR_END_OF_FILE);
- }
- KEY_MULTI_RANGE *mrange_slot, *mrange_end;
- for (mrange_slot= multi_range, mrange_end= mrange_slot+count;
- mrange_slot < mrange_end;
- mrange_slot++)
- {
- start_key= &mrange_slot->start_key;
- end_key= &mrange_slot->end_key;
- last_range= *(cur_range++);
-
- start_key->key= (const byte*) last_range->min_key;
- start_key->length= last_range->min_length;
- start_key->flag= ((last_range->flag & NEAR_MIN) ? HA_READ_AFTER_KEY :
- (last_range->flag & EQ_RANGE) ?
- HA_READ_KEY_EXACT : HA_READ_KEY_OR_NEXT);
- start_key->keypart_map= last_range->min_keypart_map;
- end_key->key= (const byte*) last_range->max_key;
- end_key->length= last_range->max_length;
- /*
- We use HA_READ_AFTER_KEY here because if we are reading on a key
- prefix. We want to find all keys with this prefix.
- */
- end_key->flag= (last_range->flag & NEAR_MAX ? HA_READ_BEFORE_KEY :
- HA_READ_AFTER_KEY);
- end_key->keypart_map= last_range->max_keypart_map;
-
- mrange_slot->range_flag= last_range->flag;
- }
-
- result= file->read_multi_range_first(&mrange, multi_range, count,
- sorted, multi_range_buff);
- if (result != HA_ERR_END_OF_FILE)
- goto end;
- in_range= FALSE; /* No matching rows; go to next set of ranges. */
- }
-
-end:
- in_range= ! result;
+ int result= file->multi_range_read_next(&dummy);
+ //psergey-merge-todo: set start_key->keypart_map and end_key->keypart_map
if (in_ror_merged_scan)
{
/* Restore bitmaps set on entry */
@@ -8318,6 +8456,7 @@
DBUG_RETURN(result);
}
+
/*
Get the next record with a different prefix.
@@ -8492,7 +8631,8 @@
*/
QUICK_SELECT_DESC::QUICK_SELECT_DESC(QUICK_RANGE_SELECT *q,
- uint used_key_parts_arg)
+ uint used_key_parts_arg,
+ bool *create_err)
:QUICK_RANGE_SELECT(*q), rev_it(rev_ranges)
{
QUICK_RANGE *r;
@@ -8598,6 +8738,7 @@
/*
Compare if found key is over max-value
Returns 0 if key <= range->max_key
+ TODO: Figure out why can't this function be as simple as cmp_prev().
*/
int QUICK_RANGE_SELECT::cmp_next(QUICK_RANGE *range_arg)
@@ -9369,8 +9510,14 @@
cur_index_tree= get_index_range_tree(cur_index, tree, param,
&cur_param_idx);
/* Check if this range tree can be used for prefix retrieval. */
- cur_quick_prefix_records= check_quick_select(param, cur_param_idx,
- cur_index_tree, TRUE);
+ COST_VECT dummy_cost;
+ uint mrr_flags= HA_MRR_USE_DEFAULT_IMPL;
+ uint mrr_bufsize=0;
+ cur_quick_prefix_records= check_quick_select(param, cur_param_idx,
+ FALSE /*don't care*/,
+ cur_index_tree, TRUE,
+ &mrr_flags, &mrr_bufsize,
+ &dummy_cost);
}
cost_group_min_max(table, cur_index_info, used_key_parts,
cur_group_key_parts, tree, cur_index_tree,
@@ -9932,6 +10079,7 @@
/* Make a QUICK_RANGE_SELECT to be used for group prefix retrieval. */
quick->quick_prefix_select= get_quick_select(param, param_idx,
index_tree,
+ HA_MRR_USE_DEFAULT_IMPL, 0,
&quick->alloc);
/*
@@ -11021,6 +11169,7 @@
DBUG_PRINT("info", ("ROR key scans (%s): %s", msg, tmp.ptr()));
DBUG_VOID_RETURN;
}
+
/*****************************************************************************
** Print a quick range for debugging
--- 1.78/sql/opt_range.h 2007-04-04 22:52:01 +04:00
+++ 1.79/sql/opt_range.h 2007-04-04 22:52:01 +04:00
@@ -261,6 +261,22 @@
class PARAM;
class SEL_ARG;
+
+/*
+ MRR range sequence, array<QUICK_RANGE> implementation: sequence traversal
+ context.
+*/
+typedef struct st_quick_range_seq_ctx
+{
+ QUICK_RANGE **first;
+ QUICK_RANGE **cur;
+ QUICK_RANGE **last;
+} QUICK_RANGE_SEQ_CTX;
+
+range_seq_t quick_range_seq_init(void *init_param, uint n_ranges, uint flags);
+uint quick_range_seq_next(range_seq_t rseq, KEY_MULTI_RANGE *range);
+
+
/*
Quick select that does a range scan on a single key. The records are
returned in key order.
@@ -268,58 +284,40 @@
class QUICK_RANGE_SELECT : public QUICK_SELECT_I
{
protected:
- bool next,dont_free,in_ror_merged_scan;
-public:
- int error;
-protected:
handler *file;
- /*
- If true, this quick select has its "own" handler object which should be
- closed no later then this quick select is deleted.
- */
- bool free_file;
- bool in_range;
- uint multi_range_count; /* copy from thd->variables.multi_range_count */
- uint multi_range_length; /* the allocated length for the array */
- uint multi_range_bufsiz; /* copy from thd->variables.read_rnd_buff_size */
- KEY_MULTI_RANGE *multi_range; /* the multi-range array (allocated and
- freed by QUICK_RANGE_SELECT) */
- HANDLER_BUFFER *multi_range_buff; /* the handler buffer (allocated and
- freed by QUICK_RANGE_SELECT) */
- MY_BITMAP column_bitmap, *save_read_set, *save_write_set;
+ DYNAMIC_ARRAY ranges; /* ordered array of range ptrs */
- friend class TRP_ROR_INTERSECT;
- friend
- QUICK_RANGE_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table,
- struct st_table_ref *ref,
- ha_rows records);
- friend bool get_quick_keys(PARAM *param,
- QUICK_RANGE_SELECT *quick,KEY_PART *key,
- SEL_ARG *key_tree,
- char *min_key, uint min_key_flag,
- char *max_key, uint max_key_flag);
- friend QUICK_RANGE_SELECT *get_quick_select(PARAM*,uint idx,
- SEL_ARG *key_tree,
- MEM_ROOT *alloc);
- friend class QUICK_SELECT_DESC;
- friend class QUICK_INDEX_MERGE_SELECT;
- friend class QUICK_ROR_INTERSECT_SELECT;
- friend class QUICK_GROUP_MIN_MAX_SELECT;
+ /* Members to deal with case when this quick select is a ROR-merged scan */
+ bool in_ror_merged_scan;
+ MY_BITMAP column_bitmap, *save_read_set, *save_write_set;
+ bool free_file; /* TRUE <=> this->file is "owned" by this quick select */
- DYNAMIC_ARRAY ranges; /* ordered array of range ptrs */
+ /* Range pointers to be used when not using MRR interface */
QUICK_RANGE **cur_range; /* current element in ranges */
-
QUICK_RANGE *last_range;
+
+ /* Members needed to use the MRR interface */
+ QUICK_RANGE_SEQ_CTX qr_traversal_ctx;
+public:
+ uint mrr_flags; /* Flags to be used with MRR interface */
+protected:
+ uint mrr_buf_size; /* copy from thd->variables.read_rnd_buff_size */
+ HANDLER_BUFFER *mrr_buf_desc; /* the handler buffer */
+
+ /* Info about index we're scanning */
KEY_PART *key_parts;
KEY_PART_INFO *key_part_info;
+
+ bool dont_free; /* Used by QUICK_SELECT_DESC */
+
int cmp_next(QUICK_RANGE *range);
int cmp_prev(QUICK_RANGE *range);
bool row_in_ranges();
public:
MEM_ROOT alloc;
- QUICK_RANGE_SELECT(THD *thd, TABLE *table,uint index_arg,bool no_alloc=0,
- MEM_ROOT *parent_alloc=NULL);
+ QUICK_RANGE_SELECT(THD *thd, TABLE *table,uint index_arg,bool no_alloc,
+ MEM_ROOT *parent_alloc, bool *create_err);
~QUICK_RANGE_SELECT();
int init();
@@ -344,10 +342,38 @@
QUICK_RANGE_SELECT(const QUICK_RANGE_SELECT& org) : QUICK_SELECT_I()
{
bcopy(&org, this, sizeof(*this));
- multi_range_length= 0;
- multi_range= NULL;
- multi_range_buff= NULL;
+ /*
+ Use default MRR implementation for reverse scans. No table engine
+ currently can do an MRR scan with output in reverse index order.
+ */
+ mrr_buf_desc= NULL;
+ mrr_flags |= HA_MRR_USE_DEFAULT_IMPL;
+ mrr_buf_size= 0;
}
+ friend class TRP_ROR_INTERSECT;
+ friend
+ QUICK_RANGE_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table,
+ struct st_table_ref *ref,
+ ha_rows records);
+ friend bool get_quick_keys(PARAM *param,
+ QUICK_RANGE_SELECT *quick,KEY_PART *key,
+ SEL_ARG *key_tree,
+ char *min_key, uint min_key_flag,
+ char *max_key, uint max_key_flag);
+ friend QUICK_RANGE_SELECT *get_quick_select(PARAM*,uint idx,
+ SEL_ARG *key_tree,
+ uint mrr_flags,
+ uint mrr_buf_size,
+ MEM_ROOT *alloc);
+ friend class QUICK_SELECT_DESC;
+ friend class QUICK_INDEX_MERGE_SELECT;
+ friend class QUICK_ROR_INTERSECT_SELECT;
+ friend class QUICK_GROUP_MIN_MAX_SELECT;
+ friend uint quick_range_seq_next(range_seq_t rseq, KEY_MULTI_RANGE *range);
+ friend range_seq_t quick_range_seq_init(void *init_param,
+ uint n_ranges, uint flags);
+ friend void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
+ bool distinct,const char *message);
};
@@ -355,8 +381,10 @@
{
public:
QUICK_RANGE_SELECT_GEOM(THD *thd, TABLE *table, uint index_arg,
- bool no_alloc, MEM_ROOT *parent_alloc)
- :QUICK_RANGE_SELECT(thd, table, index_arg, no_alloc, parent_alloc)
+ bool no_alloc, MEM_ROOT *parent_alloc,
+ bool *create_err)
+ :QUICK_RANGE_SELECT(thd, table, index_arg, no_alloc, parent_alloc,
+ create_err)
{};
virtual int get_next();
};
@@ -675,7 +703,8 @@
class QUICK_SELECT_DESC: public QUICK_RANGE_SELECT
{
public:
- QUICK_SELECT_DESC(QUICK_RANGE_SELECT *q, uint used_key_parts);
+ QUICK_SELECT_DESC(QUICK_RANGE_SELECT *q, uint used_key_parts,
+ bool *create_err);
int get_next();
bool reverse_sorted() { return 1; }
int get_type() { return QS_TYPE_RANGE_DESC; }
@@ -710,25 +739,29 @@
{
key_map tmp;
tmp.set_all();
- return test_quick_select(thd, tmp, 0, limit, force_quick_range) < 0;
+ return test_quick_select(thd, tmp, 0, limit, force_quick_range, FALSE) < 0;
}
inline bool skip_record() { return cond ? cond->val_int() == 0 : 0; }
int test_quick_select(THD *thd, key_map keys, table_map prev_tables,
- ha_rows limit, bool force_quick_range);
+ ha_rows limit, bool force_quick_range,
+ bool ordered_output);
};
-class FT_SELECT: public QUICK_RANGE_SELECT {
+class FT_SELECT: public QUICK_RANGE_SELECT
+{
public:
- FT_SELECT(THD *thd, TABLE *table, uint key) :
- QUICK_RANGE_SELECT (thd, table, key, 1) { VOID(init()); }
+ FT_SELECT(THD *thd, TABLE *table, uint key, bool *create_err) :
+ QUICK_RANGE_SELECT (thd, table, key, 1, NULL, create_err)
+ { VOID(init()); }
~FT_SELECT() { file->ft_end(); }
- int init() { return error=file->ft_init(); }
+ int init() { return file->ft_init(); }
int reset() { return 0; }
- int get_next() { return error=file->ft_read(record); }
+ int get_next() { return file->ft_read(record); }
int get_type() { return QS_TYPE_FULLTEXT; }
};
+FT_SELECT *get_ft_select(THD *thd, TABLE *table, uint key);
QUICK_RANGE_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table,
struct st_table_ref *ref,
ha_rows records);
--- 1.351/sql/sql_class.h 2007-04-04 22:52:01 +04:00
+++ 1.352/sql/sql_class.h 2007-04-04 22:52:01 +04:00
@@ -196,7 +196,6 @@
ulong max_sort_length;
ulong max_tmp_tables;
ulong max_insert_delayed_threads;
- ulong multi_range_count;
ulong myisam_repair_threads;
ulong myisam_sort_buff_size;
ulong myisam_stats_method;
@@ -208,6 +207,13 @@
ulong net_write_timeout;
ulong optimizer_prune_level;
ulong optimizer_search_depth;
+ /*
+ Controls use of Engine-MRR:
+ 0 - auto, based on cost
+ 1 - force MRR when the storage engine is capable of doing it
+ 2 - disable MRR.
+ */
+ ulong optimizer_use_mrr;
ulong preload_buff_size;
ulong query_cache_type;
ulong read_buff_size;
--- 1.507/sql/sql_select.cc 2007-04-04 22:52:01 +04:00
+++ 1.508/sql/sql_select.cc 2007-04-04 22:52:01 +04:00
@@ -214,7 +214,7 @@
static bool setup_sum_funcs(THD *thd, Item_sum **func_ptr);
static bool init_sum_functions(Item_sum **func, Item_sum **end);
static bool update_sum_func(Item_sum **func);
-static void select_describe(JOIN *join, bool need_tmp_table,bool need_order,
+void select_describe(JOIN *join, bool need_tmp_table,bool need_order,
bool distinct, const char *message=NullS);
static Item *remove_additional_cond(Item* conds);
static void add_group_and_distinct_keys(JOIN *join, JOIN_TAB *join_tab);
@@ -2232,7 +2232,7 @@
select->head=table;
table->reginfo.impossible_range=0;
if ((error= select->test_quick_select(thd, *(key_map *)keys,(table_map) 0,
- limit, 0)) == 1)
+ limit, 0, FALSE)) == 1)
DBUG_RETURN(select->quick->records);
if (error == -1)
{
@@ -3979,9 +3979,7 @@
if (table->covering_keys.is_set(key))
{
/* we can use only index tree */
- uint keys_per_block= table->file->stats.block_size/2/
- (keyinfo->key_length+table->file->ref_length)+1;
- tmp= record_count*(tmp+keys_per_block-1)/keys_per_block;
+ tmp= record_count * table->file->index_only_read_time(key, tmp);
}
else
tmp= record_count*min(tmp,s->worst_seeks);
@@ -4146,12 +4144,10 @@
if (table->covering_keys.is_set(key))
{
/* we can use only index tree */
- uint keys_per_block= table->file->stats.block_size/2/
- (keyinfo->key_length+table->file->ref_length)+1;
- tmp= record_count*(tmp+keys_per_block-1)/keys_per_block;
+ tmp= record_count * table->file->index_only_read_time(key, tmp);
}
else
- tmp= record_count*min(tmp,s->worst_seeks);
+ tmp= record_count * min(tmp,s->worst_seeks);
}
else
tmp= best_time; // Do nothing
@@ -4179,7 +4175,7 @@
This is because table scans uses index and we would not win
anything by using a table scan.
- A word for word translation of the below if-statement in psergey's
+ A word for word translation of the below if-statement in sergefp's
understanding: we check if we should use table scan if:
(1) The found 'ref' access produces more records than a table scan
(or index scan, or quick select), or 'ref' is more expensive than
@@ -5852,7 +5848,8 @@
}
}
- if (tmp || !cond)
+ if (tmp || !cond || tab->type == JT_REF || tab->type == JT_REF_OR_NULL ||
+ tab->type == JT_EQ_REF)
{
DBUG_EXECUTE("where",print_where(tmp,tab->table->alias););
SQL_SELECT *sel= tab->select= ((SQL_SELECT*)
@@ -5866,7 +5863,7 @@
The guard will turn the predicate on only after
the first match for outer tables is encountered.
*/
- if (cond)
+ if (cond && tmp)
{
/*
Because of QUICK_GROUP_MIN_MAX_SELECT there may be a select without
@@ -5958,7 +5955,8 @@
(join->select_options &
OPTION_FOUND_ROWS ?
HA_POS_ERROR :
- join->unit->select_limit_cnt), 0) < 0)
+ join->unit->select_limit_cnt), 0,
+ FALSE) < 0)
{
/*
Before reporting "Impossible WHERE" for the whole query
@@ -5971,7 +5969,8 @@
(join->select_options &
OPTION_FOUND_ROWS ?
HA_POS_ERROR :
- join->unit->select_limit_cnt),0) < 0)
+ join->unit->select_limit_cnt),0,
+ FALSE) < 0)
DBUG_RETURN(1); // Impossible WHERE
}
else
@@ -6083,6 +6082,307 @@
DBUG_RETURN(0);
}
+
+/*
+ Check if given expression uses only table fields covered by the given index
+
+ SYNOPSIS
+ uses_index_fields_only()
+ item Expression to check
+ tbl The table having the index
+ keyno The index number
+ other_tbls_ok TRUE <=> Fields of other non-const tables are allowed
+
+ DESCRIPTION
+ Check if given expression only uses fields covered by index #keyno in the
+ table tbl. The expression can use any fields in any other tables.
+
+ The expression is guaranteed not to be AND or OR - those constructs are
+ handled outside of this function.
+
+ RETURN
+ TRUE Yes
+ FALSE No
+*/
+
+bool uses_index_fields_only(Item *item, TABLE *tbl, uint keyno,
+ bool other_tbls_ok)
+{
+ if (item->const_item())
+ return TRUE;
+
+ /*
+ Don't push down the triggered conditions. Nested outer joins execution
+ code may need to evaluate a condition several times (both triggered and
+ untriggered), and there is no way to put thi
+ TODO: Consider cloning the triggered condition and using the copies for:
+ 1. push the first copy down, to have most restrictive index condition
+ possible
+ 2. Put the second copy into tab->select_cond.
+ */
+ if (item->type() == Item::FUNC_ITEM &&
+ ((Item_func*)item)->functype() == Item_func::TRIG_COND_FUNC)
+ return FALSE;
+
+ if (!(item->used_tables() & tbl->map))
+ return other_tbls_ok;
+
+ Item::Type item_type= item->type();
+ switch (item_type) {
+ case Item::FUNC_ITEM:
+ {
+ /* This is a function, apply condition recursively to arguments */
+ Item_func *item_func= (Item_func*)item;
+ Item **child;
+ Item **item_end= (item_func->arguments()) + item_func->argument_count();
+ for (child= item_func->arguments(); child != item_end; child++)
+ {
+ if (!uses_index_fields_only(*child, tbl, keyno, other_tbls_ok))
+ return FALSE;
+ }
+ return TRUE;
+ }
+ case Item::COND_ITEM:
+ {
+ /* This is a function, apply condition recursively to arguments */
+ List_iterator<Item> li(*((Item_cond*)item)->argument_list());
+ Item *item;
+ while ((item=li++))
+ {
+ if (!uses_index_fields_only(item, tbl, keyno, other_tbls_ok))
+ return FALSE;
+ }
+ return TRUE;
+ }
+ case Item::FIELD_ITEM:
+ {
+ Item_field *item_field= (Item_field*)item;
+ if (item_field->field->table != tbl)
+ return TRUE;
+ return item_field->field->part_of_key.is_set(keyno);
+ }
+ case Item::REF_ITEM:
+ return uses_index_fields_only(item->real_item(), tbl, keyno,
+ other_tbls_ok);
+ default:
+ return FALSE; /* Play it safe, don't push unknown non-const items */
+ }
+}
+
+
+#define ICP_COND_USES_INDEX_ONLY 10
+
+/*
+ Get a part of the condition that can be checked using only index fields
+
+ SYNOPSIS
+ make_cond_for_index()
+ cond The source condition
+ table The table that is partially available
+ keyno The index in the above table. Only fields covered by the index
+ are available
+ other_tbls_ok TRUE <=> Fields of other non-const tables are allowed
+
+ DESCRIPTION
+ Get a part of the condition that can be checked when for the given table
+ we have values only of fields covered by some index. The condition may
+ refer to other tables, it is assumed that we have values of all of their
+ fields.
+
+ Example:
+ make_cond_for_index(
+ "cond(t1.field) AND cond(t2.key1) AND cond(t2.non_key) AND cond(t2.key2)",
+ t2, keyno(t2.key1))
+ will return
+ "cond(t1.field) AND cond(t2.key2)"
+
+ RETURN
+ Index condition, or NULL if no condition could be inferred.
+*/
+
+Item *make_cond_for_index(Item *cond, TABLE *table, uint keyno,
+ bool other_tbls_ok)
+{
+ if (!cond)
+ return NULL;
+ if (cond->type() == Item::COND_ITEM)
+ {
+ uint n_marked= 0;
+ if (((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC)
+ {
+ Item_cond_and *new_cond=new Item_cond_and;
+ if (!new_cond)
+ return (COND*) 0;
+ List_iterator<Item> li(*((Item_cond*) cond)->argument_list());
+ Item *item;
+ while ((item=li++))
+ {
+ Item *fix= make_cond_for_index(item, table, keyno, other_tbls_ok);
+ if (fix)
+ new_cond->argument_list()->push_back(fix);
+ n_marked += test(item->marker == ICP_COND_USES_INDEX_ONLY);
+ }
+ if (n_marked ==((Item_cond*)cond)->argument_list()->elements)
+ cond->marker= ICP_COND_USES_INDEX_ONLY;
+ switch (new_cond->argument_list()->elements) {
+ case 0:
+ return (COND*) 0;
+ case 1:
+ return new_cond->argument_list()->head();
+ default:
+ new_cond->quick_fix_field();
+ return new_cond;
+ }
+ }
+ else /* It's OR */
+ {
+ Item_cond_or *new_cond=new Item_cond_or;
+ if (!new_cond)
+ return (COND*) 0;
+ List_iterator<Item> li(*((Item_cond*) cond)->argument_list());
+ Item *item;
+ while ((item=li++))
+ {
+ Item *fix= make_cond_for_index(item, table, keyno, other_tbls_ok);
+ if (!fix)
+ return (COND*) 0;
+ new_cond->argument_list()->push_back(fix);
+ n_marked += test(item->marker == ICP_COND_USES_INDEX_ONLY);
+ }
+ if (n_marked ==((Item_cond*)cond)->argument_list()->elements)
+ cond->marker= ICP_COND_USES_INDEX_ONLY;
+ new_cond->quick_fix_field();
+ new_cond->top_level_item();
+ return new_cond;
+ }
+ }
+
+ if (!uses_index_fields_only(cond, table, keyno, other_tbls_ok))
+ return (COND*) 0;
+ cond->marker= ICP_COND_USES_INDEX_ONLY;
+ return cond;
+}
+
+
+Item *make_cond_remainder(Item *cond, bool exclude_index)
+{
+ if (exclude_index && cond->marker == ICP_COND_USES_INDEX_ONLY)
+ return 0; /* Already checked */
+
+ if (cond->type() == Item::COND_ITEM)
+ {
+ table_map tbl_map= 0;
+ if (((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC)
+ {
+ /* Create new top level AND item */
+ Item_cond_and *new_cond=new Item_cond_and;
+ if (!new_cond)
+ return (COND*) 0;
+ List_iterator<Item> li(*((Item_cond*) cond)->argument_list());
+ Item *item;
+ while ((item=li++))
+ {
+ Item *fix= make_cond_remainder(item, exclude_index);
+ if (fix)
+ {
+ new_cond->argument_list()->push_back(fix);
+ tbl_map |= fix->used_tables();
+ }
+ }
+ switch (new_cond->argument_list()->elements) {
+ case 0:
+ return (COND*) 0;
+ case 1:
+ return new_cond->argument_list()->head();
+ default:
+ new_cond->quick_fix_field();
+ ((Item_cond*)new_cond)->used_tables_cache= tbl_map;
+ return new_cond;
+ }
+ }
+ else /* It's OR */
+ {
+ Item_cond_or *new_cond=new Item_cond_or;
+ if (!new_cond)
+ return (COND*) 0;
+ List_iterator<Item> li(*((Item_cond*) cond)->argument_list());
+ Item *item;
+ while ((item=li++))
+ {
+ Item *fix= make_cond_remainder(item, FALSE);
+ if (!fix)
+ return (COND*) 0;
+ new_cond->argument_list()->push_back(fix);
+ tbl_map |= fix->used_tables();
+ }
+ new_cond->quick_fix_field();
+ ((Item_cond*)new_cond)->used_tables_cache= tbl_map;
+ new_cond->top_level_item();
+ return new_cond;
+ }
+ }
+ return cond;
+}
+
+
+/*
+ Try to extract and push the index condition
+
+ SYNOPSIS
+ push_index_cond()
+ tab A join tab that has tab->table->file and its condition
+ in tab->select_cond
+ keyno Index for which extract and push the condition
+ other_tbls_ok TRUE <=> Fields of other non-const tables are allowed
+
+ DESCRIPTION
+ Try to extract and push the index condition down to table handler
+*/
+
+static void push_index_cond(JOIN_TAB *tab, uint keyno, bool other_tbls_ok)
+{
+ DBUG_ENTER("push_index_cond");
+ Item *idx_cond;
+ if (tab->table->file->index_flags(keyno, 0, 1) & HA_DO_INDEX_COND_PUSHDOWN &&
+ tab->join->thd->variables.engine_condition_pushdown)
+ {
+ DBUG_EXECUTE("where", print_where(tab->select_cond, "full cond"););
+ idx_cond= make_cond_for_index(tab->select_cond, tab->table, keyno,
+ other_tbls_ok);
+ DBUG_EXECUTE("where", print_where(idx_cond, "idx cond"););
+ if (idx_cond)
+ {
+ tab->pre_idx_push_select_cond= tab->select_cond;
+ Item *idx_remainder_cond=
+ tab->table->file->idx_cond_push(keyno, idx_cond);
+ Item *row_cond= make_cond_remainder(tab->select_cond, TRUE);
+ DBUG_EXECUTE("where", print_where(row_cond, "remainder cond"););
+
+ if (row_cond)
+ {
+ if (!idx_remainder_cond)
+ tab->select_cond= row_cond;
+ else
+ {
+ tab->select_cond= new Item_cond_and(row_cond, idx_remainder_cond);
+ tab->select_cond->quick_fix_field();
+ ((Item_cond_and*)tab->select_cond)->used_tables_cache=
+ row_cond->used_tables() | idx_remainder_cond->used_tables();
+ }
+ }
+ else
+ tab->select_cond= idx_remainder_cond;
+ if (tab->select)
+ {
+ DBUG_EXECUTE("where", print_where(tab->select->cond, "select_cond"););
+ tab->select->cond= tab->select_cond;
+ }
+ }
+ }
+ DBUG_VOID_RETURN;
+}
+
+
static void
make_join_readinfo(JOIN *join, ulonglong options)
{
@@ -6096,6 +6396,7 @@
{
JOIN_TAB *tab=join->join_tab+i;
TABLE *table=tab->table;
+ bool using_join_cache;
tab->read_record.table= table;
tab->read_record.file=table->file;
tab->next_select=sub_select; /* normal select */
@@ -6148,6 +6449,8 @@
table->key_read=1;
table->file->extra(HA_EXTRA_KEYREAD);
}
+ else
+ push_index_cond(tab, tab->ref.key, TRUE);
break;
case JT_REF_OR_NULL:
case JT_REF:
@@ -6165,6 +6468,8 @@
table->key_read=1;
table->file->extra(HA_EXTRA_KEYREAD);
}
+ else
+ push_index_cond(tab, tab->ref.key, TRUE);
if (tab->type == JT_REF)
{
tab->read_first_record= join_read_always_key;
@@ -6187,6 +6492,7 @@
If the incoming data set is already sorted don't use cache.
*/
table->status=STATUS_NO_RECORD;
+ using_join_cache= FALSE;
if (i != join->const_tables && !(options & SELECT_NO_JOIN_CACHE) &&
tab->use_quick != 2 && !tab->first_inner && !ordered_set)
{
@@ -6194,6 +6500,7 @@
!join_init_cache(join->thd,join->join_tab+join->const_tables,
i-join->const_tables))
{
+ using_join_cache= TRUE;
tab[-1].next_select=sub_select_cache; /* Patch previous */
}
}
@@ -6258,6 +6565,9 @@
tab->type=JT_NEXT; // Read with index_first / index_next
}
}
+ if (tab->select && tab->select->quick &&
+ tab->select->quick->index != MAX_KEY && ! tab->table->key_read)
+ push_index_cond(tab, tab->select->quick->index, !using_join_cache);
}
break;
default:
@@ -11329,7 +11639,8 @@
delete tab->select->quick;
tab->select->quick=0;
return tab->select->test_quick_select(tab->join->thd, tab->keys,
- (table_map) 0, HA_POS_ERROR, 0);
+ (table_map) 0, HA_POS_ERROR, 0,
+ FALSE);
}
@@ -11982,6 +12293,14 @@
We can remove binary fields and numerical fields except float,
as float comparison isn't 100 % secure
We have to keep normal strings to be able to check for end spaces
+
+ sergefp: the above seems to be too restrictive. Counterexample:
+ create table t100 (v varchar(10), key(v)) default charset=latin1;
+ insert into t100 values ('a'),('a ');
+ explain select * from t100 where v='a';
+ The EXPLAIN shows 'using Where'. Running the query returns both
+ rows, so it seems there are no problems with endspace in the most
+ frequent case?
*/
if (field->binary() &&
field->real_type() != MYSQL_TYPE_STRING &&
@@ -11997,6 +12316,38 @@
}
+/*
+ Extract a condition that can be checked after reading given table
+
+ SYNOPSIS
+ make_cond_for_table()
+ cond Condition to analyze
+ tables Tables for which "current field values" are available
+ used_table Table that we're extracting the condition for (may
+ also include PSEUDO_TABLE_BITS
+
+ DESCRIPTION
+ Extract the condition that can be checked after reading the table
+ specified in 'used_table', given that current-field values for tables
+ specified in 'tables' bitmap are available.
+
+ The function assumes that
+ - Constant parts of the condition has already been checked.
+ - Condition that could be checked for tables in 'tables' has already
+ been checked.
+
+ The function takes into account that some parts of the condition are
+ guaranteed to be true by employed 'ref' access methods (the code that
+ does this is located at the end, search down for "EQ_FUNC").
+
+
+ SEE ALSO
+ make_cond_for_info_schema uses similar algorithm
+
+ RETURN
+ Extracted condition
+*/
+
static COND *
make_cond_for_table(COND *cond, table_map tables, table_map used_table)
{
@@ -12071,6 +12422,10 @@
if (cond->marker == 2 || cond->eq_cmp_result() == Item::COND_OK)
return cond; // Not boolean op
+ /*
+ Remove equalities that are guaranteed to be true by use of 'ref' access
+ method
+ */
if (((Item_func*) cond)->functype() == Item_func::EQ_FUNC)
{
Item *left_item= ((Item_func*) cond)->arguments()[0];
@@ -12092,6 +12447,7 @@
return cond;
}
+
static Item *
part_of_refkey(TABLE *table,Field *field)
{
@@ -12106,7 +12462,7 @@
for (uint part=0 ; part < ref_parts ; part++,key_part++)
if (field->eq(key_part->field) &&
- !(key_part->key_part_flag & (HA_PART_KEY_SEG | HA_NULL_PART)))
+ !(key_part->key_part_flag & HA_PART_KEY_SEG))
return table->reginfo.join_tab->ref.items[part];
}
return (Item*) 0;
@@ -12490,6 +12846,8 @@
&usable_keys)) < MAX_KEY)
{
/* Found key that can be used to retrieve data in sorted order */
+ if (tab->pre_idx_push_select_cond)
+ tab->select_cond= tab->select->cond= tab->pre_idx_push_select_cond;
if (tab->ref.key >= 0)
{
/*
@@ -12503,6 +12861,7 @@
KEYUSE *keyuse= tab->keyuse;
while (keyuse->key != new_ref_key && keyuse->table == tab->table)
keyuse++;
+
if (create_ref_for_key(tab->join, tab, keyuse,
tab->join->const_table_map))
DBUG_RETURN(0);
@@ -12525,7 +12884,8 @@
(tab->join->select_options &
OPTION_FOUND_ROWS) ?
HA_POS_ERROR :
- tab->join->unit->select_limit_cnt,0) <=
+ tab->join->unit->select_limit_cnt,0,
+ TRUE) <=
0)
DBUG_RETURN(0);
}
@@ -12548,6 +12908,7 @@
if (!select->quick->reverse_sorted())
{
QUICK_SELECT_DESC *tmp;
+ bool create_error= FALSE;
int quick_type= select->quick->get_type();
if (quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_MERGE ||
quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT ||
@@ -12557,8 +12918,8 @@
/* ORDER BY range_key DESC */
tmp= new QUICK_SELECT_DESC((QUICK_RANGE_SELECT*)(select->quick),
- used_key_parts);
- if (!tmp || tmp->error)
+ used_key_parts, &create_error);
+ if (!tmp || create_error)
{
delete tmp;
DBUG_RETURN(0); // Reverse sort not supported
@@ -12581,7 +12942,16 @@
}
}
else if (select && select->quick)
- select->quick->sorted= 1;
+ {
+ select->quick->sorted= 1;
+ if (tab->table->file->ha_table_flags() & HA_MRR_CANT_SORT &&
+ select->quick->get_type() == QUICK_SELECT_I::QS_TYPE_RANGE)
+ {
+ ((QUICK_RANGE_SELECT*)select->quick)->mrr_flags |=
+ HA_MRR_USE_DEFAULT_IMPL;
+ }
+
+ }
DBUG_RETURN(1); /* No need to sort */
}
}
@@ -12736,7 +13106,7 @@
field, quick will contain an empty record set.
*/
if (!(select->quick= (tab->type == JT_FT ?
- new FT_SELECT(thd, table, tab->ref.key) :
+ get_ft_select(thd, table, tab->ref.key) :
get_quick_select_for_ref(thd, table, &tab->ref,
tab->found_records))))
goto err;
@@ -15041,8 +15411,8 @@
Send a description about what how the select will be done to stdout
****************************************************************************/
-static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
- bool distinct,const char *message)
+void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
+ bool distinct,const char *message)
{
List<Item> field_list;
List<Item> item_list;
@@ -15355,6 +15725,13 @@
}
else
{
+ uint keyno= MAX_KEY;
+ if (tab->ref.key_parts)
+ keyno= tab->ref.key;
+ else if (tab->select && tab->select->quick)
+ keyno = tab->select->quick->index;
+
+ tab->table->file->add_explain_extra_info(keyno, &extra);
if (quick_type == QUICK_SELECT_I::QS_TYPE_ROR_UNION ||
quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT ||
quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_MERGE)
@@ -15399,6 +15776,14 @@
}
if (table->reginfo.not_exists_optimize)
extra.append(STRING_WITH_LEN("; Not exists"));
+
+ if (quick_type == QUICK_SELECT_I::QS_TYPE_RANGE &&
+ !(((QUICK_RANGE_SELECT*)(tab->select->quick))->mrr_flags &
+ HA_MRR_USE_DEFAULT_IMPL))
+ {
+ extra.append(STRING_WITH_LEN("; Using MRR"));
+ }
+
if (need_tmp_table)
{
need_tmp_table=0;
--- 1.121/sql/sql_select.h 2007-04-04 22:52:01 +04:00
+++ 1.122/sql/sql_select.h 2007-04-04 22:52:01 +04:00
@@ -140,6 +140,14 @@
SQL_SELECT *select;
COND *select_cond;
QUICK_SELECT_I *quick;
+ /*
+ The value of select_cond before we've attempted to do Index Condition
+ Pushdown. We may need to restore everything back if we first choose one
+ index but then reconsider (see test_if_skip_sort_order() for such
+ scenarios).
+ NULL means no index condition pushdown was performed.
+ */
+ Item *pre_idx_push_select_cond;
Item **on_expr_ref; /* pointer to the associated on expression */
COND_EQUAL *cond_equal; /* multiple equalities for the on expression */
st_join_table *first_inner; /* first inner table for including outerjoin */
--- 1.282/sql/table.cc 2007-04-04 22:52:01 +04:00
+++ 1.283/sql/table.cc 2007-04-04 22:52:01 +04:00
@@ -1195,21 +1195,11 @@
share->table_name.str,
share->table_name.str);
share->crashed= 1; // Marker for CHECK TABLE
- goto to_be_deleted;
+ continue;
}
#endif
key_part->key_part_flag|= HA_PART_KEY_SEG;
}
-
- to_be_deleted:
-
- /*
- If the field can be NULL, don't optimize away the test
- key_part_column = expression from the WHERE clause
- as we need to test for NULL = NULL.
- */
- if (field->real_maybe_null())
- key_part->key_part_flag|= HA_NULL_PART;
}
keyinfo->usable_key_parts= usable_parts; // Filesort
--- 1.98/mysql-test/r/union.result 2007-04-04 22:52:02 +04:00
+++ 1.99/mysql-test/r/union.result 2007-04-04 22:52:02 +04:00
@@ -505,7 +505,7 @@
explain (select * from t1 where a=1) union (select * from t1 where b=1);
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t1 const PRIMARY PRIMARY 4 const 1
-2 UNION t1 ref b b 5 const 1 Using where
+2 UNION t1 ref b b 5 const 1
NULL UNION RESULT <union1,2> ALL NULL NULL NULL NULL NULL
drop table t1,t2;
create table t1 ( id int not null auto_increment, primary key (id) ,user_name text );
--- 1.178/mysql-test/r/subselect.result 2007-04-04 22:52:02 +04:00
+++ 1.179/mysql-test/r/subselect.result 2007-04-04 22:52:02 +04:00
@@ -720,7 +720,7 @@
1
EXPLAIN EXTENDED SELECT * FROM t2 WHERE id IN (SELECT 1);
id select_type table type possible_keys key key_len ref rows filtered Extra
-1 PRIMARY t2 ref id id 5 const 1 100.00 Using where; Using index
+1 PRIMARY t2 ref id id 5 const 1 100.00 Using index
Warnings:
Note 1249 Select 2 was reduced during optimization
Note 1003 select `test`.`t2`.`id` AS `id` from `test`.`t2` where (`test`.`t2`.`id` = 1)
@@ -732,7 +732,7 @@
2
EXPLAIN EXTENDED SELECT * FROM t2 WHERE id IN (SELECT 1+(select 1));
id select_type table type possible_keys key key_len ref rows filtered Extra
-1 PRIMARY t2 ref id id 5 const 1 100.00 Using where; Using index
+1 PRIMARY t2 ref id id 5 const 1 100.00 Using index
Warnings:
Note 1249 Select 3 was reduced during optimization
Note 1249 Select 2 was reduced during optimization
@@ -1232,7 +1232,7 @@
insert into t1 (salary) values (100),(1000),(10000),(10),(500),(5000),(50000);
explain extended SELECT id FROM t1 where salary = (SELECT MAX(salary) FROM t1);
id select_type table type possible_keys key key_len ref rows filtered Extra
-1 PRIMARY t1 ref salary salary 5 const 1 100.00 Using where
+1 PRIMARY t1 ref salary salary 5 const 1 100.00 Using index condition
2 SUBQUERY NULL NULL NULL NULL NULL NULL NULL NULL Select tables optimized away
Warnings:
Note 1003 select `test`.`t1`.`id` AS `id` from `test`.`t1` where (`test`.`t1`.`salary` = (select max(`test`.`t1`.`salary`) AS `MAX(salary)` from `test`.`t1`))
@@ -1315,7 +1315,7 @@
explain extended select * from t2 where t2.a in (select t1.a from t1,t3 where t1.b=t3.a);
id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY t2 index NULL PRIMARY 4 NULL 4 100.00 Using where; Using index
-2 DEPENDENT SUBQUERY t1 eq_ref PRIMARY PRIMARY 4 func 1 100.00 Using where
+2 DEPENDENT SUBQUERY t1 eq_ref PRIMARY PRIMARY 4 func 1 100.00 Using index condition
2 DEPENDENT SUBQUERY t3 eq_ref PRIMARY PRIMARY 4 test.t1.b 1 100.00 Using where; Using index
Warnings:
Note 1003 select `test`.`t2`.`a` AS `a` from `test`.`t2` where <in_optimizer>(`test`.`t2`.`a`,<exists>(select 1 AS `Not_used` from `test`.`t1` join `test`.`t3` where ((`test`.`t3`.`a` = `test`.`t1`.`b`) and (<cache>(`test`.`t2`.`a`) = `test`.`t1`.`a`))))
@@ -1768,7 +1768,7 @@
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE a ALL NULL NULL NULL NULL 14 100.00
1 SIMPLE b eq_ref PRIMARY PRIMARY 4 test.a.id 2 100.00
-1 SIMPLE c eq_ref PRIMARY PRIMARY 4 func 1 100.00 Using where
+1 SIMPLE c eq_ref PRIMARY PRIMARY 4 func 1 100.00 Using index condition
Warnings:
Note 1003 select `test`.`a`.`id` AS `id`,`test`.`a`.`text` AS `text`,`test`.`b`.`id` AS `id`,`test`.`b`.`text` AS `text`,`test`.`c`.`id` AS `id`,`test`.`c`.`text` AS `text` from `test`.`t1` `a` left join `test`.`t2` `b` on(((`test`.`b`.`id` = `test`.`a`.`id`) or isnull(`test`.`b`.`id`))) join `test`.`t1` `c` where (if(isnull(`test`.`b`.`id`),1000,`test`.`b`.`id`) = `test`.`c`.`id`)
drop table t1,t2;
@@ -2946,7 +2946,7 @@
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t1 system PRIMARY NULL NULL NULL 1
1 PRIMARY r const PRIMARY PRIMARY 4 const 1
-2 DEPENDENT SUBQUERY t2 range b b 40 NULL 2 Using where
+2 DEPENDENT SUBQUERY t2 range b b 40 NULL 2 Using index condition
SELECT sql_no_cache t1.a, r.a, r.b FROM t1 LEFT JOIN t2 r
ON r.a = (SELECT t2.a FROM t2 WHERE t2.c = t1.a AND t2.b <= '359899'
ORDER BY t2.c DESC, t2.b DESC LIMIT 1) WHERE t1.a = 10;
@@ -2958,7 +2958,7 @@
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t1 system PRIMARY NULL NULL NULL 1
1 PRIMARY r const PRIMARY PRIMARY 4 const 1
-2 DEPENDENT SUBQUERY t2 range b b 40 NULL 2 Using where
+2 DEPENDENT SUBQUERY t2 range b b 40 NULL 2 Using index condition
SELECT sql_no_cache t1.a, r.a, r.b FROM t1 LEFT JOIN t2 r
ON r.a = (SELECT t2.a FROM t2 WHERE t2.c = t1.a AND t2.b <= '359899'
ORDER BY t2.c, t2.b LIMIT 1) WHERE t1.a = 10;
--- 1.218/mysql-test/r/view.result 2007-04-04 22:52:02 +04:00
+++ 1.219/mysql-test/r/view.result 2007-04-04 22:52:02 +04:00
@@ -2100,12 +2100,12 @@
INSERT INTO t1 VALUES (1, 'foo1');
SELECT * FROM v1;
id f
-1 foo1
2 foo2
+1 foo1
SELECT * FROM v1;
id f
-1 foo1
2 foo2
+1 foo1
DROP VIEW v1;
DROP TABLE t1;
CREATE TABLE t1 (pk int PRIMARY KEY, b int);
@@ -2309,15 +2309,15 @@
CREATE VIEW v2 AS SELECT t3.* FROM t1,t3 WHERE t1.a=t3.a;
EXPLAIN SELECT t1.* FROM t1 JOIN t2 WHERE t1.a=t2.a AND t1.b=t2.b AND t1.a=1;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 ref a a 5 const 1 Using where; Using index
-1 SIMPLE t2 ref a a 10 const,test.t1.b 2 Using where; Using index
+1 SIMPLE t1 ref a a 5 const 1 Using index
+1 SIMPLE t2 ref a a 10 const,test.t1.b 2 Using index
EXPLAIN SELECT * FROM v1 WHERE a=1;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 ref a a 5 const 1 Using where; Using index
-1 SIMPLE t2 ref a a 10 const,test.t1.b 2 Using where; Using index
+1 SIMPLE t1 ref a a 5 const 1 Using index
+1 SIMPLE t2 ref a a 10 const,test.t1.b 2 Using index
EXPLAIN SELECT * FROM v2 WHERE a=1;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 ref a a 5 const 1 Using where; Using index
+1 SIMPLE t1 ref a a 5 const 1 Using index
1 SIMPLE t3 ALL NULL NULL NULL NULL 3 Using where
DROP VIEW v1,v2;
DROP TABLE t1,t2,t3;
--- 1.64/mysql-test/r/ps_1general.result 2007-04-04 22:52:02 +04:00
+++ 1.65/mysql-test/r/ps_1general.result 2007-04-04 22:52:02 +04:00
@@ -462,9 +462,9 @@
def key_len 253 4096 1 Y 128 31 63
def ref 253 1024 0 Y 0 31 8
def rows 8 10 1 Y 32928 0 63
-def Extra 253 255 27 N 1 31 8
+def Extra 253 255 48 N 1 31 8
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 range PRIMARY PRIMARY 4 NULL 3 Using where; Using filesort
+1 SIMPLE t1 range PRIMARY PRIMARY 4 NULL 3 Using index condition; Using MRR; Using filesort
drop table if exists t2;
create table t2 (id smallint, name varchar(20)) ;
prepare stmt1 from ' insert into t2 values(?, ?) ' ;
--- 1.52/mysql-test/r/ps_2myisam.result 2007-04-04 22:52:02 +04:00
+++ 1.53/mysql-test/r/ps_2myisam.result 2007-04-04 22:52:02 +04:00
@@ -1509,8 +1509,8 @@
execute stmt1 using @arg00, @arg01, @arg02, @arg03 ;
select a,b from t1 where a in (@arg00,@arg02) ;
a b
-81 8-1
82 8-2
+81 8-1
set @arg00=9 ;
set @arg01='nine' ;
prepare stmt1 from 'insert into t1 set a=?, b=? ';
--- 1.425/sql/ha_ndbcluster.cc 2007-04-04 22:52:02 +04:00
+++ 1.426/sql/ha_ndbcluster.cc 2007-04-04 22:52:02 +04:00
@@ -8089,72 +8089,187 @@
trans->releaseCompletedOperations();
}
-bool
-ha_ndbcluster::null_value_index_search(KEY_MULTI_RANGE *ranges,
- KEY_MULTI_RANGE *end_range,
- HANDLER_BUFFER *buffer)
+/****************************************************************************
+ * MRR interface implementation
+ ***************************************************************************/
+
+
+/*
+ Get cost and other information about MRR scan over a known list of ranges
+
+ SYNOPSIS
+ See handler::multi_range_read_info_const.
+
+ DESCRIPTION
+ The implementation is copied from handler::multi_range_read_info_const.
+ The only difference is that NDB-MRR cannot handle blob columns or keys
+ with NULLs for unique indexes. We disable MRR for those cases.
+*/
+
+ha_rows
+ha_ndbcluster::multi_range_read_info_const(uint keyno, RANGE_SEQ_IF *seq,
+ void *seq_init_param,
+ uint n_ranges_arg, uint *bufsz,
+ uint *flags, COST_VECT *cost)
{
- DBUG_ENTER("null_value_index_search");
- KEY* key_info= table->key_info + active_index;
- KEY_MULTI_RANGE *range= ranges;
- ulong reclength= table->s->reclength;
- byte *curr= (byte*)buffer->buffer;
- byte *end_of_buffer= (byte*)buffer->buffer_end;
-
- for (; range<end_range && curr+reclength <= end_of_buffer;
- range++)
+ KEY_MULTI_RANGE range;
+ range_seq_t seq_it;
+ ha_rows rows, total_rows= 0;
+ uint n_ranges=0;
+ bool null_ranges= FALSE;
+ m_write_op= FALSE;
+
+
+ seq_it= seq->init(seq_init_param, n_ranges, *flags);
+ while (!seq->next(seq_it, &range))
+ {
+ n_ranges++;
+ key_range *min_endp= range.start_key.length? &range.start_key : NULL;
+ key_range *max_endp= range.end_key.length? &range.end_key : NULL;
+ null_ranges |= (range.range_flag & NULL_RANGE);
+ if ((range.range_flag & UNIQUE_RANGE) && !(range.range_flag & NULL_RANGE))
+ rows= 1; /* there can be at most one row */
+ else
+ {
+ if (HA_POS_ERROR == (rows= this->records_in_range(keyno, min_endp,
+ max_endp)))
+ {
+ /* Can't scan one range => can't do MRR scan at all */
+ total_rows= HA_POS_ERROR;
+ break;
+ }
+ }
+ total_rows += rows;
+ }
+
+ if (total_rows != HA_POS_ERROR)
+ {
+ if (*flags & HA_MRR_USE_DEFAULT_IMPL ||
+ uses_blob_value() ||
+ ((get_index_type(keyno) == UNIQUE_INDEX &&
+ has_null_in_unique_index(keyno)) && null_ranges))
+ {
+ /* Use default MRR implementation */
+ *flags |= HA_MRR_USE_DEFAULT_IMPL;
+ *bufsz= 0;
+ }
+ else
+ {
+ /*
+ We'll be most efficient when we have buffer big enough to accomodate
+ all rows we expect.
+ */
+ *bufsz= min(*bufsz, total_rows * table_share->reclength);
+ }
+
+ cost->zero();
+ cost->avg_io_cost= 1; /* assume random seeks */
+ if ((*flags & HA_MRR_INDEX_ONLY) && total_rows > 2)
+ cost->io_count= index_only_read_time(keyno, total_rows);
+ else
+ cost->io_count= read_time(keyno, n_ranges, total_rows);
+ cost->cpu_cost= (double) total_rows / TIME_FOR_COMPARE + 0.01;
+ }
+ return total_rows;
+}
+
+
+/*
+ Get cost and other information about MRR scan over some sequence of ranges
+
+ SYNOPSIS
+ See handler::multi_range_read_info.
+*/
+
+int
+ha_ndbcluster::multi_range_read_info(uint keyno, uint n_ranges, uint keys,
+ uint *bufsz, uint *flags, COST_VECT *cost)
+{
+ int res;
+ uint save_bufsize= *bufsz;
+ res= handler::multi_range_read_info(keyno, n_ranges, keys, bufsz, flags,
+ cost);
+ if (uses_blob_value() || !(*flags & HA_MRR_NO_NULL_ENDPOINTS))
+ {
+ *flags |= HA_MRR_USE_DEFAULT_IMPL;
+ *bufsz= 0;
+ }
+ else
{
- const byte *key= range->start_key.key;
- uint key_len= range->start_key.length;
- if (check_null_in_key(key_info, key, key_len))
- DBUG_RETURN(TRUE);
- curr += reclength;
+ *flags &= ~HA_MRR_USE_DEFAULT_IMPL;
+ *bufsz= min(save_bufsize, keys * table_share->reclength);
}
- DBUG_RETURN(FALSE);
+ return res;
}
-int
-ha_ndbcluster::read_multi_range_first(KEY_MULTI_RANGE **found_range_p,
- KEY_MULTI_RANGE *ranges,
- uint range_count,
- bool sorted,
- HANDLER_BUFFER *buffer)
+#if 0
+#define DBUG_MULTI_RANGE(x) DBUG_PRINT("info", ("multi_range_read_next: case %d\n", x));
+#else
+#define DBUG_MULTI_RANGE(x)
+#endif
+
+
+
+/*
+*/
+
+int ha_ndbcluster::multi_range_read_init(RANGE_SEQ_IF *seq_funcs,
+ void *seq_init_param,
+ uint n_ranges, uint mode,
+ HANDLER_BUFFER *buffer)
{
m_write_op= FALSE;
int res;
- KEY* key_info= table->key_info + active_index;
- NDB_INDEX_TYPE cur_index_type= get_index_type(active_index);
- ulong reclength= table_share->reclength;
- NdbOperation* op;
Thd_ndb *thd_ndb= get_thd_ndb(current_thd);
- DBUG_ENTER("ha_ndbcluster::read_multi_range_first");
+ DBUG_ENTER("ha_ndbcluster::multi_range_read_init");
- /**
- * blobs and unique hash index with NULL can't be batched currently
- */
- if (uses_blob_value() ||
- (cur_index_type == UNIQUE_INDEX &&
- has_null_in_unique_index(active_index) &&
- null_value_index_search(ranges, ranges+range_count, buffer)))
+ if (mode & HA_MRR_USE_DEFAULT_IMPL)
{
m_disable_multi_read= TRUE;
- DBUG_RETURN(handler::read_multi_range_first(found_range_p,
- ranges,
- range_count,
- sorted,
- buffer));
+ DBUG_RETURN(handler::multi_range_read_init(seq_funcs, seq_init_param,
+ n_ranges, mode, buffer));
}
+
thd_ndb->query_state|= NDB_QUERY_MULTI_READ_RANGE;
m_disable_multi_read= FALSE;
+ mrr_is_output_sorted= test(mode & HA_MRR_SORTED);
/**
* Copy arguments into member variables
*/
- m_multi_ranges= ranges;
- multi_range_curr= ranges;
- multi_range_end= ranges+range_count;
- multi_range_sorted= sorted;
multi_range_buffer= buffer;
+ mrr_funcs= *seq_funcs;
+ mrr_iter= mrr_funcs.init(seq_init_param, n_ranges, mode);
+ ranges_in_seq= n_ranges;
+
+ res= multi_range_start_retrievals(-1);
+ if (first_unstarted_range == n_ranges)
+ {
+ /**
+ * Mark that we're using entire buffer (even if might not) as
+ * we haven't read all ranges for some reason
+ * This as we don't want mysqld to reuse the buffer when we read
+ * the remaining ranges
+ */
+ buffer->end_of_used_area= (byte*)multi_range_buffer->buffer_end;
+ }
+ else
+ {
+ /* Using all buffer */
+ }
+
+ DBUG_RETURN(res);
+}
+
+
+int ha_ndbcluster::multi_range_start_retrievals(int starting_range)
+{
+ int res, range_res;
+ KEY* key_info= table->key_info + active_index;
+ ulong reclength= table_share->reclength;
+ NdbOperation* op;
+ NDB_INDEX_TYPE cur_index_type= get_index_type(active_index);
+ DBUG_ENTER("multi_range_start_retrievals");
/**
* read multi range will read ranges as follows (if not ordered)
@@ -8172,8 +8287,8 @@
/**
* Variables for loop
*/
- byte *curr= (byte*)buffer->buffer;
- byte *end_of_buffer= (byte*)buffer->buffer_end;
+ byte *curr= (byte*)multi_range_buffer->buffer;
+ byte *end_of_buffer= (byte*)multi_range_buffer->buffer_end;
NdbOperation::LockMode lm=
(NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
bool need_pk = (lm == NdbOperation::LM_Read);
@@ -8182,14 +8297,19 @@
const NDBINDEX *idx= m_index[active_index].index;
const NdbOperation* lastOp= m_active_trans->getLastDefinedOperation();
NdbIndexScanOperation* scanOp= 0;
- for (; multi_range_curr<multi_range_end && curr+reclength <= end_of_buffer;
- multi_range_curr++)
+ int range_no= -1;
+ int mrr_range_no= starting_range;
+
+ while (!(range_res= mrr_funcs.next(mrr_iter, &mrr_cur_range)) &&
+ curr+reclength <= end_of_buffer)
{
+ range_no++;
+ mrr_range_no++;
part_id_range part_spec;
if (m_use_partition_function)
{
get_partition_set(table, curr, active_index,
- &multi_range_curr->start_key,
+ &mrr_cur_range.start_key,
&part_spec);
DBUG_PRINT("info", ("part_spec.start_part: %u part_spec.end_part: %u",
part_spec.start_part, part_spec.end_part));
@@ -8204,22 +8324,22 @@
partition
*/
curr += reclength;
- multi_range_curr->range_flag |= SKIP_RANGE;
+ mrr_persistent_flag_storage(mrr_iter, mrr_range_no) |= SKIP_RANGE;
continue;
}
}
switch (cur_index_type) {
case PRIMARY_KEY_ORDERED_INDEX:
- if (!(multi_range_curr->start_key.length == key_info->key_length &&
- multi_range_curr->start_key.flag == HA_READ_KEY_EXACT))
+ if (!(mrr_cur_range.start_key.length == key_info->key_length &&
+ mrr_cur_range.start_key.flag == HA_READ_KEY_EXACT))
goto range;
// else fall through
case PRIMARY_KEY_INDEX:
{
- multi_range_curr->range_flag |= UNIQUE_RANGE;
+ mrr_persistent_flag_storage(mrr_iter, mrr_range_no) |= UNIQUE_RANGE;
if ((op= m_active_trans->getNdbOperation(tab)) &&
!op->readTuple(lm) &&
- !set_primary_key(op, multi_range_curr->start_key.key) &&
+ !set_primary_key(op, mrr_cur_range.start_key.key) &&
!define_read_attrs(curr, op) &&
(!m_use_partition_function ||
(op->setPartitionId(part_spec.start_part), TRUE)))
@@ -8230,33 +8350,34 @@
}
break;
case UNIQUE_ORDERED_INDEX:
- if (!(multi_range_curr->start_key.length == key_info->key_length &&
- multi_range_curr->start_key.flag == HA_READ_KEY_EXACT &&
- !check_null_in_key(key_info, multi_range_curr->start_key.key,
- multi_range_curr->start_key.length)))
+ if (!(mrr_cur_range.start_key.length == key_info->key_length &&
+ mrr_cur_range.start_key.flag == HA_READ_KEY_EXACT &&
+ !check_null_in_key(key_info, mrr_cur_range.start_key.key,
+ mrr_cur_range.start_key.length)))
goto range;
// else fall through
case UNIQUE_INDEX:
{
- multi_range_curr->range_flag |= UNIQUE_RANGE;
+ mrr_persistent_flag_storage(mrr_iter, mrr_range_no) |= UNIQUE_RANGE;
if ((op= m_active_trans->getNdbIndexOperation(unique_idx, tab)) &&
!op->readTuple(lm) &&
- !set_index_key(op, key_info, multi_range_curr->start_key.key) &&
+ !set_index_key(op, key_info, mrr_cur_range.start_key.key) &&
!define_read_attrs(curr, op))
curr += reclength;
else
ERR_RETURN(op ? op->getNdbError() : m_active_trans->getNdbError());
break;
}
- case ORDERED_INDEX: {
+ case ORDERED_INDEX:
+ {
range:
- multi_range_curr->range_flag &= ~(uint)UNIQUE_RANGE;
+ mrr_persistent_flag_storage(mrr_iter, mrr_range_no) &= ~(uint16)UNIQUE_RANGE;
if (scanOp == 0)
{
if (m_multi_cursor)
{
scanOp= m_multi_cursor;
- DBUG_ASSERT(scanOp->getSorted() == sorted);
+ DBUG_ASSERT(scanOp->getSorted() == mrr_is_output_sorted);
DBUG_ASSERT(scanOp->getLockMode() ==
(NdbOperation::LockMode)get_ndb_lock_type(m_lock.type));
if (scanOp->reset_bounds(m_force_send))
@@ -8265,7 +8386,7 @@
end_of_buffer -= reclength;
}
else if ((scanOp= m_active_trans->getNdbIndexScanOperation(idx, tab))
- &&!scanOp->readTuples(lm, 0, parallelism, sorted,
+ &&!scanOp->readTuples(lm, 0, parallelism, mrr_is_output_sorted,
FALSE, TRUE, need_pk, TRUE)
&&!generate_scan_filter(m_cond_stack, scanOp)
&&!define_read_attrs(end_of_buffer-reclength, scanOp))
@@ -8280,10 +8401,9 @@
}
}
- const key_range *keys[2]= { &multi_range_curr->start_key,
- &multi_range_curr->end_key };
- if ((res= set_bounds(scanOp, active_index, FALSE, keys,
- multi_range_curr-ranges)))
+ const key_range *keys[2]= { &mrr_cur_range.start_key,
+ &mrr_cur_range.end_key };
+ if ((res= set_bounds(scanOp, active_index, FALSE, keys, range_no)))
DBUG_RETURN(res);
break;
}
@@ -8294,21 +8414,6 @@
}
}
- if (multi_range_curr != multi_range_end)
- {
- /**
- * Mark that we're using entire buffer (even if might not) as
- * we haven't read all ranges for some reason
- * This as we don't want mysqld to reuse the buffer when we read
- * the remaining ranges
- */
- buffer->end_of_used_area= (byte*)buffer->buffer_end;
- }
- else
- {
- buffer->end_of_used_area= curr;
- }
-
/**
* Set first operation in multi range
*/
@@ -8316,40 +8421,37 @@
lastOp ? lastOp->next() : m_active_trans->getFirstDefinedOperation();
if (!(res= execute_no_commit_ie(this, m_active_trans,true)))
{
- m_multi_range_defined= multi_range_curr;
- multi_range_curr= ranges;
- m_multi_range_result_ptr= (byte*)buffer->buffer;
- DBUG_RETURN(read_multi_range_next(found_range_p));
+ m_multi_range_result_ptr= (byte*)multi_range_buffer->buffer;
+ first_running_range= first_range_in_batch= starting_range + 1;
+ first_unstarted_range= mrr_range_no + 1;
+ DBUG_RETURN(0);
}
ERR_RETURN(m_active_trans->getNdbError());
}
-#if 0
-#define DBUG_MULTI_RANGE(x) DBUG_PRINT("info", ("read_multi_range_next: case %d\n", x));
-#else
-#define DBUG_MULTI_RANGE(x)
-#endif
-int
-ha_ndbcluster::read_multi_range_next(KEY_MULTI_RANGE ** multi_range_found_p)
+int ha_ndbcluster::multi_range_read_next(char **range_info)
{
- DBUG_ENTER("ha_ndbcluster::read_multi_range_next");
- if (m_disable_multi_read)
+ DBUG_ENTER("ha_ndbcluster::multi_range_read_next");
+
+ if (m_disable_multi_read)
{
DBUG_MULTI_RANGE(11);
- DBUG_RETURN(handler::read_multi_range_next(multi_range_found_p));
+ DBUG_RETURN(handler::multi_range_read_next(range_info));
}
int res;
int range_no;
ulong reclength= table_share->reclength;
const NdbOperation* op= m_current_multi_operation;
- for (;multi_range_curr < m_multi_range_defined; multi_range_curr++)
+
+ //for each range (we should have remembered the number)
+ for (;first_running_range < first_unstarted_range; first_running_range++)
{
DBUG_MULTI_RANGE(12);
- if (multi_range_curr->range_flag & SKIP_RANGE)
+ if (mrr_persistent_flag_storage(mrr_iter, first_running_range) & SKIP_RANGE)
continue;
- if (multi_range_curr->range_flag & UNIQUE_RANGE)
+ if (mrr_persistent_flag_storage(mrr_iter, first_running_range) & UNIQUE_RANGE)
{
if (op->getNdbError().code == 0)
{
@@ -8361,7 +8463,7 @@
m_multi_range_result_ptr += reclength;
continue;
}
- else if (m_multi_cursor && !multi_range_sorted)
+ else if (m_multi_cursor && !mrr_is_output_sorted)
{
DBUG_MULTI_RANGE(1);
if ((res= fetch_next(m_multi_cursor)) == 0)
@@ -8376,7 +8478,7 @@
goto close_scan;
}
}
- else if (m_multi_cursor && multi_range_sorted)
+ else if (m_multi_cursor && mrr_is_output_sorted)
{
if (m_active_cursor && (res= fetch_next(m_multi_cursor)))
{
@@ -8385,7 +8487,7 @@
}
range_no= m_multi_cursor->get_range_no();
- uint current_range_no= multi_range_curr - m_multi_ranges;
+ uint current_range_no= first_running_range - first_range_in_batch;
if ((uint) range_no == current_range_no)
{
DBUG_MULTI_RANGE(4);
@@ -8409,7 +8511,7 @@
DBUG_MULTI_RANGE(15);
goto close_scan;
}
- multi_range_curr--; // Will be increased in for-loop
+ first_running_range--; // Will be increased in for-loop
continue;
}
}
@@ -8417,7 +8519,7 @@
{
DBUG_MULTI_RANGE(7);
/**
- * Corresponds to range 5 in example in read_multi_range_first
+ * Corresponds to range 5 in example in multi_range_start_retrievals
*/
(void)1;
continue;
@@ -8439,7 +8541,7 @@
}
}
- if (multi_range_curr == multi_range_end)
+ if (first_running_range == ranges_in_seq)
{
DBUG_MULTI_RANGE(16);
Thd_ndb *thd_ndb= get_thd_ndb(current_thd);
@@ -8447,21 +8549,17 @@
DBUG_RETURN(HA_ERR_END_OF_FILE);
}
- /**
- * Read remaining ranges
- */
- DBUG_RETURN(read_multi_range_first(multi_range_found_p,
- multi_range_curr,
- multi_range_end - multi_range_curr,
- multi_range_sorted,
- multi_range_buffer));
-
+ /* Read remaining ranges */
+ if ((res= multi_range_start_retrievals(first_running_range-1)))
+ DBUG_RETURN(res);
+ DBUG_RETURN(multi_range_read_next(range_info));
found:
/**
* Found a record belonging to a scan
*/
m_active_cursor= m_multi_cursor;
- * multi_range_found_p= m_multi_ranges + range_no;
+ //* multi_range_found_p= m_multi_ranges + range_no;
+ *range_info= mrr_get_ptr_by_idx(mrr_iter, first_running_range);
memcpy(table->record[0], m_multi_range_cursor_result_ptr, reclength);
setup_recattr(m_active_cursor->getFirstRecAttr());
unpack_record(table->record[0]);
@@ -8473,17 +8571,21 @@
* Found a record belonging to a pk/index op,
* copy result and move to next to prepare for next call
*/
- * multi_range_found_p= multi_range_curr;
+ *range_info= mrr_get_ptr_by_idx(mrr_iter, first_running_range++);
memcpy(table->record[0], m_multi_range_result_ptr, reclength);
setup_recattr(op->getFirstRecAttr());
unpack_record(table->record[0]);
table->status= 0;
- multi_range_curr++;
m_current_multi_operation= m_active_trans->getNextCompletedOperation(op);
m_multi_range_result_ptr += reclength;
DBUG_RETURN(0);
}
+
+
+/*****************************************************************************
+ * MRR implementation ends
+ ****************************************************************************/
int
ha_ndbcluster::setup_recattr(const NdbRecAttr* curr)
--- 1.172/sql/ha_ndbcluster.h 2007-04-04 22:52:02 +04:00
+++ 1.173/sql/ha_ndbcluster.h 2007-04-04 22:52:02 +04:00
@@ -663,12 +663,24 @@
int alter_tablespace(st_alter_tablespace *info);
/**
- * Multi range stuff
+ * Multi Range Read interface
*/
- int read_multi_range_first(KEY_MULTI_RANGE **found_range_p,
- KEY_MULTI_RANGE*ranges, uint range_count,
- bool sorted, HANDLER_BUFFER *buffer);
- int read_multi_range_next(KEY_MULTI_RANGE **found_range_p);
+ int multi_range_read_init(RANGE_SEQ_IF *seq, void *seq_init_param,
+ uint n_ranges, uint mode, HANDLER_BUFFER *buf);
+ int multi_range_read_next(char **range_info);
+ ha_rows multi_range_read_info_const(uint keyno, RANGE_SEQ_IF *seq,
+ void *seq_init_param,
+ uint n_ranges, uint *bufsz,
+ uint *flags, COST_VECT *cost);
+ int multi_range_read_info(uint keyno, uint n_ranges, uint keys,
+ uint *bufsz, uint *flags, COST_VECT *cost);
+private:
+ uint first_running_range;
+ uint first_range_in_batch;
+ uint first_unstarted_range;
+ int multi_range_start_retrievals(int first_range);
+public:
+
bool null_value_index_search(KEY_MULTI_RANGE *ranges,
KEY_MULTI_RANGE *end_range,
HANDLER_BUFFER *buffer);
@@ -975,8 +987,6 @@
Ndb_cond_stack *m_cond_stack;
bool m_disable_multi_read;
byte *m_multi_range_result_ptr;
- KEY_MULTI_RANGE *m_multi_ranges;
- KEY_MULTI_RANGE *m_multi_range_defined;
const NdbOperation *m_current_multi_operation;
NdbIndexScanOperation *m_multi_cursor;
byte *m_multi_range_cursor_result_ptr;
--- 1.6/mysql-test/r/mix2_myisam.result 2007-04-04 22:52:02 +04:00
+++ 1.7/mysql-test/r/mix2_myisam.result 2007-04-04 22:52:02 +04:00
@@ -1114,11 +1114,11 @@
29267
explain select * from t1 where c between 1 and 2500;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 range c c 5 NULL # Using where
+1 SIMPLE t1 range c c 5 NULL # Using index condition; Using MRR
update t1 set c=a;
explain select * from t1 where c between 1 and 2500;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 range c c 5 NULL # Using where
+1 SIMPLE t1 range c c 5 NULL # Using index condition; Using MRR
drop table t1,t2;
create table t1 (id int primary key auto_increment, fk int, index index_fk (fk)) engine=MyISAM;
insert into t1 (id) values (null),(null),(null),(null),(null);
@@ -1559,7 +1559,7 @@
*a *a*a *
explain select * from t1 where v='a';
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 ref v,v_2 # 13 const # Using where
+1 SIMPLE t1 ref v,v_2 # 13 const # Using index condition
select v,count(*) from t1 group by v limit 10;
v count(*)
a 1
@@ -1735,7 +1735,7 @@
1 SIMPLE t1 ref v v 303 const # Using where; Using index
explain select * from t1 where v='a';
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 ref v v 303 const # Using where
+1 SIMPLE t1 ref v v 303 const # Using index condition
select v,count(*) from t1 group by v limit 10;
v count(*)
a 1
--- 1.137/mysql-test/r/create.result 2007-04-04 22:52:02 +04:00
+++ 1.138/mysql-test/r/create.result 2007-04-04 22:52:02 +04:00
@@ -104,7 +104,7 @@
create table t2 (key (b)) select * from t1;
explain select * from t2 where b="world";
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t2 ref B B 21 const 1 Using where
+1 SIMPLE t2 ref B B 21 const 1 Using index condition
select * from t2 where b="world";
a B
3 world
--- 1.22/mysql-test/r/explain.result 2007-04-04 22:52:02 +04:00
+++ 1.23/mysql-test/r/explain.result 2007-04-04 22:52:02 +04:00
@@ -13,7 +13,7 @@
3 foo
explain select * from t1 where str is null;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 ref str str 11 const 1 Using where
+1 SIMPLE t1 ref str str 11 const 1 Using index condition
explain select * from t1 where str="foo";
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 const str str 11 const 1
@@ -50,7 +50,7 @@
id select_type table type possible_keys key key_len ref rows Extra
set names latin1;
select 3 into @v1;
--- 1.40/mysql-test/r/func_in.result 2007-04-04 22:52:02 +04:00
+++ 1.41/mysql-test/r/func_in.result 2007-04-04 22:52:02 +04:00
@@ -241,7 +241,7 @@
explain
select * from t2 where a NOT IN (0, 2,4,6,8,10,12,14,16,18);
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t2 range a a 5 NULL 12 Using where
+1 SIMPLE t2 range a a 5 NULL 12 Using index condition; Using MRR
select * from t2 where a NOT IN (0, 2,4,6,8,10,12,14,16,18);
a filler
1 yes
@@ -256,10 +256,10 @@
19 yes
explain select * from t2 force index(a) where a NOT IN (2,2,2,2,2,2);
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t2 range a a 5 NULL 912 Using where
+1 SIMPLE t2 range a a 5 NULL 912 Using index condition; Using MRR
explain select * from t2 force index(a) where a <> 2;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t2 range a a 5 NULL 912 Using where
+1 SIMPLE t2 range a a 5 NULL 912 Using index condition; Using MRR
drop table t2;
create table t2 (a datetime, filler char(200), key(a));
insert into t2 select '2006-04-25 10:00:00' + interval C.a minute,
@@ -271,7 +271,7 @@
'2006-04-25 10:00:00','2006-04-25 10:02:00','2006-04-25 10:04:00',
'2006-04-25 10:06:00', '2006-04-25 10:08:00');
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t2 range a a 9 NULL 18 Using where
+1 SIMPLE t2 range a a 9 NULL 18 Using index condition; Using MRR
select * from t2 where a NOT IN (
'2006-04-25 10:00:00','2006-04-25 10:02:00','2006-04-25 10:04:00',
'2006-04-25 10:06:00', '2006-04-25 10:08:00');
@@ -295,7 +295,7 @@
('barbas','1'), ('bazbazbay', '1'),('zz','1');
explain select * from t2 where a not in('foo','barbar', 'bazbazbaz');
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t2 range a a 13 NULL 7 Using where
+1 SIMPLE t2 range a a 13 NULL 7 Using index condition; Using MRR
drop table t2;
create table t2 (a decimal(10,5), filler char(200), key(a));
insert into t2 select 345.67890, 'no' from t1 A, t1 B;
@@ -306,7 +306,7 @@
explain
select * from t2 where a not in (345.67890, 43245.34, 64224.56344);
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t2 range a a 7 NULL 7 Using where
+1 SIMPLE t2 range a a 7 NULL 7 Using index condition; Using MRR
select * from t2 where a not in (345.67890, 43245.34, 64224.56344);
a filler
0.00000 1
--- 1.52/mysql-test/r/heap.result 2007-04-04 22:52:02 +04:00
+++ 1.53/mysql-test/r/heap.result 2007-04-04 22:52:02 +04:00
@@ -204,7 +204,7 @@
INSERT INTO t1 VALUES (10), (10), (10);
EXPLAIN SELECT * FROM t1 WHERE a=10;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 ref a a 5 const 3 Using where
+1 SIMPLE t1 ref a a 5 const 3
SELECT * FROM t1 WHERE a=10;
a
10
--- 1.188/mysql-test/r/innodb.result 2007-04-04 22:52:02 +04:00
+++ 1.189/mysql-test/r/innodb.result 2007-04-04 22:52:02 +04:00
@@ -1300,7 +1300,7 @@
623
explain select * from t1 where c between 1 and 2500;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 range c c 5 NULL # Using where
+1 SIMPLE t1 range c c 5 NULL # Using index condition; Using MRR
update t1 set c=a;
explain select * from t1 where c between 1 and 2500;
id select_type table type possible_keys key key_len ref rows Extra
@@ -2011,7 +2011,7 @@
*a *a*a *
explain select * from t1 where v='a';
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 ref v,v_2 # 13 const # Using where
+1 SIMPLE t1 ref v,v_2 # 13 const # Using index condition
select v,count(*) from t1 group by v limit 10;
v count(*)
a 1
@@ -2187,7 +2187,7 @@
1 SIMPLE t1 ref v v 303 const # Using where; Using index
explain select * from t1 where v='a';
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 ref v v 303 const # Using where
+1 SIMPLE t1 ref v v 303 const # Using index condition
select v,count(*) from t1 group by v limit 10;
v count(*)
a 1
--- 1.44/mysql-test/r/join.result 2007-04-04 22:52:02 +04:00
+++ 1.45/mysql-test/r/join.result 2007-04-04 22:52:02 +04:00
@@ -774,7 +774,7 @@
explain select * from t2,t3 where t2.a < 200 and t2.b=t3.b;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t2 ALL a,b NULL NULL NULL 1000 Using where
-1 SIMPLE t3 ref b b 5 test.t2.b 1 Using where
+1 SIMPLE t3 ref b b 5 test.t2.b 1
drop table t1, t2, t3;
create table t1 (a int);
insert into t1 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
--- 1.63/mysql-test/r/merge.result 2007-04-04 22:52:02 +04:00
+++ 1.64/mysql-test/r/merge.result 2007-04-04 22:52:02 +04:00
@@ -625,7 +625,7 @@
EXPLAIN SELECT * FROM t1 WHERE fileset_id = 2
AND file_code BETWEEN '0000000115' AND '0000000120' LIMIT 1;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 range PRIMARY,files PRIMARY 35 NULL 5 Using where
+1 SIMPLE t1 range PRIMARY,files PRIMARY 35 NULL 5 Using index condition; Using MRR
EXPLAIN SELECT * FROM t2 WHERE fileset_id = 2
AND file_code = '0000000115' LIMIT 1;
id select_type table type possible_keys key key_len ref rows Extra
--- 1.66/mysql-test/r/order_by.result 2007-04-04 22:52:02 +04:00
+++ 1.67/mysql-test/r/order_by.result 2007-04-04 22:52:02 +04:00
@@ -514,7 +514,7 @@
EXPLAIN SELECT t1.gid, t3.uid from t1, t3 where t1.skr = t3.uid order by t1.gid,t3.skr;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 6 Using temporary; Using filesort
-1 SIMPLE t3 eq_ref PRIMARY PRIMARY 2 test.t1.skr 1 Using where
+1 SIMPLE t3 eq_ref PRIMARY PRIMARY 2 test.t1.skr 1 Using index condition
drop table t1,t2,t3;
CREATE TABLE t1 (
`titre` char(80) NOT NULL default '',
@@ -601,7 +601,7 @@
INSERT INTO t1 VALUES ('0',3,'0'),('0',2,'1'),('0',1,'2'),('1',2,'1'),('1',1,'3'), ('1',0,'2'),('2',3,'0'),('2',2,'1'),('2',1,'2'),('2',3,'0'),('2',2,'1'),('2',1,'2'),('3',2,'1'),('3',1,'2'),('3','3','3');
EXPLAIN SELECT * FROM t1 WHERE FieldKey = '1' ORDER BY LongVal;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 ref FieldKey,LongField,StringField LongField 38 const 3 Using where
+1 SIMPLE t1 ref FieldKey,LongField,StringField LongField 38 const 3 Using index condition; Using where
SELECT * FROM t1 WHERE FieldKey = '1' ORDER BY LongVal;
FieldKey LongVal StringVal
1 0 2
@@ -609,7 +609,7 @@
1 2 1
EXPLAIN SELECT * FROM t1 WHERE FieldKey > '2' ORDER BY LongVal;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 range FieldKey,LongField,StringField FieldKey 38 NULL 4 Using where; Using filesort
+1 SIMPLE t1 range FieldKey,LongField,StringField FieldKey 38 NULL 4 Using index condition; Using MRR; Using filesort
SELECT * FROM t1 WHERE FieldKey > '2' ORDER BY LongVal;
FieldKey LongVal StringVal
3 1 2
@@ -638,7 +638,7 @@
insert into t1 values (2, 1), (1, 1), (4, NULL), (3, NULL), (6, 2), (5, 2);
explain select * from t1 where b=1 or b is null order by a;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 ref_or_null b b 5 const 3 Using where; Using filesort
+1 SIMPLE t1 ref_or_null b b 5 const 3 Using filesort
select * from t1 where b=1 or b is null order by a;
a b
1 1
@@ -647,7 +647,7 @@
4 NULL
explain select * from t1 where b=2 or b is null order by a;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 ref_or_null b b 5 const 4 Using where; Using filesort
+1 SIMPLE t1 ref_or_null b b 5 const 4 Using filesort
select * from t1 where b=2 or b is null order by a;
a b
3 NULL
--- 1.60/mysql-test/r/range.result 2007-04-04 22:52:02 +04:00
+++ 1.61/mysql-test/r/range.result 2007-04-04 22:52:02 +04:00
@@ -717,6 +717,147 @@
d8c4177d2380fc201.39666693
d8c4177d24ccef970.14957924
DROP TABLE t1;
+create table t1 (
+c1 char(10), c2 char(10), c3 char(10), c4 char(10),
+c5 char(10), c6 char(10), c7 char(10), c8 char(10),
+c9 char(10), c10 char(10), c11 char(10), c12 char(10),
+c13 char(10), c14 char(10), c15 char(10), c16 char(10),
+index(c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12,c13,c14,c15,c16)
+);
+insert into t1 (c1) values ('1'),('1'),('1'),('1');
+select * from t1 where
+c1 in ("abcdefgh", "123456789", "qwertyuio", "asddfgh",
+"abcdefg1", "123456781", "qwertyui1", "asddfg1",
+"abcdefg2", "123456782", "qwertyui2", "asddfg2",
+"abcdefg3", "123456783", "qwertyui3", "asddfg3",
+"abcdefg4", "123456784", "qwertyui4", "asddfg4",
+"abcdefg5", "123456785", "qwertyui5", "asddfg5",
+"abcdefg6", "123456786", "qwertyui6", "asddfg6",
+"abcdefg7", "123456787", "qwertyui7", "asddfg7",
+"abcdefg8", "123456788", "qwertyui8", "asddfg8",
+"abcdefg9", "123456789", "qwertyui9", "asddfg9",
+"abcdefgA", "12345678A", "qwertyuiA", "asddfgA",
+"abcdefgB", "12345678B", "qwertyuiB", "asddfgB",
+"abcdefgC", "12345678C", "qwertyuiC", "asddfgC")
+and c2 in ("abcdefgh", "123456789", "qwertyuio", "asddfgh",
+"abcdefg1", "123456781", "qwertyui1", "asddfg1",
+"abcdefg2", "123456782", "qwertyui2", "asddfg2",
+"abcdefg3", "123456783", "qwertyui3", "asddfg3",
+"abcdefg4", "123456784", "qwertyui4", "asddfg4",
+"abcdefg5", "123456785", "qwertyui5", "asddfg5",
+"abcdefg6", "123456786", "qwertyui6", "asddfg6",
+"abcdefg7", "123456787", "qwertyui7", "asddfg7",
+"abcdefg8", "123456788", "qwertyui8", "asddfg8",
+"abcdefg9", "123456789", "qwertyui9", "asddfg9",
+"abcdefgA", "12345678A", "qwertyuiA", "asddfgA",
+"abcdefgB", "12345678B", "qwertyuiB", "asddfgB",
+"abcdefgC", "12345678C", "qwertyuiC", "asddfgC")
+and c3 in ("abcdefgh", "123456789", "qwertyuio", "asddfgh",
+"abcdefg1", "123456781", "qwertyui1", "asddfg1",
+"abcdefg2", "123456782", "qwertyui2", "asddfg2",
+"abcdefg3", "123456783", "qwertyui3", "asddfg3",
+"abcdefg4", "123456784", "qwertyui4", "asddfg4",
+"abcdefg5", "123456785", "qwertyui5", "asddfg5",
+"abcdefg6", "123456786", "qwertyui6", "asddfg6",
+"abcdefg7", "123456787", "qwertyui7", "asddfg7",
+"abcdefg8", "123456788", "qwertyui8", "asddfg8",
+"abcdefg9", "123456789", "qwertyui9", "asddfg9",
+"abcdefgA", "12345678A", "qwertyuiA", "asddfgA",
+"abcdefgB", "12345678B", "qwertyuiB", "asddfgB",
+"abcdefgC", "12345678C", "qwertyuiC", "asddfgC")
+and c4 in ("abcdefgh", "123456789", "qwertyuio", "asddfgh",
+"abcdefg1", "123456781", "qwertyui1", "asddfg1",
+"abcdefg2", "123456782", "qwertyui2", "asddfg2",
+"abcdefg3", "123456783", "qwertyui3", "asddfg3",
+"abcdefg4", "123456784", "qwertyui4", "asddfg4",
+"abcdefg5", "123456785", "qwertyui5", "asddfg5",
+"abcdefg6", "123456786", "qwertyui6", "asddfg6",
+"abcdefg7", "123456787", "qwertyui7", "asddfg7",
+"abcdefg8", "123456788", "qwertyui8", "asddfg8",
+"abcdefg9", "123456789", "qwertyui9", "asddfg9",
+"abcdefgA", "12345678A", "qwertyuiA", "asddfgA",
+"abcdefgB", "12345678B", "qwertyuiB", "asddfgB",
+"abcdefgC", "12345678C", "qwertyuiC", "asddfgC")
+and c5 in ("abcdefgh", "123456789", "qwertyuio", "asddfgh",
+"abcdefg1", "123456781", "qwertyui1", "asddfg1",
+"abcdefg2", "123456782", "qwertyui2", "asddfg2",
+"abcdefg3", "123456783", "qwertyui3", "asddfg3",
+"abcdefg4", "123456784", "qwertyui4", "asddfg4",
+"abcdefg5", "123456785", "qwertyui5", "asddfg5",
+"abcdefg6", "123456786", "qwertyui6", "asddfg6",
+"abcdefg7", "123456787", "qwertyui7", "asddfg7",
+"abcdefg8", "123456788", "qwertyui8", "asddfg8",
+"abcdefg9", "123456789", "qwertyui9", "asddfg9",
+"abcdefgA", "12345678A", "qwertyuiA", "asddfgA",
+"abcdefgB", "12345678B", "qwertyuiB", "asddfgB",
+"abcdefgC", "12345678C", "qwertyuiC", "asddfgC")
+and c6 in ("abcdefgh", "123456789", "qwertyuio", "asddfgh",
+"abcdefg1", "123456781", "qwertyui1", "asddfg1",
+"abcdefg2", "123456782", "qwertyui2", "asddfg2",
+"abcdefg3", "123456783", "qwertyui3", "asddfg3",
+"abcdefg4", "123456784", "qwertyui4", "asddfg4",
+"abcdefg5", "123456785", "qwertyui5", "asddfg5",
+"abcdefg6", "123456786", "qwertyui6", "asddfg6",
+"abcdefg7", "123456787", "qwertyui7", "asddfg7",
+"abcdefg8", "123456788", "qwertyui8", "asddfg8",
+"abcdefg9", "123456789", "qwertyui9", "asddfg9",
+"abcdefgA", "12345678A", "qwertyuiA", "asddfgA",
+"abcdefgB", "12345678B", "qwertyuiB", "asddfgB",
+"abcdefgC", "12345678C", "qwertyuiC", "asddfgC")
+and c7 in ("abcdefgh", "123456789", "qwertyuio", "asddfgh",
+"abcdefg1", "123456781", "qwertyui1", "asddfg1",
+"abcdefg2", "123456782", "qwertyui2", "asddfg2",
+"abcdefg3", "123456783", "qwertyui3", "asddfg3",
+"abcdefg4", "123456784", "qwertyui4", "asddfg4",
+"abcdefg5", "123456785", "qwertyui5", "asddfg5",
+"abcdefg6", "123456786", "qwertyui6", "asddfg6",
+"abcdefg7", "123456787", "qwertyui7", "asddfg7",
+"abcdefg8", "123456788", "qwertyui8", "asddfg8",
+"abcdefg9", "123456789", "qwertyui9", "asddfg9",
+"abcdefgA", "12345678A", "qwertyuiA", "asddfgA",
+"abcdefgB", "12345678B", "qwertyuiB", "asddfgB",
+"abcdefgC", "12345678C", "qwertyuiC", "asddfgC")
+and c8 in ("abcdefgh", "123456789", "qwertyuio", "asddfgh",
+"abcdefg1", "123456781", "qwertyui1", "asddfg1",
+"abcdefg2", "123456782", "qwertyui2", "asddfg2",
+"abcdefg3", "123456783", "qwertyui3", "asddfg3",
+"abcdefg4", "123456784", "qwertyui4", "asddfg4",
+"abcdefg5", "123456785", "qwertyui5", "asddfg5",
+"abcdefg6", "123456786", "qwertyui6", "asddfg6",
+"abcdefg7", "123456787", "qwertyui7", "asddfg7",
+"abcdefg8", "123456788", "qwertyui8", "asddfg8",
+"abcdefg9", "123456789", "qwertyui9", "asddfg9",
+"abcdefgA", "12345678A", "qwertyuiA", "asddfgA",
+"abcdefgB", "12345678B", "qwertyuiB", "asddfgB",
+"abcdefgC", "12345678C", "qwertyuiC", "asddfgC")
+and c9 in ("abcdefgh", "123456789", "qwertyuio", "asddfgh",
+"abcdefg1", "123456781", "qwertyui1", "asddfg1",
+"abcdefg2", "123456782", "qwertyui2", "asddfg2",
+"abcdefg3", "123456783", "qwertyui3", "asddfg3",
+"abcdefg4", "123456784", "qwertyui4", "asddfg4",
+"abcdefg5", "123456785", "qwertyui5", "asddfg5",
+"abcdefg6", "123456786", "qwertyui6", "asddfg6",
+"abcdefg7", "123456787", "qwertyui7", "asddfg7",
+"abcdefg8", "123456788", "qwertyui8", "asddfg8",
+"abcdefg9", "123456789", "qwertyui9", "asddfg9",
+"abcdefgA", "12345678A", "qwertyuiA", "asddfgA",
+"abcdefgB", "12345678B", "qwertyuiB", "asddfgB",
+"abcdefgC", "12345678C", "qwertyuiC", "asddfgC")
+and c10 in ("abcdefgh", "123456789", "qwertyuio", "asddfgh",
+"abcdefg1", "123456781", "qwertyui1", "asddfg1",
+"abcdefg2", "123456782", "qwertyui2", "asddfg2",
+"abcdefg3", "123456783", "qwertyui3", "asddfg3",
+"abcdefg4", "123456784", "qwertyui4", "asddfg4",
+"abcdefg5", "123456785", "qwertyui5", "asddfg5",
+"abcdefg6", "123456786", "qwertyui6", "asddfg6",
+"abcdefg7", "123456787", "qwertyui7", "asddfg7",
+"abcdefg8", "123456788", "qwertyui8", "asddfg8",
+"abcdefg9", "123456789", "qwertyui9", "asddfg9",
+"abcdefgA", "12345678A", "qwertyuiA", "asddfgA",
+"abcdefgB", "12345678B", "qwertyuiB", "asddfgB",
+"abcdefgC", "12345678C", "qwertyuiC", "asddfgC");
+c1 c2 c3 c4 c5 c6 c7 c8 c9 c10 c11 c12 c13 c14 c15 c16
+drop table t1;
End of 4.1 tests
CREATE TABLE t1 (
id int(11) NOT NULL auto_increment,
--- 1.156/mysql-test/r/select.result 2007-04-04 22:52:02 +04:00
+++ 1.157/mysql-test/r/select.result 2007-04-04 22:52:02 +04:00
@@ -185,37 +185,37 @@
1 SIMPLE t2 range fld1 fld1 4 NULL 4 Using where; Using index
select fld1,fld3 from t2 where companynr = 37 and fld3 like 'f%';
fld1 fld3
-218401 faithful
+012001 flanking
+013602 foldout
+013606 fingerings
018007 fanatic
-228311 fated
018017 featherweight
-218022 feed
-088303 feminine
-058004 Fenton
-038017 fetched
018054 fetters
-208101 fiftieth
-238007 filial
-013606 fingerings
-218008 finishers
-038205 firearm
-188505 fitting
-202301 Fitzpatrick
-238008 fixedly
-012001 flanking
018103 flint
018104 flopping
+036002 funereal
+038017 fetched
+038205 firearm
+058004 Fenton
+088303 feminine
+186002 freakish
188007 flurried
-013602 foldout
+188505 fitting
+198006 furthermore
+202301 Fitzpatrick
+208101 fiftieth
+208113 freest
+218008 finishers
+218022 feed
+218401 faithful
226205 foothill
-232102 forgivably
+226209 furnishings
228306 forthcoming
-186002 freakish
-208113 freest
+228311 fated
231315 freezes
-036002 funereal
-226209 furnishings
-198006 furthermore
+232102 forgivably
+238007 filial
+238008 fixedly
select fld3 from t2 where fld3 like "L%" and fld3 = "ok";
fld3
select fld3 from t2 where (fld3 like "C%" and fld3 = "Chantilly");
@@ -2362,7 +2362,7 @@
insert into t2 values (1,3), (2,3), (3,4), (4,4);
explain select * from t1 left join t2 on a=c where d in (4);
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t2 ref c,d d 5 const 2 Using where
+1 SIMPLE t2 ref c,d d 5 const 2 Using index condition
1 SIMPLE t1 ALL a NULL NULL NULL 4 Using where
select * from t1 left join t2 on a=c where d in (4);
a b c d
@@ -2370,7 +2370,7 @@
4 2 4 4
explain select * from t1 left join t2 on a=c where d = 4;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t2 ref c,d d 5 const 2 Using where
+1 SIMPLE t2 ref c,d d 5 const 2 Using index condition
1 SIMPLE t1 ALL a NULL NULL NULL 4 Using where
select * from t1 left join t2 on a=c where d = 4;
a b c d
@@ -2716,7 +2716,7 @@
where (t1.c=t2.a or (t1.c=t3.a and t2.a=t3.b)) and t1.b=556476786 and
t2.b like '%%' order by t2.b limit 0,1;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 ref b,c b 5 const 1 Using where; Using temporary; Using filesort
+1 SIMPLE t1 ref b,c b 5 const 1 Using temporary; Using filesort
1 SIMPLE t3 index PRIMARY,a,b PRIMARY 8 NULL 2 Using index
1 SIMPLE t2 ALL PRIMARY NULL NULL NULL 2 Range checked for each record (index map: 0x1)
DROP TABLE t1,t2,t3;
@@ -3409,7 +3409,7 @@
FROM t2, t1 WHERE t2.sku=20 AND (t2.sku=t1.sku OR t2.sppr=t1.sku);
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t2 const PRIMARY PRIMARY 4 const 1
-1 SIMPLE t1 range PRIMARY PRIMARY 4 NULL 2 Using where
+1 SIMPLE t1 range PRIMARY PRIMARY 4 NULL 2 Using index condition; Using MRR
DROP TABLE t1,t2;
CREATE TABLE t1 (i TINYINT UNSIGNED NOT NULL);
INSERT t1 SET i = 0;
@@ -3445,7 +3445,7 @@
explain select * from t2 A, t2 B where A.a=5 and A.b=5 and A.C<5
and B.a=5 and B.b=A.e and (B.b =1 or B.b = 3 or B.b=5);
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE A range PRIMARY PRIMARY 12 NULL 4 Using where
+1 SIMPLE A range PRIMARY PRIMARY 12 NULL 4 Using index condition; Using where; Using MRR
1 SIMPLE B ref PRIMARY PRIMARY 8 const,test.A.e 10
drop table t1, t2;
CREATE TABLE t1 (a int PRIMARY KEY, b int, INDEX(b));
@@ -3459,13 +3459,13 @@
EXPLAIN
SELECT a, c, d, f FROM t1,t2 WHERE a=c AND b BETWEEN 4 AND 6;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 range PRIMARY,b b 5 NULL 3 Using where
-1 SIMPLE t2 ref c c 5 test.t1.a 2 Using where
+1 SIMPLE t1 range PRIMARY,b b 5 NULL 3 Using index condition; Using MRR
+1 SIMPLE t2 ref c c 5 test.t1.a 2
EXPLAIN
SELECT a, c, d, f FROM t1,t2 WHERE a=c AND b BETWEEN 4 AND 6 AND a > 0;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 range PRIMARY,b b 5 NULL 3 Using where
-1 SIMPLE t2 ref c c 5 test.t1.a 2 Using where
+1 SIMPLE t1 range PRIMARY,b b 5 NULL 3 Using index condition; Using where; Using MRR
+1 SIMPLE t2 ref c c 5 test.t1.a 2
DROP TABLE t1, t2;
create table t1 (
a int unsigned not null auto_increment primary key,
@@ -3525,7 +3525,7 @@
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 const PRIMARY PRIMARY 4 const 1
1 SIMPLE t2 const idx1 NULL NULL NULL 1
-1 SIMPLE t3 ref idx1 idx1 5 const 3 Using where
+1 SIMPLE t3 ref idx1 idx1 5 const 3
SELECT * FROM t1 LEFT JOIN t2 ON t2.b=t1.a INNER JOIN t3 ON t3.d=t1.id
WHERE t1.id=2;
id a b c d e
@@ -3554,19 +3554,19 @@
FROM t1 JOIN t2 ON t2.fk=t1.pk
WHERE t2.fk < 'c' AND t2.pk=t1.fk;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 range PRIMARY PRIMARY 12 NULL 3 Using where
+1 SIMPLE t1 range PRIMARY PRIMARY 12 NULL 3 Using index condition; Using MRR
1 SIMPLE t2 eq_ref PRIMARY PRIMARY 18 test.t1.fk 1 Using where
EXPLAIN SELECT t2.*
FROM t1 JOIN t2 ON t2.fk=t1.pk
WHERE t2.fk BETWEEN 'a' AND 'b' AND t2.pk=t1.fk;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 range PRIMARY PRIMARY 12 NULL 2 Using where
+1 SIMPLE t1 range PRIMARY PRIMARY 12 NULL 2 Using index condition; Using MRR
1 SIMPLE t2 eq_ref PRIMARY PRIMARY 18 test.t1.fk 1 Using where
EXPLAIN SELECT t2.*
FROM t1 JOIN t2 ON t2.fk=t1.pk
WHERE t2.fk IN ('a','b') AND t2.pk=t1.fk;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 range PRIMARY PRIMARY 12 NULL 2 Using where
+1 SIMPLE t1 range PRIMARY PRIMARY 12 NULL 2 Using index condition; Using MRR
1 SIMPLE t2 eq_ref PRIMARY PRIMARY 18 test.t1.fk 1 Using where
DROP TABLE t1,t2;
CREATE TABLE t1 (a int, b varchar(20) NOT NULL, PRIMARY KEY(a));
@@ -3601,7 +3601,7 @@
t3.a=t2.a AND t3.c IN ('bb','ee');
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 const PRIMARY PRIMARY 4 const 1
-1 SIMPLE t2 range si si 5 NULL 4 Using where
+1 SIMPLE t2 range si si 5 NULL 4 Using index condition; Using MRR
1 SIMPLE t3 eq_ref PRIMARY,ci PRIMARY 4 test.t2.a 1 Using where
EXPLAIN
SELECT t3.a FROM t1,t2,t3
@@ -3609,7 +3609,7 @@
t3.a=t2.a AND t3.c IN ('bb','ee') ;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 const PRIMARY PRIMARY 4 const 1
-1 SIMPLE t2 range si,ai si 5 NULL 4 Using where
+1 SIMPLE t2 range si,ai si 5 NULL 4 Using index condition; Using MRR
1 SIMPLE t3 eq_ref PRIMARY,ci PRIMARY 4 test.t2.a 1 Using where
EXPLAIN
SELECT t3.a FROM t1,t2 FORCE INDEX (si),t3
@@ -3617,7 +3617,7 @@
t3.c IN ('bb','ee');
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 const PRIMARY PRIMARY 4 const 1
-1 SIMPLE t2 range si si 5 NULL 2 Using where
+1 SIMPLE t2 range si si 5 NULL 2 Using index condition; Using MRR
1 SIMPLE t3 eq_ref PRIMARY,ci PRIMARY 4 test.t2.a 1 Using where
EXPLAIN
SELECT t3.a FROM t1,t2,t3
@@ -3625,7 +3625,7 @@
t3.c IN ('bb','ee');
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 const PRIMARY PRIMARY 4 const 1
-1 SIMPLE t2 range si,ai si 5 NULL 2 Using where
+1 SIMPLE t2 range si,ai si 5 NULL 2 Using index condition; Using MRR
1 SIMPLE t3 eq_ref PRIMARY,ci PRIMARY 4 test.t2.a 1 Using where
DROP TABLE t1,t2,t3;
CREATE TABLE t1 ( f1 int primary key, f2 int, f3 int, f4 int, f5 int, f6 int, checked_out int);
@@ -3745,7 +3745,7 @@
AND t1.ts BETWEEN "2006-01-01" AND "2006-12-31";
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t2 const PRIMARY PRIMARY 4 const 1
-1 SIMPLE t1 range ts ts 4 NULL 1 Using where
+1 SIMPLE t1 range ts ts 4 NULL 1 Using index condition; Using where; Using MRR
Warnings:
Warning 1292 Incorrect datetime value: '2999-12-31 00:00:00' for column 'ts' at row 1
SELECT * FROM t1 LEFT JOIN t2 ON (t1.a=t2.a) WHERE t1.a=30
--- 1.157/mysql-test/t/innodb.test 2007-04-04 22:52:02 +04:00
+++ 1.158/mysql-test/t/innodb.test 2007-04-04 22:52:02 +04:00
@@ -22,7 +22,7 @@
drop table if exists t1,t2,t3,t4;
drop database if exists mysqltest;
--enable_warnings
-
+#set engine_condition_pushdown=0;
create table t1 (id int unsigned not null auto_increment, code tinyint unsigned not null, name char(20) not null, primary key (id), key (code), unique (name)) engine=innodb;
insert into t1 (code, name) values (1, 'Tim'), (1, 'Monty'), (2, 'David'), (2, 'Erik'), (3, 'Sasha'), (3, 'Jeremy'), (4, 'Matt');
@@ -439,7 +439,6 @@
alter table t1 drop index sca_pic, add index sca_pic (cat_code, sca_pic);
select count(*) from t1 where sca_code='PD' and sca_pic is null;
select count(*) from t1 where cat_code='E';
-
alter table t1 drop index sca_pic, add index (sca_pic, cat_code);
select count(*) from t1 where sca_code='PD' and sca_pic is null;
select count(*) from t1 where sca_pic >= 'n';
--- 1.326/storage/innobase/handler/ha_innodb.cc 2007-04-04 22:52:02 +04:00
+++ 1.327/storage/innobase/handler/ha_innodb.cc 2007-04-04 22:52:02 +04:00
@@ -56,6 +56,11 @@
*/
static handlerton *legacy_innodb_hton;
+C_MODE_START
+static my_bool index_cond_func_innodb(void *arg);
+C_MODE_END
+
+
/*-----------------------------------------------------------------*/
/* These variables are used to implement (semi-)synchronous MySQL binlog
replication for InnoDB tables. */
@@ -912,10 +917,16 @@
HA_PRIMARY_KEY_REQUIRED_FOR_POSITION |
HA_PRIMARY_KEY_IN_READ_INDEX |
HA_CAN_GEOMETRY | HA_PARTIAL_COLUMN_READ |
- HA_TABLE_SCAN_ON_INDEX),
+ HA_TABLE_SCAN_ON_INDEX | HA_NEED_READ_RANGE_BUFFER |
+ HA_MRR_CANT_SORT),
start_of_scan(0),
- num_write_row(0)
-{}
+ num_write_row(0),
+ ds_mrr((DsMrr_impl::range_check_toggle_func_t)
+ &ha_innobase::toggle_range_check),
+ cond_keyno(MAX_KEY)
+{
+ ds_mrr.h= this;
+}
/*************************************************************************
Updates the user_thd field in a handle and also allocates a new InnoDB
@@ -2386,6 +2397,7 @@
((row_prebuilt_t*)innobase_prebuilt)->mysql_row_len =
table->s->reclength;
+ ((row_prebuilt_t*)innobase_prebuilt)->idx_cond_func= NULL;
/* Looks like MySQL-3.23 sometimes has primary key number != 0 */
@@ -3053,6 +3065,7 @@
only if templ_type is
ROW_MYSQL_REC_FIELDS */
TABLE* table, /* in: MySQL table */
+ ha_innobase* file, /* in: ha_innobase handler */
uint templ_type) /* in: ROW_MYSQL_WHOLE_ROW or
ROW_MYSQL_REC_FIELDS */
{
@@ -3067,7 +3080,9 @@
ulint i;
/* byte offset of the end of last requested column */
ulint mysql_prefix_len = 0;
-
+ ibool do_idx_cond_push= FALSE;
+ ibool need_second_pass= FALSE;
+
if (prebuilt->select_lock_type == LOCK_X) {
/* We always retrieve the whole clustered index record if we
use exclusive row level locks, for example, if the read is
@@ -3137,8 +3152,20 @@
prebuilt->templ_contains_blob = FALSE;
- /* Note that in InnoDB, i is the column number. MySQL calls columns
- 'fields'. */
+
+ if (file->active_index == file->cond_keyno &&
+ file->active_index != MAX_KEY &&
+ file->active_index != 0 /* file->primary_key psergey-todo:!*/)
+ do_idx_cond_push= need_second_pass= TRUE;
+
+ /*
+ Ok, now build an array of mysql_row_templ_struct structures.
+ If index condition pushdown is used, the array is split into two
+ parts: first go index fields, then go table fields.
+
+ Note that in InnoDB, i is the column number. MySQL calls columns
+ 'fields'.
+ */
for (i = 0; i < n_fields; i++) {
templ = prebuilt->mysql_template + n_requested_fields;
field = table->field[i];
@@ -3148,6 +3175,8 @@
and which we can skip. */
register const ibool index_contains_field =
dict_index_contains_col_or_prefix(index, i);
+ register const ibool index_covers_field =
+ field->part_of_key.is_set(file->active_index);
if (!index_contains_field && prebuilt->read_just_key) {
/* If this is a 'key read', we do not need
@@ -3180,8 +3209,12 @@
/* This field is not needed in the query, skip it */
goto skip_field;
- }
include_field:
+ if (do_idx_cond_push &&
+ (need_second_pass && !index_covers_field ||
+ !need_second_pass && index_covers_field))
+ goto skip_field;
+ }
n_requested_fields++;
templ->col_no = i;
@@ -3235,18 +3268,35 @@
prebuilt->templ_contains_blob = TRUE;
}
skip_field:
- ;
+ if (need_second_pass && (i+1 == n_fields))
+ {
+ prebuilt->n_index_fields= n_requested_fields;
+ need_second_pass= FALSE;
+ i= (~(ulint)0); /* to start from 0 */
+ }
}
prebuilt->n_template = n_requested_fields;
prebuilt->mysql_prefix_len = mysql_prefix_len;
+ if (do_idx_cond_push)
+ {
+ prebuilt->idx_cond_func= index_cond_func_innodb;
+ prebuilt->idx_cond_func_arg= file;
+ }
+ else
+ {
+ prebuilt->idx_cond_func= NULL;
+ prebuilt->n_index_fields= n_requested_fields;
+ }
+ file->in_range_read= FALSE;
+
if (index != clust_index && prebuilt->need_to_access_clustered) {
/* Change rec_field_no's to correspond to the clustered index
record */
- for (i = 0; i < n_requested_fields; i++) {
+ for (i = do_idx_cond_push? prebuilt->n_index_fields : 0;
+ i < n_requested_fields; i++) {
templ = prebuilt->mysql_template + i;
-
templ->rec_field_no = dict_col_get_clust_pos_noninline(
&index->table->cols[templ->col_no],
clust_index);
@@ -3433,7 +3483,8 @@
/* Build the template used in converting quickly between
the two database formats */
- build_template(prebuilt, NULL, table, ROW_MYSQL_WHOLE_ROW);
+ build_template(prebuilt, NULL, table,
+ this, ROW_MYSQL_WHOLE_ROW);
}
innodb_srv_conc_enter_innodb(prebuilt->trx);
@@ -3864,6 +3915,7 @@
int error = 0;
DBUG_ENTER("index_end");
active_index=MAX_KEY;
+ in_range_check_pushed_down= FALSE;
DBUG_RETURN(error);
}
@@ -4006,7 +4058,7 @@
necessarily prebuilt->index, but can also be the clustered index */
if (prebuilt->sql_stat_start) {
- build_template(prebuilt, user_thd, table,
+ build_template(prebuilt, user_thd, table, this,
ROW_MYSQL_REC_FIELDS);
}
@@ -4140,7 +4192,7 @@
the flag ROW_MYSQL_WHOLE_ROW below, but that caused unnecessary
copying. Starting from MySQL-4.1 we use a more efficient flag here. */
- build_template(prebuilt, user_thd, table, ROW_MYSQL_REC_FIELDS);
+ build_template(prebuilt, user_thd, table, this, ROW_MYSQL_REC_FIELDS);
DBUG_RETURN(0);
}
@@ -4421,7 +4473,6 @@
{
row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
int error;
- uint keynr = active_index;
DBUG_ENTER("rnd_pos");
DBUG_DUMP("key", (char*) pos, ref_length);
@@ -4431,22 +4482,6 @@
ut_a(prebuilt->trx ==
(trx_t*) current_thd->ha_data[ht->slot]);
- if (prebuilt->clust_index_was_generated) {
- /* No primary key was defined for the table and we
- generated the clustered index from the row id: the
- row reference is the row id, not any key value
- that MySQL knows of */
-
- error = change_active_index(MAX_KEY);
- } else {
- error = change_active_index(primary_key);
- }
-
- if (error) {
- DBUG_PRINT("error", ("Got error: %d", error));
- DBUG_RETURN(error);
- }
-
/* Note that we assume the length of the row reference is fixed
for the table, and it is == ref_length */
@@ -4456,8 +4491,6 @@
DBUG_PRINT("error", ("Got error: %d", error));
}
- change_active_index(keynr);
-
DBUG_RETURN(error);
}
@@ -5752,7 +5785,7 @@
/* Build the template; we will use a dummy template
in index scans done in checking */
- build_template(prebuilt, NULL, table, ROW_MYSQL_WHOLE_ROW);
+ build_template(prebuilt, NULL, table, this, ROW_MYSQL_WHOLE_ROW);
}
ret = row_check_table_for_mysql(prebuilt);
@@ -6123,6 +6156,12 @@
case HA_EXTRA_RESET_STATE:
prebuilt->keep_other_fields_on_keyread = 0;
prebuilt->read_just_key = 0;
+
+ /* Reset index condition pushdown state */
+ cond_keyno= MAX_KEY;
+ in_range_read= FALSE;
+ prebuilt->idx_cond_func= NULL;
+ cond_keyno= MAX_KEY;
break;
case HA_EXTRA_NO_KEYREAD:
prebuilt->read_just_key = 0;
@@ -6147,7 +6186,13 @@
row_mysql_prebuilt_free_blob_heap(prebuilt);
}
prebuilt->keep_other_fields_on_keyread = 0;
+ /* Reset index condition pushdown state */
+ cond_keyno= MAX_KEY;
+ in_range_read= FALSE;
+ prebuilt->idx_cond_func= NULL;
+
prebuilt->read_just_key = 0;
+ cond_keyno= MAX_KEY;
return 0;
}
@@ -7622,14 +7667,6 @@
uint table_changes)
{
if (table_changes != IS_EQUAL_YES) {
-
- return COMPATIBLE_DATA_NO;
- }
-
- /* Check that auto_increment value was not changed */
- if ((info->used_fields & HA_CREATE_USED_AUTO) &&
- info->auto_increment_value != 0) {
-
return COMPATIBLE_DATA_NO;
}
@@ -7676,4 +7713,107 @@
}
mysql_declare_plugin_end;
+/****************************************************************************
+ * DS-MRR implementation
+ ***************************************************************************/
+
+/**
+ * Multi Range Read interface, DS-MRR calls
+ */
+
+int ha_innobase::multi_range_read_init(RANGE_SEQ_IF *seq, void *seq_init_param,
+ uint n_ranges, uint mode, HANDLER_BUFFER *buf)
+{
+ return ds_mrr.dsmrr_init(this, &table->key_info[active_index],
+ seq, seq_init_param, n_ranges, mode, buf);
+}
+
+int ha_innobase::multi_range_read_next(char **range_info)
+{
+ return ds_mrr.dsmrr_next(this, range_info);
+}
+
+ha_rows ha_innobase::multi_range_read_info_const(uint keyno, RANGE_SEQ_IF *seq,
+ void *seq_init_param,
+ uint n_ranges, uint *bufsz,
+ uint *flags,
+ COST_VECT *cost)
+{
+ return ds_mrr.dsmrr_info_const(keyno, seq, seq_init_param, n_ranges, bufsz,
+ flags, cost);
+}
+
+int ha_innobase::multi_range_read_info(uint keyno, uint n_ranges, uint keys,
+ uint *bufsz, uint *flags, COST_VECT *cost)
+{
+ return ds_mrr.dsmrr_info(keyno, n_ranges, keys, bufsz, flags, cost);
+}
+
#endif
+
+
+/**
+ * Index Condition Pushdown interface implementation
+ */
+
+C_MODE_START
+
+/* Index condition check function to be called from within Innobase */
+
+static my_bool index_cond_func_innodb(void *arg)
+{
+ ha_innobase *h= (ha_innobase*)arg;
+ if (h->in_range_read)
+ {
+ if (h->compare_key(h->end_range) > 0)
+ return 2; /* caller should return HA_ERR_END_OF_FILE already */
+ }
+ return (my_bool)h->idx_cond->val_int();
+}
+
+C_MODE_END
+
+
+Item *ha_innobase::idx_cond_push(uint keyno_arg, Item* idx_cond_arg)
+{
+ if (keyno_arg != primary_key)
+ {
+ cond_keyno= keyno_arg;
+ idx_cond= idx_cond_arg;
+ return NULL; /* Table handler will check the entire condition */
+ in_range_check_pushed_down= TRUE;
+ }
+ return idx_cond_arg; /* Table handler will not make any checks */
+}
+
+
+void ha_innobase::add_explain_extra_info(uint keyno, String *extra)
+{
+ if (cond_keyno != MAX_KEY && idx_cond && keyno==cond_keyno)
+ extra->append(STRING_WITH_LEN("; Using index condition"));
+}
+
+
+int ha_innobase::read_range_first(const key_range *start_key,
+ const key_range *end_key,
+ bool eq_range_arg,
+ bool sorted /* ignored */)
+{
+ int res;
+ if (!eq_range_arg)
+ in_range_read= TRUE;
+ res= handler::read_range_first(start_key, end_key, eq_range_arg, sorted);
+ if (res)
+ in_range_read= FALSE;
+ return res;
+}
+
+
+int ha_innobase::read_range_next()
+{
+ int res= handler::read_range_next();
+ if (res)
+ in_range_read= FALSE;
+ return res;
+}
+
--- 1.134/storage/innobase/handler/ha_innodb.h 2007-04-04 22:52:02 +04:00
+++ 1.135/storage/innobase/handler/ha_innodb.h 2007-04-04 22:52:02 +04:00
@@ -60,7 +60,7 @@
ulong upd_and_key_val_buff_len;
/* the length of each of the previous
two buffers */
- ulong int_table_flags;
+ ulonglong int_table_flags;
uint primary_key;
ulong start_of_scan; /* this is set to 1 when we are
starting a table scan but have not
@@ -97,7 +97,8 @@
HA_READ_PREV |
HA_READ_ORDER |
HA_READ_RANGE |
- HA_KEYREAD_ONLY);
+ HA_KEYREAD_ONLY |
+ ((idx == primary_key)? 0 : HA_DO_INDEX_COND_PUSHDOWN));
}
uint max_supported_keys() const { return MAX_KEY; }
/* An InnoDB page must store >= 2 keys;
@@ -200,6 +201,43 @@
int cmp_ref(const byte *ref1, const byte *ref2);
bool check_if_incompatible_data(HA_CREATE_INFO *info,
uint table_changes);
+public:
+ /**
+ * Multi Range Read interface
+ */
+ int multi_range_read_init(RANGE_SEQ_IF *seq, void *seq_init_param,
+ uint n_ranges, uint mode, HANDLER_BUFFER *buf);
+ int multi_range_read_next(char **range_info);
+ ha_rows multi_range_read_info_const(uint keyno, RANGE_SEQ_IF *seq,
+ void *seq_init_param,
+ uint n_ranges, uint *bufsz,
+ uint *flags, COST_VECT *cost);
+ int multi_range_read_info(uint keyno, uint n_ranges, uint keys,
+ uint *bufsz, uint *flags, COST_VECT *cost);
+ DsMrr_impl ds_mrr;
+
+ void add_explain_extra_info(uint keyno, String *extra);
+
+ /* Index Condition Pushdown implementation */
+ Item *idx_cond; /* The pushed condition. Valid iff cond_keyno != MAX_KEY */
+ uint cond_keyno; /* The index which the above condition is for */
+
+ /*
+ TRUE <=> We're reading a range that is not equivalent to
+ "keypart1=const1 AND ... keypartK=constK"
+ if we're reading such range we should check if we've ran out of range
+ before checking the index condition
+ */
+ bool in_range_read;
+ void toggle_range_check(bool on)
+ {
+ in_range_read= on;
+ }
+
+ int read_range_first(const key_range *start_key, const key_range *end_key,
+ bool eq_range_arg, bool sorted);
+ int read_range_next();
+ Item *idx_cond_push(uint keyno, Item* idx_cond);
};
extern SHOW_VAR innodb_status_variables[];
--- 1.109/mysql-test/r/myisam.result 2007-04-04 22:52:02 +04:00
+++ 1.110/mysql-test/r/myisam.result 2007-04-04 22:52:02 +04:00
@@ -943,6 +943,156 @@
Name Engine Version Row_format Rows Avg_row_length Data_length Max_data_length Index_length Data_free Auto_increment Create_time Update_time Check_time Collation Checksum Create_options Comment
t1 MyISAM 10 Dynamic X X X 72057594037927935 X X X X X X latin1_swedish_ci X max_rows=4100100100 avg_row_length=70100
DROP TABLE t1;
+CREATE TABLE t1 (c1 TEXT NOT NULL, KEY c1 (c1(10))) ENGINE=MyISAM;
+INSERT INTO t1 VALUES
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)), (CHAR(9,65)),
+(''), (''), (''), (''),
+(' B'), (' B'), (' B'), (' B');
+SELECT DISTINCT COUNT(*) FROM t1 WHERE c1 = '';
+COUNT(*)
+4
+SELECT DISTINCT length(c1), c1 FROM t1 WHERE c1 = '';
+length(c1) c1
+0
+SELECT DISTINCT COUNT(*) FROM t1 IGNORE INDEX (c1) WHERE c1 = '';
+COUNT(*)
+4
+SELECT DISTINCT length(c1), c1 FROM t1 IGNORE INDEX (c1) WHERE c1 = '';
+length(c1) c1
+0
+SELECT DISTINCT length(c1), c1 FROM t1 ORDER BY c1;
+length(c1) c1
+0
+2 A
+2 B
+DROP TABLE t1;
End of 4.1 tests
set storage_engine=MyISAM;
drop table if exists t1,t2,t3;
--- 1.223/sql/set_var.cc 2007-04-04 22:52:02 +04:00
+++ 1.224/sql/set_var.cc 2007-04-04 22:52:02 +04:00
@@ -329,8 +329,6 @@
&SV::max_tmp_tables);
sys_var_long_ptr sys_max_write_lock_count("max_write_lock_count",
&max_write_lock_count);
-sys_var_thd_ulong sys_multi_range_count("multi_range_count",
- &SV::multi_range_count);
sys_var_long_ptr sys_myisam_data_pointer_size("myisam_data_pointer_size",
&myisam_data_pointer_size);
sys_var_thd_ulonglong sys_myisam_max_sort_file_size("myisam_max_sort_file_size", &SV::myisam_max_sort_file_size, fix_myisam_max_sort_file_size, 1);
@@ -343,7 +341,6 @@
&SV::myisam_stats_method,
&myisam_stats_method_typelib,
NULL);
-
sys_var_thd_ulong sys_net_buffer_length("net_buffer_length",
&SV::net_buffer_length);
sys_var_thd_ulong sys_net_read_timeout("net_read_timeout",
@@ -361,10 +358,23 @@
sys_var_thd_bool sys_old_alter_table("old_alter_table",
&SV::old_alter_table);
sys_var_thd_bool sys_old_passwords("old_passwords", &SV::old_passwords);
+
sys_var_thd_ulong sys_optimizer_prune_level("optimizer_prune_level",
&SV::optimizer_prune_level);
sys_var_thd_ulong sys_optimizer_search_depth("optimizer_search_depth",
&SV::optimizer_search_depth);
+
+const char *optimizer_use_mrr_names[] = {"auto", "force", "disable", NullS};
+TYPELIB optimizer_use_mrr_typelib= {
+ array_elements(optimizer_use_mrr_names) - 1, "",
+ optimizer_use_mrr_names, NULL
+};
+
+sys_var_thd_enum sys_optimizer_use_mrr("optimizer_use_mrr",
+ &SV::optimizer_use_mrr,
+ &optimizer_use_mrr_typelib,
+ NULL);
+
sys_var_thd_ulong sys_preload_buff_size("preload_buffer_size",
&SV::preload_buff_size);
sys_var_thd_ulong sys_read_buff_size("read_buffer_size",
@@ -906,7 +916,6 @@
{sys_max_tmp_tables.name, (char*) &sys_max_tmp_tables, SHOW_SYS},
{sys_max_user_connections.name,(char*) &sys_max_user_connections, SHOW_SYS},
{sys_max_write_lock_count.name, (char*) &sys_max_write_lock_count,SHOW_SYS},
- {sys_multi_range_count.name, (char*) &sys_multi_range_count, SHOW_SYS},
{sys_myisam_data_pointer_size.name, (char*) &sys_myisam_data_pointer_size, SHOW_SYS},
{sys_myisam_max_sort_file_size.name, (char*) &sys_myisam_max_sort_file_size,
SHOW_SYS},
@@ -952,6 +961,7 @@
SHOW_SYS},
{sys_optimizer_search_depth.name,(char*) &sys_optimizer_search_depth,
SHOW_SYS},
+ {sys_optimizer_use_mrr.name, (char*) &sys_optimizer_use_mrr, SHOW_SYS},
{"pid_file", (char*) pidfile_name, SHOW_CHAR},
{"plugin_dir", (char*) opt_plugin_dir, SHOW_CHAR},
{"port", (char*) &mysqld_port, SHOW_INT},
--- 1.35/sql/ha_partition.h 2007-04-04 22:52:02 +04:00
+++ 1.36/sql/ha_partition.h 2007-04-04 22:52:02 +04:00
@@ -753,7 +753,11 @@
*/
virtual ulong index_flags(uint inx, uint part, bool all_parts) const
{
- return m_file[0]->index_flags(inx, part, all_parts);
+ /*
+ TODO: sergefp: Support Index Condition Pushdown in this table handler.
+ */
+ return m_file[0]->index_flags(inx, part, all_parts) &
+ ~HA_DO_INDEX_COND_PUSHDOWN;
}
/*
--- 1.8/mysql-test/r/compress.result 2007-04-04 22:52:02 +04:00
+++ 1.9/mysql-test/r/compress.result 2007-04-04 22:52:02 +04:00
@@ -1,6 +1,9 @@
SHOW STATUS LIKE 'Compression';
Variable_name Value
Compression ON
+select * from information_schema.session_status where variable_name= 'COMPRESSION';
+VARIABLE_NAME VARIABLE_VALUE
+COMPRESSION 1.0000000
drop table if exists t1,t2,t3,t4;
CREATE TABLE t1 (
Period smallint(4) unsigned zerofill DEFAULT '0000' NOT NULL,
--- 1.37/BitKeeper/triggers/post-commit 2007-04-04 22:52:02 +04:00
+++ 1.38/BitKeeper/triggers/post-commit 2007-04-04 22:52:02 +04:00
@@ -5,7 +5,7 @@
COMMITS=commits@stripped
DOCS=docs-commit@stripped
LIMIT=10000
-VERSION="5.1"
+VERSION="5.2"
BKROOT=`bk root`
if [ -x /usr/sbin/sendmail ]; then
--- 1.7/mysql-test/r/subselect3.result 2007-04-04 22:52:02 +04:00
+++ 1.8/mysql-test/r/subselect3.result 2007-04-04 22:52:02 +04:00
@@ -645,3 +645,50 @@
2 2 0
3 3 1
drop table t1,t2;
+CREATE TABLE t1 (a int, b INT, c CHAR(10) NOT NULL, PRIMARY KEY (a, b));
+INSERT INTO t1 VALUES (1,1,'a'), (1,2,'b'), (1,3,'c'), (1,4,'d'), (1,5,'e'),
+(2,1,'f'), (2,2,'g'), (2,3,'h'), (3,4,'i'),(3,3,'j'), (3,2,'k'), (3,1,'l'),
+(1,9,'m');
+CREATE TABLE t2 (a int, b INT, c CHAR(10) NOT NULL, PRIMARY KEY (a, b));
+INSERT INTO t2 SELECT * FROM t1;
+SELECT a, MAX(b), (SELECT t.c FROM t1 AS t WHERE t1.a=t.a AND t.b=MAX(t1.b))
+as test FROM t1 GROUP BY a;
+a MAX(b) test
+1 9 m
+2 3 h
+3 4 i
+SELECT * FROM t1 GROUP by t1.a
+HAVING (MAX(t1.b) > (SELECT MAX(t2.b) FROM t2 WHERE t2.c < t1.c
+HAVING MAX(t2.b+t1.a) < 10));
+a b c
+SELECT a,b,c FROM t1 WHERE b in (9,3,4) ORDER BY b,c;
+a b c
+1 3 c
+2 3 h
+3 3 j
+1 4 d
+3 4 i
+1 9 m
+SELECT a, MAX(b),
+(SELECT COUNT(DISTINCT t.c) FROM t1 AS t WHERE t1.a=t.a AND t.b=MAX(t1.b)
+LIMIT 1)
+as cnt,
+(SELECT t.b FROM t1 AS t WHERE t1.a=t.a AND t.b=MAX(t1.b) LIMIT 1)
+as t_b,
+(SELECT t.c FROM t1 AS t WHERE t1.a=t.a AND t.b=MAX(t1.b) LIMIT 1)
+as t_b,
+(SELECT t.c FROM t1 AS t WHERE t1.a=t.a AND t.b=MAX(t1.b) ORDER BY t.c LIMIT 1)
+as t_b
+FROM t1 GROUP BY a;
+a MAX(b) cnt t_b t_b t_b
+1 9 1 9 m m
+2 3 1 3 h h
+3 4 1 4 i i
+SELECT a, MAX(b),
+(SELECT t.c FROM t1 AS t WHERE t1.a=t.a AND t.b=MAX(t1.b) LIMIT 1) as test
+FROM t1 GROUP BY a;
+a MAX(b) test
+1 9 m
+2 3 h
+3 4 i
+DROP TABLE t1, t2;
| Thread |
|---|
| • bk commit into 5.1 tree (sergefp:1.2558) | Sergey Petrunia | 4 Apr |