diff --git a/include/mhash.h b/include/mhash.h index 722a9dffc9e973c038a056336a9524595200e07d..e6bfc1943896cc35fffa1589125b72cda194803f 100644 --- a/include/mhash.h +++ b/include/mhash.h @@ -170,6 +170,32 @@ _mh(next_slot)(mh_int_t slot, mh_int_t inc, mh_int_t size) return slot >= size ? slot - size : slot; } +#if defined(mh_hash_key) && defined(mh_eq_key) +/** + * If it is necessary to search by something different + * than a hash node, define mh_hash_key and mh_eq_key + * and use mh_find(). + */ +static inline mh_int_t +_mh(find)(struct _mh(t) *h, mh_key_t key, mh_arg_t arg) +{ + (void) arg; + + mh_int_t k = mh_hash_key(key, arg); + mh_int_t i = k % h->n_buckets; + mh_int_t inc = 1 + k % (h->n_buckets - 1); + for (;;) { + if ((mh_exist(h, i) && mh_eq_key(key, mh_node(h, i), arg))) + return i; + + if (!mh_dirty(h, i)) + return h->n_buckets; + + i = _mh(next_slot)(i, inc, h->n_buckets); + } +} +#endif + static inline mh_int_t _mh(get)(struct _mh(t) *h, const mh_node_t *node, mh_arg_t arg) @@ -504,9 +530,12 @@ _mh(dump)(struct _mh(t) *h) #undef mh_int_t #undef mh_node_t #undef mh_arg_t +#undef mh_key_t #undef mh_name #undef mh_hash +#undef mh_hash_key #undef mh_eq +#undef mh_eq_key #undef mh_node #undef mh_dirty #undef mh_place diff --git a/src/box/hash_index.cc b/src/box/hash_index.cc index bb88876104dabd550e556dc97310c359412fdd51..4b726caf25bef92cb292909768e972de1ca73bba 100644 --- a/src/box/hash_index.cc +++ b/src/box/hash_index.cc @@ -32,75 +32,125 @@ #include "pickle.h" #include "exception.h" #include "space.h" -#include "assoc.h" #include "errinj.h" -/* {{{ HashIndex Iterators ****************************************/ - -struct hash_i32_iterator { - struct iterator base; /* Must be the first member. */ - struct mh_i32ptr_t *hash; - mh_int_t h_pos; -}; - -struct hash_i64_iterator { - struct iterator base; - struct mh_i64ptr_t *hash; - mh_int_t h_pos; -}; +#include "third_party/PMurHash.h" -struct hash_lstr_iterator { - struct iterator base; - struct mh_lstrptr_t *hash; - mh_int_t h_pos; +enum { + HASH_SEED = 13U }; -void -hash_iterator_free(struct iterator *iterator) +static inline bool +mh_index_eq(struct tuple *const *tuple_a, struct tuple *const *tuple_b, + const struct key_def *key_def) { - assert(iterator->free == hash_iterator_free); - free(iterator); + return tuple_compare(*tuple_a, *tuple_b, key_def) == 0; } -struct tuple * -hash_iterator_i32_ge(struct iterator *ptr) +static inline bool +mh_index_eq_key(const char *key, struct tuple *const *tuple, + const struct key_def *key_def) { - assert(ptr->free == hash_iterator_free); - struct hash_i32_iterator *it = (struct hash_i32_iterator *) ptr; + return tuple_compare_with_key(*tuple, key, key_def->part_count, + key_def) == 0; +} - while (it->h_pos < mh_end(it->hash)) { - if (mh_exist(it->hash, it->h_pos)) - return (struct tuple *) mh_i32ptr_node(it->hash, it->h_pos++)->val; - it->h_pos++; + +static inline uint32_t +mh_index_hash(struct tuple *const *tuple, const struct key_def *key_def) +{ + struct key_part *part = key_def->parts; + uint32_t size; + /* + * Speed up the simplest case when we have a + * single-part hash over an integer field. + */ + if (key_def->part_count == 1 && part->type == NUM) + return *(uint32_t *) tuple_field(*tuple, part->fieldno, &size); + + uint32_t h = HASH_SEED; + uint32_t carry = 0; + uint32_t total_size = 0; + + for ( ; part < key_def->parts + key_def->part_count; part++) { + const char *field = tuple_field(*tuple, part->fieldno, &size); + assert(size < INT32_MAX); + PMurHash32_Process(&h, &carry, field, size); + total_size += size; } - return NULL; + + return PMurHash32_Result(h, carry, total_size); } -struct tuple * -hash_iterator_i64_ge(struct iterator *ptr) +static inline uint32_t +mh_index_hash_key(const char *key, const struct key_def *key_def) { - assert(ptr->free == hash_iterator_free); - struct hash_i64_iterator *it = (struct hash_i64_iterator *) ptr; + struct key_part *part = key_def->parts; - while (it->h_pos < mh_end(it->hash)) { - if (mh_exist(it->hash, it->h_pos)) - return (struct tuple *) mh_i64ptr_node( - it->hash,it->h_pos++)->val; - it->h_pos++; + if (key_def->part_count == 1 && part->type == NUM) { + (void) load_varint32(&key); + return *(uint32_t *) key; } - return NULL; + uint32_t h = HASH_SEED; + uint32_t carry = 0; + uint32_t total_size = 0; + + for ( ; part < key_def->parts + key_def->part_count; part++) { + uint32_t size = load_varint32(&key); + if (part->type == NUM64 && size == sizeof(uint32_t)) { + /* Allow search in NUM64 indexes using NUM keys. */ + uint64_t u64 = *(uint32_t *) key; + PMurHash32_Process(&h, &carry, &u64, sizeof(uint64_t)); + total_size += sizeof(uint64_t); + } else { + assert(size < INT32_MAX); + PMurHash32_Process(&h, &carry, key, size); + total_size += size; + } + key += size; + } + + return PMurHash32_Result(h, carry, total_size); +} + +#define mh_int_t uint32_t +#define mh_arg_t const struct key_def * + +#define mh_hash(a, arg) mh_index_hash(a, arg) +#define mh_hash_key(a, arg) mh_index_hash_key(a, arg) +#define mh_eq(a, b, arg) mh_index_eq(a, b, arg) +#define mh_eq_key(a, b, arg) mh_index_eq_key(a, b, arg) + +#define mh_key_t const char * +typedef struct tuple * mh_node_t; +#define mh_name _index +#define MH_SOURCE 1 +#include <mhash.h> + +/* {{{ HashIndex Iterators ****************************************/ + +struct hash_iterator { + struct iterator base; /* Must be the first member. */ + struct mh_index_t *hash; + uint32_t h_pos; +}; + +void +hash_iterator_free(struct iterator *iterator) +{ + assert(iterator->free == hash_iterator_free); + free(iterator); } struct tuple * -hash_iterator_lstr_ge(struct iterator *ptr) +hash_iterator_ge(struct iterator *ptr) { assert(ptr->free == hash_iterator_free); - struct hash_lstr_iterator *it = (struct hash_lstr_iterator *) ptr; + struct hash_iterator *it = (struct hash_iterator *) ptr; while (it->h_pos < mh_end(it->hash)) { if (mh_exist(it->hash, it->h_pos)) - return (struct tuple *) mh_lstrptr_node( - it->hash,it->h_pos++)->val; + return *mh_index_node(it->hash, it->h_pos++); it->h_pos++; } return NULL; @@ -113,147 +163,29 @@ hash_iterator_eq_next(struct iterator *it __attribute__((unused))) } static struct tuple * -hash_iterator_i32_eq(struct iterator *it) -{ - it->next = hash_iterator_eq_next; - return hash_iterator_i32_ge(it); -} - -static struct tuple * -hash_iterator_i64_eq(struct iterator *it) -{ - it->next = hash_iterator_eq_next; - return hash_iterator_i64_ge(it); -} - -static struct tuple * -hash_iterator_lstr_eq(struct iterator *it) +hash_iterator_eq(struct iterator *it) { it->next = hash_iterator_eq_next; - return hash_iterator_lstr_ge(it); + return hash_iterator_ge(it); } /* }}} */ /* {{{ HashIndex -- base class for all hashes. ********************/ -class Hash32Index: public HashIndex { -public: - Hash32Index(struct key_def *key_def, struct space *space); - ~Hash32Index(); - - virtual size_t - size() const; - - virtual struct tuple * - random(u32 rnd) const; - - virtual struct tuple * - findByKey(const char *key, u32 part_count) const; - - virtual struct tuple * - replace(struct tuple *old_tuple, struct tuple *new_tuple, - enum dup_replace_mode mode); - - struct iterator * - allocIterator() const; - - void - initIterator(struct iterator *iterator, enum iterator_type type, - const char *key, u32 part_count) const; - - virtual void - reserve(u32 n_tuples); -private: - struct mh_i32ptr_t *int_hash; -}; - -class Hash64Index: public HashIndex { -public: - Hash64Index(struct key_def *key_def, struct space *space); - ~Hash64Index(); - - virtual size_t - size() const; - - virtual struct tuple * - random(u32 rnd) const; - - virtual struct tuple * - findByKey(const char *key, u32 part_count) const; - - virtual struct tuple * - replace(struct tuple *old_tuple, struct tuple *new_tuple, - enum dup_replace_mode mode); - - struct iterator * - allocIterator() const; - - void - initIterator(struct iterator *iterator, enum iterator_type type, - const char *key, u32 part_count) const; - - virtual void - reserve(u32 n_tuples); -private: - struct mh_i64ptr_t *int64_hash; -}; - -class HashStrIndex: public HashIndex { -public: - HashStrIndex(struct key_def *key_def, struct space *space); - ~HashStrIndex(); - - virtual size_t - size() const; - - virtual struct tuple * - random(u32 rnd) const; - - virtual struct tuple * - findByKey(const char *key, u32 part_count) const; - - virtual struct tuple * - replace(struct tuple *old_tuple, struct tuple *new_tuple, - enum dup_replace_mode mode); - - struct iterator * - allocIterator() const; - - void - initIterator(struct iterator *iterator, enum iterator_type type, - const char *key, u32 part_count) const; - - virtual void - reserve(u32 n_tuples); -private: - struct mh_lstrptr_t *str_hash; -}; - -HashIndex * -HashIndex::factory(struct key_def *key_def, struct space *space) +HashIndex::HashIndex(struct key_def *key_def, struct space *space) + : Index(key_def, space) { - /* - * Hash index always has a single-field key. - */ - switch (key_def->parts[0].type) { - case NUM: - return new Hash32Index(key_def, space); /* 32-bit integer hash */ - case NUM64: - return new Hash64Index(key_def, space); /* 64-bit integer hash */ - case STRING: - return new HashStrIndex(key_def, space); /* string hash */ - default: - assert(false); + hash = mh_index_new(); + if (hash == NULL) { + tnt_raise(ClientError, ER_MEMORY_ISSUE, sizeof(hash), + "HashIndex", "hash"); } - - return NULL; } -HashIndex::HashIndex(struct key_def *key_def, struct space *space) - : Index(key_def, space) +HashIndex::~HashIndex() { - /* Nothing */ + mh_index_delete(hash); } void @@ -293,299 +225,84 @@ HashIndex::build(Index *pk) replace(NULL, tuple, DUP_INSERT); } -struct tuple * -HashIndex::min() const -{ - tnt_raise(ClientError, ER_UNSUPPORTED, "Hash index", "min()"); - return NULL; -} - -struct tuple * -HashIndex::max() const -{ - tnt_raise(ClientError, ER_UNSUPPORTED, "Hash index", "max()"); - return NULL; -} - - - -/* }}} */ - -/* {{{ Hash32Index ************************************************/ - -static inline struct mh_i32ptr_node_t -int32_key_to_node(const char *key) -{ - u32 key_size = load_varint32(&key); - if (key_size != 4) - tnt_raise(ClientError, ER_KEY_FIELD_TYPE, "u32"); - struct mh_i32ptr_node_t node = { *(u32 *) key, NULL }; - return node; -} - -static inline struct mh_i32ptr_node_t -int32_tuple_to_node(struct tuple *tuple, struct key_def *key_def) -{ - const char *field = tuple_field_old(tuple, key_def->parts[0].fieldno); - struct mh_i32ptr_node_t node = int32_key_to_node(field); - node.val = tuple; - return node; -} - - -Hash32Index::Hash32Index(struct key_def *key_def, struct space *space) - : HashIndex(key_def, space) -{ - int_hash = mh_i32ptr_new(); -} - -Hash32Index::~Hash32Index() -{ - mh_i32ptr_delete(int_hash); -} - void -Hash32Index::reserve(u32 n_tuples) +HashIndex::reserve(u32 n_tuples) { - mh_i32ptr_reserve(int_hash, n_tuples, NULL); + mh_index_reserve(hash, n_tuples, key_def); } size_t -Hash32Index::size() const +HashIndex::size() const { - return mh_size(int_hash); + return mh_size(hash); } struct tuple * -Hash32Index::random(u32 rnd) const +HashIndex::min() const { - mh_int_t k = mh_i32ptr_random(int_hash, rnd); - if (k != mh_end(int_hash)) - return (struct tuple *) mh_i32ptr_node(int_hash, k)->val; + tnt_raise(ClientError, ER_UNSUPPORTED, "Hash index", "min()"); return NULL; } struct tuple * -Hash32Index::findByKey(const char *key, u32 part_count) const -{ - assert(key_def->is_unique && part_count == key_def->part_count); - struct tuple *ret = NULL; - struct mh_i32ptr_node_t node = int32_key_to_node(key); - mh_int_t k = mh_i32ptr_get(int_hash, &node, NULL); - if (k != mh_end(int_hash)) - ret = (struct tuple *) mh_i32ptr_node(int_hash, k)->val; -#ifdef DEBUG - say_debug("Hash32Index find(self:%p, key:%i) = %p", self, node.key, ret); -#endif - return ret; -} - -struct tuple * -Hash32Index::replace(struct tuple *old_tuple, struct tuple *new_tuple, - enum dup_replace_mode mode) -{ - struct mh_i32ptr_node_t new_node, old_node; - uint32_t errcode; - - if (new_tuple) { - struct mh_i32ptr_node_t *dup_node = &old_node; - new_node = int32_tuple_to_node(new_tuple, key_def); - mh_int_t pos = mh_i32ptr_put(int_hash, &new_node, - &dup_node, NULL); - - ERROR_INJECT(ERRINJ_INDEX_ALLOC, - { - mh_i32ptr_del(int_hash, pos, NULL); - pos = mh_end(int_hash); - }); - - if (pos == mh_end(int_hash)) { - tnt_raise(LoggedError, ER_MEMORY_ISSUE, (ssize_t) pos, - "int hash", "key"); - } - struct tuple *dup_tuple = (dup_node ? - (struct tuple *) dup_node->val - : NULL); - errcode = replace_check_dup(old_tuple, dup_tuple, mode); - - if (errcode) { - mh_i32ptr_remove(int_hash, &new_node, NULL); - if (dup_node) { - pos = mh_i32ptr_put(int_hash, dup_node, - NULL, NULL); - if (pos == mh_end(int_hash)) { - panic("Failed to allocate memory in " - "recover of int hash"); - } - } - tnt_raise(ClientError, errcode, index_n(this)); - } - if (dup_tuple) - return dup_tuple; - } - if (old_tuple) { - old_node = int32_tuple_to_node(old_tuple, key_def); - mh_i32ptr_remove(int_hash, &old_node, NULL); - } - return old_tuple; -} - - -struct iterator * -Hash32Index::allocIterator() const -{ - struct hash_i32_iterator *it = (struct hash_i32_iterator *) - malloc(sizeof(struct hash_i32_iterator)); - if (it) { - memset(it, 0, sizeof(*it)); - it->base.next = hash_iterator_i32_ge; - it->base.free = hash_iterator_free; - } - return (struct iterator *) it; -} - -void -Hash32Index::initIterator(struct iterator *ptr, enum iterator_type type, - const char *key, u32 part_count) const -{ - assert ((part_count == 1 && key != NULL) || part_count == 0); - (void) part_count; - assert(ptr->free == hash_iterator_free); - - struct hash_i32_iterator *it = (struct hash_i32_iterator *) ptr; - struct mh_i32ptr_node_t node; - - switch (type) { - case ITER_GE: - if (key != NULL) { - node = int32_key_to_node(key); - it->h_pos = mh_i32ptr_get(int_hash, &node, NULL); - it->base.next = hash_iterator_i32_ge; - break; - } - /* Fall through. */ - case ITER_ALL: - it->h_pos = mh_begin(int_hash); - it->base.next = hash_iterator_i32_ge; - break; - case ITER_EQ: - node = int32_key_to_node(key); - it->h_pos = mh_i32ptr_get(int_hash, &node, NULL); - it->base.next = hash_iterator_i32_eq; - break; - default: - tnt_raise(ClientError, ER_UNSUPPORTED, - "Hash index", "requested iterator type"); - } - it->hash = int_hash; -} - -/* }}} */ - -/* {{{ Hash64Index ************************************************/ - -static inline struct mh_i64ptr_node_t -int64_key_to_node(const char *key) -{ - u32 key_size = load_varint32(&key); - if (key_size != 8) - tnt_raise(ClientError, ER_KEY_FIELD_TYPE, "u64"); - struct mh_i64ptr_node_t node = { *(u64 *) key, NULL }; - return node; -} - -static inline struct mh_i64ptr_node_t -int64_tuple_to_node(struct tuple *tuple, struct key_def *key_def) -{ - const char *field = tuple_field_old(tuple, key_def->parts[0].fieldno); - struct mh_i64ptr_node_t node = int64_key_to_node(field); - node.val = tuple; - return node; -} - -Hash64Index::Hash64Index(struct key_def *key_def, struct space *space) - : HashIndex(key_def, space) -{ - int64_hash = mh_i64ptr_new(); -} - -Hash64Index::~Hash64Index() -{ - mh_i64ptr_delete(int64_hash); -} - -void -Hash64Index::reserve(u32 n_tuples) -{ - mh_i64ptr_reserve(int64_hash, n_tuples, NULL); -} - -size_t -Hash64Index::size() const +HashIndex::max() const { - return mh_size(int64_hash); + tnt_raise(ClientError, ER_UNSUPPORTED, "Hash index", "max()"); + return NULL; } struct tuple * -Hash64Index::random(u32 rnd) const +HashIndex::random(u32 rnd) const { - mh_int_t k = mh_i64ptr_random(int64_hash, rnd); - if (k != mh_end(int64_hash)) - return (struct tuple *) mh_i64ptr_node(int64_hash, k)->val; + uint32_t k = mh_index_random(hash, rnd); + if (k != mh_end(hash)) + return *mh_index_node(hash, k); return NULL; } struct tuple * -Hash64Index::findByKey(const char *key, u32 part_count) const +HashIndex::findByKey(const char *key, u32 part_count) const { assert(key_def->is_unique && part_count == key_def->part_count); - (void) part_count; struct tuple *ret = NULL; - struct mh_i64ptr_node_t node = int64_key_to_node(key); - mh_int_t k = mh_i64ptr_get(int64_hash, &node, NULL); - if (k != mh_end(int64_hash)) - ret = (struct tuple *) mh_i64ptr_node(int64_hash, k)->val; -#ifdef DEBUG - say_debug("Hash64Index find(self:%p, key:%i) = %p", self, node.key, ret); -#endif + uint32_t k = mh_index_find(hash, key, key_def); + if (k != mh_end(hash)) + ret = *mh_index_node(hash, k); return ret; } struct tuple * -Hash64Index::replace(struct tuple *old_tuple, struct tuple *new_tuple, - enum dup_replace_mode mode) +HashIndex::replace(struct tuple *old_tuple, struct tuple *new_tuple, + enum dup_replace_mode mode) { - struct mh_i64ptr_node_t new_node, old_node; uint32_t errcode; if (new_tuple) { - struct mh_i64ptr_node_t *dup_node = &old_node; - new_node = int64_tuple_to_node(new_tuple, key_def); - mh_int_t pos = mh_i64ptr_put(int64_hash, &new_node, - &dup_node, NULL); + struct tuple *dup_tuple = NULL; + struct tuple **dup_node = &dup_tuple; + uint32_t pos = mh_index_put(hash, &new_tuple, + &dup_node, key_def); ERROR_INJECT(ERRINJ_INDEX_ALLOC, { - mh_i64ptr_del(int64_hash, pos, NULL); - pos = mh_end(int64_hash); + mh_index_del(hash, pos, key_def); + pos = mh_end(hash); }); - if (pos == mh_end(int64_hash)) { + if (pos == mh_end(hash)) { tnt_raise(LoggedError, ER_MEMORY_ISSUE, (ssize_t) pos, - "int hash", "key"); + "hash", "key"); } - struct tuple *dup_tuple = (dup_node ? - (struct tuple *) dup_node->val : - NULL); errcode = replace_check_dup(old_tuple, dup_tuple, mode); if (errcode) { - mh_i64ptr_remove(int64_hash, &new_node, NULL); - if (dup_node) { - pos = mh_i64ptr_put(int64_hash, dup_node, NULL, NULL); - if (pos == mh_end(int64_hash)) { + mh_index_remove(hash, &new_tuple, key_def); + if (dup_tuple) { + pos = mh_index_put(hash, &dup_tuple, NULL, + key_def); + if (pos == mh_end(hash)) { panic("Failed to allocate memory in " "recover of int hash"); } @@ -598,229 +315,57 @@ Hash64Index::replace(struct tuple *old_tuple, struct tuple *new_tuple, } if (old_tuple) { - old_node = int64_tuple_to_node(old_tuple, key_def); - mh_i64ptr_remove(int64_hash, &old_node, NULL); + mh_index_remove(hash, &old_tuple, key_def); } - return old_tuple; } - struct iterator * -Hash64Index::allocIterator() const -{ - struct hash_i64_iterator *it = (struct hash_i64_iterator *) - malloc(sizeof(struct hash_i64_iterator)); - if (it) { - memset(it, 0, sizeof(*it)); - it->base.next = hash_iterator_i64_ge; - it->base.free = hash_iterator_free; +HashIndex::allocIterator() const +{ + struct hash_iterator *it = (struct hash_iterator *) + calloc(1, sizeof(*it)); + if (it == NULL) { + tnt_raise(ClientError, ER_MEMORY_ISSUE, + sizeof(struct hash_iterator), + "HashIndex", "iterator"); } + it->base.next = hash_iterator_ge; + it->base.free = hash_iterator_free; return (struct iterator *) it; } void -Hash64Index::initIterator(struct iterator *ptr, enum iterator_type type, - const char *key, u32 part_count) const +HashIndex::initIterator(struct iterator *ptr, enum iterator_type type, + const char *key, u32 part_count) const { - assert ((part_count == 1 && key != NULL) || part_count == 0); + assert (key != NULL || part_count == 0); (void) part_count; assert(ptr->free == hash_iterator_free); - struct hash_i64_iterator *it = (struct hash_i64_iterator *) ptr; - struct mh_i64ptr_node_t node; - switch (type) { - case ITER_GE: - if (key != NULL) { - node = int64_key_to_node(key); - it->h_pos = mh_i64ptr_get(int64_hash, &node, NULL); - it->base.next = hash_iterator_i64_ge; - break; - } - /* Fall through. */ - case ITER_ALL: - it->h_pos = mh_begin(int64_hash); - it->base.next = hash_iterator_i64_ge; - break; - case ITER_EQ: - node = int64_key_to_node(key); - it->h_pos = mh_i64ptr_get(int64_hash, &node, NULL); - it->base.next = hash_iterator_i64_eq; - break; - default: - tnt_raise(ClientError, ER_UNSUPPORTED, - "Hash index", "requested iterator type"); - } - it->hash = int64_hash; -} - -/* }}} */ - -/* {{{ HashStrIndex ***********************************************/ - -static inline struct mh_lstrptr_node_t -lstrptr_tuple_to_node(struct tuple *tuple, struct key_def *key_def) -{ - const char *field = tuple_field_old(tuple, key_def->parts[0].fieldno); - if (field == NULL) - tnt_raise(ClientError, ER_NO_SUCH_FIELD, - key_def->parts[0].fieldno); - - struct mh_lstrptr_node_t node = { field, tuple }; - return node; -} - -HashStrIndex::HashStrIndex(struct key_def *key_def, struct space *space) - : HashIndex(key_def, space) -{ - str_hash = mh_lstrptr_new(); -} - -HashStrIndex::~HashStrIndex() -{ - mh_lstrptr_delete(str_hash); -} - -void -HashStrIndex::reserve(u32 n_tuples) -{ - mh_lstrptr_reserve(str_hash, n_tuples, NULL); -} - - -size_t -HashStrIndex::size() const -{ - return mh_size(str_hash); -} - -struct tuple * -HashStrIndex::random(u32 rnd) const -{ - mh_int_t k = mh_lstrptr_random(str_hash, rnd); - if (k != mh_end(str_hash)) - return (struct tuple *) mh_lstrptr_node(str_hash, k)->val; - return NULL; -} - -struct tuple * -HashStrIndex::findByKey(const char *key, u32 part_count) const -{ - assert(key_def->is_unique && part_count == key_def->part_count); - (void) part_count; - - struct tuple *ret = NULL; - const struct mh_lstrptr_node_t node = { key, NULL }; - mh_int_t k = mh_lstrptr_get(str_hash, &node, NULL); - if (k != mh_end(str_hash)) - ret = (struct tuple *) mh_lstrptr_node(str_hash, k)->val; -#ifdef DEBUG - u32 key_size = load_varint32(&key); - say_debug("HashStrIndex find(self:%p, key:(%i)'%.*s') = %p", - self, key_size, key_size, key, ret); -#endif - return ret; -} - -struct tuple * -HashStrIndex::replace(struct tuple *old_tuple, struct tuple *new_tuple, - enum dup_replace_mode mode) -{ - struct mh_lstrptr_node_t new_node, old_node; - uint32_t errcode; - - if (new_tuple) { - struct mh_lstrptr_node_t *dup_node = &old_node; - new_node = lstrptr_tuple_to_node(new_tuple, key_def); - mh_int_t pos = mh_lstrptr_put(str_hash, &new_node, - &dup_node, NULL); - - ERROR_INJECT(ERRINJ_INDEX_ALLOC, - { - mh_lstrptr_del(str_hash, pos, NULL); - pos = mh_end(str_hash); - }); - - if (pos == mh_end(str_hash)) { - tnt_raise(LoggedError, ER_MEMORY_ISSUE, (ssize_t) pos, - "str hash", "key"); - } - struct tuple *dup_tuple = dup_node - ? (struct tuple *) dup_node->val - : NULL; - errcode = replace_check_dup(old_tuple, dup_tuple, mode); - - if (errcode) { - mh_lstrptr_remove(str_hash, &new_node, NULL); - if (dup_node) { - pos = mh_lstrptr_put(str_hash, dup_node, - NULL, NULL); - if (pos == mh_end(str_hash)) { - panic("Failed to allocate memory in " - "recover of str hash"); - } - } - tnt_raise(ClientError, errcode, index_n(this)); - } - if (dup_tuple) - return dup_tuple; - } - if (old_tuple) { - old_node = lstrptr_tuple_to_node(old_tuple, key_def); - mh_lstrptr_remove(str_hash, &old_node, NULL); - } - return old_tuple; -} - -struct iterator * -HashStrIndex::allocIterator() const -{ - struct hash_lstr_iterator *it = (struct hash_lstr_iterator *) - malloc(sizeof(struct hash_lstr_iterator)); - if (it) { - memset(it, 0, sizeof(*it)); - it->base.next = hash_iterator_lstr_ge; - it->base.free = hash_iterator_free; - } - return (struct iterator *) it; -} - -void -HashStrIndex::initIterator(struct iterator *ptr, enum iterator_type type, - const char *key, u32 part_count) const -{ - assert ((part_count == 1 && key != NULL) || part_count == 0); - (void) part_count; - - assert(ptr->free == hash_iterator_free); - struct hash_lstr_iterator *it = (struct hash_lstr_iterator *) ptr; - struct mh_lstrptr_node_t node; + struct hash_iterator *it = (struct hash_iterator *) ptr; switch (type) { case ITER_GE: if (key != NULL) { - node.key = key; - it->h_pos = mh_lstrptr_get(str_hash, &node, NULL); - it->base.next = hash_iterator_lstr_ge; + it->h_pos = mh_index_find(hash, key, key_def); + it->base.next = hash_iterator_ge; break; } /* Fall through. */ case ITER_ALL: - it->base.next = hash_iterator_lstr_ge; - it->h_pos = mh_begin(str_hash); + it->h_pos = mh_begin(hash); + it->base.next = hash_iterator_ge; break; case ITER_EQ: - node.key = key; - it->h_pos = mh_lstrptr_get(str_hash, &node, NULL); - it->base.next = hash_iterator_lstr_eq; + it->h_pos = mh_index_find(hash, key, key_def); + it->base.next = hash_iterator_eq; break; default: tnt_raise(ClientError, ER_UNSUPPORTED, "Hash index", "requested iterator type"); } - it->hash = str_hash; + it->hash = hash; } - /* }}} */ - diff --git a/src/box/hash_index.h b/src/box/hash_index.h index 409d171cb2cf08321d209dec7870447c47d5f1e6..b315e72ae9057e235b3e3e9b572ca6109324a837 100644 --- a/src/box/hash_index.h +++ b/src/box/hash_index.h @@ -31,33 +31,35 @@ #include "index.h" +struct mh_index_t; class HashIndex: public Index { public: - static HashIndex * - factory(struct key_def *key_def, struct space *space); - HashIndex(struct key_def *key_def, struct space *space); + ~HashIndex(); virtual void beginBuild(); virtual void buildNext(struct tuple *tuple); virtual void endBuild(); virtual void build(Index *pk); - virtual size_t size() const = 0; + virtual size_t size() const; virtual struct tuple *min() const; virtual struct tuple *max() const; - virtual struct tuple *random(u32 rnd) const = 0; - virtual struct tuple *findByKey(const char *key, u32 part_count) const = 0; + virtual struct tuple *random(u32 rnd) const; + virtual struct tuple *findByKey(const char *key, u32 part_count) const; virtual struct tuple *replace(struct tuple *old_tuple, struct tuple *new_tuple, - enum dup_replace_mode mode) = 0; + enum dup_replace_mode mode); - virtual struct iterator *allocIterator() const = 0; + virtual struct iterator *allocIterator() const; virtual void initIterator(struct iterator *iterator, enum iterator_type type, - const char *key, u32 part_count) const = 0; + const char *key, u32 part_count) const; + + virtual void reserve(u32 n_tuples); - virtual void reserve(u32 n_tuples) = 0; +protected: + struct mh_index_t *hash; }; #endif /* TARANTOOL_BOX_HASH_INDEX_H_INCLUDED */ diff --git a/src/box/index.cc b/src/box/index.cc index b5e07081fdeaee9acb36d7601fd5c804b7bb70a1..9a7c212c5c6a254df8237dc12ce37e803ce5f976 100644 --- a/src/box/index.cc +++ b/src/box/index.cc @@ -143,7 +143,7 @@ Index::factory(enum index_type type, struct key_def *key_def, struct space *spac { switch (type) { case HASH: - return HashIndex::factory(key_def, space); + return new HashIndex(key_def, space); case TREE: return new TreeIndex(key_def, space); case BITSET: diff --git a/src/box/space.cc b/src/box/space.cc index dc9eb92017f1d9a74ecb3514e291fd0f9e264f9d..90b6e3f3eb00b082b646b443dbf278fd8be52c3c 100644 --- a/src/box/space.cc +++ b/src/box/space.cc @@ -586,12 +586,6 @@ check_spaces(struct tarantool_cfg *conf) switch (index_type) { case HASH: /* check hash index */ - /* hash index must has single-field key */ - if (key_part_count != 1) { - out_warning(CNF_OK, "(space = %zu index = %zu) " - "hash index must has a single-field key", i, j); - return -1; - } /* hash index must be unique */ if (!index->unique) { out_warning(CNF_OK, "(space = %zu index = %zu) " diff --git a/test/big/hash.result b/test/big/hash.result index 6099e94a9c3c51624d3f20c588b009316310a63f..1734cc3de78fc9df1f894d1266f36c53b15695d5 100644 --- a/test/big/hash.result +++ b/test/big/hash.result @@ -156,25 +156,41 @@ error: 'Invalid key part count in an exact match (expected 1, got 2)' # Insert valid fieds -lua box.space[11]:insert('00000000', 'value1 v1.0', 'value2 v1.0') +lua box.space[11]:insert(0ULL, 'value1 v1.0', 'value2 v1.0') --- - - 3472328296227680304: {'value1 v1.0', 'value2 v1.0'} + - 0: {'value1 v1.0', 'value2 v1.0'} ... -lua box.space[11]:insert('00000001', 'value1 v1.0', 'value2 v1.0') +lua box.space[11]:insert(1ULL, 'value1 v1.0', 'value2 v1.0') --- - - 3544385890265608240: {'value1 v1.0', 'value2 v1.0'} + - 1: {'value1 v1.0', 'value2 v1.0'} ... -lua box.space[11]:insert('00000002', 'value1 v1.0', 'value2 v1.0') +lua box.space[11]:insert(2ULL, 'value1 v1.0', 'value2 v1.0') --- - - 3616443484303536176: {'value1 v1.0', 'value2 v1.0'} + - 2: {'value1 v1.0', 'value2 v1.0'} ... -lua box.space[11]:insert('00000003', 'value1 v1.0', 'value2 v1.0') +lua box.space[11]:insert(3ULL, 'value1 v1.0', 'value2 v1.0') --- - - 3688501078341464112: {'value1 v1.0', 'value2 v1.0'} + - 3: {'value1 v1.0', 'value2 v1.0'} ... # Insert invalid fields +lua box.space[11]:insert(100, 'value1 v1.0', 'value2 v1.0') +--- +error: 'Supplied key field type does not match index type: expected u64' +... +lua box.space[11]:insert(101, 'value1 v1.0', 'value2 v1.0') +--- +error: 'Supplied key field type does not match index type: expected u64' +... +lua box.space[11]:insert(102, 'value1 v1.0', 'value2 v1.0') +--- +error: 'Supplied key field type does not match index type: expected u64' +... +lua box.space[11]:insert(103, 'value1 v1.0', 'value2 v1.0') +--- +error: 'Supplied key field type does not match index type: expected u64' +... lua box.space[11]:insert('invalid key', 'value1 v1.0', 'value2 v1.0') --- error: 'Supplied key field type does not match index type: expected u64' @@ -187,24 +203,36 @@ error: 'Supplied key field type does not match index type: expected u64' # Replace valid fieds -lua box.space[11]:replace('00000003', 'value1 v1.31', 'value2 1.12') +lua box.space[11]:replace(3ULL, 'value1 v1.31', 'value2 1.12') --- - - 3688501078341464112: {'value1 v1.31', 'value2 1.12'} + - 3: {'value1 v1.31', 'value2 1.12'} ... -lua box.space[11]:replace('00000001', 'value1 v1.32', 'value2 1.72') +lua box.space[11]:replace(1ULL, 'value1 v1.32', 'value2 1.72') --- - - 3544385890265608240: {'value1 v1.32', 'value2 1.72'} + - 1: {'value1 v1.32', 'value2 1.72'} ... -lua box.space[11]:replace('00000002', 'value1 v1.43', 'value2 1.92') +lua box.space[11]:replace(2ULL, 'value1 v1.43', 'value2 1.92') --- - - 3616443484303536176: {'value1 v1.43', 'value2 1.92'} + - 2: {'value1 v1.43', 'value2 1.92'} ... # Replace invalid fields -lua box.space[10]:replace('invalid key', 'value1 v1.0', 'value2 v1.0') +lua box.space[11]:replace(3, 'value1 v1.31', 'value2 1.12') --- -error: 'Supplied key field type does not match index type: expected u32' +error: 'Supplied key field type does not match index type: expected u64' +... +lua box.space[11]:replace(1, 'value1 v1.32', 'value2 1.72') +--- +error: 'Supplied key field type does not match index type: expected u64' +... +lua box.space[11]:replace(2, 'value1 v1.43', 'value2 1.92') +--- +error: 'Supplied key field type does not match index type: expected u64' +... +lua box.space[11]:replace('invalid key', 'value1 v1.0', 'value2 v1.0') +--- +error: 'Supplied key field type does not match index type: expected u64' ... #-----------------------------------------------------------------------------# @@ -214,26 +242,51 @@ error: 'Supplied key field type does not match index type: expected u32' # select by valid keys -lua box.space[11]:select(0, '00000000') +lua box.space[11]:select(0, 0ULL) --- - - 3472328296227680304: {'value1 v1.0', 'value2 v1.0'} + - 0: {'value1 v1.0', 'value2 v1.0'} ... -lua box.space[11]:select(0, '00000001') +lua box.space[11]:select(0, 1ULL) --- - - 3544385890265608240: {'value1 v1.32', 'value2 1.72'} + - 1: {'value1 v1.32', 'value2 1.72'} ... -lua box.space[11]:select(0, '00000002') +lua box.space[11]:select(0, 2ULL) --- - - 3616443484303536176: {'value1 v1.43', 'value2 1.92'} + - 2: {'value1 v1.43', 'value2 1.92'} ... -lua box.space[11]:select(0, '00000003') +lua box.space[11]:select(0, 3ULL) --- - - 3688501078341464112: {'value1 v1.31', 'value2 1.12'} + - 3: {'value1 v1.31', 'value2 1.12'} ... -lua box.space[11]:select(0, '00000004') +lua box.space[11]:select(0, 4ULL) --- ... -lua box.space[11]:select(0, '00000005') +lua box.space[11]:select(0, 5ULL) +--- +... + +# select by valid NUM keys + +lua box.space[11]:select(0, 0) +--- + - 0: {'value1 v1.0', 'value2 v1.0'} +... +lua box.space[11]:select(0, 1) +--- + - 1: {'value1 v1.32', 'value2 1.72'} +... +lua box.space[11]:select(0, 2) +--- + - 2: {'value1 v1.43', 'value2 1.92'} +... +lua box.space[11]:select(0, 3) +--- + - 3: {'value1 v1.31', 'value2 1.12'} +... +lua box.space[11]:select(0, 4) +--- +... +lua box.space[11]:select(0, 5) --- ... @@ -255,26 +308,67 @@ error: 'Invalid key part count (expected [0..1], got 2)' # delete by valid keys -lua box.space[11]:delete('00000000') +lua box.space[11]:delete(0ULL) +--- + - 0: {'value1 v1.0', 'value2 v1.0'} +... +lua box.space[11]:delete(1ULL) +--- + - 1: {'value1 v1.32', 'value2 1.72'} +... +lua box.space[11]:delete(2ULL) --- - - 3472328296227680304: {'value1 v1.0', 'value2 v1.0'} + - 2: {'value1 v1.43', 'value2 1.92'} ... -lua box.space[11]:delete('00000001') +lua box.space[11]:delete(3ULL) --- - - 3544385890265608240: {'value1 v1.32', 'value2 1.72'} + - 3: {'value1 v1.31', 'value2 1.12'} ... -lua box.space[11]:delete('00000002') +lua box.space[11]:delete(4ULL) --- - - 3616443484303536176: {'value1 v1.43', 'value2 1.92'} ... -lua box.space[11]:delete('00000003') +lua box.space[11]:delete(5ULL) +--- +... +lua box.space[11]:insert(0ULL, 'value1 v1.0', 'value2 v1.0') +--- + - 0: {'value1 v1.0', 'value2 v1.0'} +... +lua box.space[11]:insert(1ULL, 'value1 v1.0', 'value2 v1.0') --- - - 3688501078341464112: {'value1 v1.31', 'value2 1.12'} + - 1: {'value1 v1.0', 'value2 v1.0'} +... +lua box.space[11]:insert(2ULL, 'value1 v1.0', 'value2 v1.0') +--- + - 2: {'value1 v1.0', 'value2 v1.0'} +... +lua box.space[11]:insert(3ULL, 'value1 v1.0', 'value2 v1.0') +--- + - 3: {'value1 v1.0', 'value2 v1.0'} +... + +# delete by valid NUM keys + +lua box.space[11]:delete(0) +--- + - 0: {'value1 v1.0', 'value2 v1.0'} +... +lua box.space[11]:delete(1) +--- + - 1: {'value1 v1.0', 'value2 v1.0'} +... +lua box.space[11]:delete(2) +--- + - 2: {'value1 v1.0', 'value2 v1.0'} +... +lua box.space[11]:delete(3) +--- + - 3: {'value1 v1.0', 'value2 v1.0'} ... -lua box.space[11]:delete('00000004') +lua box.space[11]:delete(4) --- ... -lua box.space[11]:delete('00000005') +lua box.space[11]:delete(5) --- ... diff --git a/test/big/hash.test b/test/big/hash.test index 51c46574092d540d1b34acf510427594c2ee57da..79b510266fb3c45aa865205e710497c77712f985 100644 --- a/test/big/hash.test +++ b/test/big/hash.test @@ -107,17 +107,20 @@ print """ print """ # Insert valid fieds """ -exec admin "lua box.space[11]:insert('00000000', 'value1 v1.0', 'value2 v1.0')" -exec admin "lua box.space[11]:insert('00000001', 'value1 v1.0', 'value2 v1.0')" -exec admin "lua box.space[11]:insert('00000002', 'value1 v1.0', 'value2 v1.0')" -exec admin "lua box.space[11]:insert('00000003', 'value1 v1.0', 'value2 v1.0')" +exec admin "lua box.space[11]:insert(0ULL, 'value1 v1.0', 'value2 v1.0')" +exec admin "lua box.space[11]:insert(1ULL, 'value1 v1.0', 'value2 v1.0')" +exec admin "lua box.space[11]:insert(2ULL, 'value1 v1.0', 'value2 v1.0')" +exec admin "lua box.space[11]:insert(3ULL, 'value1 v1.0', 'value2 v1.0')" print """ # Insert invalid fields """ +exec admin "lua box.space[11]:insert(100, 'value1 v1.0', 'value2 v1.0')" +exec admin "lua box.space[11]:insert(101, 'value1 v1.0', 'value2 v1.0')" +exec admin "lua box.space[11]:insert(102, 'value1 v1.0', 'value2 v1.0')" +exec admin "lua box.space[11]:insert(103, 'value1 v1.0', 'value2 v1.0')" exec admin "lua box.space[11]:insert('invalid key', 'value1 v1.0', 'value2 v1.0')" - print """ #-----------------------------------------------------------------------------# # 64-bit hash replace fields tests @@ -127,15 +130,17 @@ print """ print """ # Replace valid fieds """ -exec admin "lua box.space[11]:replace('00000003', 'value1 v1.31', 'value2 1.12')" -exec admin "lua box.space[11]:replace('00000001', 'value1 v1.32', 'value2 1.72')" -exec admin "lua box.space[11]:replace('00000002', 'value1 v1.43', 'value2 1.92')" +exec admin "lua box.space[11]:replace(3ULL, 'value1 v1.31', 'value2 1.12')" +exec admin "lua box.space[11]:replace(1ULL, 'value1 v1.32', 'value2 1.72')" +exec admin "lua box.space[11]:replace(2ULL, 'value1 v1.43', 'value2 1.92')" print """ # Replace invalid fields """ -exec admin "lua box.space[10]:replace('invalid key', 'value1 v1.0', 'value2 v1.0')" - +exec admin "lua box.space[11]:replace(3, 'value1 v1.31', 'value2 1.12')" +exec admin "lua box.space[11]:replace(1, 'value1 v1.32', 'value2 1.72')" +exec admin "lua box.space[11]:replace(2, 'value1 v1.43', 'value2 1.92')" +exec admin "lua box.space[11]:replace('invalid key', 'value1 v1.0', 'value2 v1.0')" print """ #-----------------------------------------------------------------------------# @@ -146,12 +151,22 @@ print """ print """ # select by valid keys """ -exec admin "lua box.space[11]:select(0, '00000000')" -exec admin "lua box.space[11]:select(0, '00000001')" -exec admin "lua box.space[11]:select(0, '00000002')" -exec admin "lua box.space[11]:select(0, '00000003')" -exec admin "lua box.space[11]:select(0, '00000004')" -exec admin "lua box.space[11]:select(0, '00000005')" +exec admin "lua box.space[11]:select(0, 0ULL)" +exec admin "lua box.space[11]:select(0, 1ULL)" +exec admin "lua box.space[11]:select(0, 2ULL)" +exec admin "lua box.space[11]:select(0, 3ULL)" +exec admin "lua box.space[11]:select(0, 4ULL)" +exec admin "lua box.space[11]:select(0, 5ULL)" + +print """ +# select by valid NUM keys +""" +exec admin "lua box.space[11]:select(0, 0)" +exec admin "lua box.space[11]:select(0, 1)" +exec admin "lua box.space[11]:select(0, 2)" +exec admin "lua box.space[11]:select(0, 3)" +exec admin "lua box.space[11]:select(0, 4)" +exec admin "lua box.space[11]:select(0, 5)" print """ # select by invalid keys @@ -159,7 +174,6 @@ print """ exec admin "lua box.space[11]:select(0, 'invalid key')" exec admin "lua box.space[11]:select(0, '00000001', '00000002')" - print """ #-----------------------------------------------------------------------------# # 64-bit hash delete fields test @@ -169,12 +183,27 @@ print """ print """ # delete by valid keys """ -exec admin "lua box.space[11]:delete('00000000')" -exec admin "lua box.space[11]:delete('00000001')" -exec admin "lua box.space[11]:delete('00000002')" -exec admin "lua box.space[11]:delete('00000003')" -exec admin "lua box.space[11]:delete('00000004')" -exec admin "lua box.space[11]:delete('00000005')" +exec admin "lua box.space[11]:delete(0ULL)" +exec admin "lua box.space[11]:delete(1ULL)" +exec admin "lua box.space[11]:delete(2ULL)" +exec admin "lua box.space[11]:delete(3ULL)" +exec admin "lua box.space[11]:delete(4ULL)" +exec admin "lua box.space[11]:delete(5ULL)" + +exec admin "lua box.space[11]:insert(0ULL, 'value1 v1.0', 'value2 v1.0')" +exec admin "lua box.space[11]:insert(1ULL, 'value1 v1.0', 'value2 v1.0')" +exec admin "lua box.space[11]:insert(2ULL, 'value1 v1.0', 'value2 v1.0')" +exec admin "lua box.space[11]:insert(3ULL, 'value1 v1.0', 'value2 v1.0')" + +print """ +# delete by valid NUM keys +""" +exec admin "lua box.space[11]:delete(0)" +exec admin "lua box.space[11]:delete(1)" +exec admin "lua box.space[11]:delete(2)" +exec admin "lua box.space[11]:delete(3)" +exec admin "lua box.space[11]:delete(4)" +exec admin "lua box.space[11]:delete(5)" print """ # delete by invalid keys diff --git a/test/big/iterator.result b/test/big/iterator.result index 21609efd1f75cbdd77a41fc7e0513989b4c3f294..1c832db3fdf2f25c8526613911879a6a91c76c10 100644 --- a/test/big/iterator.result +++ b/test/big/iterator.result @@ -768,7 +768,7 @@ error: 'utils.lua:27: Key part count 4 is greater than index part count 2' ... #-----------------------------------------------------------------------------# -# Iterator: hash multi-part non-unique +# Iterator: hash single-part unique #-----------------------------------------------------------------------------# lua iterate(20, 4, 0, 1) @@ -840,6 +840,68 @@ lua iterate(20, 4, 0, 1, box.index.GE, 'pid_999') sorted output ... +#-----------------------------------------------------------------------------# +# Iterator: hash multi-part unique +#-----------------------------------------------------------------------------# + +lua iterate(20, 5, 1, 3, box.index.ALL) +--- +sorted output +$sid_001$tid_997$ +$sid_001$tid_998$ +$sid_002$tid_996$ +$sid_002$tid_997$ +$sid_003$tid_996$ +$sid_004$tid_996$ +$sid_005$tid_994$ +$sid_005$tid_995$ +$sid_005$tid_996$ +$sid_006$tid_996$ +... +lua iterate(20, 5, 1, 3, box.index.EQ) +--- +error: 'Invalid key part count in an exact match (expected 2, got 0)' +... +lua iterate(20, 5, 1, 3, box.index.EQ, 'sid_005') +--- +error: 'Invalid key part count in an exact match (expected 2, got 1)' +... +lua iterate(20, 5, 1, 3, box.index.GE) +--- +sorted output +$sid_001$tid_997$ +$sid_001$tid_998$ +$sid_002$tid_996$ +$sid_002$tid_997$ +$sid_003$tid_996$ +$sid_004$tid_996$ +$sid_005$tid_994$ +$sid_005$tid_995$ +$sid_005$tid_996$ +$sid_006$tid_996$ +... +lua iterate(20, 2, 1, 3, box.index.EQ, 'sid_005', 'tid_995') +--- +$sid_005$tid_995$ +... +lua iterate(20, 2, 1, 3, box.index.EQ, 'sid_005', 'tid_999') +--- +... +lua iterate(20, 2, 1, 3, box.index.EQ, 'sid_005', 'tid_995', 'a') +--- +error: 'utils.lua:27: Key part count 3 is greater than index part count 2' +... +lua iterate(20, 2, 1, 3, box.index.GE, 'sid_005', 'tid_995') +--- +$sid_005$tid_995$ +$sid_005$tid_996$ +$sid_006$tid_996$ +... +lua iterate(20, 2, 1, 3, box.index.GE, 'sid_005', 'tid_999') +--- +$sid_006$tid_996$ +... + #-----------------------------------------------------------------------------# # Iterator: various #-----------------------------------------------------------------------------# diff --git a/test/big/iterator.test b/test/big/iterator.test index 53fd5531bce1051cd2e18a455fad100f1f20f723..2a737ee5b490526e48d84efa3574598ff8e0a045 100644 --- a/test/big/iterator.test +++ b/test/big/iterator.test @@ -135,7 +135,7 @@ exec admin "lua iterate(20, 3, 2, 4, box.index.LT, 'tid_996', 'to', 'many', 'key print """ #-----------------------------------------------------------------------------# -# Iterator: hash multi-part non-unique +# Iterator: hash single-part unique #-----------------------------------------------------------------------------# """ @@ -148,6 +148,21 @@ exec admin "lua iterate(20, 4, 0, 1, box.index.EQ, 'pid_666')" exec admin "lua iterate(20, 4, 0, 1, box.index.GE, 'pid_001')" exec admin "lua iterate(20, 4, 0, 1, box.index.GE, 'pid_999')" +print """ +#-----------------------------------------------------------------------------# +# Iterator: hash multi-part unique +#-----------------------------------------------------------------------------# +""" +exec admin "lua iterate(20, 5, 1, 3, box.index.ALL)" +exec admin "lua iterate(20, 5, 1, 3, box.index.EQ)" +exec admin "lua iterate(20, 5, 1, 3, box.index.EQ, 'sid_005')" +exec admin "lua iterate(20, 5, 1, 3, box.index.GE)" +exec admin "lua iterate(20, 2, 1, 3, box.index.EQ, 'sid_005', 'tid_995')" +exec admin "lua iterate(20, 2, 1, 3, box.index.EQ, 'sid_005', 'tid_999')" +exec admin "lua iterate(20, 2, 1, 3, box.index.EQ, 'sid_005', 'tid_995', 'a')" +exec admin "lua iterate(20, 2, 1, 3, box.index.GE, 'sid_005', 'tid_995')" +exec admin "lua iterate(20, 2, 1, 3, box.index.GE, 'sid_005', 'tid_999')" + print """ #-----------------------------------------------------------------------------# # Iterator: various diff --git a/test/big/tarantool.cfg b/test/big/tarantool.cfg index 0f9f416055bc0a9060f935ceca19c73b0fdb1141..73973d4782f41309bfd525a7ff09f66f32c9b34c 100644 --- a/test/big/tarantool.cfg +++ b/test/big/tarantool.cfg @@ -283,6 +283,14 @@ space[20].index[4].unique = 1 space[20].index[4].key_field[0].fieldno = 0 space[20].index[4].key_field[0].type = "STR" +# Hash multi-part unique +space[20].index[5].type = "HASH" +space[20].index[5].unique = 1 +space[20].index[5].key_field[0].fieldno = 1 +space[20].index[5].key_field[0].type = "STR" +space[20].index[5].key_field[1].fieldno = 2 +space[20].index[5].key_field[1].type = "STR" + # hash::replace space[21].enabled = true