summaryrefslogtreecommitdiff
path: root/storage
diff options
context:
space:
mode:
Diffstat (limited to 'storage')
-rw-r--r--storage/federatedx/ha_federatedx.cc9
-rw-r--r--storage/heap/ha_heap.cc15
-rw-r--r--storage/heap/hp_create.c8
-rw-r--r--storage/heap/hp_hash.c58
-rw-r--r--storage/innobase/handler/ha_innodb.cc1
-rw-r--r--storage/innodb_plugin/handler/ha_innodb.cc2
-rw-r--r--storage/maria/ha_maria.cc115
-rw-r--r--storage/maria/ha_maria.h37
-rw-r--r--storage/maria/ma_extra.c9
-rw-r--r--storage/maria/ma_ft_boolean_search.c13
-rw-r--r--storage/maria/ma_ft_nlq_search.c4
-rw-r--r--storage/maria/ma_key.c64
-rw-r--r--storage/maria/ma_open.c4
-rw-r--r--storage/maria/ma_rkey.c24
-rw-r--r--storage/maria/ma_rnext.c9
-rw-r--r--storage/maria/ma_rnext_same.c9
-rw-r--r--storage/maria/ma_sort.c6
-rw-r--r--storage/maria/maria_def.h9
-rw-r--r--storage/maria/maria_pack.c36
-rw-r--r--storage/myisam/ft_boolean_search.c14
-rw-r--r--storage/myisam/ft_nlq_search.c4
-rw-r--r--storage/myisam/ha_myisam.cc131
-rw-r--r--storage/myisam/ha_myisam.h30
-rw-r--r--storage/myisam/mi_extra.c6
-rw-r--r--storage/myisam/mi_key.c57
-rw-r--r--storage/myisam/mi_open.c35
-rw-r--r--storage/myisam/mi_rkey.c92
-rw-r--r--storage/myisam/mi_rnext.c40
-rw-r--r--storage/myisam/mi_rnext_same.c9
-rw-r--r--storage/myisam/mi_rprev.c38
-rwxr-xr-xstorage/myisam/mi_test_all.sh1
-rw-r--r--storage/myisam/myisamdef.h6
-rw-r--r--storage/myisam/myisampack.c36
-rw-r--r--storage/myisam/sort.c6
-rw-r--r--storage/myisammrg/myrg_queue.c4
-rw-r--r--storage/myisammrg/myrg_rnext.c4
-rw-r--r--storage/myisammrg/myrg_rnext_same.c4
-rw-r--r--storage/myisammrg/myrg_rprev.c4
-rw-r--r--storage/oqgraph/ha_oqgraph.cc2
-rw-r--r--storage/xtradb/handler/ha_innodb.cc167
-rw-r--r--storage/xtradb/handler/ha_innodb.h18
-rw-r--r--storage/xtradb/include/row0mysql.h10
-rw-r--r--storage/xtradb/row/row0sel.c115
43 files changed, 1035 insertions, 230 deletions
diff --git a/storage/federatedx/ha_federatedx.cc b/storage/federatedx/ha_federatedx.cc
index 2749034cba2..434423b78b3 100644
--- a/storage/federatedx/ha_federatedx.cc
+++ b/storage/federatedx/ha_federatedx.cc
@@ -1453,7 +1453,8 @@ static void fill_server(MEM_ROOT *mem_root, FEDERATEDX_SERVER *server,
key.q_append('\0');
server->password= (const char *) (intptr) key.length();
key.append(password);
-
+ key.c_ptr_safe(); // Ensure we have end \0
+
server->key_length= key.length();
server->key= (uchar *) memdup_root(mem_root, key.ptr(), key.length()+1);
@@ -1583,7 +1584,7 @@ static FEDERATEDX_SHARE *get_share(const char *table_name, TABLE *table)
tmp_share.table_name_length, ident_quote_char);
if (!(share= (FEDERATEDX_SHARE *) memdup_root(&mem_root, (char*)&tmp_share, sizeof(*share))) ||
- !(share->select_query= (char*) strmake_root(&mem_root, query.ptr(), query.length() + 1)))
+ !(share->select_query= (char*) strmake_root(&mem_root, query.ptr(), query.length())))
goto error;
share->mem_root= mem_root;
@@ -3435,11 +3436,13 @@ bool ha_federatedx::get_error_message(int error, String* buf)
buf->qs_append(remote_error_number);
buf->append(STRING_WITH_LEN(": "));
buf->append(remote_error_buf);
+ /* Ensure string ends with \0 */
+ (void) buf->c_ptr_safe();
remote_error_number= 0;
remote_error_buf[0]= '\0';
}
- DBUG_PRINT("exit", ("message: %s", buf->ptr()));
+ DBUG_PRINT("exit", ("message: %s", buf->c_ptr_safe()));
DBUG_RETURN(FALSE);
}
diff --git a/storage/heap/ha_heap.cc b/storage/heap/ha_heap.cc
index 42437a22614..2d2c23a2ed6 100644
--- a/storage/heap/ha_heap.cc
+++ b/storage/heap/ha_heap.cc
@@ -654,7 +654,8 @@ int ha_heap::create(const char *name, TABLE *table_arg,
seg->type != HA_KEYTYPE_VARTEXT1 &&
seg->type != HA_KEYTYPE_VARTEXT2 &&
seg->type != HA_KEYTYPE_VARBINARY1 &&
- seg->type != HA_KEYTYPE_VARBINARY2)
+ seg->type != HA_KEYTYPE_VARBINARY2 &&
+ seg->type != HA_KEYTYPE_BIT)
seg->type= HA_KEYTYPE_BINARY;
}
seg->start= (uint) key_part->offset;
@@ -686,6 +687,18 @@ int ha_heap::create(const char *name, TABLE *table_arg,
auto_key= key+ 1;
auto_key_type= field->key_type();
}
+ if (seg->type == HA_KEYTYPE_BIT)
+ {
+ seg->bit_length= ((Field_bit *) field)->bit_len;
+ seg->bit_start= ((Field_bit *) field)->bit_ofs;
+ seg->bit_pos= (uint) (((Field_bit *) field)->bit_ptr -
+ (uchar*) table_arg->record[0]);
+ }
+ else
+ {
+ seg->bit_length= seg->bit_start= 0;
+ seg->bit_pos= 0;
+ }
}
}
mem_per_row+= MY_ALIGN(share->reclength + 1, sizeof(char*));
diff --git a/storage/heap/hp_create.c b/storage/heap/hp_create.c
index bc8183bf777..2036bd7166f 100644
--- a/storage/heap/hp_create.c
+++ b/storage/heap/hp_create.c
@@ -104,6 +104,14 @@ int heap_create(const char *name, uint keys, HP_KEYDEF *keydef,
*/
keyinfo->seg[j].type= HA_KEYTYPE_VARTEXT1;
break;
+ case HA_KEYTYPE_BIT:
+ /*
+ The odd bits which stored separately (if they are present
+ (bit_pos, bit_length)) are already present in seg[j].length as
+ additional byte.
+ See field.h, function key_length()
+ */
+ break;
default:
break;
}
diff --git a/storage/heap/hp_hash.c b/storage/heap/hp_hash.c
index aaaa0fe833f..fb9ea44a424 100644
--- a/storage/heap/hp_hash.c
+++ b/storage/heap/hp_hash.c
@@ -349,6 +349,15 @@ ulong hp_rec_hashnr(register HP_KEYDEF *keydef, register const uchar *rec)
}
else
{
+ if (seg->type == HA_KEYTYPE_BIT && seg->bit_length)
+ {
+ uchar bits= get_rec_bits(rec + seg->bit_pos,
+ seg->bit_start, seg->bit_length);
+ nr^=(ulong) ((((uint) nr & 63)+nr2)*((uint) bits))+ (nr << 8);
+ nr2+=3;
+ end--;
+ }
+
for (; pos < end ; pos++)
{
nr^=(ulong) ((((uint) nr & 63)+nr2)*((uint) *pos))+ (nr << 8);
@@ -465,6 +474,14 @@ ulong hp_rec_hashnr(register HP_KEYDEF *keydef, register const uchar *rec)
else
{
uchar *end= pos+seg->length;
+ if (seg->type == HA_KEYTYPE_BIT && seg->bit_length)
+ {
+ uchar bits= get_rec_bits(rec + seg->bit_pos,
+ seg->bit_start, seg->bit_length);
+ nr *=16777619;
+ nr ^=(uint) bits;
+ end--;
+ }
for ( ; pos < end ; pos++)
{
nr *=16777619;
@@ -577,7 +594,18 @@ int hp_rec_key_cmp(HP_KEYDEF *keydef, const uchar *rec1, const uchar *rec2,
}
else
{
- if (bcmp(rec1+seg->start,rec2+seg->start,seg->length))
+ uint dec= 0;
+ if (seg->type == HA_KEYTYPE_BIT && seg->bit_length)
+ {
+ uchar bits1= get_rec_bits(rec1 + seg->bit_pos,
+ seg->bit_start, seg->bit_length);
+ uchar bits2= get_rec_bits(rec2 + seg->bit_pos,
+ seg->bit_start, seg->bit_length);
+ if (bits1 != bits2)
+ return 1;
+ dec= 1;
+ }
+ if (bcmp(rec1 + seg->start, rec2 + seg->start, seg->length - dec))
return 1;
}
}
@@ -660,7 +688,18 @@ int hp_key_cmp(HP_KEYDEF *keydef, const uchar *rec, const uchar *key)
}
else
{
- if (bcmp(rec+seg->start,key,seg->length))
+ uint dec= 0;
+ if (seg->type == HA_KEYTYPE_BIT && seg->bit_length)
+ {
+ uchar bits= get_rec_bits(rec + seg->bit_pos,
+ seg->bit_start, seg->bit_length);
+ if (bits != (*key))
+ return 1;
+ dec= 1;
+ key++;
+ }
+
+ if (bcmp(rec + seg->start, key, seg->length - dec))
return 1;
}
}
@@ -689,6 +728,12 @@ void hp_make_key(HP_KEYDEF *keydef, uchar *key, const uchar *rec)
}
if (seg->type == HA_KEYTYPE_VARTEXT1)
char_length+= seg->bit_start; /* Copy also length */
+ else if (seg->type == HA_KEYTYPE_BIT && seg->bit_length)
+ {
+ *key++= get_rec_bits(rec + seg->bit_pos,
+ seg->bit_start, seg->bit_length);
+ char_length--;
+ }
memcpy(key,rec+seg->start,(size_t) char_length);
key+= char_length;
}
@@ -720,7 +765,8 @@ uint hp_rb_make_key(HP_KEYDEF *keydef, uchar *key,
{
uint length= seg->length;
uchar *pos= (uchar*) rec + seg->start;
-
+ DBUG_ASSERT(seg->type != HA_KEYTYPE_BIT);
+
#ifdef HAVE_ISNAN
if (seg->type == HA_KEYTYPE_FLOAT)
{
@@ -784,6 +830,12 @@ uint hp_rb_make_key(HP_KEYDEF *keydef, uchar *key,
seg->charset->cset->fill(seg->charset, (char*) key + char_length,
seg->length - char_length, ' ');
}
+ if (seg->type == HA_KEYTYPE_BIT && seg->bit_length)
+ {
+ *key++= get_rec_bits(rec + seg->bit_pos,
+ seg->bit_start, seg->bit_length);
+ char_length--;
+ }
memcpy(key, rec + seg->start, (size_t) char_length);
key+= seg->length;
}
diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc
index 4c52326a58a..b2af257273b 100644
--- a/storage/innobase/handler/ha_innodb.cc
+++ b/storage/innobase/handler/ha_innodb.cc
@@ -6521,6 +6521,7 @@ ha_innobase::info_low(
}
stats.check_time = 0;
+ stats.mrr_length_per_rec= ref_length + 8; // 8 = max(sizeof(void *));
if (stats.records == 0) {
stats.mean_rec_length = 0;
diff --git a/storage/innodb_plugin/handler/ha_innodb.cc b/storage/innodb_plugin/handler/ha_innodb.cc
index 9715cb54d2c..116ee12e76f 100644
--- a/storage/innodb_plugin/handler/ha_innodb.cc
+++ b/storage/innodb_plugin/handler/ha_innodb.cc
@@ -7684,6 +7684,8 @@ ha_innobase::info_low(
}
stats.check_time = 0;
+ stats.mrr_length_per_rec= ref_length + 8; // 8 = max(sizeof(void *));
+
if (stats.records == 0) {
stats.mean_rec_length = 0;
diff --git a/storage/maria/ha_maria.cc b/storage/maria/ha_maria.cc
index a22b9e995c7..6f01d1e9b5e 100644
--- a/storage/maria/ha_maria.cc
+++ b/storage/maria/ha_maria.cc
@@ -1036,6 +1036,16 @@ int ha_maria::open(const char *name, int mode, uint test_if_locked)
if (file->s->options & (HA_OPTION_CHECKSUM | HA_OPTION_COMPRESS_RECORD))
int_table_flags |= HA_HAS_NEW_CHECKSUM;
+ /*
+ For static size rows, tell MariaDB that we will access all bytes
+ in the record when writing it. This signals MariaDB to initalize
+ the full row to ensure we don't get any errors from valgrind and
+ that all bytes in the row is properly reset.
+ */
+ if (file->s->data_file_type == STATIC_RECORD &&
+ (file->s->has_varchar_fields | file->s->has_null_fields))
+ int_table_flags|= HA_RECORD_MUST_BE_CLEAN_ON_WRITE;
+
for (i= 0; i < table->s->keys; i++)
{
plugin_ref parser= table->key_info[i].parser;
@@ -1052,6 +1062,8 @@ int ha_maria::open(const char *name, int mode, uint test_if_locked)
int ha_maria::close(void)
{
MARIA_HA *tmp= file;
+ if (!tmp)
+ return 0;
file= 0;
return maria_close(tmp);
}
@@ -1091,7 +1103,7 @@ int ha_maria::check(THD * thd, HA_CHECK_OPT * check_opt)
param.thd= thd;
param.op_name= "check";
param.db_name= table->s->db.str;
- param.table_name= table->alias;
+ param.table_name= table->alias.c_ptr();
param.testflag= check_opt->flags | T_CHECK | T_SILENT;
param.stats_method= (enum_handler_stats_method)THDVAR(thd,stats_method);
@@ -1190,7 +1202,7 @@ int ha_maria::analyze(THD *thd, HA_CHECK_OPT * check_opt)
param.thd= thd;
param.op_name= "analyze";
param.db_name= table->s->db.str;
- param.table_name= table->alias;
+ param.table_name= table->alias.c_ptr();
param.testflag= (T_FAST | T_CHECK | T_SILENT | T_STATISTICS |
T_DONT_CHECK_CHECKSUM);
param.using_global_keycache= 1;
@@ -1485,7 +1497,7 @@ int ha_maria::repair(THD *thd, HA_CHECK *param, bool do_optimize)
_ma_copy_nontrans_state_information(file);
param->db_name= table->s->db.str;
- param->table_name= table->alias;
+ param->table_name= table->alias.c_ptr();
param->tmpfile_createflag= O_RDWR | O_TRUNC;
param->using_global_keycache= 1;
param->thd= thd;
@@ -2141,6 +2153,20 @@ int ha_maria::delete_row(const uchar * buf)
return maria_delete(file, buf);
}
+C_MODE_START
+
+ICP_RESULT index_cond_func_maria(void *arg)
+{
+ ha_maria *h= (ha_maria*)arg;
+ if (h->end_range)
+ {
+ if (h->compare_key2(h->end_range) > 0)
+ return ICP_OUT_OF_RANGE; /* caller should return HA_ERR_END_OF_FILE already */
+ }
+ return h->pushed_idx_cond->val_int() ? ICP_MATCH : ICP_NO_MATCH;
+}
+
+C_MODE_END
int ha_maria::index_read_map(uchar * buf, const uchar * key,
key_part_map keypart_map,
@@ -2230,6 +2256,25 @@ int ha_maria::index_next_same(uchar * buf,
}
+int ha_maria::index_init(uint idx, bool sorted)
+{
+ active_index=idx;
+ if (pushed_idx_cond_keyno == idx)
+ ma_set_index_cond_func(file, index_cond_func_maria, this);
+ return 0;
+}
+
+
+int ha_maria::index_end()
+{
+ active_index=MAX_KEY;
+ ma_set_index_cond_func(file, NULL, 0);
+ in_range_check_pushed_down= FALSE;
+ ds_mrr.dsmrr_close();
+ return 0;
+}
+
+
int ha_maria::rnd_init(bool scan)
{
if (scan)
@@ -2240,6 +2285,7 @@ int ha_maria::rnd_init(bool scan)
int ha_maria::rnd_end()
{
+ ds_mrr.dsmrr_close();
/* Safe to call even if we don't have started a scan */
maria_scan_end(file);
return 0;
@@ -2312,6 +2358,7 @@ int ha_maria::info(uint flag, my_bool lock_table_share)
ref_length= maria_info.reflength;
share->db_options_in_use= maria_info.options;
stats.block_size= maria_block_size;
+ stats.mrr_length_per_rec= maria_info.reflength + 8; // 8 = max(sizeof(void *))
/* Update share */
if (lock_table_share)
@@ -2393,6 +2440,10 @@ int ha_maria::extra(enum ha_extra_function operation)
int ha_maria::reset(void)
{
+ pushed_idx_cond= NULL;
+ pushed_idx_cond_keyno= MAX_KEY;
+ ma_set_index_cond_func(file, NULL, 0);
+ ds_mrr.dsmrr_close();
if (file->trn)
{
/* Next statement is a new statement. Ensure it's logged */
@@ -3554,6 +3605,64 @@ static struct st_mysql_show_var aria_status_variables[]= {
{NullS, NullS, SHOW_LONG}
};
+/****************************************************************************
+ * Maria MRR implementation: use DS-MRR
+ ***************************************************************************/
+
+int ha_maria::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, seq, seq_init_param, n_ranges, mode, buf);
+}
+
+int ha_maria::multi_range_read_next(char **range_info)
+{
+ return ds_mrr.dsmrr_next(range_info);
+}
+
+ha_rows ha_maria::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)
+{
+ /*
+ This call is here because there is no location where this->table would
+ already be known.
+ TODO: consider moving it into some per-query initialization call.
+ */
+ ds_mrr.init(this, table);
+ return ds_mrr.dsmrr_info_const(keyno, seq, seq_init_param, n_ranges, bufsz,
+ flags, cost);
+}
+
+ha_rows ha_maria::multi_range_read_info(uint keyno, uint n_ranges, uint keys,
+ uint key_parts, uint *bufsz,
+ uint *flags, COST_VECT *cost)
+{
+ ds_mrr.init(this, table);
+ return ds_mrr.dsmrr_info(keyno, n_ranges, keys, key_parts, bufsz, flags, cost);
+}
+
+/* MyISAM MRR implementation ends */
+
+
+/* Index condition pushdown implementation*/
+
+
+Item *ha_maria::idx_cond_push(uint keyno_arg, Item* idx_cond_arg)
+{
+ pushed_idx_cond_keyno= keyno_arg;
+ pushed_idx_cond= idx_cond_arg;
+ in_range_check_pushed_down= TRUE;
+ if (active_index == pushed_idx_cond_keyno)
+ ma_set_index_cond_func(file, index_cond_func_maria, this);
+ return NULL;
+}
+
+
+
+
struct st_mysql_storage_engine maria_storage_engine=
{ MYSQL_HANDLERTON_INTERFACE_VERSION };
diff --git a/storage/maria/ha_maria.h b/storage/maria/ha_maria.h
index 39307e5efdd..5e7b068d01c 100644
--- a/storage/maria/ha_maria.h
+++ b/storage/maria/ha_maria.h
@@ -13,6 +13,8 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+#ifndef HA_MARIA_INCLUDED
+#define HA_MARIA_INCLUDED
#ifdef USE_PRAGMA_INTERFACE
#pragma interface /* gcc class implementation */
@@ -22,6 +24,16 @@
#include <maria.h>
+#define HA_RECOVER_NONE 0 /* No automatic recover */
+#define HA_RECOVER_DEFAULT 1 /* Automatic recover active */
+#define HA_RECOVER_BACKUP 2 /* Make a backupfile on recover */
+#define HA_RECOVER_FORCE 4 /* Recover even if we loose rows */
+#define HA_RECOVER_QUICK 8 /* Don't check rows in data file */
+
+C_MODE_START
+ICP_RESULT index_cond_func_maria(void *arg);
+C_MODE_END
+
extern ulong maria_sort_buffer_size;
extern TYPELIB maria_recover_typelib;
extern ulong maria_recover_options;
@@ -56,7 +68,7 @@ public:
{
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 | HA_DO_INDEX_COND_PUSHDOWN);
}
uint max_supported_keys() const
{ return MARIA_MAX_KEY; }
@@ -98,6 +110,8 @@ public:
key->charset(), table->record[0]);
}
int ft_read(uchar * buf);
+ int index_init(uint idx, bool sorted);
+ int index_end();
int rnd_init(bool scan);
int rnd_end(void);
int rnd_next(uchar * buf);
@@ -159,4 +173,25 @@ public:
return file;
}
static int implicit_commit(THD *thd, bool new_trn);
+ /**
+ * 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);
+ ha_rows multi_range_read_info(uint keyno, uint n_ranges, uint keys,
+ uint key_parts, uint *bufsz,
+ uint *flags, COST_VECT *cost);
+
+ /* Index condition pushdown implementation */
+ Item *idx_cond_push(uint keyno, Item* idx_cond);
+private:
+ DsMrr_impl ds_mrr;
+ friend ICP_RESULT index_cond_func_maria(void *arg);
};
+
+#endif /* HA_MARIA_INCLUDED */
diff --git a/storage/maria/ma_extra.c b/storage/maria/ma_extra.c
index e436a51248a..d39abc1a68e 100644
--- a/storage/maria/ma_extra.c
+++ b/storage/maria/ma_extra.c
@@ -480,6 +480,14 @@ int maria_extra(MARIA_HA *info, enum ha_extra_function function,
} /* maria_extra */
+void ma_set_index_cond_func(MARIA_HA *info, index_cond_func_t func,
+ void *func_arg)
+{
+ info->index_cond_func= func;
+ info->index_cond_func_arg= func_arg;
+}
+
+
/*
Start/Stop Inserting Duplicates Into a Table, WL#1648.
*/
@@ -626,3 +634,4 @@ int _ma_flush_table_files(MARIA_HA *info, uint flush_data_or_index,
maria_mark_crashed(info);
return 1;
}
+
diff --git a/storage/maria/ma_ft_boolean_search.c b/storage/maria/ma_ft_boolean_search.c
index 40b0623adb1..1b153f1a76f 100644
--- a/storage/maria/ma_ft_boolean_search.c
+++ b/storage/maria/ma_ft_boolean_search.c
@@ -473,14 +473,15 @@ static void _ftb_init_index_search(FT_INFO *ftb)
int i;
FTB_WORD *ftbw;
- if ((ftb->state != READY && ftb->state !=INDEX_DONE) ||
- ftb->keynr == NO_SUCH_KEY)
+ if (ftb->state == UNINITIALIZED || ftb->keynr == NO_SUCH_KEY)
return;
ftb->state=INDEX_SEARCH;
- for (i=ftb->queue.elements; i; i--)
+ for (i= queue_last_element(&ftb->queue);
+ (int) i >= (int) queue_first_element(&ftb->queue);
+ i--)
{
- ftbw=(FTB_WORD *)(ftb->queue.root[i]);
+ ftbw=(FTB_WORD *)(queue_element(&ftb->queue, i));
if (ftbw->flags & FTB_FLAG_TRUNC)
{
@@ -585,7 +586,7 @@ FT_INFO * maria_ft_init_boolean_search(MARIA_HA *info, uint keynr,
sizeof(void *))))
goto err;
reinit_queue(&ftb->queue, ftb->queue.max_elements, 0, 0,
- (int (*)(void*, uchar*, uchar*))FTB_WORD_cmp, 0);
+ (int (*)(void*, uchar*, uchar*))FTB_WORD_cmp, 0, 0, 0);
for (ftbw= ftb->last_word; ftbw; ftbw= ftbw->prev)
queue_insert(&ftb->queue, (uchar *)ftbw);
ftb->list=(FTB_WORD **)alloc_root(&ftb->mem_root,
@@ -828,7 +829,7 @@ int maria_ft_boolean_read_next(FT_INFO *ftb, char *record)
/* update queue */
_ft2_search(ftb, ftbw, 0);
- queue_replaced(& ftb->queue);
+ queue_replace_top(&ftb->queue);
}
ftbe=ftb->root;
diff --git a/storage/maria/ma_ft_nlq_search.c b/storage/maria/ma_ft_nlq_search.c
index 927f34f8b72..3bb7defcaaf 100644
--- a/storage/maria/ma_ft_nlq_search.c
+++ b/storage/maria/ma_ft_nlq_search.c
@@ -253,12 +253,12 @@ FT_INFO *maria_ft_init_nlq_search(MARIA_HA *info, uint keynr, uchar *query,
{
QUEUE best;
init_queue(&best,ft_query_expansion_limit,0,0, (queue_compare) &FT_DOC_cmp,
- 0);
+ 0, 0, 0);
tree_walk(&aio.dtree, (tree_walk_action) &walk_and_push,
&best, left_root_right);
while (best.elements)
{
- my_off_t docid=((FT_DOC *)queue_remove(& best, 0))->dpos;
+ my_off_t docid= ((FT_DOC *)queue_remove_top(&best))->dpos;
if (!(*info->read_record)(info, record, docid))
{
info->update|= HA_STATE_AKTIV;
diff --git a/storage/maria/ma_key.c b/storage/maria/ma_key.c
index 08702a9109e..ac23bf5fef6 100644
--- a/storage/maria/ma_key.c
+++ b/storage/maria/ma_key.c
@@ -26,7 +26,8 @@
#define CHECK_KEYS /* Enable safety checks */
-static int _ma_put_key_in_record(MARIA_HA *info,uint keynr,uchar *record);
+static int _ma_put_key_in_record(MARIA_HA *info, uint keynr,
+ my_bool unpack_blobs, uchar *record);
#define FIX_LENGTH(cs, pos, length, char_length) \
do { \
@@ -476,6 +477,9 @@ void _ma_copy_key(MARIA_KEY *to, const MARIA_KEY *from)
_ma_put_key_in_record()
info MARIA handler
keynr Key number that was used
+ unpack_blobs TRUE <=> Unpack blob columns
+ FALSE <=> Skip them. This is used by index condition
+ pushdown check function
record Store key here
Last read key is in info->lastkey
@@ -489,7 +493,7 @@ void _ma_copy_key(MARIA_KEY *to, const MARIA_KEY *from)
*/
static int _ma_put_key_in_record(register MARIA_HA *info, uint keynr,
- uchar *record)
+ my_bool unpack_blobs, uchar *record)
{
reg2 uchar *key;
uchar *pos,*key_end;
@@ -582,16 +586,19 @@ static int _ma_put_key_in_record(register MARIA_HA *info, uint keynr,
if (length > keyseg->length || key+length > key_end)
goto err;
#endif
- memcpy(record+keyseg->start+keyseg->bit_start,
- (char*) &blob_ptr,sizeof(char*));
- memcpy(blob_ptr,key,length);
- blob_ptr+=length;
+ if (unpack_blobs)
+ {
+ memcpy(record+keyseg->start+keyseg->bit_start,
+ (char*) &blob_ptr,sizeof(char*));
+ memcpy(blob_ptr,key,length);
+ blob_ptr+=length;
- /* The above changed info->lastkey2. Inform maria_rnext_same(). */
- info->update&= ~HA_STATE_RNEXT_SAME;
+ /* The above changed info->lastkey2. Inform maria_rnext_same(). */
+ info->update&= ~HA_STATE_RNEXT_SAME;
- _ma_store_blob_length(record+keyseg->start,
- (uint) keyseg->bit_start,length);
+ _ma_store_blob_length(record+keyseg->start,
+ (uint) keyseg->bit_start,length);
+ }
key+=length;
}
else if (keyseg->flag & HA_SWAP_KEY)
@@ -621,6 +628,7 @@ static int _ma_put_key_in_record(register MARIA_HA *info, uint keynr,
DBUG_RETURN(0);
err:
+ DBUG_PRINT("info",("error"));
DBUG_RETURN(1); /* Crashed row */
} /* _ma_put_key_in_record */
@@ -634,7 +642,7 @@ int _ma_read_key_record(MARIA_HA *info, uchar *buf, MARIA_RECORD_POS filepos)
{
if (info->lastinx >= 0)
{ /* Read only key */
- if (_ma_put_key_in_record(info,(uint) info->lastinx,buf))
+ if (_ma_put_key_in_record(info, (uint)info->lastinx, TRUE, buf))
{
maria_print_error(info->s, HA_ERR_CRASHED);
my_errno=HA_ERR_CRASHED;
@@ -649,6 +657,40 @@ int _ma_read_key_record(MARIA_HA *info, uchar *buf, MARIA_RECORD_POS filepos)
}
+
+/*
+ Save current key tuple to record and call index condition check function
+
+ SYNOPSIS
+ ma_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
+ ICP_ERROR Error
+ ICP_NO_MATCH Index condition is not satisfied, continue scanning
+ ICP_MATCH Index condition is satisfied
+ ICP_OUT_OF_RANGE Index condition is not satisfied, end the scan.
+*/
+
+int ma_check_index_cond(register MARIA_HA *info, uint keynr, uchar *record)
+{
+ if (info->index_cond_func)
+ {
+ if (_ma_put_key_in_record(info, keynr, FALSE, record))
+ {
+ maria_print_error(info->s, HA_ERR_CRASHED);
+ my_errno=HA_ERR_CRASHED;
+ return -1;
+ }
+ return info->index_cond_func(info->index_cond_func_arg);
+ }
+ return 1;
+}
+
+
/*
Retrieve auto_increment info
diff --git a/storage/maria/ma_open.c b/storage/maria/ma_open.c
index 0890ff7e000..9d3e75cf4dc 100644
--- a/storage/maria/ma_open.c
+++ b/storage/maria/ma_open.c
@@ -765,6 +765,10 @@ MARIA_HA *maria_open(const char *name, int mode, uint open_flags)
share->blobs[j].offset= share->columndef[i].offset;
j++;
}
+ if (share->columndef[i].type == FIELD_VARCHAR)
+ share->has_varchar_fields= 1;
+ if (share->columndef[i].null_bit)
+ share->has_null_fields= 1;
}
share->columndef[i].type= FIELD_LAST; /* End marker */
disk_pos= _ma_column_nr_read(disk_pos, share->column_nr,
diff --git a/storage/maria/ma_rkey.c b/storage/maria/ma_rkey.c
index 85b3b463f49..e488be0ec86 100644
--- a/storage/maria/ma_rkey.c
+++ b/storage/maria/ma_rkey.c
@@ -34,6 +34,7 @@ int maria_rkey(MARIA_HA *info, uchar *buf, int inx, const uchar *key_data,
HA_KEYSEG *last_used_keyseg;
uint32 nextflag;
MARIA_KEY key;
+ int icp_res= 1;
DBUG_ENTER("maria_rkey");
DBUG_PRINT("enter", ("base: 0x%lx buf: 0x%lx inx: %d search_flag: %d",
(long) info, (long) buf, inx, search_flag));
@@ -109,9 +110,13 @@ int maria_rkey(MARIA_HA *info, uchar *buf, int inx, const uchar *key_data,
are inserted by other threads after we got our table lock
("concurrent inserts"). The record may not even be present yet.
Keys are inserted into the index(es) before the record is
- inserted into the data file.
+ inserted into the data file.
+
+ If index condition is present, it must be either satisfied or
+ not satisfied with an out-of-range condition.
*/
- if ((*share->row_is_visible)(info))
+ if ((*share->row_is_visible)(info) &&
+ ((icp_res= ma_check_index_cond(info, inx, buf)) != 0))
break;
/* The key references a concurrently inserted record. */
@@ -123,7 +128,7 @@ int maria_rkey(MARIA_HA *info, uchar *buf, int inx, const uchar *key_data,
info->cur_row.lastpos= HA_OFFSET_ERROR;
break;
}
-
+
do
{
uint not_used[2];
@@ -154,18 +159,25 @@ int maria_rkey(MARIA_HA *info, uchar *buf, int inx, const uchar *key_data,
break;
/* purecov: end */
}
- } while (!(*share->row_is_visible)(info));
+
+ } while (!(*share->row_is_visible)(info) ||
+ ((icp_res= ma_check_index_cond(info, inx, buf)) == 0));
}
}
if (share->lock_key_trees)
rw_unlock(&keyinfo->root_lock);
- if (info->cur_row.lastpos == HA_OFFSET_ERROR)
+ if (info->cur_row.lastpos == HA_OFFSET_ERROR || (icp_res != 1))
{
+ if (icp_res == 2)
+ {
+ info->cur_row.lastpos= HA_OFFSET_ERROR;
+ my_errno= HA_ERR_KEY_NOT_FOUND;
+ }
fast_ma_writeinfo(info);
goto err;
}
-
+
/* Calculate length of the found key; Used by maria_rnext_same */
if ((keyinfo->flag & HA_VAR_LENGTH_KEY))
info->last_rkey_length= _ma_keylength_part(keyinfo, info->lastkey_buff,
diff --git a/storage/maria/ma_rnext.c b/storage/maria/ma_rnext.c
index c49bbb19e83..570ea6a0c1d 100644
--- a/storage/maria/ma_rnext.c
+++ b/storage/maria/ma_rnext.c
@@ -30,6 +30,7 @@ int maria_rnext(MARIA_HA *info, uchar *buf, int inx)
uint flag;
MARIA_SHARE *share= info->s;
MARIA_KEYDEF *keyinfo;
+ int icp_res= 1;
uint update_mask= HA_STATE_NEXT_FOUND;
DBUG_ENTER("maria_rnext");
@@ -105,7 +106,8 @@ int maria_rnext(MARIA_HA *info, uchar *buf, int inx)
if (!error)
{
- while (!(*share->row_is_visible)(info))
+ while (!(*share->row_is_visible)(info) ||
+ ((icp_res= ma_check_index_cond(info, inx, buf)) == 0))
{
/* Skip rows inserted by other threads since we got a lock */
if ((error= _ma_search_next(info, &info->last_key,
@@ -120,8 +122,11 @@ int maria_rnext(MARIA_HA *info, uchar *buf, int inx)
/* Don't clear if database-changed */
info->update&= (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED);
info->update|= update_mask;
+
+ if (icp_res == 2)
+ my_errno=HA_ERR_END_OF_FILE; /* got beyond the end of scanned range */
- if (error)
+ if (error || icp_res != 1)
{
if (my_errno == HA_ERR_KEY_NOT_FOUND)
my_errno=HA_ERR_END_OF_FILE;
diff --git a/storage/maria/ma_rnext_same.c b/storage/maria/ma_rnext_same.c
index cbd81d20816..f67a76a366f 100644
--- a/storage/maria/ma_rnext_same.c
+++ b/storage/maria/ma_rnext_same.c
@@ -30,6 +30,7 @@ int maria_rnext_same(MARIA_HA *info, uchar *buf)
int error;
uint inx,not_used[2];
MARIA_KEYDEF *keyinfo;
+ int icp_res= 1;
DBUG_ENTER("maria_rnext_same");
if ((int) (inx= info->lastinx) < 0 ||
@@ -80,7 +81,8 @@ int maria_rnext_same(MARIA_HA *info, uchar *buf)
break;
}
/* Skip rows that are inserted by other threads since we got a lock */
- if ((info->s->row_is_visible)(info))
+ if ((info->s->row_is_visible)(info) &&
+ ((icp_res= ma_check_index_cond(info, inx, buf)) != 0))
break;
}
}
@@ -90,7 +92,10 @@ int maria_rnext_same(MARIA_HA *info, uchar *buf)
info->update&= (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED);
info->update|= HA_STATE_NEXT_FOUND | HA_STATE_RNEXT_SAME;
- if (error)
+ if (icp_res == 2)
+ my_errno=HA_ERR_END_OF_FILE; /* got beyond the end of scanned range */
+
+ if (error || icp_res != 1)
{
if (my_errno == HA_ERR_KEY_NOT_FOUND)
my_errno=HA_ERR_END_OF_FILE;
diff --git a/storage/maria/ma_sort.c b/storage/maria/ma_sort.c
index 5c5bd8f8d6c..a36bb980145 100644
--- a/storage/maria/ma_sort.c
+++ b/storage/maria/ma_sort.c
@@ -939,7 +939,7 @@ merge_buffers(MARIA_SORT_PARAM *info, uint keys, IO_CACHE *from_file,
if (init_queue(&queue,(uint) (Tb-Fb)+1,offsetof(BUFFPEK,key),0,
(int (*)(void*, uchar *,uchar*)) info->key_cmp,
- (void*) info))
+ (void*) info, 0, 0))
DBUG_RETURN(1); /* purecov: inspected */
for (buffpek= Fb ; buffpek <= Tb ; buffpek++)
@@ -988,7 +988,7 @@ merge_buffers(MARIA_SORT_PARAM *info, uint keys, IO_CACHE *from_file,
uchar *base= buffpek->base;
uint max_keys=buffpek->max_keys;
- VOID(queue_remove(&queue,0));
+ VOID(queue_remove_top(&queue));
/* Put room used by buffer to use in other buffer */
for (refpek= (BUFFPEK**) &queue_top(&queue);
@@ -1013,7 +1013,7 @@ merge_buffers(MARIA_SORT_PARAM *info, uint keys, IO_CACHE *from_file,
}
else if (error == -1)
goto err; /* purecov: inspected */
- queue_replaced(&queue); /* Top element has been replaced */
+ queue_replace_top(&queue); /* Top element has been replaced */
}
}
buffpek=(BUFFPEK*) queue_top(&queue);
diff --git a/storage/maria/maria_def.h b/storage/maria/maria_def.h
index 2fbc84bae5f..4195be4a53a 100644
--- a/storage/maria/maria_def.h
+++ b/storage/maria/maria_def.h
@@ -378,6 +378,8 @@ typedef struct st_maria_share
my_bool temporary;
/* Below flag is needed to make log tables work with concurrent insert */
my_bool is_log_table;
+ my_bool has_null_fields;
+ my_bool has_varchar_fields; /* If table has varchar fields */
my_bool changed, /* If changed since lock */
global_changed, /* If changed since open */
@@ -484,6 +486,7 @@ typedef struct st_maria_block_scan
MARIA_RECORD_POS row_base_page;
} MARIA_BLOCK_SCAN;
+typedef ICP_RESULT (*index_cond_func_t)(void *param);
struct st_maria_handler
{
@@ -588,6 +591,9 @@ struct st_maria_handler
uchar *maria_rtree_recursion_state; /* For RTREE */
uchar length_buff[5]; /* temp buff to store blob lengths */
int maria_rtree_recursion_depth;
+
+ index_cond_func_t index_cond_func; /* Index condition function */
+ void *index_cond_func_arg; /* parameter for the func */
};
/* Some defines used by maria-functions */
@@ -1261,3 +1267,6 @@ extern my_bool maria_flush_log_for_page_none(uchar *page,
pgcache_page_no_t page_no,
uchar *data_ptr);
extern PAGECACHE *maria_log_pagecache;
+extern void ma_set_index_cond_func(MARIA_HA *info, index_cond_func_t func,
+ void *func_arg);
+int ma_check_index_cond(register MARIA_HA *info, uint keynr, uchar *record);
diff --git a/storage/maria/maria_pack.c b/storage/maria/maria_pack.c
index 983a38e69e7..8332af60de5 100644
--- a/storage/maria/maria_pack.c
+++ b/storage/maria/maria_pack.c
@@ -590,7 +590,7 @@ static int compress(PACK_MRG_INFO *mrg,char *result_table)
Create a global priority queue in preparation for making
temporary Huffman trees.
*/
- if (init_queue(&queue,256,0,0,compare_huff_elements,0))
+ if (init_queue(&queue, 256, 0, 0, compare_huff_elements, 0, 0, 0))
goto err;
/*
@@ -1523,7 +1523,7 @@ static int make_huff_tree(HUFF_TREE *huff_tree, HUFF_COUNTS *huff_counts)
if (queue.max_elements < found)
{
delete_queue(&queue);
- if (init_queue(&queue,found,0,0,compare_huff_elements,0))
+ if (init_queue(&queue,found, 0, 0, compare_huff_elements, 0, 0, 0))
return -1;
}
@@ -1627,8 +1627,7 @@ static int make_huff_tree(HUFF_TREE *huff_tree, HUFF_COUNTS *huff_counts)
Make a priority queue from the queue. Construct its index so that we
have a partially ordered tree.
*/
- for (i=found/2 ; i > 0 ; i--)
- _downheap(&queue,i);
+ queue_fix(&queue);
/* The Huffman algorithm. */
bytes_packed=0; bits_packed=0;
@@ -1639,12 +1638,9 @@ static int make_huff_tree(HUFF_TREE *huff_tree, HUFF_COUNTS *huff_counts)
Popping from a priority queue includes a re-ordering of the queue,
to get the next least incidence element to the top.
*/
- a=(HUFF_ELEMENT*) queue_remove(&queue,0);
- /*
- Copy the next least incidence element. The queue implementation
- reserves root[0] for temporary purposes. root[1] is the top.
- */
- b=(HUFF_ELEMENT*) queue.root[1];
+ a=(HUFF_ELEMENT*) queue_remove_top(&queue);
+ /* Copy the next least incidence element */
+ b=(HUFF_ELEMENT*) queue_top(&queue);
/* Get a new element from the element buffer. */
new_huff_el=huff_tree->element_buffer+found+i;
/* The new element gets the sum of the two least incidence elements. */
@@ -1666,8 +1662,8 @@ static int make_huff_tree(HUFF_TREE *huff_tree, HUFF_COUNTS *huff_counts)
Replace the copied top element by the new element and re-order the
queue.
*/
- queue.root[1]=(uchar*) new_huff_el;
- queue_replaced(&queue);
+ queue_top(&queue)= (uchar*) new_huff_el;
+ queue_replace_top(&queue);
}
huff_tree->root=(HUFF_ELEMENT*) queue.root[1];
huff_tree->bytes_packed=bytes_packed+(bits_packed+7)/8;
@@ -1798,8 +1794,7 @@ static my_off_t calc_packed_length(HUFF_COUNTS *huff_counts,
Make a priority queue from the queue. Construct its index so that we
have a partially ordered tree.
*/
- for (i=(found+1)/2 ; i > 0 ; i--)
- _downheap(&queue,i);
+ queue_fix(&queue);
/* The Huffman algorithm. */
for (i=0 ; i < found-1 ; i++)
@@ -1813,12 +1808,9 @@ static my_off_t calc_packed_length(HUFF_COUNTS *huff_counts,
incidence). Popping from a priority queue includes a re-ordering
of the queue, to get the next least incidence element to the top.
*/
- a= (my_off_t*) queue_remove(&queue, 0);
- /*
- Copy the next least incidence element. The queue implementation
- reserves root[0] for temporary purposes. root[1] is the top.
- */
- b= (my_off_t*) queue.root[1];
+ a= (my_off_t*) queue_remove_top(&queue);
+ /* Copy the next least incidence element. */
+ b= (my_off_t*) queue_top(&queue);
/* Create a new element in a local (automatic) buffer. */
new_huff_el= element_buffer + i;
/* The new element gets the sum of the two least incidence elements. */
@@ -1838,8 +1830,8 @@ static my_off_t calc_packed_length(HUFF_COUNTS *huff_counts,
queue. This successively replaces the references to counts by
references to HUFF_ELEMENTs.
*/
- queue.root[1]=(uchar*) new_huff_el;
- queue_replaced(&queue);
+ queue_top(&queue)= (uchar*) new_huff_el;
+ queue_replace_top(&queue);
}
DBUG_RETURN(bytes_packed+(bits_packed+7)/8);
}
diff --git a/storage/myisam/ft_boolean_search.c b/storage/myisam/ft_boolean_search.c
index 3a6368f338d..dc7eb763b77 100644
--- a/storage/myisam/ft_boolean_search.c
+++ b/storage/myisam/ft_boolean_search.c
@@ -482,16 +482,18 @@ static int _ft2_search(FTB *ftb, FTB_WORD *ftbw, my_bool init_search)
static void _ftb_init_index_search(FT_INFO *ftb)
{
- int i;
+ uint i;
FTB_WORD *ftbw;
if (ftb->state == UNINITIALIZED || ftb->keynr == NO_SUCH_KEY)
return;
ftb->state=INDEX_SEARCH;
- for (i=ftb->queue.elements; i; i--)
+ for (i= queue_last_element(&ftb->queue);
+ (int) i >= (int) queue_first_element(&ftb->queue);
+ i--)
{
- ftbw=(FTB_WORD *)(ftb->queue.root[i]);
+ ftbw=(FTB_WORD *)(queue_element(&ftb->queue, i));
if (ftbw->flags & FTB_FLAG_TRUNC)
{
@@ -595,12 +597,12 @@ FT_INFO * ft_init_boolean_search(MI_INFO *info, uint keynr, uchar *query,
sizeof(void *))))
goto err;
reinit_queue(&ftb->queue, ftb->queue.max_elements, 0, 0,
- (int (*)(void*, uchar*, uchar*))FTB_WORD_cmp, 0);
+ (int (*)(void*, uchar*, uchar*))FTB_WORD_cmp, 0, 0, 0);
for (ftbw= ftb->last_word; ftbw; ftbw= ftbw->prev)
queue_insert(&ftb->queue, (uchar *)ftbw);
ftb->list=(FTB_WORD **)alloc_root(&ftb->mem_root,
sizeof(FTB_WORD *)*ftb->queue.elements);
- memcpy(ftb->list, ftb->queue.root+1, sizeof(FTB_WORD *)*ftb->queue.elements);
+ memcpy(ftb->list, &queue_top(&ftb->queue), sizeof(FTB_WORD *)*ftb->queue.elements);
my_qsort2(ftb->list, ftb->queue.elements, sizeof(FTB_WORD *),
(qsort2_cmp)FTB_WORD_cmp_list, (void*) ftb->charset);
if (ftb->queue.elements<2) ftb->with_scan &= ~FTB_FLAG_TRUNC;
@@ -839,7 +841,7 @@ int ft_boolean_read_next(FT_INFO *ftb, char *record)
/* update queue */
_ft2_search(ftb, ftbw, 0);
- queue_replaced(& ftb->queue);
+ queue_replace_top(&ftb->queue);
}
ftbe=ftb->root;
diff --git a/storage/myisam/ft_nlq_search.c b/storage/myisam/ft_nlq_search.c
index 90ad2d635b7..bcce2cc8d60 100644
--- a/storage/myisam/ft_nlq_search.c
+++ b/storage/myisam/ft_nlq_search.c
@@ -250,12 +250,12 @@ FT_INFO *ft_init_nlq_search(MI_INFO *info, uint keynr, uchar *query,
{
QUEUE best;
init_queue(&best,ft_query_expansion_limit,0,0, (queue_compare) &FT_DOC_cmp,
- 0);
+ 0, 0, 0);
tree_walk(&aio.dtree, (tree_walk_action) &walk_and_push,
&best, left_root_right);
while (best.elements)
{
- my_off_t docid=((FT_DOC *)queue_remove(& best, 0))->dpos;
+ my_off_t docid= ((FT_DOC *)queue_remove_top(&best))->dpos;
if (!(*info->read_record)(info,docid,record))
{
info->update|= HA_STATE_AKTIV;
diff --git a/storage/myisam/ha_myisam.cc b/storage/myisam/ha_myisam.cc
index 09a20383937..b2fad816eba 100644
--- a/storage/myisam/ha_myisam.cc
+++ b/storage/myisam/ha_myisam.cc
@@ -746,6 +746,16 @@ int ha_myisam::open(const char *name, int mode, uint test_if_locked)
int_table_flags|= HA_HAS_OLD_CHECKSUM;
}
+ /*
+ For static size rows, tell MariaDB that we will access all bytes
+ in the record when writing it. This signals MariaDB to initalize
+ the full row to ensure we don't get any errors from valgrind and
+ that all bytes in the row is properly reset.
+ */
+ if ((file->s->options & HA_OPTION_PACK_RECORD) &&
+ (file->s->has_varchar_fields | file->s->has_null_fields))
+ int_table_flags|= HA_RECORD_MUST_BE_CLEAN_ON_WRITE;
+
for (i= 0; i < table->s->keys; i++)
{
plugin_ref parser= table->key_info[i].parser;
@@ -771,6 +781,8 @@ int ha_myisam::open(const char *name, int mode, uint test_if_locked)
int ha_myisam::close(void)
{
MI_INFO *tmp=file;
+ if (!tmp)
+ return 0;
file=0;
return mi_close(tmp);
}
@@ -810,7 +822,7 @@ int ha_myisam::check(THD* thd, HA_CHECK_OPT* check_opt)
param.thd = thd;
param.op_name = "check";
param.db_name= table->s->db.str;
- param.table_name= table->alias;
+ param.table_name= table->alias.c_ptr();
param.testflag = check_opt->flags | T_CHECK | T_SILENT;
param.stats_method= (enum_handler_stats_method)thd->variables.myisam_stats_method;
@@ -903,7 +915,7 @@ int ha_myisam::analyze(THD *thd, HA_CHECK_OPT* check_opt)
param.thd = thd;
param.op_name= "analyze";
param.db_name= table->s->db.str;
- param.table_name= table->alias;
+ param.table_name= table->alias.c_ptr();
param.testflag= (T_FAST | T_CHECK | T_SILENT | T_STATISTICS |
T_DONT_CHECK_CHECKSUM);
param.using_global_keycache = 1;
@@ -1129,7 +1141,7 @@ int ha_myisam::repair(THD *thd, HA_CHECK &param, bool do_optimize)
DBUG_ENTER("ha_myisam::repair");
param.db_name= table->s->db.str;
- param.table_name= table->alias;
+ param.table_name= table->alias.c_ptr();
param.tmpfile_createflag = O_RDWR | O_TRUNC;
param.using_global_keycache = 1;
param.thd= thd;
@@ -1701,6 +1713,48 @@ int ha_myisam::delete_row(const uchar *buf)
return mi_delete(file,buf);
}
+
+C_MODE_START
+
+ICP_RESULT index_cond_func_myisam(void *arg)
+{
+ ha_myisam *h= (ha_myisam*)arg;
+ if (h->end_range)
+ {
+ if (h->compare_key2(h->end_range) > 0)
+ return ICP_OUT_OF_RANGE; /* caller should return HA_ERR_END_OF_FILE already */
+ }
+ return (ICP_RESULT) test(h->pushed_idx_cond->val_int());
+}
+
+C_MODE_END
+
+
+int ha_myisam::index_init(uint idx, bool sorted)
+{
+ active_index=idx;
+ if (pushed_idx_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;
+ //pushed_idx_cond_keyno= MAX_KEY;
+ mi_set_index_cond_func(file, NULL, 0);
+ in_range_check_pushed_down= FALSE;
+ ds_mrr.dsmrr_close();
+ return 0;
+}
+
+int ha_myisam::rnd_end()
+{
+ ds_mrr.dsmrr_close();
+ return 0;
+}
+
int ha_myisam::index_read_map(uchar *buf, const uchar *key,
key_part_map keypart_map,
enum ha_rkey_function find_flag)
@@ -1806,6 +1860,16 @@ int ha_myisam::info(uint flag)
stats.max_data_file_length= misam_info.max_data_file_length;
stats.max_index_file_length= misam_info.max_index_file_length;
stats.create_time= (ulong) misam_info.create_time;
+ /*
+ We want the value of stats.mrr_length_per_rec to be platform independent.
+ The size of the chunk at the end of the join buffer used for MRR needs
+ is calculated now basing on the values passed in the stats structure.
+ The remaining part of the join buffer is used for records. A different
+ number of records in the buffer results in a different number of buffer
+ refills and in a different order of records in the result set.
+ */
+ stats.mrr_length_per_rec= misam_info.reflength + 8; // 8=max(sizeof(void *))
+
ref_length= misam_info.reflength;
share->db_options_in_use= misam_info.options;
stats.block_size= myisam_block_size; /* record block size */
@@ -1861,8 +1925,13 @@ int ha_myisam::extra(enum ha_extra_function operation)
return mi_extra(file, operation, 0);
}
+
int ha_myisam::reset(void)
{
+ pushed_idx_cond= NULL;
+ pushed_idx_cond_keyno= MAX_KEY;
+ mi_set_index_cond_func(file, NULL, 0);
+ ds_mrr.dsmrr_close();
return mi_reset(file);
}
@@ -2145,6 +2214,62 @@ static int myisam_init(void *p)
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, seq, seq_init_param, n_ranges, mode, buf);
+}
+
+int ha_myisam::multi_range_read_next(char **range_info)
+{
+ return ds_mrr.dsmrr_next(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)
+{
+ /*
+ This call is here because there is no location where this->table would
+ already be known.
+ TODO: consider moving it into some per-query initialization call.
+ */
+ ds_mrr.init(this, table);
+ return ds_mrr.dsmrr_info_const(keyno, seq, seq_init_param, n_ranges, bufsz,
+ flags, cost);
+}
+
+ha_rows ha_myisam::multi_range_read_info(uint keyno, uint n_ranges, uint keys,
+ uint key_parts, uint *bufsz,
+ uint *flags, COST_VECT *cost)
+{
+ ds_mrr.init(this, table);
+ return ds_mrr.dsmrr_info(keyno, n_ranges, keys, key_parts, bufsz, flags, cost);
+}
+
+/* MyISAM MRR implementation ends */
+
+
+/* Index condition pushdown implementation*/
+
+
+Item *ha_myisam::idx_cond_push(uint keyno_arg, Item* idx_cond_arg)
+{
+ pushed_idx_cond_keyno= keyno_arg;
+ pushed_idx_cond= idx_cond_arg;
+ in_range_check_pushed_down= TRUE;
+ if (active_index == pushed_idx_cond_keyno)
+ mi_set_index_cond_func(file, index_cond_func_myisam, this);
+ return NULL;
+}
+
+
struct st_mysql_storage_engine myisam_storage_engine=
{ MYSQL_HANDLERTON_INTERFACE_VERSION };
diff --git a/storage/myisam/ha_myisam.h b/storage/myisam/ha_myisam.h
index d8f307c4aa7..b9700e0ef90 100644
--- a/storage/myisam/ha_myisam.h
+++ b/storage/myisam/ha_myisam.h
@@ -35,6 +35,10 @@ extern ulong myisam_sort_buffer_size;
extern TYPELIB myisam_recover_typelib;
extern ulong myisam_recover_options;
+C_MODE_START
+ICP_RESULT index_cond_func_myisam(void *arg);
+C_MODE_END
+
class ha_myisam: public handler
{
MI_INFO *file;
@@ -51,11 +55,15 @@ class ha_myisam: public handler
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();
+ int rnd_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 | HA_DO_INDEX_COND_PUSHDOWN);
}
uint max_supported_keys() const { return MI_MAX_KEY; }
uint max_supported_key_length() const { return HA_MAX_KEY_LENGTH; }
@@ -148,4 +156,24 @@ class ha_myisam: public handler
{
return file;
}
+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);
+ ha_rows multi_range_read_info(uint keyno, uint n_ranges, uint keys,
+ uint key_parts, uint *bufsz,
+ uint *flags, COST_VECT *cost);
+
+ /* Index condition pushdown implementation */
+ Item *idx_cond_push(uint keyno, Item* idx_cond);
+private:
+ DsMrr_impl ds_mrr;
+ friend ICP_RESULT index_cond_func_myisam(void *arg);
};
diff --git a/storage/myisam/mi_extra.c b/storage/myisam/mi_extra.c
index 7b8f5249b3e..648e5fb0024 100644
--- a/storage/myisam/mi_extra.c
+++ b/storage/myisam/mi_extra.c
@@ -410,6 +410,12 @@ int mi_extra(MI_INFO *info, enum ha_extra_function function, void *extra_arg)
DBUG_RETURN(error);
} /* mi_extra */
+void mi_set_index_cond_func(MI_INFO *info, index_cond_func_t func,
+ void *func_arg)
+{
+ info->index_cond_func= func;
+ info->index_cond_func_arg= func_arg;
+}
/*
Start/Stop Inserting Duplicates Into a Table, WL#1648.
diff --git a/storage/myisam/mi_key.c b/storage/myisam/mi_key.c
index 94f3f34ec58..52273f013ec 100644
--- a/storage/myisam/mi_key.c
+++ b/storage/myisam/mi_key.c
@@ -31,7 +31,8 @@
set_if_smaller(char_length,length); \
} while(0)
-static int _mi_put_key_in_record(MI_INFO *info,uint keynr,uchar *record);
+static int _mi_put_key_in_record(MI_INFO *info,uint keynr,
+ my_bool unpack_blobs, uchar *record);
/*
Make a intern key from a record
@@ -312,6 +313,9 @@ uint _mi_pack_key(register MI_INFO *info, uint keynr, uchar *key, uchar *old,
_mi_put_key_in_record()
info MyISAM handler
keynr Key number that was used
+ unpack_blobs TRUE <=> Unpack blob columns
+ FALSE <=> Skip them. This is used by index condition
+ pushdown check function
record Store key here
Last read key is in info->lastkey
@@ -325,7 +329,7 @@ uint _mi_pack_key(register MI_INFO *info, uint keynr, uchar *key, uchar *old,
*/
static int _mi_put_key_in_record(register MI_INFO *info, uint keynr,
- uchar *record)
+ my_bool unpack_blobs, uchar *record)
{
reg2 uchar *key;
uchar *pos,*key_end;
@@ -418,16 +422,19 @@ static int _mi_put_key_in_record(register MI_INFO *info, uint keynr,
if (length > keyseg->length || key+length > key_end)
goto err;
#endif
- memcpy(record+keyseg->start+keyseg->bit_start,
- (char*) &blob_ptr,sizeof(char*));
- memcpy(blob_ptr,key,length);
- blob_ptr+=length;
+ if (unpack_blobs)
+ {
+ memcpy(record+keyseg->start+keyseg->bit_start,
+ (char*) &blob_ptr,sizeof(char*));
+ memcpy(blob_ptr,key,length);
+ blob_ptr+=length;
- /* The above changed info->lastkey2. Inform mi_rnext_same(). */
- info->update&= ~HA_STATE_RNEXT_SAME;
+ /* The above changed info->lastkey2. Inform mi_rnext_same(). */
+ info->update&= ~HA_STATE_RNEXT_SAME;
- _mi_store_blob_length(record+keyseg->start,
- (uint) keyseg->bit_start,length);
+ _mi_store_blob_length(record+keyseg->start,
+ (uint) keyseg->bit_start,length);
+ }
key+=length;
}
else if (keyseg->flag & HA_SWAP_KEY)
@@ -471,7 +478,7 @@ int _mi_read_key_record(MI_INFO *info, my_off_t filepos, uchar *buf)
{
if (info->lastinx >= 0)
{ /* Read only key */
- if (_mi_put_key_in_record(info,(uint) info->lastinx,buf))
+ if (_mi_put_key_in_record(info,(uint) info->lastinx, TRUE, buf))
{
mi_print_error(info->s, HA_ERR_CRASHED);
my_errno=HA_ERR_CRASHED;
@@ -487,6 +494,34 @@ int _mi_read_key_record(MI_INFO *info, my_off_t filepos, uchar *buf)
/*
+ 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
+ ICP_ERROR Error
+ ICP_NO_MATCH Index condition is not satisfied, continue scanning
+ ICP_MATCH Index condition is satisfied
+ ICP_OUT_OF_RANGE Index condition is not satisfied, end the scan.
+*/
+
+int mi_check_index_cond(register MI_INFO *info, uint keynr, uchar *record)
+{
+ if (_mi_put_key_in_record(info, keynr, FALSE, record))
+ {
+ mi_print_error(info->s, HA_ERR_CRASHED);
+ my_errno=HA_ERR_CRASHED;
+ return ICP_ERROR;
+ }
+ return info->index_cond_func(info->index_cond_func_arg);
+}
+
+/*
Retrieve auto_increment info
SYNOPSIS
diff --git a/storage/myisam/mi_open.c b/storage/myisam/mi_open.c
index 9d69f8622e8..89a0ef61081 100644
--- a/storage/myisam/mi_open.c
+++ b/storage/myisam/mi_open.c
@@ -79,14 +79,14 @@ MI_INFO *mi_open(const char *name, int mode, uint open_flags)
{
int lock_error,kfile,open_mode,save_errno,have_rtree=0, realpath_err;
uint i,j,len,errpos,head_length,base_pos,offset,info_length,keys,
- key_parts,unique_key_parts,fulltext_keys,uniques;
+ key_parts,unique_key_parts,base_key_parts,fulltext_keys,uniques;
char name_buff[FN_REFLEN], org_name[FN_REFLEN], index_name[FN_REFLEN],
data_name[FN_REFLEN];
uchar *disk_cache, *disk_pos, *end_pos;
MI_INFO info,*m_info,*old_info;
MYISAM_SHARE share_buff,*share;
- ulong rec_per_key_part[HA_MAX_POSSIBLE_KEY*HA_MAX_KEY_SEG];
- my_off_t key_root[HA_MAX_POSSIBLE_KEY],key_del[MI_MAX_KEY_BLOCK_SIZE];
+ ulong *rec_per_key_part= 0;
+ my_off_t *key_root, *key_del;
ulonglong max_key_file_length, max_data_file_length;
DBUG_ENTER("mi_open");
@@ -111,9 +111,6 @@ MI_INFO *mi_open(const char *name, int mode, uint open_flags)
{
share= &share_buff;
bzero((uchar*) &share_buff,sizeof(share_buff));
- share_buff.state.rec_per_key_part=rec_per_key_part;
- share_buff.state.key_root=key_root;
- share_buff.state.key_del=key_del;
share_buff.key_cache= multi_key_cache_search((uchar*) name_buff,
strlen(name_buff),
dflt_key_cache);
@@ -203,7 +200,7 @@ MI_INFO *mi_open(const char *name, int mode, uint open_flags)
keys= (uint) share->state.header.keys;
uniques= (uint) share->state.header.uniques;
fulltext_keys= (uint) share->state.header.fulltext_keys;
- key_parts= mi_uint2korr(share->state.header.key_parts);
+ base_key_parts= key_parts= mi_uint2korr(share->state.header.key_parts);
unique_key_parts= mi_uint2korr(share->state.header.unique_key_parts);
if (len != MI_STATE_INFO_SIZE)
{
@@ -213,7 +210,12 @@ MI_INFO *mi_open(const char *name, int mode, uint open_flags)
}
share->state_diff_length=len-MI_STATE_INFO_SIZE;
- mi_state_info_read(disk_cache, &share->state);
+ if (!mi_state_info_read(disk_cache, &share->state))
+ goto err;
+ rec_per_key_part= share->state.rec_per_key_part;
+ key_root= share->state.key_root;
+ key_del= share->state.key_del;
+
len= mi_uint2korr(share->state.header.base_info_length);
if (len != MI_BASE_INFO_SIZE)
{
@@ -296,7 +298,8 @@ MI_INFO *mi_open(const char *name, int mode, uint open_flags)
if (!my_multi_malloc(MY_WME,
&share,sizeof(*share),
- &share->state.rec_per_key_part,sizeof(long)*key_parts,
+ &share->state.rec_per_key_part,
+ sizeof(long)*base_key_parts,
&share->keyinfo,keys*sizeof(MI_KEYDEF),
&share->uniqueinfo,uniques*sizeof(MI_UNIQUEDEF),
&share->keyparts,
@@ -320,7 +323,7 @@ MI_INFO *mi_open(const char *name, int mode, uint open_flags)
errpos=4;
*share=share_buff;
memcpy((char*) share->state.rec_per_key_part,
- (char*) rec_per_key_part, sizeof(long)*key_parts);
+ (char*) rec_per_key_part, sizeof(long)*base_key_parts);
memcpy((char*) share->state.key_root,
(char*) key_root, sizeof(my_off_t)*keys);
memcpy((char*) share->state.key_del,
@@ -666,6 +669,7 @@ MI_INFO *mi_open(const char *name, int mode, uint open_flags)
pthread_mutex_unlock(&THR_LOCK_myisam);
bzero(info.buff, share->base.max_key_block_length * 2);
+ my_free(rec_per_key_part, MYF(MY_ALLOW_ZERO_PTR));
if (myisam_log_file >= 0)
{
@@ -695,6 +699,7 @@ err:
case 3:
if (! lock_error)
VOID(my_lock(kfile, F_UNLCK, 0L, F_TO_EOF, MYF(MY_SEEK_NOT_DONE)));
+ my_free(rec_per_key_part, MYF(MY_ALLOW_ZERO_PTR));
/* fall through */
case 2:
my_afree(disk_cache);
@@ -982,6 +987,16 @@ uchar *mi_state_info_read(uchar *ptr, MI_STATE_INFO *state)
ptr+= state->state_diff_length;
+ if (!state->rec_per_key_part)
+ {
+ if (!my_multi_malloc(MY_WME,
+ &state->rec_per_key_part,sizeof(long)*key_parts,
+ &state->key_root, keys*sizeof(my_off_t),
+ &state->key_del, key_blocks*sizeof(my_off_t),
+ NullS))
+ return(0);
+ }
+
for (i=0; i < keys; i++)
{
state->key_root[i]= mi_sizekorr(ptr); ptr +=8;
diff --git a/storage/myisam/mi_rkey.c b/storage/myisam/mi_rkey.c
index f20b0366683..da757d270bc 100644
--- a/storage/myisam/mi_rkey.c
+++ b/storage/myisam/mi_rkey.c
@@ -29,6 +29,7 @@ int mi_rkey(MI_INFO *info, uchar *buf, int inx, const uchar *key,
MI_KEYDEF *keyinfo;
HA_KEYSEG *last_used_keyseg;
uint pack_key_length, use_key_length, nextflag;
+ ICP_RESULT res= ICP_NO_MATCH;
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));
@@ -105,55 +106,62 @@ int mi_rkey(MI_INFO *info, uchar *buf, int inx, const uchar *key,
saved the current data_file_length. Concurrent inserts always go
to the end of the file. So we can test if the found key
references a new record.
+
+ If we are 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.
+
+ We do also continue searching if an index condition check function
+ is available.
*/
- if (info->lastpos >= info->state->data_file_length)
+ 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)) == ICP_NO_MATCH))
{
- /* The key references a concurrently inserted record. */
+ 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 &&
- last_used_keyseg == keyinfo->seg + keyinfo->keysegs)
+ ha_key_cmp(keyinfo->seg, key_buff, info->lastkey, use_key_length,
+ SEARCH_FIND, not_used))
{
- /* Simply ignore the key if it matches exactly. (Bug #29838) */
my_errno= HA_ERR_KEY_NOT_FOUND;
info->lastpos= HA_OFFSET_ERROR;
+ break;
}
- else
- {
- /*
- If 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.
- */
- 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 a 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; /* purecov: inspected */
- /*
- 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))
- {
- /* purecov: begin inspected */
- my_errno= HA_ERR_KEY_NOT_FOUND;
- info->lastpos= HA_OFFSET_ERROR;
- break;
- /* purecov: end */
- }
- } while (info->lastpos >= info->state->data_file_length);
- }
+ }
+ if (res == ICP_OUT_OF_RANGE)
+ {
+ 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));
+ }
+ /*
+ Error if no row found within the data file. (Bug #29838)
+ Do not overwrite my_errno if already at HA_OFFSET_ERROR.
+ */
+ if (info->lastpos != HA_OFFSET_ERROR &&
+ info->lastpos >= info->state->data_file_length)
+ {
+ info->lastpos= HA_OFFSET_ERROR;
+ my_errno= HA_ERR_KEY_NOT_FOUND;
}
}
}
diff --git a/storage/myisam/mi_rnext.c b/storage/myisam/mi_rnext.c
index b9bbda3cacb..b1c261dd6b1 100644
--- a/storage/myisam/mi_rnext.c
+++ b/storage/myisam/mi_rnext.c
@@ -28,6 +28,7 @@ int mi_rnext(MI_INFO *info, uchar *buf, int inx)
{
int error,changed;
uint flag;
+ ICP_RESULT res= 0;
uint update_mask= HA_STATE_NEXT_FOUND;
DBUG_ENTER("mi_rnext");
@@ -96,23 +97,36 @@ int mi_rnext(MI_INFO *info, uchar *buf, int inx)
}
}
- if (info->s->concurrent_insert)
+ if (!error)
{
- if (!error)
+ while ((info->s->concurrent_insert &&
+ info->lastpos >= info->state->data_file_length) ||
+ (info->index_cond_func &&
+ (res= mi_check_index_cond(info, inx, buf)) == ICP_NO_MATCH))
{
- while (info->lastpos >= info->state->data_file_length)
- {
- /* Skip rows inserted by other threads since we got a lock */
- if ((error=_mi_search_next(info,info->s->keyinfo+inx,
- info->lastkey,
- info->lastkey_length,
- SEARCH_BIGGER,
- info->s->state.key_root[inx])))
- break;
- }
+ /*
+ Skip rows that are either inserted by other threads since
+ we got a lock or do not match pushed index conditions
+ */
+ if ((error=_mi_search_next(info,info->s->keyinfo+inx,
+ info->lastkey,
+ info->lastkey_length,
+ SEARCH_BIGGER,
+ info->s->state.key_root[inx])))
+ break;
+ }
+ if (!error && res == ICP_OUT_OF_RANGE)
+ {
+ if (info->s->concurrent_insert)
+ rw_unlock(&info->s->key_root_lock[inx]);
+ info->lastpos= HA_OFFSET_ERROR;
+ DBUG_RETURN(my_errno= HA_ERR_END_OF_FILE);
}
- rw_unlock(&info->s->key_root_lock[inx]);
}
+
+ if (info->s->concurrent_insert)
+ rw_unlock(&info->s->key_root_lock[inx]);
+
/* Don't clear if database-changed */
info->update&= (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED);
info->update|= update_mask;
diff --git a/storage/myisam/mi_rnext_same.c b/storage/myisam/mi_rnext_same.c
index 1892fe3e1e0..df3b678d663 100644
--- a/storage/myisam/mi_rnext_same.c
+++ b/storage/myisam/mi_rnext_same.c
@@ -75,8 +75,13 @@ int mi_rnext_same(MI_INFO *info, uchar *buf)
info->lastpos= HA_OFFSET_ERROR;
break;
}
- /* Skip rows that are inserted by other threads since we got a lock */
- if (info->lastpos < info->state->data_file_length)
+ /*
+ Skip
+ - rows that are inserted by other threads since we got a lock
+ - rows that don't match index condition */
+ if (info->lastpos < info->state->data_file_length &&
+ (!info->index_cond_func ||
+ mi_check_index_cond(info, inx, buf) != ICP_NO_MATCH))
break;
}
}
diff --git a/storage/myisam/mi_rprev.c b/storage/myisam/mi_rprev.c
index d1407012590..93bd224bd56 100644
--- a/storage/myisam/mi_rprev.c
+++ b/storage/myisam/mi_rprev.c
@@ -51,22 +51,36 @@ int mi_rprev(MI_INFO *info, uchar *buf, int inx)
error=_mi_search(info,share->keyinfo+inx,info->lastkey,
USE_WHOLE_KEY, flag, share->state.key_root[inx]);
- if (share->concurrent_insert)
+ if (!error)
{
- if (!error)
+ int res= 0;
+ while ((share->concurrent_insert &&
+ info->lastpos >= info->state->data_file_length) ||
+ (info->index_cond_func &&
+ !(res= mi_check_index_cond(info, inx, buf))))
{
- while (info->lastpos >= info->state->data_file_length)
- {
- /* Skip rows that are inserted by other threads since we got a lock */
- if ((error=_mi_search_next(info,share->keyinfo+inx,info->lastkey,
- info->lastkey_length,
- SEARCH_SMALLER,
- share->state.key_root[inx])))
- break;
- }
+ /*
+ Skip rows that are either inserted by other threads since
+ we got a lock or do not match pushed index conditions
+ */
+ if ((error=_mi_search_next(info,share->keyinfo+inx,info->lastkey,
+ info->lastkey_length,
+ SEARCH_SMALLER,
+ share->state.key_root[inx])))
+ break;
+ }
+ if (!error && res == 2)
+ {
+ if (share->concurrent_insert)
+ rw_unlock(&share->key_root_lock[inx]);
+ info->lastpos= HA_OFFSET_ERROR;
+ DBUG_RETURN(my_errno= HA_ERR_END_OF_FILE);
}
- rw_unlock(&share->key_root_lock[inx]);
}
+
+ if (share->concurrent_insert)
+ rw_unlock(&share->key_root_lock[inx]);
+
info->update&= (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED);
info->update|= HA_STATE_PREV_FOUND;
if (error)
diff --git a/storage/myisam/mi_test_all.sh b/storage/myisam/mi_test_all.sh
index 5989d9cfaf0..c6bc686e885 100755
--- a/storage/myisam/mi_test_all.sh
+++ b/storage/myisam/mi_test_all.sh
@@ -5,6 +5,7 @@
valgrind="valgrind --alignment=8 --leak-check=yes"
silent="-s"
+rm -f test1.TMD
if test -f mi_test1$MACH ; then suffix=$MACH ; else suffix=""; fi
./mi_test1$suffix $silent
diff --git a/storage/myisam/myisamdef.h b/storage/myisam/myisamdef.h
index a0570b20b4f..20974e0b717 100644
--- a/storage/myisam/myisamdef.h
+++ b/storage/myisam/myisamdef.h
@@ -235,6 +235,7 @@ typedef struct st_mi_isam_share
rw_lock_t mmap_lock;
} MYISAM_SHARE;
+typedef ICP_RESULT (*index_cond_func_t)(void *param);
struct st_myisam_info
{
@@ -304,6 +305,8 @@ struct st_myisam_info
my_bool page_changed;
/* If info->buff has to be reread for rnext */
my_bool buff_used;
+ index_cond_func_t index_cond_func; /* Index condition function */
+ void *index_cond_func_arg; /* parameter for the func */
#ifdef THREAD
THR_LOCK_DATA lock;
#endif
@@ -726,6 +729,7 @@ my_bool mi_dynmap_file(MI_INFO *info, my_off_t size);
int mi_munmap_file(MI_INFO *info);
void mi_remap_file(MI_INFO *info, my_off_t size);
+int mi_check_index_cond(register MI_INFO *info, uint keynr, uchar *record);
/* Functions needed by mi_check */
int killed_ptr(HA_CHECK *param);
void mi_check_print_error _VARARGS((HA_CHECK *param, const char *fmt, ...));
@@ -734,6 +738,8 @@ void mi_check_print_info _VARARGS((HA_CHECK *param, const char *fmt, ...));
#ifdef THREAD
pthread_handler_t thr_find_all_keys(void *arg);
#endif
+extern void mi_set_index_cond_func(MI_INFO *info, index_cond_func_t func,
+ void *func_arg);
int flush_blocks(HA_CHECK *param, KEY_CACHE *key_cache, File file,
ulonglong *dirty_part_map);
#ifdef __cplusplus
diff --git a/storage/myisam/myisampack.c b/storage/myisam/myisampack.c
index ba3ff3cdf0d..17add155193 100644
--- a/storage/myisam/myisampack.c
+++ b/storage/myisam/myisampack.c
@@ -576,7 +576,7 @@ static int compress(PACK_MRG_INFO *mrg,char *result_table)
Create a global priority queue in preparation for making
temporary Huffman trees.
*/
- if (init_queue(&queue,256,0,0,compare_huff_elements,0))
+ if (init_queue(&queue, 256, 0, 0, compare_huff_elements, 0, 0, 0))
goto err;
/*
@@ -1511,7 +1511,7 @@ static int make_huff_tree(HUFF_TREE *huff_tree, HUFF_COUNTS *huff_counts)
if (queue.max_elements < found)
{
delete_queue(&queue);
- if (init_queue(&queue,found,0,0,compare_huff_elements,0))
+ if (init_queue(&queue,found, 0, 0, compare_huff_elements, 0, 0, 0))
return -1;
}
@@ -1615,8 +1615,7 @@ static int make_huff_tree(HUFF_TREE *huff_tree, HUFF_COUNTS *huff_counts)
Make a priority queue from the queue. Construct its index so that we
have a partially ordered tree.
*/
- for (i=found/2 ; i > 0 ; i--)
- _downheap(&queue,i);
+ queue_fix(&queue);
/* The Huffman algorithm. */
bytes_packed=0; bits_packed=0;
@@ -1627,12 +1626,9 @@ static int make_huff_tree(HUFF_TREE *huff_tree, HUFF_COUNTS *huff_counts)
Popping from a priority queue includes a re-ordering of the queue,
to get the next least incidence element to the top.
*/
- a=(HUFF_ELEMENT*) queue_remove(&queue,0);
- /*
- Copy the next least incidence element. The queue implementation
- reserves root[0] for temporary purposes. root[1] is the top.
- */
- b=(HUFF_ELEMENT*) queue.root[1];
+ a=(HUFF_ELEMENT*) queue_remove_top(&queue);
+ /* Copy the next least incidence element */
+ b=(HUFF_ELEMENT*) queue_top(&queue);
/* Get a new element from the element buffer. */
new_huff_el=huff_tree->element_buffer+found+i;
/* The new element gets the sum of the two least incidence elements. */
@@ -1654,8 +1650,8 @@ static int make_huff_tree(HUFF_TREE *huff_tree, HUFF_COUNTS *huff_counts)
Replace the copied top element by the new element and re-order the
queue.
*/
- queue.root[1]=(uchar*) new_huff_el;
- queue_replaced(&queue);
+ queue_top(&queue)= (uchar*) new_huff_el;
+ queue_replace_top(&queue);
}
huff_tree->root=(HUFF_ELEMENT*) queue.root[1];
huff_tree->bytes_packed=bytes_packed+(bits_packed+7)/8;
@@ -1786,8 +1782,7 @@ static my_off_t calc_packed_length(HUFF_COUNTS *huff_counts,
Make a priority queue from the queue. Construct its index so that we
have a partially ordered tree.
*/
- for (i=(found+1)/2 ; i > 0 ; i--)
- _downheap(&queue,i);
+ queue_fix(&queue);
/* The Huffman algorithm. */
for (i=0 ; i < found-1 ; i++)
@@ -1801,12 +1796,9 @@ static my_off_t calc_packed_length(HUFF_COUNTS *huff_counts,
incidence). Popping from a priority queue includes a re-ordering
of the queue, to get the next least incidence element to the top.
*/
- a= (my_off_t*) queue_remove(&queue, 0);
- /*
- Copy the next least incidence element. The queue implementation
- reserves root[0] for temporary purposes. root[1] is the top.
- */
- b= (my_off_t*) queue.root[1];
+ a= (my_off_t*) queue_remove_top(&queue);
+ /* Copy the next least incidence element. */
+ b= (my_off_t*) queue_top(&queue);
/* Create a new element in a local (automatic) buffer. */
new_huff_el= element_buffer + i;
/* The new element gets the sum of the two least incidence elements. */
@@ -1826,8 +1818,8 @@ static my_off_t calc_packed_length(HUFF_COUNTS *huff_counts,
queue. This successively replaces the references to counts by
references to HUFF_ELEMENTs.
*/
- queue.root[1]=(uchar*) new_huff_el;
- queue_replaced(&queue);
+ queue_top(&queue)= (uchar*) new_huff_el;
+ queue_replace_top(&queue);
}
DBUG_RETURN(bytes_packed+(bits_packed+7)/8);
}
diff --git a/storage/myisam/sort.c b/storage/myisam/sort.c
index fd0bd971e10..2b6d5167c93 100644
--- a/storage/myisam/sort.c
+++ b/storage/myisam/sort.c
@@ -920,7 +920,7 @@ merge_buffers(MI_SORT_PARAM *info, uint keys, IO_CACHE *from_file,
if (init_queue(&queue,(uint) (Tb-Fb)+1,offsetof(BUFFPEK,key),0,
(int (*)(void*, uchar *,uchar*)) info->key_cmp,
- (void*) info))
+ (void*) info, 0, 0))
DBUG_RETURN(1); /* purecov: inspected */
for (buffpek= Fb ; buffpek <= Tb ; buffpek++)
@@ -969,7 +969,7 @@ merge_buffers(MI_SORT_PARAM *info, uint keys, IO_CACHE *from_file,
uchar *base= buffpek->base;
uint max_keys=buffpek->max_keys;
- VOID(queue_remove(&queue,0));
+ VOID(queue_remove_top(&queue));
/* Put room used by buffer to use in other buffer */
for (refpek= (BUFFPEK**) &queue_top(&queue);
@@ -994,7 +994,7 @@ merge_buffers(MI_SORT_PARAM *info, uint keys, IO_CACHE *from_file,
}
else if (error == -1)
goto err; /* purecov: inspected */
- queue_replaced(&queue); /* Top element has been replaced */
+ queue_replace_top(&queue); /* Top element has been replaced */
}
}
buffpek=(BUFFPEK*) queue_top(&queue);
diff --git a/storage/myisammrg/myrg_queue.c b/storage/myisammrg/myrg_queue.c
index d2579053784..cf862b53b86 100644
--- a/storage/myisammrg/myrg_queue.c
+++ b/storage/myisammrg/myrg_queue.c
@@ -52,7 +52,7 @@ int _myrg_init_queue(MYRG_INFO *info,int inx,enum ha_rkey_function search_flag)
if (init_queue(q,info->tables, 0,
(myisam_readnext_vec[search_flag] == SEARCH_SMALLER),
queue_key_cmp,
- info->open_tables->table->s->keyinfo[inx].seg))
+ info->open_tables->table->s->keyinfo[inx].seg, 0, 0))
error=my_errno;
}
else
@@ -60,7 +60,7 @@ int _myrg_init_queue(MYRG_INFO *info,int inx,enum ha_rkey_function search_flag)
if (reinit_queue(q,info->tables, 0,
(myisam_readnext_vec[search_flag] == SEARCH_SMALLER),
queue_key_cmp,
- info->open_tables->table->s->keyinfo[inx].seg))
+ info->open_tables->table->s->keyinfo[inx].seg, 0, 0))
error=my_errno;
}
}
diff --git a/storage/myisammrg/myrg_rnext.c b/storage/myisammrg/myrg_rnext.c
index 82d5cbf38b1..1442ee08dd4 100644
--- a/storage/myisammrg/myrg_rnext.c
+++ b/storage/myisammrg/myrg_rnext.c
@@ -32,7 +32,7 @@ int myrg_rnext(MYRG_INFO *info, uchar *buf, int inx)
{
if (err == HA_ERR_END_OF_FILE)
{
- queue_remove(&(info->by_key),0);
+ queue_remove_top(&(info->by_key));
if (!info->by_key.elements)
return HA_ERR_END_OF_FILE;
}
@@ -43,7 +43,7 @@ int myrg_rnext(MYRG_INFO *info, uchar *buf, int inx)
{
/* Found here, adding to queue */
queue_top(&(info->by_key))=(uchar *)(info->current_table);
- queue_replaced(&(info->by_key));
+ queue_replace_top(&(info->by_key));
}
/* now, mymerge's read_next is as simple as one queue_top */
diff --git a/storage/myisammrg/myrg_rnext_same.c b/storage/myisammrg/myrg_rnext_same.c
index ad7bbfb0f6e..14b41dbe756 100644
--- a/storage/myisammrg/myrg_rnext_same.c
+++ b/storage/myisammrg/myrg_rnext_same.c
@@ -29,7 +29,7 @@ int myrg_rnext_same(MYRG_INFO *info, uchar *buf)
{
if (err == HA_ERR_END_OF_FILE)
{
- queue_remove(&(info->by_key),0);
+ queue_remove_top(&(info->by_key));
if (!info->by_key.elements)
return HA_ERR_END_OF_FILE;
}
@@ -40,7 +40,7 @@ int myrg_rnext_same(MYRG_INFO *info, uchar *buf)
{
/* Found here, adding to queue */
queue_top(&(info->by_key))=(uchar *)(info->current_table);
- queue_replaced(&(info->by_key));
+ queue_replace_top(&(info->by_key));
}
/* now, mymerge's read_next is as simple as one queue_top */
diff --git a/storage/myisammrg/myrg_rprev.c b/storage/myisammrg/myrg_rprev.c
index 66c94974940..0c560a0b73d 100644
--- a/storage/myisammrg/myrg_rprev.c
+++ b/storage/myisammrg/myrg_rprev.c
@@ -32,7 +32,7 @@ int myrg_rprev(MYRG_INFO *info, uchar *buf, int inx)
{
if (err == HA_ERR_END_OF_FILE)
{
- queue_remove(&(info->by_key),0);
+ queue_remove_top(&(info->by_key));
if (!info->by_key.elements)
return HA_ERR_END_OF_FILE;
}
@@ -43,7 +43,7 @@ int myrg_rprev(MYRG_INFO *info, uchar *buf, int inx)
{
/* Found here, adding to queue */
queue_top(&(info->by_key))=(uchar *)(info->current_table);
- queue_replaced(&(info->by_key));
+ queue_replace_top(&(info->by_key));
}
/* now, mymerge's read_prev is as simple as one queue_top */
diff --git a/storage/oqgraph/ha_oqgraph.cc b/storage/oqgraph/ha_oqgraph.cc
index cf9ef3d8997..fa506363a51 100644
--- a/storage/oqgraph/ha_oqgraph.cc
+++ b/storage/oqgraph/ha_oqgraph.cc
@@ -766,7 +766,7 @@ int ha_oqgraph::fill_record(byte *record, const open_query::row &row)
if (row.weight_indicator)
{
field[3]->set_notnull();
- field[3]->store((double) row.weight);
+ field[3]->store((double) row.weight, 0);
}
if (row.seq_indicator)
diff --git a/storage/xtradb/handler/ha_innodb.cc b/storage/xtradb/handler/ha_innodb.cc
index ee060f61c75..42b818c41e1 100644
--- a/storage/xtradb/handler/ha_innodb.cc
+++ b/storage/xtradb/handler/ha_innodb.cc
@@ -125,6 +125,12 @@ static pthread_cond_t commit_cond;
static pthread_mutex_t commit_cond_m;
static bool innodb_inited = 0;
+C_MODE_START
+static int index_cond_func_innodb(void *arg);
+C_MODE_END
+
+
+
#define INSIDE_HA_INNOBASE_CC
/* In the Windows plugin, the return value of current_thd is
@@ -3229,7 +3235,7 @@ ha_innobase::index_flags(
const
{
return(HA_READ_NEXT | HA_READ_PREV | HA_READ_ORDER
- | HA_READ_RANGE | HA_KEYREAD_ONLY);
+ | HA_READ_RANGE | HA_KEYREAD_ONLY | HA_DO_INDEX_COND_PUSHDOWN);
}
/****************************************************************//**
@@ -4619,8 +4625,9 @@ build_template(
THD* thd, /*!< in: current user thread, used
only if templ_type is
ROW_MYSQL_REC_FIELDS */
- TABLE* table, /*!< in: MySQL table */
- uint templ_type) /*!< in: ROW_MYSQL_WHOLE_ROW or
+ 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 */
{
dict_index_t* index;
@@ -4634,7 +4641,9 @@ build_template(
ulint sql_idx, innodb_idx=0;
/* 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
@@ -4704,6 +4713,16 @@ build_template(
prebuilt->templ_contains_blob = FALSE;
+
+ /*
+ Setup index condition pushdown (note: we don't need to check if
+ this is a scan on primary key as that is checked in idx_cond_push)
+ */
+ if (file->active_index == file->pushed_idx_cond_keyno &&
+ file->active_index != MAX_KEY &&
+ templ_type == ROW_MYSQL_REC_FIELDS)
+ do_idx_cond_push= need_second_pass= TRUE;
+
/* Note that in InnoDB, i is the column number. MySQL calls columns
'fields'. */
for (sql_idx = 0; sql_idx < n_fields; sql_idx++) {
@@ -4717,6 +4736,8 @@ build_template(
and which we can skip. */
register const ibool index_contains_field =
dict_index_contains_col_or_prefix(index, innodb_idx);
+ 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
@@ -4749,8 +4770,12 @@ build_template(
/* 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 = innodb_idx;
@@ -4805,6 +4830,13 @@ include_field:
prebuilt->templ_contains_blob = TRUE;
}
skip_field:
+ if (need_second_pass && (sql_idx+1 == n_fields))
+ {
+ prebuilt->n_index_fields= n_requested_fields;
+ need_second_pass= FALSE;
+ sql_idx= (~(ulint)0); /* to start from 0 */
+ innodb_idx= (~(ulint)0); /* to start from 0 */ ///psergey-merge-merge-last-change
+ }
if (field->stored_in_db) {
innodb_idx++;
}
@@ -4813,12 +4845,23 @@ skip_field:
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;
+ }
+
if (index != clust_index && prebuilt->need_to_access_clustered) {
/* Change rec_field_no's to correspond to the clustered index
record */
- for (ulint i = 0; i < n_requested_fields; i++) {
+ for (ulint i = do_idx_cond_push? prebuilt->n_index_fields : 0;
+ i < n_requested_fields; i++) {
templ = prebuilt->mysql_template + i;
-
templ->rec_field_no = templ->clust_rec_field_no;
}
}
@@ -5081,7 +5124,7 @@ no_commit:
/* 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);
@@ -5620,6 +5663,8 @@ ha_innobase::index_end(void)
int error = 0;
DBUG_ENTER("index_end");
active_index=MAX_KEY;
+ in_range_check_pushed_down= FALSE;
+ ds_mrr.dsmrr_close();
DBUG_RETURN(error);
}
@@ -5780,7 +5825,8 @@ ha_innobase::index_read(
necessarily prebuilt->index, but can also be the clustered index */
if (prebuilt->sql_stat_start) {
- build_template(prebuilt, user_thd, table, ROW_MYSQL_REC_FIELDS);
+ build_template(prebuilt, user_thd, table, this,
+ ROW_MYSQL_REC_FIELDS);
}
if (key_ptr) {
@@ -5990,7 +6036,7 @@ ha_innobase::change_active_index(
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);
}
@@ -8118,6 +8164,7 @@ ha_innobase::info_low(
}
stats.check_time = 0;
+ stats.mrr_length_per_rec= ref_length + 8; // 8 = max(sizeof(void *));
if (stats.records == 0) {
stats.mean_rec_length = 0;
@@ -8321,7 +8368,7 @@ ha_innobase::check(
/* 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);
}
if (prebuilt->table->ibd_file_missing) {
@@ -8814,6 +8861,10 @@ ha_innobase::extra(
break;
case HA_EXTRA_RESET_STATE:
reset_template(prebuilt);
+ /* Reset index condition pushdown state */
+ pushed_idx_cond= FALSE;
+ pushed_idx_cond_keyno= MAX_KEY;
+ prebuilt->idx_cond_func= NULL;
break;
case HA_EXTRA_NO_KEYREAD:
prebuilt->read_just_key = 0;
@@ -8861,6 +8912,12 @@ ha_innobase::reset()
reset_template(prebuilt);
+ /* Reset index condition pushdown state */
+ pushed_idx_cond_keyno= MAX_KEY;
+ pushed_idx_cond= NULL;
+ ds_mrr.dsmrr_close();
+ prebuilt->idx_cond_func= NULL;
+
/* TODO: This should really be reset in reset_template() but for now
it's safer to do it explicitly here. */
@@ -11973,3 +12030,91 @@ test_innobase_convert_name()
}
#endif /* UNIV_COMPILE_TEST_FUNCS */
+
+
+/****************************************************************************
+ * 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, seq, seq_init_param, n_ranges, mode, buf);
+}
+
+int ha_innobase::multi_range_read_next(char **range_info)
+{
+ return ds_mrr.dsmrr_next(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)
+{
+ /* See comments in ha_myisam::multi_range_read_info_const */
+ ds_mrr.init(this, table);
+
+ if (prebuilt->select_lock_type != LOCK_NONE)
+ *flags |= HA_MRR_USE_DEFAULT_IMPL;
+
+ ha_rows res= ds_mrr.dsmrr_info_const(keyno, seq, seq_init_param, n_ranges,
+ bufsz, flags, cost);
+ return res;
+}
+
+ha_rows ha_innobase::multi_range_read_info(uint keyno, uint n_ranges, uint keys,
+ uint key_parts, uint *bufsz,
+ uint *flags, COST_VECT *cost)
+{
+ ds_mrr.init(this, table);
+ ha_rows res= ds_mrr.dsmrr_info(keyno, n_ranges, keys, key_parts, bufsz,
+ flags, cost);
+ return res;
+}
+
+
+
+/**
+ * Index Condition Pushdown interface implementation
+ */
+
+C_MODE_START
+
+/*
+ Index condition check function to be called from within Innobase.
+ See note on ICP_RESULT for return values description.
+*/
+
+static int index_cond_func_innodb(void *arg)
+{
+ ha_innobase *h= (ha_innobase*)arg;
+ if (h->end_range)
+ {
+ if (h->compare_key2(h->end_range) > 0)
+ return ICP_OUT_OF_RANGE; /* caller should return HA_ERR_END_OF_FILE already */
+ }
+ return h->pushed_idx_cond->val_int()? ICP_MATCH : ICP_NO_MATCH;
+}
+
+C_MODE_END
+
+
+Item *ha_innobase::idx_cond_push(uint keyno_arg, Item* idx_cond_arg)
+{
+ if ((keyno_arg != primary_key) && (prebuilt->select_lock_type == LOCK_NONE))
+ {
+ pushed_idx_cond_keyno= keyno_arg;
+ pushed_idx_cond= idx_cond_arg;
+ in_range_check_pushed_down= TRUE;
+ return NULL; /* Table handler will check the entire condition */
+ }
+ return idx_cond_arg; /* Table handler will not make any checks */
+}
+
diff --git a/storage/xtradb/handler/ha_innodb.h b/storage/xtradb/handler/ha_innodb.h
index c60a5eae19e..8945ce95ee5 100644
--- a/storage/xtradb/handler/ha_innodb.h
+++ b/storage/xtradb/handler/ha_innodb.h
@@ -222,6 +222,24 @@ class ha_innobase: public handler
/** @} */
bool check_if_incompatible_data(HA_CREATE_INFO *info,
uint table_changes);
+ bool check_if_supported_virtual_columns(void) { return TRUE; }
+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);
+ ha_rows multi_range_read_info(uint keyno, uint n_ranges, uint keys,
+ uint key_parts, uint *bufsz,
+ uint *flags, COST_VECT *cost);
+ DsMrr_impl ds_mrr;
+
+ Item *idx_cond_push(uint keyno, Item* idx_cond);
};
/* Some accessor functions which the InnoDB plugin needs, but which
diff --git a/storage/xtradb/include/row0mysql.h b/storage/xtradb/include/row0mysql.h
index 141f4beb81e..fbb9ee92a90 100644
--- a/storage/xtradb/include/row0mysql.h
+++ b/storage/xtradb/include/row0mysql.h
@@ -577,7 +577,9 @@ struct mysql_row_templ_struct {
#define ROW_PREBUILT_ALLOCATED 78540783
#define ROW_PREBUILT_FREED 26423527
+typedef int (*index_cond_func_t)(void *param);
/** A struct for (sometimes lazily) prebuilt structures in an Innobase table
+
handle used within MySQL; these are used to save CPU time. */
struct row_prebuilt_struct {
@@ -775,6 +777,14 @@ struct row_prebuilt_struct {
/*----------------------*/
ulint magic_n2; /*!< this should be the same as
magic_n */
+ /*----------------------*/
+ index_cond_func_t idx_cond_func;/* Index Condition Pushdown function,
+ or NULL if there is none set */
+ void* idx_cond_func_arg;/* ICP function argument */
+ ulint n_index_fields; /* Number of fields at the start of
+ mysql_template. Valid only when using
+ ICP. */
+ /*----------------------*/
};
#define ROW_PREBUILT_FETCH_MAGIC_N 465765687
diff --git a/storage/xtradb/row/row0sel.c b/storage/xtradb/row/row0sel.c
index 82ad30bb390..4c6a91f3c69 100644
--- a/storage/xtradb/row/row0sel.c
+++ b/storage/xtradb/row/row0sel.c
@@ -2680,8 +2680,10 @@ row_sel_store_mysql_rec(
ibool rec_clust, /*!< in: TRUE if rec is in the
clustered index instead of
prebuilt->index */
- const ulint* offsets) /*!< in: array returned by
- rec_get_offsets(rec) */
+ const ulint* offsets, /* in: array returned by
+ rec_get_offsets() */
+ ulint start_field_no, /* in: start from this field */
+ ulint end_field_no) /* in: end at this field */
{
mem_heap_t* extern_field_heap = NULL;
mem_heap_t* heap;
@@ -2697,7 +2699,7 @@ row_sel_store_mysql_rec(
prebuilt->blob_heap = NULL;
}
- for (i = 0; i < prebuilt->n_template; i++) {
+ for (i = start_field_no; i < end_field_no /* prebuilt->n_template */ ; i++) {
const mysql_row_templ_t*templ = prebuilt->mysql_template + i;
const byte* data;
@@ -3142,10 +3144,14 @@ row_sel_pop_cached_row_for_mysql(
/* Copy NULL bit of the current field from cached_rec
to buf */
if (templ->mysql_null_bit_mask) {
- buf[templ->mysql_null_byte_offset]
+ /*buf[templ->mysql_null_byte_offset]
^= (buf[templ->mysql_null_byte_offset]
^ cached_rec[templ->mysql_null_byte_offset])
- & (byte)templ->mysql_null_bit_mask;
+ & (byte)templ->mysql_null_bit_mask;*/
+ byte *null_byte= buf + templ->mysql_null_byte_offset;
+ (*null_byte)&= ~templ->mysql_null_bit_mask;
+ (*null_byte)|= cached_rec[templ->mysql_null_byte_offset] &
+ templ->mysql_null_bit_mask;
}
}
}
@@ -3182,7 +3188,10 @@ row_sel_push_cache_row_for_mysql(
ibool rec_clust, /*!< in: TRUE if rec is in the
clustered index instead of
prebuilt->index */
- const ulint* offsets) /*!< in: rec_get_offsets(rec) */
+ const ulint* offsets, /* in: rec_get_offsets() */
+ ulint start_field_no, /* in: start from this field */
+ byte* remainder_buf) /* in: if start_field_no !=0,
+ where to take prev fields */
{
byte* buf;
ulint i;
@@ -3216,12 +3225,44 @@ row_sel_push_cache_row_for_mysql(
prebuilt->mysql_row_len);
if (UNIV_UNLIKELY(!row_sel_store_mysql_rec(
- prebuilt->fetch_cache[
+ prebuilt->fetch_cache[
prebuilt->n_fetch_cached],
- prebuilt, rec, rec_clust, offsets))) {
+ prebuilt,
+ rec,
+ rec_clust,
+ offsets,
+ start_field_no,
+ prebuilt->n_template))) {
return(FALSE);
}
+ if (start_field_no) {
+
+ for (i=0; i < start_field_no; i++) {
+ register ulint offs;
+ mysql_row_templ_t* templ;
+ register byte * null_byte;
+
+ templ = prebuilt->mysql_template + i;
+
+ if (templ->mysql_null_bit_mask) {
+ offs = templ->mysql_null_byte_offset;
+
+ null_byte= prebuilt->fetch_cache[
+ prebuilt->n_fetch_cached]+offs;
+ (*null_byte)&= ~templ->mysql_null_bit_mask;
+ (*null_byte)|= (*(remainder_buf + offs) &
+ templ->mysql_null_bit_mask);
+ }
+
+ offs = templ->mysql_col_offset;
+ memcpy(prebuilt->fetch_cache[prebuilt->n_fetch_cached]
+ + offs,
+ remainder_buf + offs,
+ templ->mysql_col_len);
+ }
+ }
+
prebuilt->n_fetch_cached++;
return(TRUE);
}
@@ -3361,8 +3402,10 @@ row_search_for_mysql(
mem_heap_t* heap = NULL;
ulint offsets_[REC_OFFS_NORMAL_SIZE];
ulint* offsets = offsets_;
+ ibool some_fields_in_buffer;
ibool table_lock_waited = FALSE;
ibool problematic_use = FALSE;
+ ibool get_clust_rec = 0;
rec_offs_init(offsets_);
@@ -3626,8 +3669,9 @@ row_search_for_mysql(
ut_ad(!rec_get_deleted_flag(rec, comp));
if (!row_sel_store_mysql_rec(buf, prebuilt,
- rec, FALSE,
- offsets)) {
+ rec, FALSE,
+ offsets, 0,
+ prebuilt->n_template)) {
/* Only fresh inserts may contain
incomplete externally stored
columns. Pretend that such
@@ -4289,7 +4333,8 @@ no_gap_lock:
if (!lock_sec_rec_cons_read_sees(
rec, trx->read_view)) {
- goto requires_clust_rec;
+ get_clust_rec = TRUE;
+ goto idx_cond_check;
}
}
}
@@ -4334,12 +4379,36 @@ no_gap_lock:
goto next_rec;
}
+
+idx_cond_check:
+ if (prebuilt->idx_cond_func) {
+ int res;
+ ibool ib_res;
+ ut_ad(prebuilt->template_type != ROW_MYSQL_DUMMY_TEMPLATE);
+ offsets = rec_get_offsets(rec, index, offsets, ULINT_UNDEFINED, &heap);
+ ib_res= row_sel_store_mysql_rec(buf, prebuilt, rec, FALSE,
+ offsets, 0, prebuilt->n_index_fields);
+ /*
+ The above call will fail and return FALSE when requested to
+ store an "externally stored column" (afaiu, a blob). Index
+ Condition Pushdown is not supported for indexes with blob
+ columns, so we should never get this error.
+ */
+ ut_ad(ib_res);
+ res= prebuilt->idx_cond_func(prebuilt->idx_cond_func_arg);
+ if (res == 0)
+ goto next_rec;
+ if (res == 2) {
+ err = DB_RECORD_NOT_FOUND;
+ goto idx_cond_failed;
+ }
+ }
+
/* Get the clustered index record if needed, if we did not do the
search using the clustered index. */
+ if (get_clust_rec || (index != clust_index
+ && prebuilt->need_to_access_clustered)) {
- if (index != clust_index && prebuilt->need_to_access_clustered) {
-
-requires_clust_rec:
/* We use a 'goto' to the preceding label if a consistent
read of a secondary index record requires us to look up old
versions of the associated clustered index record. */
@@ -4435,10 +4504,15 @@ requires_clust_rec:
are BLOBs in the fields to be fetched. In HANDLER we do
not cache rows because there the cursor is a scrollable
cursor. */
+ some_fields_in_buffer = (index != clust_index
+ && prebuilt->idx_cond_func);
if (!row_sel_push_cache_row_for_mysql(prebuilt, result_rec,
result_rec != rec,
- offsets)) {
+ offsets,
+ some_fields_in_buffer?
+ prebuilt->n_index_fields : 0,
+ buf)) {
/* Only fresh inserts may contain incomplete
externally stored columns. Pretend that such
records do not exist. Such records may only be
@@ -4479,7 +4553,10 @@ requires_clust_rec:
if (!row_sel_store_mysql_rec(buf, prebuilt, result_rec,
result_rec != rec,
- offsets)) {
+ offsets,
+ prebuilt->idx_cond_func?
+ prebuilt->n_index_fields: 0,
+ prebuilt->n_template)) {
/* Only fresh inserts may contain
incomplete externally stored
columns. Pretend that such records do
@@ -4515,6 +4592,9 @@ got_row:
HANDLER command where the user can move the cursor with PREV or NEXT
even after a unique search. */
+ err = DB_SUCCESS;
+
+idx_cond_failed:
if (!unique_search_from_clust_index
|| prebuilt->select_lock_type != LOCK_NONE
|| prebuilt->used_in_HANDLER) {
@@ -4524,12 +4604,11 @@ got_row:
btr_pcur_store_position(pcur, &mtr);
}
- err = DB_SUCCESS;
-
goto normal_return;
next_rec:
/* Reset the old and new "did semi-consistent read" flags. */
+ get_clust_rec = FALSE;
if (UNIV_UNLIKELY(prebuilt->row_read_type
== ROW_READ_DID_SEMI_CONSISTENT)) {
prebuilt->row_read_type = ROW_READ_TRY_SEMI_CONSISTENT;