From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: From: Vladimir Davydov Subject: [PATCH 7/7] vinyl: implement multikey index support Date: Wed, 8 May 2019 20:22:39 +0300 Message-Id: <7186bf2838df75524d8f221b2a1c654cfe4d5ecd.1557334828.git.vdavydov.dev@gmail.com> In-Reply-To: References: In-Reply-To: References: To: tarantool-patches@freelists.org List-ID: In case of multikey indexes, we use vy_entry.hint to store multikey array entry index instead of a comparison hint. So all we need to do is patch all places where a statement is inserted so that in case the key definition is multikey we iterate over all multikey indexes and insert an entry for each of them. The rest will be done automatically as vinyl stores and compares vy_entry objects, which have hints built-in, while comparators and other generic functions have already been patched to treat hints as multikey indexes. There are just a few places we need to patch: - vy_tx_set, which inserts a statement into a transaction write set. - vy_build_insert_stmt, which is used to fill the new index on index creation and DDL recovery. - vy_build_on_replace, which forwards modifications done to the space during index creation to the new index. - vy_check_is_unique_secondary, which checks a secondary index for conflicts on insertion of a new statement. - vy_tx_handle_deferred_delete, which generates deferred DELETE statements if the old tuple is found in memory or in cache. - vy_deferred_delete_on_replace, which applies deferred DELETEs on compaction. Plus, we need to teach vy_get_by_secondary_tuple to match a full multikey tuple to a partial multikey tuple or a key, which implies iterating over all multikey indexes of the full tuple and comparing them to the corresponding entries to the partial tuple. We already have tests that check the functionality for memtx. Enable and tweak it a little so that it can be used for vinyl as well. --- src/box/vinyl.c | 143 ++++++++++++++++++++++++------------------ src/box/vy_stmt.h | 25 ++++++++ src/box/vy_tx.c | 43 +++++++++---- src/box/vy_tx.h | 19 +----- test/box/bitset.result | 15 +++++ test/box/bitset.test.lua | 6 ++ test/box/hash.result | 15 +++++ test/box/hash.test.lua | 6 ++ test/box/rtree_misc.result | 15 +++++ test/box/rtree_misc.test.lua | 6 ++ test/engine/engine.cfg | 3 - test/engine/multikey.result | 40 +----------- test/engine/multikey.test.lua | 16 +---- 13 files changed, 209 insertions(+), 143 deletions(-) diff --git a/src/box/vinyl.c b/src/box/vinyl.c index eddbdbed..5be10d59 100644 --- a/src/box/vinyl.c +++ b/src/box/vinyl.c @@ -701,11 +701,6 @@ vinyl_space_check_index_def(struct space *space, struct index_def *index_def) return -1; } } - if (key_def_is_multikey(index_def->key_def)) { - diag_set(ClientError, ER_UNSUPPORTED, - "Vinyl", "multikey indexes"); - return -1; - } return 0; } @@ -1355,19 +1350,24 @@ vy_get_by_secondary_tuple(struct vy_lsm *lsm, struct vy_tx *tx, lsm->pk->stat.lookup++; - if (vy_point_lookup(lsm->pk, tx, rv, key, result) != 0) { + struct vy_entry pk_entry; + if (vy_point_lookup(lsm->pk, tx, rv, key, &pk_entry) != 0) { rc = -1; goto out; } - /* - * Note, result stores a hint computed for the primary - * index while entry was read from a secondary index so - * we must not use vy_entry_compare() here. - */ - if (result->stmt == NULL || - vy_stmt_compare(result->stmt, HINT_NONE, entry.stmt, - HINT_NONE, lsm->cmp_def) != 0) { + bool match = false; + struct vy_entry full_entry; + if (pk_entry.stmt != NULL) { + vy_stmt_foreach_entry(full_entry, pk_entry.stmt, lsm->cmp_def) { + if (vy_entry_compare(full_entry, entry, + lsm->cmp_def) == 0) { + match = true; + break; + } + } + } + if (!match) { /* * If a tuple read from a secondary index doesn't * match the tuple corresponding to it in the @@ -1377,11 +1377,10 @@ vy_get_by_secondary_tuple(struct vy_lsm *lsm, struct vy_tx *tx, * case silently skip this tuple. */ vy_stmt_counter_acct_tuple(&lsm->stat.skip, entry.stmt); - if (result->stmt != NULL) { + if (pk_entry.stmt != NULL) { vy_stmt_counter_acct_tuple(&lsm->pk->stat.skip, - result->stmt); - tuple_unref(result->stmt); - *result = vy_entry_none(); + pk_entry.stmt); + tuple_unref(pk_entry.stmt); } /* * We must purge stale tuples from the cache before @@ -1390,6 +1389,7 @@ vy_get_by_secondary_tuple(struct vy_lsm *lsm, struct vy_tx *tx, * the tuple cache implementation. */ vy_cache_on_write(&lsm->cache, entry, NULL); + *result = vy_entry_none(); goto out; } @@ -1401,21 +1401,19 @@ vy_get_by_secondary_tuple(struct vy_lsm *lsm, struct vy_tx *tx, * the DELETE statement is not written to secondary indexes * immediately. */ - if (tx != NULL && vy_tx_track_point(tx, lsm->pk, *result) != 0) { - tuple_unref(result->stmt); + if (tx != NULL && vy_tx_track_point(tx, lsm->pk, pk_entry) != 0) { + tuple_unref(pk_entry.stmt); rc = -1; goto out; } if ((*rv)->vlsn == INT64_MAX) { - vy_cache_add(&lsm->pk->cache, *result, + vy_cache_add(&lsm->pk->cache, pk_entry, vy_entry_none(), key, ITER_EQ); } - /* Inherit the hint from the secondary index entry. */ - result->hint = entry.hint; - - vy_stmt_counter_acct_tuple(&lsm->pk->stat.get, result->stmt); + vy_stmt_counter_acct_tuple(&lsm->pk->stat.get, pk_entry.stmt); + *result = full_entry; out: tuple_unref(key.stmt); return rc; @@ -1576,35 +1574,22 @@ vy_check_is_unique_primary(struct vy_tx *tx, const struct vy_read_view **rv, return 0; } -/** - * Check if insertion of a new tuple violates unique constraint - * of a secondary index. - * @param tx Current transaction. - * @param rv Read view. - * @param space_name Space name. - * @param index_name Index name. - * @param lsm LSM tree corresponding to the index. - * @param stmt New tuple. - * - * @retval 0 Success, unique constraint is satisfied. - * @retval -1 Duplicate is found or read error occurred. - */ static int -vy_check_is_unique_secondary(struct vy_tx *tx, const struct vy_read_view **rv, - const char *space_name, const char *index_name, - struct vy_lsm *lsm, struct tuple *stmt) +vy_check_is_unique_secondary_one(struct vy_tx *tx, const struct vy_read_view **rv, + const char *space_name, const char *index_name, + struct vy_lsm *lsm, struct tuple *stmt, + int multikey_idx) { assert(lsm->index_id > 0); assert(vy_stmt_type(stmt) == IPROTO_INSERT || vy_stmt_type(stmt) == IPROTO_REPLACE); - if (!lsm->check_is_unique) - return 0; if (lsm->key_def->is_nullable && - tuple_key_contains_null(stmt, lsm->key_def, -1)) + tuple_key_contains_null(stmt, lsm->key_def, multikey_idx)) return 0; struct tuple *key = vy_stmt_extract_key(stmt, lsm->key_def, - lsm->env->key_format, -1); + lsm->env->key_format, + multikey_idx); if (key == NULL) return -1; struct tuple *found; @@ -1639,6 +1624,39 @@ vy_check_is_unique_secondary(struct vy_tx *tx, const struct vy_read_view **rv, /** * Check if insertion of a new tuple violates unique constraint + * of a secondary index. + * @param tx Current transaction. + * @param rv Read view. + * @param space_name Space name. + * @param index_name Index name. + * @param lsm LSM tree corresponding to the index. + * @param stmt New tuple. + * + * @retval 0 Success, unique constraint is satisfied. + * @retval -1 Duplicate is found or read error occurred. + */ +static int +vy_check_is_unique_secondary(struct vy_tx *tx, const struct vy_read_view **rv, + const char *space_name, const char *index_name, + struct vy_lsm *lsm, struct tuple *stmt) +{ + if (!lsm->check_is_unique) + return 0; + if (!key_def_is_multikey(lsm->cmp_def)) { + return vy_check_is_unique_secondary_one(tx, rv, + space_name, index_name, lsm, stmt, -1); + } + int count = tuple_multikey_count(stmt, lsm->cmp_def); + for (int i = 0; i < count; ++i) { + if (vy_check_is_unique_secondary_one(tx, rv, + space_name, index_name, lsm, stmt, i) != 0) + return -1; + } + return 0; +} + +/** + * Check if insertion of a new tuple violates unique constraint * of any index of the space. * @param env Vinyl environment. * @param tx Current transaction. @@ -4063,11 +4081,10 @@ vy_build_on_replace(struct trigger *trigger, void *event) /* Forward the statement to the new LSM tree. */ if (stmt->old_tuple != NULL) { - struct tuple *delete = vy_stmt_extract_key(stmt->old_tuple, - lsm->cmp_def, lsm->env->key_format, -1); + struct tuple *delete = vy_stmt_new_surrogate_delete(format, + stmt->old_tuple); if (delete == NULL) goto err; - vy_stmt_set_type(delete, IPROTO_DELETE); int rc = vy_tx_set(tx, lsm, delete); tuple_unref(delete); if (rc != 0) @@ -4114,12 +4131,13 @@ vy_build_insert_stmt(struct vy_lsm *lsm, struct vy_mem *mem, return -1; vy_stmt_set_lsn(region_stmt, lsn); struct vy_entry entry; - entry.stmt = region_stmt; - entry.hint = vy_stmt_hint(region_stmt, lsm->cmp_def); - if (vy_mem_insert(mem, entry) != 0) - return -1; - vy_mem_commit_stmt(mem, entry); - vy_stmt_counter_acct_tuple(&lsm->stat.memory.count, region_stmt); + vy_stmt_foreach_entry(entry, region_stmt, lsm->cmp_def) { + if (vy_mem_insert(mem, entry) != 0) + return -1; + vy_mem_commit_stmt(mem, entry); + vy_stmt_counter_acct_tuple(&lsm->stat.memory.count, + region_stmt); + } return 0; } @@ -4218,11 +4236,10 @@ vy_build_recover_stmt(struct vy_lsm *lsm, struct vy_lsm *pk, struct tuple *insert = NULL; struct tuple *old_tuple = old.stmt; if (old_tuple != NULL) { - delete = vy_stmt_extract_key(old_tuple, lsm->cmp_def, - lsm->env->key_format, -1); + delete = vy_stmt_new_surrogate_delete(lsm->mem_format, + old_tuple); if (delete == NULL) return -1; - vy_stmt_set_type(delete, IPROTO_DELETE); } enum iproto_type type = vy_stmt_type(mem_stmt); if (type == IPROTO_REPLACE || type == IPROTO_INSERT) { @@ -4578,13 +4595,15 @@ vy_deferred_delete_on_replace(struct trigger *trigger, void *event) mem = lsm->mem; } struct vy_entry entry; - entry.stmt = delete; - entry.hint = vy_stmt_hint(delete, lsm->cmp_def); - rc = vy_lsm_set(lsm, mem, entry, ®ion_stmt); + vy_stmt_foreach_entry(entry, delete, lsm->cmp_def) { + rc = vy_lsm_set(lsm, mem, entry, ®ion_stmt); + if (rc != 0) + break; + entry.stmt = region_stmt; + vy_lsm_commit_stmt(lsm, mem, entry); + } if (rc != 0) break; - entry.stmt = region_stmt; - vy_lsm_commit_stmt(lsm, mem, entry); if (!is_first_statement) continue; diff --git a/src/box/vy_stmt.h b/src/box/vy_stmt.h index b0731d3d..0026d614 100644 --- a/src/box/vy_stmt.h +++ b/src/box/vy_stmt.h @@ -750,6 +750,31 @@ vy_entry_compare_with_raw_key(struct vy_entry entry, key, key_hint, key_def); } +/** + * Iterate over each key indexed in the given statement. + * @param entry loop variable + * @param src_stmt source statement + * @param key_def key definition + * + * For a multikey index, entry.hint is set to multikey entry offset + * and the loop iterates over each offset stored in the statement. + * + * For a unikey index, entry.hint is initialized with vy_stmt_hint() + * and the loop breaks after the first iteration. + * + * entry.stmt is set to src_stmt on each iteration. + */ +#define vy_stmt_foreach_entry(entry, src_stmt, key_def) \ + for (uint32_t multikey_idx = 0, \ + multikey_count = !key_def_is_multikey((key_def)) ? 1 : \ + tuple_multikey_count((src_stmt), (key_def)); \ + multikey_idx < multikey_count && \ + (((entry).stmt = (src_stmt)), \ + ((entry).hint = !key_def_is_multikey((key_def)) ? \ + vy_stmt_hint((src_stmt), (key_def)) : \ + multikey_idx), true); \ + ++multikey_idx) + #if defined(__cplusplus) } /* extern "C" */ #endif /* defined(__cplusplus) */ diff --git a/src/box/vy_tx.c b/src/box/vy_tx.c index 92d7c2a1..b1dee0fa 100644 --- a/src/box/vy_tx.c +++ b/src/box/vy_tx.c @@ -629,17 +629,21 @@ vy_tx_handle_deferred_delete(struct vy_tx *tx, struct txv *v) int rc = 0; for (uint32_t i = 1; i < space->index_count; i++) { struct vy_lsm *lsm = vy_lsm(space->index[i]); - struct vy_entry delete_entry; - delete_entry.stmt = delete_stmt; - delete_entry.hint = vy_stmt_hint(delete_stmt, lsm->cmp_def); - struct txv *delete_txv = txv_new(tx, lsm, delete_entry, - UINT64_MAX); - if (delete_txv == NULL) { - rc = -1; + struct vy_entry entry; + vy_stmt_foreach_entry(entry, delete_stmt, lsm->cmp_def) { + struct txv *delete_txv = txv_new(tx, lsm, entry, + UINT64_MAX); + if (delete_txv == NULL) { + rc = -1; + break; + } + stailq_insert_entry(&tx->log, delete_txv, v, + next_in_log); + vy_stmt_counter_acct_tuple(&lsm->stat.txw.count, + entry.stmt); + } + if (rc != 0) break; - } - stailq_insert_entry(&tx->log, delete_txv, v, next_in_log); - vy_stmt_counter_acct_tuple(&lsm->stat.txw.count, delete_stmt); } tuple_unref(delete_stmt); return rc; @@ -1028,7 +1032,12 @@ vy_tx_track_point(struct vy_tx *tx, struct vy_lsm *lsm, struct vy_entry entry) return vy_tx_track(tx, lsm, entry, true, entry, true); } -int +/** + * Add one statement entry to a transaction. We add one entry + * for each index, and with multikey indexes it is possible there + * are multiple entries of a single statement in a single index. + */ +static int vy_tx_set_entry(struct vy_tx *tx, struct vy_lsm *lsm, struct vy_entry entry, uint64_t column_mask) { @@ -1123,6 +1132,18 @@ vy_tx_set_entry(struct vy_tx *tx, struct vy_lsm *lsm, return 0; } +int +vy_tx_set_with_colmask(struct vy_tx *tx, struct vy_lsm *lsm, + struct tuple *stmt, uint64_t column_mask) +{ + struct vy_entry entry; + vy_stmt_foreach_entry(entry, stmt, lsm->cmp_def) { + if (vy_tx_set_entry(tx, lsm, entry, column_mask) != 0) + return -1; + } + return 0; +} + void tx_manager_abort_writers_for_ddl(struct tx_manager *xm, struct space *space) { diff --git a/src/box/vy_tx.h b/src/box/vy_tx.h index a5c0856d..2712263b 100644 --- a/src/box/vy_tx.h +++ b/src/box/vy_tx.h @@ -393,15 +393,6 @@ int vy_tx_track_point(struct vy_tx *tx, struct vy_lsm *lsm, struct vy_entry entry); /** - * Add one statement entry to a transaction. We add one entry - * for each index, and with multikey indexes it is possible there - * are multiple entries of a single statement in a single index. - */ -int -vy_tx_set_entry(struct vy_tx *tx, struct vy_lsm *lsm, - struct vy_entry entry, uint64_t column_mask); - -/** * Insert a statement into a transaction write set. * @param tx Transaction. * @param lsm LSM tree the statement is for. @@ -411,15 +402,9 @@ vy_tx_set_entry(struct vy_tx *tx, struct vy_lsm *lsm, * @retval 0 Success * @retval -1 Memory allocation error. */ -static inline int +int vy_tx_set_with_colmask(struct vy_tx *tx, struct vy_lsm *lsm, - struct tuple *stmt, uint64_t column_mask) -{ - struct vy_entry entry; - entry.stmt = stmt; - entry.hint = vy_stmt_hint(stmt, lsm->cmp_def); - return vy_tx_set_entry(tx, lsm, entry, column_mask); -} + struct tuple *stmt, uint64_t column_mask); static inline int vy_tx_set(struct vy_tx *tx, struct vy_lsm *lsm, struct tuple *stmt) diff --git a/test/box/bitset.result b/test/box/bitset.result index a06207df..78f74ec3 100644 --- a/test/box/bitset.result +++ b/test/box/bitset.result @@ -1981,3 +1981,18 @@ s:drop() s = nil --- ... +-- Bitset index cannot be multikey. +s = box.schema.space.create('test') +--- +... +_ = s:create_index('primary') +--- +... +_ = s:create_index('bitset', {type = 'bitset', parts = {{'[2][*]', 'unsigned'}}}) +--- +- error: 'Can''t create or modify index ''bitset'' in space ''test'': BITSET index + cannot be multikey' +... +s:drop() +--- +... diff --git a/test/box/bitset.test.lua b/test/box/bitset.test.lua index d9760e27..eb013a1c 100644 --- a/test/box/bitset.test.lua +++ b/test/box/bitset.test.lua @@ -147,3 +147,9 @@ for j=1,100 do check(math.random(9) - 1, {iterator = box.index.BITS_ALL_NOT_SET} good s:drop() s = nil + +-- Bitset index cannot be multikey. +s = box.schema.space.create('test') +_ = s:create_index('primary') +_ = s:create_index('bitset', {type = 'bitset', parts = {{'[2][*]', 'unsigned'}}}) +s:drop() diff --git a/test/box/hash.result b/test/box/hash.result index f0c30ade..9f08c49b 100644 --- a/test/box/hash.result +++ b/test/box/hash.result @@ -832,3 +832,18 @@ s:get(-9007199254740994LL) s:drop() --- ... +-- Hash index cannot be multikey. +s = box.schema.space.create('test') +--- +... +_ = s:create_index('primary') +--- +... +_ = s:create_index('hash', {type = 'hash', parts = {{'[2][*]', 'unsigned'}}}) +--- +- error: 'Can''t create or modify index ''hash'' in space ''test'': HASH index cannot + be multikey' +... +s:drop() +--- +... diff --git a/test/box/hash.test.lua b/test/box/hash.test.lua index d97312e3..9801873c 100644 --- a/test/box/hash.test.lua +++ b/test/box/hash.test.lua @@ -347,3 +347,9 @@ s:get(-1LL) s:get(9007199254740992LL) s:get(-9007199254740994LL) s:drop() + +-- Hash index cannot be multikey. +s = box.schema.space.create('test') +_ = s:create_index('primary') +_ = s:create_index('hash', {type = 'hash', parts = {{'[2][*]', 'unsigned'}}}) +s:drop() diff --git a/test/box/rtree_misc.result b/test/box/rtree_misc.result index 36d5b8f5..6e48bacc 100644 --- a/test/box/rtree_misc.result +++ b/test/box/rtree_misc.result @@ -667,3 +667,18 @@ i:select({1, 2, 3, 4, 5, 6}, {iterator = 'BITS_ALL_SET' } ) s:drop() --- ... +-- Rtree index cannot be multikey. +s = box.schema.space.create('test') +--- +... +_ = s:create_index('primary') +--- +... +_ = s:create_index('rtree', {type = 'rtree', parts = {{'[2][*]', 'array'}}}) +--- +- error: 'Can''t create or modify index ''rtree'' in space ''test'': RTREE index cannot + be multikey' +... +s:drop() +--- +... diff --git a/test/box/rtree_misc.test.lua b/test/box/rtree_misc.test.lua index cef66fc1..000a928e 100644 --- a/test/box/rtree_misc.test.lua +++ b/test/box/rtree_misc.test.lua @@ -237,3 +237,9 @@ i:select({0, 0, 0}, {iterator = 'neighbor'}) i:select({1, 2, 3, 4, 5, 6}, {iterator = 'BITS_ALL_SET' } ) s:drop() + +-- Rtree index cannot be multikey. +s = box.schema.space.create('test') +_ = s:create_index('primary') +_ = s:create_index('rtree', {type = 'rtree', parts = {{'[2][*]', 'array'}}}) +s:drop() diff --git a/test/engine/engine.cfg b/test/engine/engine.cfg index 8f34bae8..9f07629b 100644 --- a/test/engine/engine.cfg +++ b/test/engine/engine.cfg @@ -2,9 +2,6 @@ "*": { "memtx": {"engine": "memtx"}, "vinyl": {"engine": "vinyl"} - }, - "multikey.test.lua": { - "memtx": {"engine": "memtx"} } } diff --git a/test/engine/multikey.result b/test/engine/multikey.result index 293e27f1..1d5d9e20 100644 --- a/test/engine/multikey.result +++ b/test/engine/multikey.result @@ -7,20 +7,6 @@ engine = test_run:get_cfg('engine') -- -- gh-1260: Multikey indexes. -- -s = box.schema.space.create('withdata', {engine = 'vinyl'}) ---- -... -pk = s:create_index('pk') ---- -... --- Vinyl's space can't be multikey (yet). -_ = s:create_index('idx', {parts = {{3, 'str', path = '[*].fname'}, {3, 'str', path = '[*].sname'}}}) ---- -- error: Vinyl does not support multikey indexes -... -s:drop() ---- -... s = box.schema.space.create('withdata', {engine = engine}) --- ... @@ -33,22 +19,6 @@ _ = s:create_index('idx', {parts = {{3, 'str', path = '[*].fname'}}}) pk = s:create_index('pk') --- ... --- Only tree index type may be mutlikey. -_ = s:create_index('idx', {type = 'hash', unique = true, parts = {{3, 'str', path = '[*].fname'}}}) ---- -- error: 'Can''t create or modify index ''idx'' in space ''withdata'': HASH index - cannot be multikey' -... -_ = s:create_index('idx', {type = 'bitset', unique = false, parts = {{3, 'str', path = '[*].fname'}}}) ---- -- error: 'Can''t create or modify index ''idx'' in space ''withdata'': BITSET index - cannot be multikey' -... -_ = s:create_index('idx', {type = 'rtree', unique = false, parts = {{3, 'array', path = '[*].fname'}}}) ---- -- error: 'Can''t create or modify index ''idx'' in space ''withdata'': RTREE index - cannot be multikey' -... -- Test incompatible multikey index parts. _ = s:create_index('idx3', {parts = {{3, 'str', path = '[*].fname'}, {3, 'str', path = '["data"][*].sname'}}}) --- @@ -146,10 +116,8 @@ arr_idx:select({1}) - [5, [1, 1, 1], [{'fname': 'A', 'sname': 'B'}, {'fname': 'C', 'sname': 'D'}, { 'fname': 'A', 'sname': 'B'}]] ... -s:delete(5) +_ = s:delete(5) --- -- [5, [1, 1, 1], [{'fname': 'A', 'sname': 'B'}, {'fname': 'C', 'sname': 'D'}, {'fname': 'A', - 'sname': 'B'}]] ... -- Check that there is no garbage in index. arr_idx:select({1}) @@ -162,9 +130,8 @@ idx:get({'A', 'B'}) idx:get({'C', 'D'}) --- ... -idx:delete({'Vasya', 'Pupkin'}) +_ = idx:delete({'Vasya', 'Pupkin'}) --- -- [1, [1, 2, 3], [{'fname': 'James', 'sname': 'Bond'}, {'fname': 'Vasya', 'sname': 'Pupkin'}]] ... s:insert({6, {1, 2}, {{fname='Vasya', sname='Pupkin'}}}) --- @@ -286,9 +253,8 @@ idx0:select() - - [1, [1, 1, 1]] - [2, [2, 2]] ... -idx0:delete(2) +_ = idx0:delete(2) --- -- [2, [2, 2]] ... idx0:get(2) --- diff --git a/test/engine/multikey.test.lua b/test/engine/multikey.test.lua index 0916bf52..f32f49d2 100644 --- a/test/engine/multikey.test.lua +++ b/test/engine/multikey.test.lua @@ -4,20 +4,10 @@ engine = test_run:get_cfg('engine') -- -- gh-1260: Multikey indexes. -- -s = box.schema.space.create('withdata', {engine = 'vinyl'}) -pk = s:create_index('pk') --- Vinyl's space can't be multikey (yet). -_ = s:create_index('idx', {parts = {{3, 'str', path = '[*].fname'}, {3, 'str', path = '[*].sname'}}}) -s:drop() - s = box.schema.space.create('withdata', {engine = engine}) -- Primary index must be unique so it can't be multikey. _ = s:create_index('idx', {parts = {{3, 'str', path = '[*].fname'}}}) pk = s:create_index('pk') --- Only tree index type may be mutlikey. -_ = s:create_index('idx', {type = 'hash', unique = true, parts = {{3, 'str', path = '[*].fname'}}}) -_ = s:create_index('idx', {type = 'bitset', unique = false, parts = {{3, 'str', path = '[*].fname'}}}) -_ = s:create_index('idx', {type = 'rtree', unique = false, parts = {{3, 'array', path = '[*].fname'}}}) -- Test incompatible multikey index parts. _ = s:create_index('idx3', {parts = {{3, 'str', path = '[*].fname'}, {3, 'str', path = '["data"][*].sname'}}}) _ = s:create_index('idx2', {parts = {{3, 'str', path = '[*].fname'}, {3, 'str', path = '[*].sname[*].a'}}}) @@ -43,12 +33,12 @@ idx:select() -- Duplicates in multikey parts. s:insert({5, {1, 1, 1}, {{fname='A', sname='B'}, {fname='C', sname='D'}, {fname='A', sname='B'}}}) arr_idx:select({1}) -s:delete(5) +_ = s:delete(5) -- Check that there is no garbage in index. arr_idx:select({1}) idx:get({'A', 'B'}) idx:get({'C', 'D'}) -idx:delete({'Vasya', 'Pupkin'}) +_ = idx:delete({'Vasya', 'Pupkin'}) s:insert({6, {1, 2}, {{fname='Vasya', sname='Pupkin'}}}) s:insert({7, {1}, {{fname='James', sname='Bond'}}}) arr_idx:select({1}) @@ -84,7 +74,7 @@ idx0:get(2) idx0:get(1) idx0:get(3) idx0:select() -idx0:delete(2) +_ = idx0:delete(2) idx0:get(2) idx0:select() s:drop() -- 2.11.0