From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from localhost (localhost [127.0.0.1]) by turing.freelists.org (Avenir Technologies Mail Multiplex) with ESMTP id 11B7324910 for ; Tue, 24 Jul 2018 07:58:25 -0400 (EDT) Received: from turing.freelists.org ([127.0.0.1]) by localhost (turing.freelists.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id BH3O2bFz_OUP for ; Tue, 24 Jul 2018 07:58:24 -0400 (EDT) Received: from smtp42.i.mail.ru (smtp42.i.mail.ru [94.100.177.102]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by turing.freelists.org (Avenir Technologies Mail Multiplex) with ESMTPS id 6224823599 for ; Tue, 24 Jul 2018 07:58:24 -0400 (EDT) Received: from [185.6.245.156] (port=60392 helo=mimeev-ThinkPad-T460p.mail.msk) by smtp42.i.mail.ru with esmtpa (envelope-from ) id 1fhvxO-0000KL-OE for tarantool-patches@freelists.org; Tue, 24 Jul 2018 14:58:22 +0300 From: imeevma@tarantool.org Subject: [tarantool-patches] [PATCH v3 7/7] box: methods for ephemeral space and its index Date: Tue, 24 Jul 2018 14:58:22 +0300 Message-Id: In-Reply-To: References: Sender: tarantool-patches-bounce@freelists.org Errors-to: tarantool-patches-bounce@freelists.org Reply-To: tarantool-patches@freelists.org List-help: List-unsubscribe: List-software: Ecartis version 1.0.0 List-Id: tarantool-patches List-subscribe: List-owner: List-post: List-archive: To: tarantool-patches@freelists.org This patch defines most methods for index of ephemeral space and ephemeral space. Closes #3375. --- src/box/box.cc | 64 ++- src/box/box.h | 21 + src/box/index.cc | 158 ++++++++ src/box/index.h | 137 +++++++ src/box/lua/index.c | 422 +++++++++++++++++++- src/box/lua/misc.cc | 10 +- src/box/lua/misc.h | 9 + src/box/lua/schema.lua | 156 ++++++++ test/box/ephemeral_space.result | 802 +++++++++++++++++++++++++++++++++++++- test/box/ephemeral_space.test.lua | 240 ++++++++++++ test/engine/iterator.result | 2 +- 11 files changed, 1992 insertions(+), 29 deletions(-) diff --git a/src/box/box.cc b/src/box/box.cc index e811496..40d0f24 100644 --- a/src/box/box.cc +++ b/src/box/box.cc @@ -1099,16 +1099,11 @@ box_process1(struct request *request, box_tuple_t **result) return box_process_rw(request, space, result); } -int -box_select(uint32_t space_id, uint32_t index_id, - int iterator, uint32_t offset, uint32_t limit, - const char *key, const char *key_end, - struct port *port) +static inline int +box_select_impl(struct space *space, uint32_t index_id, + int iterator, uint32_t offset, uint32_t limit, + const char *key, struct port *port) { - (void)key_end; - - rmean_collect(rmean_box, IPROTO_SELECT, 1); - if (iterator < 0 || iterator >= iterator_type_MAX) { diag_set(ClientError, ER_ILLEGAL_PARAMS, "Invalid iterator type"); @@ -1116,11 +1111,6 @@ box_select(uint32_t space_id, uint32_t index_id, return -1; } - struct space *space = space_cache_find(space_id); - if (space == NULL) - return -1; - if (access_check_space(space, PRIV_R) != 0) - return -1; struct index *index = index_find(space, index_id); if (index == NULL) return -1; @@ -1135,16 +1125,10 @@ box_select(uint32_t space_id, uint32_t index_id, return -1; }); - struct txn *txn; - if (txn_begin_ro_stmt(space, &txn) != 0) - return -1; - struct iterator *it = index_create_iterator(index, type, key, part_count); - if (it == NULL) { - txn_rollback_stmt(); + if (it == NULL) return -1; - } int rc = 0; uint32_t found = 0; @@ -1167,9 +1151,36 @@ box_select(uint32_t space_id, uint32_t index_id, if (rc != 0) { port_destroy(port); + return -1; + } + return 0; +} + +int +box_select(uint32_t space_id, uint32_t index_id, + int iterator, uint32_t offset, uint32_t limit, + const char *key, const char *key_end, + struct port *port) +{ + (void)key_end; + + rmean_collect(rmean_box, IPROTO_SELECT, 1); + + struct space *space = space_cache_find(space_id); + if (space == NULL) + return -1; + if (access_check_space(space, PRIV_R) != 0) + return -1; + struct txn *txn; + if (txn_begin_ro_stmt(space, &txn) != 0) + return -1; + + if (box_select_impl(space, index_id, iterator, offset, limit, key, + port) != 0) { txn_rollback_stmt(); return -1; } + txn_commit_ro_stmt(txn); return 0; } @@ -1251,6 +1262,17 @@ box_upsert(uint32_t space_id, uint32_t index_id, const char *tuple, } int +box_ephemeral_select(struct space *space, uint32_t index_id, + int iterator, uint32_t offset, uint32_t limit, + const char *key, const char *key_end, + struct port *port) +{ + (void)key_end; + return box_select_impl(space, index_id, iterator, offset, limit, key, + port); +} + +int box_ephemeral_insert(struct space *space, const char *tuple, const char *tuple_end, box_tuple_t **result) { diff --git a/src/box/box.h b/src/box/box.h index e582fbc..40fc31b 100644 --- a/src/box/box.h +++ b/src/box/box.h @@ -404,6 +404,27 @@ int box_process1(struct request *request, box_tuple_t **result); /** + * Select the number of tuple matched conditions. + * + * \param space ephemeral space. + * \param index_id id of index for select. + * \param iterator iterator to use for select. + * \param offset offset for select. + * \param limit limit for select. + * \param key key for select. + * \param key_end end of \a key. + * \param port port to save result of select to. + * + * \retval -1 on error. + * \retval 0 on success. + */ +int +box_ephemeral_select(struct space *space, uint32_t index_id, + int iterator, uint32_t offset, uint32_t limit, + const char *key, const char *key_end, + struct port *port); + +/** * Execute an INSERT request for ephemeral spaces. * * \param space ephemeral space. diff --git a/src/box/index.cc b/src/box/index.cc index 372e0f3..e2cb4f0 100644 --- a/src/box/index.cc +++ b/src/box/index.cc @@ -198,6 +198,15 @@ box_index_len(uint32_t space_id, uint32_t index_id) } ssize_t +box_index_len_ephemeral(struct space *space, uint32_t index_id) +{ + struct index *index = index_find(space, index_id); + if (index == NULL) + return -1; + return index_size(index); +} + +ssize_t box_index_bsize(uint32_t space_id, uint32_t index_id) { struct space *space; @@ -208,6 +217,15 @@ box_index_bsize(uint32_t space_id, uint32_t index_id) return index_bsize(index); } +ssize_t +box_index_bsize_ephemeral(struct space *space, uint32_t index_id) +{ + struct index *index = index_find(space, index_id); + if (index == NULL) + return 0; + return index_bsize(index); +} + int box_index_random(uint32_t space_id, uint32_t index_id, uint32_t rnd, box_tuple_t **result) @@ -226,6 +244,19 @@ box_index_random(uint32_t space_id, uint32_t index_id, uint32_t rnd, } int +box_index_random_ephemeral(struct space *space, uint32_t index_id, + uint32_t rnd, box_tuple_t **result) +{ + assert(result != NULL); + struct index *index = index_find(space, index_id); + if (index == NULL || index_random(index, rnd, result) != 0) + return -1; + if (*result != NULL) + tuple_bless(*result); + return 0; +} + +int box_index_get(uint32_t space_id, uint32_t index_id, const char *key, const char *key_end, box_tuple_t **result) { @@ -259,6 +290,29 @@ box_index_get(uint32_t space_id, uint32_t index_id, const char *key, } int +box_index_get_ephemeral(struct space *space, uint32_t index_id, const char *key, + const char *key_end, box_tuple_t **result) +{ + assert(key != NULL && key_end != NULL && result != NULL); + mp_tuple_assert(key, key_end); + struct index *index = index_find(space, index_id); + if (index == NULL) + return -1; + if (!index->def->opts.is_unique) { + diag_set(ClientError, ER_MORE_THAN_ONE_TUPLE); + return -1; + } + uint32_t part_count = mp_decode_array(&key); + if (exact_key_validate(index->def->key_def, key, part_count)) + return -1; + if (index_get(index, key, part_count, result) != 0) + return -1; + if (*result != NULL) + tuple_bless(*result); + return 0; +} + +int box_index_min(uint32_t space_id, uint32_t index_id, const char *key, const char *key_end, box_tuple_t **result) { @@ -291,6 +345,29 @@ box_index_min(uint32_t space_id, uint32_t index_id, const char *key, } int +box_index_min_ephemeral(struct space *space, uint32_t index_id, const char *key, + const char *key_end, box_tuple_t **result) +{ + assert(key != NULL && key_end != NULL && result != NULL); + mp_tuple_assert(key, key_end); + struct index *index = index_find(space, index_id); + if (index == NULL) + return -1; + if (index->def->type != TREE) { + diag_set(UnsupportedIndexFeature, index->def, "min()"); + return -1; + } + uint32_t part_count = mp_decode_array(&key); + if (key_validate(index->def, ITER_GE, key, part_count)) + return -1; + if (index_min(index, key, part_count, result) != 0) + return -1; + if (*result != NULL) + tuple_bless(*result); + return 0; +} + +int box_index_max(uint32_t space_id, uint32_t index_id, const char *key, const char *key_end, box_tuple_t **result) { @@ -322,6 +399,29 @@ box_index_max(uint32_t space_id, uint32_t index_id, const char *key, return 0; } +int +box_index_max_ephemeral(struct space *space, uint32_t index_id, const char *key, + const char *key_end, box_tuple_t **result) +{ + mp_tuple_assert(key, key_end); + assert(result != NULL); + struct index *index = index_find(space, index_id); + if (index == NULL) + return -1; + if (index->def->type != TREE) { + diag_set(UnsupportedIndexFeature, index->def, "max()"); + return -1; + } + uint32_t part_count = mp_decode_array(&key); + if (key_validate(index->def, ITER_LE, key, part_count)) + return -1; + if (index_max(index, key, part_count, result) != 0) + return -1; + if (*result != NULL) + tuple_bless(*result); + return 0; +} + ssize_t box_index_count(uint32_t space_id, uint32_t index_id, int type, const char *key, const char *key_end) @@ -354,6 +454,30 @@ box_index_count(uint32_t space_id, uint32_t index_id, int type, return count; } +ssize_t +box_index_count_ephemeral(struct space *space, uint32_t index_id, int type, + const char *key, const char *key_end) +{ + assert(key != NULL && key_end != NULL); + mp_tuple_assert(key, key_end); + if (type < 0 || type >= iterator_type_MAX) { + diag_set(ClientError, ER_ILLEGAL_PARAMS, + "Invalid iterator type"); + return -1; + } + enum iterator_type itype = (enum iterator_type) type; + struct index *index = index_find(space, index_id); + if (index == NULL) + return -1; + uint32_t part_count = mp_decode_array(&key); + if (key_validate(index->def, itype, key, part_count)) + return -1; + ssize_t count = index_count(index, itype, key, part_count); + if (count < 0) + return -1; + return count; +} + /* }}} */ /* {{{ Iterators ************************************************/ @@ -391,6 +515,30 @@ box_index_iterator(uint32_t space_id, uint32_t index_id, int type, return it; } +box_iterator_t * +box_index_iterator_ephemeral(struct space *space, uint32_t index_id, int type, + const char *key, const char *key_end) +{ + assert(key != NULL && key_end != NULL); + mp_tuple_assert(key, key_end); + if (type < 0 || type >= iterator_type_MAX) { + diag_set(ClientError, ER_ILLEGAL_PARAMS, + "Invalid iterator type"); + return NULL; + } + struct index *index = index_find(space, index_id); + if (index == NULL) + return NULL; + enum iterator_type itype = (enum iterator_type) type; + assert(mp_typeof(*key) == MP_ARRAY); /* checked by Lua */ + uint32_t part_count = mp_decode_array(&key); + if (key_validate(index->def, itype, key, part_count)) + return NULL; + struct iterator *it = index_create_iterator(index, itype, + key, part_count); + return it; +} + int box_iterator_next(box_iterator_t *itr, box_tuple_t **result) { @@ -435,6 +583,16 @@ box_index_compact(uint32_t space_id, uint32_t index_id) return 0; } +int +box_index_compact_ephemeral(struct space *space, uint32_t index_id) +{ + struct index *index = index_find(space, index_id); + if (index == NULL) + return -1; + index_compact(index); + return 0; +} + /* }}} */ /* {{{ Internal API */ diff --git a/src/box/index.h b/src/box/index.h index 73ee000..83e30bf 100644 --- a/src/box/index.h +++ b/src/box/index.h @@ -236,6 +236,143 @@ box_index_stat(uint32_t space_id, uint32_t index_id, int box_index_compact(uint32_t space_id, uint32_t index_id); +/** + * Return the number of element in the index. + * + * \param space - ephemeral space. + * \param index_id - index identifier. + * + * \retval -1 on error. + * \retval >= 0 on success. + */ +ssize_t +box_index_len_ephemeral(struct space *space, uint32_t index_id); + +/** + * Return the number of bytes used in memory by the index. + * If space have no index it returns 0. + * + * \param space ephemeral space. + * \param index_id index identifier. + * + * \retval -1 on error. + * \retval >= 0 on success. + */ +ssize_t +box_index_bsize_ephemeral(struct space *space, uint32_t index_id); + +/** + * Return a random tuple from the index. + * + * \param space ephemeral space. + * \param index_id index identifier. + * \param rnd random seed. + * \param[out] result a tuple or NULL if index is empty. + * + * \retval -1 on error. + * \retval 0 on success. + */ +int +box_index_random_ephemeral(struct space *space, uint32_t index_id, + uint32_t rnd, box_tuple_t **result); + +/** + * Get a tuple from index by the key. + * + * \param space ephemeral space. + * \param index_id index identifier. + * \param key encoded key in MsgPack Array format. + * \param key_end the end of encoded \a key. + * \param[out] result a tuple or NULL if index is empty. + * + * \retval -1 on error. + * \retval 0 on success. + * \pre key != NULL + */ +int +box_index_get_ephemeral(struct space *space, uint32_t index_id, const char *key, + const char *key_end, box_tuple_t **result); + +/** + * Return first (minimal) tuple matched the provided key. + * + * \param space ephemeral space. + * \param index_id index identifier. + * \param key encoded key in MsgPack Array format. + * \param key_end the end of encoded \a key. + * \param[out] result a tuple or NULL if index is empty. + * + * \retval -1 on error. + * \retval 0 on success. + */ +int +box_index_min_ephemeral(struct space *space, uint32_t index_id, const char *key, + const char *key_end, box_tuple_t **result); + +/** + * Return last (maximal) tuple matched the provided key. + * + * \param space ephemeral space. + * \param index_id index identifier. + * \param key encoded key in MsgPack Array format. + * \param key_end the end of encoded \a key. + * \param[out] result a tuple or NULL if index is empty. + * + * \retval -1 on error. + * \retval 0 on success. + */ +int +box_index_max_ephemeral(struct space *space, uint32_t index_id, const char *key, + const char *key_end, box_tuple_t **result); + +/** + * Count the number of tuple matched the provided key. + * + * \param space ephemeral space. + * \param index_id index identifier. + * \param key encoded key in MsgPack Array format. + * \param key_end the end of encoded \a key. + * \param[out] result a tuple or NULL if index is empty. + * + * \retval -1 on error. + * \retval 0 on success. + */ +ssize_t +box_index_count_ephemeral(struct space *space, uint32_t index_id, int type, + const char *key, const char *key_end); + +/** + * Allocate and initialize iterator for ephemeral space + * + * A returned iterator must be destroyed by box_iterator_free(). + * + * \param space ephemeral space. + * \param index_id index identifier. + * \param type \link iterator_type iterator type \endlink + * \param key encoded key in MsgPack Array format ([part1, part2, ...]). + * \param key_end the end of encoded \a key + * + * \retval NULL on error (check box_error_last()) + * \retval iterator otherwise + * \sa box_iterator_next() + * \sa box_iterator_free() + */ +box_iterator_t * +box_index_iterator_ephemeral(struct space *space, uint32_t index_id, int type, + const char *key, const char *key_end); + +/** + * Trigger index compaction. + * + * \param space ephemeral space. + * \param index_id index identifier. + * + * \retval -1 on error. + * \retval 0 on success. + */ +int +box_index_compact_ephemeral(struct space *space, uint32_t index_id); + struct iterator { /** * Iterate to the next tuple. diff --git a/src/box/lua/index.c b/src/box/lua/index.c index ef89c39..79427e8 100644 --- a/src/box/lua/index.c +++ b/src/box/lua/index.c @@ -35,7 +35,9 @@ #include "box/info.h" #include "box/lua/info.h" #include "box/lua/tuple.h" -#include "box/lua/misc.h" /* lbox_encode_tuple_on_gc() */ +#include "box/lua/misc.h" +#include "box/port.h" +#include "box/box.h" /** {{{ box.index Lua library: access to spaces and indexes */ @@ -58,6 +60,30 @@ lbox_insert(lua_State *L) return luaT_pushtupleornil(L, result); } +/** + * Insert tuple into ephemeral space. + * + * @param L Lua stack to get space and tuple from. + * + * @retval not nil tuple that was inserted. + * @retval nil error. + */ +static int +lbox_insert_ephemeral(lua_State *L) +{ + if (lua_gettop(L) != 2) + return luaL_error(L, "Usage space:insert(tuple)"); + + struct space *space = lua_checkephemeralspace(L, 1); + size_t tuple_len; + const char *tuple = lbox_encode_tuple_on_gc(L, 2, &tuple_len); + + struct tuple *result; + if (box_ephemeral_insert(space, tuple, tuple + tuple_len, &result) != 0) + return luaT_error(L); + return luaT_pushtupleornil(L, result); +} + static int lbox_replace(lua_State *L) { @@ -74,6 +100,31 @@ lbox_replace(lua_State *L) return luaT_pushtupleornil(L, result); } +/** + * Replace tuple from ephemeral space. + * + * @param L Lua stack to get space and tuple from. + * + * @retval not nil tuple that was inserted. + * @retval nil error. + */ +static int +lbox_replace_ephemeral(lua_State *L) +{ + if (lua_gettop(L) != 2) + return luaL_error(L, "Usage space:replace(tuple)"); + + struct space *space = lua_checkephemeralspace(L, 1); + size_t tuple_len; + const char *tuple = lbox_encode_tuple_on_gc(L, 2, &tuple_len); + + struct tuple *result; + if (box_ephemeral_replace(space, tuple, tuple + tuple_len, + &result) != 0) + return luaT_error(L); + return luaT_pushtupleornil(L, result); +} + static int lbox_index_update(lua_State *L) { @@ -96,6 +147,36 @@ lbox_index_update(lua_State *L) return luaT_pushtupleornil(L, result); } +/** + * Update tuple matched provided key. + * + * @param L Lua stack to get space, index_id, key and ops from. + * + * @retval not nil tuple - result of update. + * @retval nil error. + */ +static int +lbox_index_update_ephemeral(lua_State *L) +{ + if (lua_gettop(L) != 4 || !lua_isnumber(L, 2) || + (!lua_istable(L, 3) && luaT_istuple(L, 3) == NULL) || + (!lua_istable(L, 4) && luaT_istuple(L, 4) == NULL)) + return luaL_error(L, "Usage index:update(key, ops)"); + + struct space *space = lua_checkephemeralspace(L, 1); + uint32_t index_id = lua_tonumber(L, 2); + size_t key_len; + const char *key = lbox_encode_tuple_on_gc(L, 3, &key_len); + size_t ops_len; + const char *ops = lbox_encode_tuple_on_gc(L, 4, &ops_len); + + struct tuple *result; + if (box_ephemeral_update(space, index_id, key, key + key_len, + ops, ops + ops_len, 1, &result) != 0) + return luaT_error(L); + return luaT_pushtupleornil(L, result); +} + static int lbox_upsert(lua_State *L) { @@ -117,6 +198,35 @@ lbox_upsert(lua_State *L) return luaT_pushtupleornil(L, result); } +/** + * Insert or update tuple from ephemeral space. + * + * @param L Lua stack to get space, tuple and ops from. + * + * @retval not nil tuple - result of upsert. + * @retval nil error. + */ +static int +lbox_upsert_ephemeral(lua_State *L) +{ + if (lua_gettop(L) != 3 || + (!lua_istable(L, 2) && luaT_istuple(L, 2) == NULL) || + (!lua_istable(L, 3) && luaT_istuple(L, 3) == NULL)) + return luaL_error(L, "Usage index:upsert(key, ops)"); + + struct space *space = lua_checkephemeralspace(L, 1); + size_t tuple_len; + const char *tuple = lbox_encode_tuple_on_gc(L, 2, &tuple_len); + size_t ops_len; + const char *ops = lbox_encode_tuple_on_gc(L, 3, &ops_len); + + struct tuple *result; + if (box_ephemeral_upsert(space, 0, tuple, tuple + tuple_len, + ops, ops + ops_len, 1, &result) != 0) + return luaT_error(L); + return luaT_pushtupleornil(L, result); +} + static int lbox_index_delete(lua_State *L) { @@ -135,6 +245,73 @@ lbox_index_delete(lua_State *L) return luaT_pushtupleornil(L, result); } +/** + * Delete tuple matched the provided key. + * + * @param L Lua stack to get space, index_id and key from. + * + * @retval not nil tuple that was deleted. + * @retval nil error. + */ +static int +lbox_index_delete_ephemeral(lua_State *L) +{ + if (lua_gettop(L) != 3 || !lua_isnumber(L, 2) || + (!lua_istable(L, 3) && luaT_istuple(L, 3) == NULL)) + return luaL_error(L, "Usage index:delete(key)"); + + struct space *space = lua_checkephemeralspace(L, 1); + uint32_t index_id = lua_tonumber(L, 2); + size_t key_len; + const char *key = lbox_encode_tuple_on_gc(L, 3, &key_len); + + struct tuple *result; + if (box_ephemeral_delete(space, index_id, key, key + key_len, + &result) != 0) + return luaT_error(L); + return luaT_pushtupleornil(L, result); +} + +/** + * Push number of element in the index on stack. + * + * @param L Lua stack to get space from. + * + * @retval number number number of return values. + */ +static int +lbox_index_len_ephemeral(struct lua_State *L) +{ + if (lua_gettop(L) != 2 || !lua_isnumber(L, 2)) + return luaL_error(L, "Usage: index:len()"); + + struct space *space = lua_checkephemeralspace(L, 1); + uint32_t index_id = lua_tonumber(L, 2); + + lua_pushnumber(L, box_index_len_ephemeral(space, index_id)); + return 1; +} + +/** + * Push number of bytes used in memory by index on stack. + * + * @param L Lua stack to get space from. + * + * @retval number number of return values. + */ +static int +lbox_index_bsize_ephemeral(struct lua_State *L) +{ + if (lua_gettop(L) != 2 || !lua_isnumber(L, 2)) + return luaL_error(L, "Usage: index:bsize()"); + + struct space *space = lua_checkephemeralspace(L, 1); + uint32_t index_id = lua_tonumber(L, 2); + + lua_pushnumber(L, box_index_bsize_ephemeral(space, index_id)); + return 1; +} + static int lbox_index_random(lua_State *L) { @@ -152,6 +329,30 @@ lbox_index_random(lua_State *L) return luaT_pushtupleornil(L, tuple); } +/** + * Return a random tuple from the index. + * + * @param L Lua stack to get space and seed from. + * + * @retval not nil random tuple. + * @retval nil error. + */ +static int +lbox_index_random_ephemeral(lua_State *L) +{ + if (lua_gettop(L) != 3 || !lua_isnumber(L, 2) || !lua_isnumber(L, 3)) + return luaL_error(L, "Usage index:random(seed)"); + + struct space *space = lua_checkephemeralspace(L, 1); + uint32_t index_id = lua_tonumber(L, 2); + uint32_t rnd = lua_tonumber(L, 3); + + struct tuple *tuple; + if (box_index_random_ephemeral(space, index_id, rnd, &tuple) != 0) + return luaT_error(L); + return luaT_pushtupleornil(L, tuple); +} + static int lbox_index_get(lua_State *L) { @@ -169,6 +370,32 @@ lbox_index_get(lua_State *L) return luaT_pushtupleornil(L, tuple); } +/** + * Return a tuple from the index by given key. + * + * @param L Lua stack to get space and key from. + * + * @retval not nil matched tuple. + * @retval nil error. + */ +static int +lbox_index_get_ephemeral(lua_State *L) +{ + if (lua_gettop(L) != 3 || !lua_isnumber(L, 2)) + return luaL_error(L, "Usage index:get(key)"); + + struct space *space = lua_checkephemeralspace(L, 1); + uint32_t index_id = lua_tonumber(L, 2); + size_t key_len; + const char *key = lbox_encode_tuple_on_gc(L, 3, &key_len); + + struct tuple *tuple; + if (box_index_get_ephemeral(space, index_id, key, key + key_len, + &tuple) != 0) + return luaT_error(L); + return luaT_pushtupleornil(L, tuple); +} + static int lbox_index_min(lua_State *L) { @@ -186,6 +413,32 @@ lbox_index_min(lua_State *L) return luaT_pushtupleornil(L, tuple); } +/** + * Return first (minimal) tuple from the index matched provided + * key. + * + * @param L Lua stack to get space and key from. + * + * @retval not nil matched tuple. + * @retval nil error. + */ +static int +lbox_index_min_ephemeral(lua_State *L) +{ + if (lua_gettop(L) != 3 || !lua_isnumber(L, 2)) + return luaL_error(L, "Usage index:min(key)"); + struct space *space = lua_checkephemeralspace(L, 1); + uint32_t index_id = lua_tonumber(L, 2); + size_t key_len; + const char *key = lbox_encode_tuple_on_gc(L, 3, &key_len); + + struct tuple *tuple; + if (box_index_min_ephemeral(space, index_id, key, key + key_len, + &tuple) != 0) + return luaT_error(L); + return luaT_pushtupleornil(L, tuple); +} + static int lbox_index_max(lua_State *L) { @@ -203,6 +456,33 @@ lbox_index_max(lua_State *L) return luaT_pushtupleornil(L, tuple); } +/** + * Return last (maximal) tuple from the index matched provided + * key. + * + * @param L Lua stack to get space and key from. + * + * @retval not nil matched tuple. + * @retval nil error. + */ +static int +lbox_index_max_ephemeral(lua_State *L) +{ + if (lua_gettop(L) != 3 || !lua_isnumber(L, 2)) + return luaL_error(L, "Usage index:max(key)"); + + struct space *space = lua_checkephemeralspace(L, 1); + uint32_t index_id = lua_tonumber(L, 2); + size_t key_len; + const char *key = lbox_encode_tuple_on_gc(L, 3, &key_len); + + struct tuple *tuple; + if (box_index_max_ephemeral(space, index_id, key, key + key_len, + &tuple) != 0) + return luaT_error(L); + return luaT_pushtupleornil(L, tuple); +} + static int lbox_index_count(lua_State *L) { @@ -226,6 +506,65 @@ lbox_index_count(lua_State *L) return 1; } +/** + * Count tuples matched provided key and push this number on + * stack. + * + * @param L Lua stack to get space and key from. + * + * @retval number number of return values. + */ +static int +lbox_index_count_ephemeral(lua_State *L) +{ + if (lua_gettop(L) != 4 || !lua_isnumber(L, 2) || !lua_isnumber(L, 3)) + return luaL_error(L, "Usage index:count(type, key)"); + + struct space *space = lua_checkephemeralspace(L, 1); + uint32_t index_id = lua_tonumber(L, 2); + uint32_t iterator = lua_tonumber(L, 3); + size_t key_len; + const char *key = lbox_encode_tuple_on_gc(L, 4, &key_len); + + ssize_t count = box_index_count_ephemeral(space, index_id, iterator, + key, key + key_len); + if (count == -1) + return luaT_error(L); + lua_pushinteger(L, count); + return 1; +} + +/** + * Select tuples matched conditions and push them on stack as + * table. + * + * @param L Lua stack to get space, iterator, offset, limit and + * key from. + * + * @retval number number of return values. + */ +static int +lbox_index_select_ephemeral(lua_State *L) +{ + if (lua_gettop(L) != 5) { + return luaL_error(L, "Usage index:select(iterator, offset, "\ + "limit, key)"); + } + struct space *space = lua_checkephemeralspace(L, 1); + int iterator = luaL_checknumber(L, 2); + uint32_t offset = luaL_checknumber(L, 3); + uint32_t limit = luaL_checknumber(L, 4); + size_t key_len; + const char *key = lbox_encode_tuple_on_gc(L, 5, &key_len); + struct port port; + if (box_ephemeral_select(space, 0, iterator, offset, limit, + key, key + key_len, &port) != 0) + return luaT_error(L); + lbox_port_to_table(L, &port); + port_destroy(&port); + return 1; /* lua table with tuples */ +} + static void box_index_init_iterator_types(struct lua_State *L, int idx) { @@ -264,6 +603,40 @@ lbox_index_iterator(lua_State *L) return 1; } +/** + * Creates iterator for ephemeral space according to given type + * and pushes it on stack. + * + * @param L Lua stack to get space, index_id, iterator type and + * encoded with msgpack key from. + * + * @retval number number of return values. + */ +static int +lbox_index_iterator_ephemeral(lua_State *L) +{ + if (lua_gettop(L) != 4 || !lua_isnumber(L, 2) || !lua_isnumber(L, 3)) + return luaL_error(L, "Usage index:iterator(type, key)"); + + struct space *space = lua_checkephemeralspace(L, 1); + uint32_t index_id = lua_tonumber(L, 2); + uint32_t iterator = lua_tonumber(L, 3); + size_t mpkey_len; + /* Key encoded by Lua */ + const char *mpkey = lua_tolstring(L, 4, &mpkey_len); + struct iterator *it = + box_index_iterator_ephemeral(space, index_id, iterator, mpkey, + mpkey + mpkey_len); + if (it == NULL) + return luaT_error(L); + + assert(CTID_STRUCT_ITERATOR_REF != 0); + struct iterator **ptr = + (struct iterator **)luaL_pushcdata(L, CTID_STRUCT_ITERATOR_REF); + *ptr = it; /* NULL handled by Lua, gc also set by Lua */ + return 1; +} + static int lbox_iterator_next(lua_State *L) { @@ -328,6 +701,24 @@ lbox_index_compact(lua_State *L) return 0; } +/** + * Run index compaction. + * + * @param L Lua stack to get space and key from. + */ +static int +lbox_index_compact_ephemeral(lua_State *L) +{ + if (lua_gettop(L) != 2 || !lua_isnumber(L, 2)) + return luaL_error(L, "Usage index:compact()"); + struct space *space = lua_checkephemeralspace(L, 1); + uint32_t index_id = lua_tonumber(L, 2); + + if (box_index_compact_ephemeral(space, index_id) != 0) + return luaT_error(L); + return 0; +} + /* }}} */ void @@ -340,6 +731,12 @@ box_lua_index_init(struct lua_State *L) CTID_STRUCT_ITERATOR_REF = luaL_ctypeid(L, "struct iterator&"); assert(CTID_STRUCT_ITERATOR_REF != 0); + lua_getfield(L, LUA_GLOBALSINDEX, "box"); + lua_getfield(L, -1, "internal"); + lua_newtable(L); + lua_setfield(L, -2, "index_ephemeral_methods"); + lua_pop(L, 2); /* box, internal */ + static const struct luaL_Reg indexlib [] = { {NULL, NULL} }; @@ -370,4 +767,27 @@ box_lua_index_init(struct lua_State *L) luaL_register(L, "box.internal", boxlib_internal); lua_pop(L, 1); + + static const struct luaL_Reg ephemeral_index_lib[] = { + {"insert", lbox_insert_ephemeral}, + {"replace", lbox_replace_ephemeral}, + {"update", lbox_index_update_ephemeral}, + {"upsert", lbox_upsert_ephemeral}, + {"delete", lbox_index_delete_ephemeral}, + {"len", lbox_index_len_ephemeral}, + {"bsize", lbox_index_bsize_ephemeral}, + {"random", lbox_index_random_ephemeral}, + {"select", lbox_index_select_ephemeral}, + {"get", lbox_index_get_ephemeral}, + {"min", lbox_index_min_ephemeral}, + {"max", lbox_index_max_ephemeral}, + {"count", lbox_index_count_ephemeral}, + {"iterator", lbox_index_iterator_ephemeral}, + {"compact", lbox_index_compact_ephemeral}, + {NULL, NULL} + }; + + luaL_register(L, "box.internal.index_ephemeral_methods", + ephemeral_index_lib); + lua_pop(L, 1); } diff --git a/src/box/lua/misc.cc b/src/box/lua/misc.cc index 13ca18c..32ffb20 100644 --- a/src/box/lua/misc.cc +++ b/src/box/lua/misc.cc @@ -70,11 +70,7 @@ lua_checkephemeralspace(struct lua_State *L, int idx) return *(struct space **) data; } -/* }}} */ - -/** {{{ Lua/C implementation of index:select(): used only by Vinyl **/ - -static inline void +void lbox_port_to_table(lua_State *L, struct port *port_base) { struct port_tuple *port = port_tuple(port_base); @@ -87,6 +83,10 @@ lbox_port_to_table(lua_State *L, struct port *port_base) } } +/* }}} */ + +/** {{{ Lua/C implementation of index:select(): used only by Vinyl **/ + static int lbox_select(lua_State *L) { diff --git a/src/box/lua/misc.h b/src/box/lua/misc.h index 6162baa..6859142 100644 --- a/src/box/lua/misc.h +++ b/src/box/lua/misc.h @@ -54,6 +54,15 @@ lbox_encode_tuple_on_gc(struct lua_State *L, int idx, size_t *p_len); struct space * lua_checkephemeralspace(struct lua_State *L, int idx); +/** + * Transform given port to Lua table. + * + * @param L Lua stack to push table to. + * @param port_base port to transform. + */ +void +lbox_port_to_table(lua_State *L, struct port *port_base); + void box_lua_misc_init(struct lua_State *L); diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua index cc8c66b..3b40401 100644 --- a/src/box/lua/schema.lua +++ b/src/box/lua/schema.lua @@ -1098,6 +1098,8 @@ local index_new_ephemeral = box.internal.space.index_new_ephemeral box.internal.space.index_new_ephemeral = nil local index_delete_ephemeral = box.internal.space.index_delete_ephemeral box.internal.space.index_delete_ephemeral = nil +local index_ephemeral_methods = box.internal.index_ephemeral_methods +box.internal.index_ephemeral_methods = nil local index_ephemeral_mt = {} local create_ephemeral_index = function(space, name, options) @@ -1234,6 +1236,13 @@ local function check_index_arg(index, method) error(string.format(fmt, method, method)) end end +local function check_index_arg_ephemeral(index, method) + if type(index) ~= 'table' or type(index.space) ~= 'table' or + param_type(index.space.space) ~= 'cdata' then + local fmt = 'Use index:%s(...) instead of index.%s(...)' + error(string.format(fmt, method, method)) + end +end box.internal.check_index_arg = check_index_arg -- for net.box -- Helper function to check that space have primary key and return it @@ -1642,6 +1651,81 @@ space_mt.__index = space_mt -- Metatable for primary index of ephemeral space index_ephemeral_mt.drop = drop_ephemeral_index +index_ephemeral_mt.update = function(index, key, ops) + check_index_arg_ephemeral(index, 'update') + key = keify(key) + return index_ephemeral_methods.update(index.space.space, index.id, key, ops) +end +index_ephemeral_mt.delete = function(index, key) + check_index_arg_ephemeral(index, 'delete') + key = keify(key) + return index_ephemeral_methods.delete(index.space.space, index.id, key) +end +index_ephemeral_mt.len = function(index) + check_index_arg_ephemeral(index, 'len') + local ret = index_ephemeral_methods.len(index.space.space, index.id) + if ret == -1 then + box.error() + end + return tonumber(ret) +end +index_ephemeral_mt.__len = index_ephemeral_mt.len +index_ephemeral_mt.bsize = function(index) + check_index_arg_ephemeral(index, 'bsize') + local ret = index_ephemeral_methods.bsize(index.space.space, index.id) + if ret == -1 then + box.error() + end + return tonumber(ret) +end +index_ephemeral_mt.random = function(index, rnd) + check_index_arg_ephemeral(index, 'random') + rnd = rnd or math.random() + return index_ephemeral_methods.random(index.space.space, index.id, rnd); +end +index_ephemeral_mt.get = function(index, key) + check_index_arg_ephemeral(index, 'get') + key = keify(key) + return index_ephemeral_methods.get(index.space.space, index.id, key) +end +index_ephemeral_mt.select = function(index, key, opts) + check_index_arg_ephemeral(index, 'select') + local key = keify(key) + local iterator, offset, limit = check_select_opts(opts, #key == 0) + return index_ephemeral_methods.select(index.space.space, iterator, offset, limit, key) +end +index_ephemeral_mt.min = function(index, key) + check_index_arg_ephemeral(index, 'min') + key = keify(key) + return index_ephemeral_methods.min(index.space.space, index.id, key); +end +index_ephemeral_mt.max = function(index, key) + check_index_arg_ephemeral(index, 'max') + key = keify(key) + return index_ephemeral_methods.max(index.space.space, index.id, key); +end +index_ephemeral_mt.count = function(index, key, opts) + check_index_arg_ephemeral(index, 'count') + key = keify(key) + local itype = check_iterator_type(opts, #key == 0); + return index_ephemeral_methods.count(index.space.space, index.id, itype, + key); +end +index_ephemeral_mt.pairs = function(index, key, opts) + check_index_arg_ephemeral(index, 'pairs') + key = keify(key) + local itype = check_iterator_type(opts, #key == 0); + local keymp = msgpack.encode(key) + local keybuf = ffi.string(keymp, #keymp) + local cdata = index_ephemeral_methods.iterator(index.space.space, index.id, + itype, keymp); + return fun.wrap(iterator_gen_luac, keybuf, + ffi.gc(cdata, builtin.box_iterator_free)) +end +index_ephemeral_mt.compact = function(index) + check_index_arg_ephemeral(index, 'compact') + return index_ephemeral_methods.compact(index.space.space, index.id) +end index_ephemeral_mt.__index = index_ephemeral_mt -- Metatable for ephemeral space @@ -1665,9 +1749,81 @@ space_ephemeral_mt.bsize = function(space) check_ephemeral_space_arg(space, 'bsize') return builtin.space_bsize(space.space) end +space_ephemeral_mt.auto_increment = function(space, tuple) + check_ephemeral_space_arg(space, 'auto_increment') + local max_tuple = check_primary_index(space):max() + local max = 0 + if max_tuple ~= nil then + max = max_tuple[1] + end + table.insert(tuple, 1, max + 1) + return space:insert(tuple) +end +space_ephemeral_mt.insert = function(space, tuple) + check_ephemeral_space_arg(space, 'insert') + return index_ephemeral_methods.insert(space.space, tuple); +end +space_ephemeral_mt.replace = function(space, tuple) + check_ephemeral_space_arg(space, 'replace') + return index_ephemeral_methods.replace(space.space, tuple); +end +space_ephemeral_mt.upsert = function(space, tuple_key, ops, deprecated) + check_ephemeral_space_arg(space, 'upsert') + if deprecated ~= nil then + local msg = "Error: extra argument in upsert call: " + msg = msg .. tostring(deprecated) + msg = msg .. ". Usage :upsert(tuple, operations)" + box.error(box.error.PROC_LUA, msg) + end + return index_ephemeral_methods.upsert(space.space, tuple_key, ops); +end +space_ephemeral_mt.pairs = function(space, key, opts) + check_ephemeral_space_arg(space, 'pairs') + local pk = space.index[0] + if pk == nil then + -- empty space without indexes, return empty iterator + return fun.iter({}) + end + return pk:pairs(key, opts) +end +space_ephemeral_mt.__pairs = space_ephemeral_mt.pairs -- Lua 5.2 compatibility +space_ephemeral_mt.__ipairs = space_ephemeral_mt.pairs -- Lua 5.2 compatibility +space_ephemeral_mt.truncate = function(space) + check_ephemeral_space_arg(space, 'truncate') + local index = space.index[0] + local name = index.name + local options = index.options + index:drop() + space:create_index(name, options) +end space_ephemeral_mt.create_index = create_ephemeral_index space_ephemeral_mt.drop = box.schema.space.drop_ephemeral space_ephemeral_mt.__index = space_ephemeral_mt +space_ephemeral_mt.put = space_ephemeral_methods.replace +space_ephemeral_mt.update = function(space, key, ops) + check_ephemeral_space_arg(space, 'update') + return check_primary_index(space):update(key, ops) +end +space_ephemeral_mt.delete = function(space, key) + check_ephemeral_space_arg(space, 'delete') + return check_primary_index(space):delete(key) +end +space_ephemeral_mt.get = function(space, key) + check_ephemeral_space_arg(space, 'get') + return check_primary_index(space):get(key) +end +space_ephemeral_mt.select = function(space, key, opts) + check_ephemeral_space_arg(space, 'select') + return check_primary_index(space):select(key, opts) +end +space_ephemeral_mt.count = function(space, key, opts) + check_ephemeral_space_arg(space, 'count') + return check_primary_index(space):count(key, opts) +end +space_ephemeral_mt.len = function(space) + check_ephemeral_space_arg(space, 'len') + return check_primary_index(space):len() +end box.schema.index_mt = base_index_mt box.schema.memtx_index_mt = memtx_index_mt diff --git a/test/box/ephemeral_space.result b/test/box/ephemeral_space.result index 4ef395f..3137f63 100644 --- a/test/box/ephemeral_space.result +++ b/test/box/ephemeral_space.result @@ -135,7 +135,7 @@ s:frommap({ddd = 1, aaa = 2, ccc = 3, eee = 4}) ... s:frommap() --- -- error: 'builtin/box/schema.lua:1659: Usage: space:frommap(map, opts)' +- error: 'builtin/box/schema.lua:1743: Usage: space:frommap(map, opts)' ... s:frommap({}) --- @@ -245,3 +245,803 @@ i = s:create_index('a', {type = 'bitset', parts = {1, 'unsigned', 2, 'unsigned'} - error: 'Can''t create or modify index ''a'' in space ''ephemeral'': primary key must be unique' ... +-- Ephemeral space: methods +s = box.schema.space.create_ephemeral({field_count = 3}) +--- +... +i = s:create_index('a') +--- +... +s:insert{1} +--- +- error: Tuple field count 1 does not match space field count 3 +... +s:insert{2,2,2} +--- +- [2, 2, 2] +... +s:drop() +--- +... +s = box.schema.space.create_ephemeral() +--- +... +i = s:create_index('a', { type = 'tree', parts = {1, 'string'} }) +--- +... +s:insert{'1'} +--- +- ['1'] +... +s:get{'1'} +--- +- ['1'] +... +s:insert{'1'} +--- +- error: Duplicate key exists in unique index 'a' in space 'ephemeral' +... +s:insert{1} +--- +- error: 'Tuple field 1 type does not match one required by operation: expected string' +... +i:drop() +--- +... +i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} }) +--- +... +s:insert{1} +--- +- [1] +... +s:get{1} +--- +- [1] +... +s:insert{1} +--- +- error: Duplicate key exists in unique index 'a' in space 'ephemeral' +... +s:insert{'1'} +--- +- error: 'Tuple field 1 type does not match one required by operation: expected unsigned' +... +i:drop() +--- +... +i = s:create_index('a', { type = 'tree', parts = {1, 'string'} }) +--- +... +s:replace{'1'} +--- +- ['1'] +... +s:get{'1'} +--- +- ['1'] +... +s:replace{'1'} +--- +- ['1'] +... +s:replace{1} +--- +- error: 'Tuple field 1 type does not match one required by operation: expected string' +... +i:drop() +--- +... +i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} }) +--- +... +s:replace{1} +--- +- [1] +... +s:get{1} +--- +- [1] +... +s:replace{1} +--- +- [1] +... +s:replace{'1'} +--- +- error: 'Tuple field 1 type does not match one required by operation: expected unsigned' +... +i:drop() +--- +... +i = s:create_index('a', { type = 'tree', parts = {1, 'string'} }) +--- +... +s:replace{'1'} +--- +- ['1'] +... +s:get{'1'} +--- +- ['1'] +... +s:replace{'1'} +--- +- ['1'] +... +s:replace{1} +--- +- error: 'Tuple field 1 type does not match one required by operation: expected string' +... +i:drop() +--- +... +i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} }) +--- +... +s:upsert({1, 0}, {{'+', 2, 1}}) +--- +... +s:get{1} +--- +- [1, 0] +... +s:upsert({1, 0}, {{'+', 2, 1}}) +--- +... +s:get{1} +--- +- [1, 1] +... +s:upsert({1, 0}, {{'+', 1, 1}}) +--- +... +s:get{1} +--- +- [1, 1] +... +s:get{2} +--- +... +s:upsert({'1'}, {{'!', 2, 100}}) +--- +- error: 'Tuple field 1 type does not match one required by operation: expected unsigned' +... +s:upsert({1}, {{'a', 2, 100}}) +--- +- error: Unknown UPDATE operation +... +i:drop() +--- +... +i = s:create_index('a') +--- +... +s:insert{1, 2, 3, 4, 5} +--- +- [1, 2, 3, 4, 5] +... +s:update({1}, {{'#', 1, 1}}) +--- +- error: Attempt to modify a tuple field which is part of index 'a' in space 'ephemeral' +... +s:update({1}, {{'#', 1, "only one record please"}}) +--- +- error: 'Argument type in operation ''#'' on field 1 does not match field type: expected + a number of fields to delete' +... +i:drop() +--- +... +i = s:create_index('a') +--- +... +s:insert{1, 0} +--- +- [1, 0] +... +s:update(1, {{'+', 2, 10}}) +--- +- [1, 10] +... +s:update(1, {{'+', 2, 15}}) +--- +- [1, 25] +... +s:update(1, {{'-', 2, 5}}) +--- +- [1, 20] +... +s:update(1, {{'-', 2, 20}}) +--- +- [1, 0] +... +s:update(1, {{'|', 2, 0x9}}) +--- +- [1, 9] +... +s:update(1, {{'|', 2, 0x6}}) +--- +- [1, 15] +... +s:update(1, {{'&', 2, 0xabcde}}) +--- +- [1, 14] +... +s:update(1, {{'&', 2, 0x2}}) +--- +- [1, 2] +... +s:update(1, {{'^', 2, 0xa2}}) +--- +- [1, 160] +... +s:update(1, {{'^', 2, 0xa2}}) +--- +- [1, 2] +... +i:drop() +--- +... +i = s:create_index('a') +--- +... +s:insert{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} +--- +- [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] +... +s:update({0}, {{'#', 42, 1}}) +--- +- error: Field 42 was not found in the tuple +... +s:update({0}, {{'#', 4, 'abirvalg'}}) +--- +- error: 'Argument type in operation ''#'' on field 4 does not match field type: expected + a number of fields to delete' +... +s:update({0}, {{'#', 2, 1}, {'#', 4, 2}, {'#', 6, 1}}) +--- +- [0, 2, 3, 6, 7, 9, 10, 11, 12, 13, 14, 15] +... +s:update({0}, {{'#', 4, 3}}) +--- +- [0, 2, 3, 10, 11, 12, 13, 14, 15] +... +s:update({0}, {{'#', 5, 123456}}) +--- +- [0, 2, 3, 10] +... +s:update({0}, {{'#', 3, 4294967295}}) +--- +- [0, 2] +... +s:update({0}, {{'#', 2, 0}}) +--- +- error: 'Field 2 UPDATE error: cannot delete 0 fields' +... +i:drop() +--- +... +i = s:create_index('a', { type = 'tree', parts = {1, 'string'} }) +--- +... +s:insert{'1'} +--- +- ['1'] +... +s:insert{'5'} +--- +- ['5'] +... +s:insert{'6'} +--- +- ['6'] +... +s:insert{'11'} +--- +- ['11'] +... +t = {} for state, v in i:pairs({}, {iterator = 'ALL'}) do table.insert(t, v) end +--- +... +t +--- +- - ['1'] + - ['11'] + - ['5'] + - ['6'] +... +t = {} for state, v in i:pairs({}, {iterator = 'GE'}) do table.insert(t, v) end +--- +... +t +--- +- - ['1'] + - ['11'] + - ['5'] + - ['6'] +... +t = {} for state, v in i:pairs('5', {iterator = 'GE'}) do table.insert(t, v) end +--- +... +t +--- +- - ['5'] + - ['6'] +... +t = {} for state, v in i:pairs('5', {iterator = 'GT'}) do table.insert(t, v) end +--- +... +t +--- +- - ['6'] +... +t = {} for state, v in i:pairs({}, {iterator = 'LE'}) do table.insert(t, v) end +--- +... +t +--- +- - ['6'] + - ['5'] + - ['11'] + - ['1'] +... +t = {} for state, v in i:pairs('5', {iterator = 'LE'}) do table.insert(t, v) end +--- +... +t +--- +- - ['5'] + - ['11'] + - ['1'] +... +t = {} for state, v in i:pairs({}, {iterator = 'LT'}) do table.insert(t, v) end +--- +... +t +--- +- - ['6'] + - ['5'] + - ['11'] + - ['1'] +... +t = {} for state, v in i:pairs('5', {iterator = 'LT'}) do table.insert(t, v) end +--- +... +t +--- +- - ['11'] + - ['1'] +... +i:drop() +--- +... +i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} }) +--- +... +s:insert{1} +--- +- [1] +... +s:insert{5} +--- +- [5] +... +s:insert{11} +--- +- [11] +... +t = {} for state, v in i:pairs({}, {iterator = 'ALL'}) do table.insert(t, v) end +--- +... +t +--- +- - [1] + - [5] + - [11] +... +t = {} for state, v in i:pairs({}, {iterator = 'GE'}) do table.insert(t, v) end +--- +... +t +--- +- - [1] + - [5] + - [11] +... +t = {} for state, v in i:pairs(5, {iterator = 'GE'}) do table.insert(t, v) end +--- +... +t +--- +- - [5] + - [11] +... +t = {} for state, v in i:pairs(5, {iterator = 'GT'}) do table.insert(t, v) end +--- +... +t +--- +- - [11] +... +t = {} for state, v in i:pairs({}, {iterator = 'LE'}) do table.insert(t, v) end +--- +... +t +--- +- - [11] + - [5] + - [1] +... +t = {} for state, v in i:pairs(5, {iterator = 'LE'}) do table.insert(t, v) end +--- +... +t +--- +- - [5] + - [1] +... +t = {} for state, v in i:pairs({}, {iterator = 'LT'}) do table.insert(t, v) end +--- +... +t +--- +- - [11] + - [5] + - [1] +... +t = {} for state, v in i:pairs(5, {iterator = 'LT'}) do table.insert(t, v) end +--- +... +t +--- +- - [1] +... +i:drop() +--- +... +i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned', 2, 'unsigned'} }) +--- +... +s:insert{1, 1} +--- +- [1, 1] +... +s:insert{5, 5} +--- +- [5, 5] +... +s:insert{11, 11} +--- +- [11, 11] +... +t = {} for state, v in i:pairs({}, {iterator = 'ALL'}) do table.insert(t, v) end +--- +... +t +--- +- - [1, 1] + - [5, 5] + - [11, 11] +... +t = {} for state, v in i:pairs({}, {iterator = 'GE'}) do table.insert(t, v) end +--- +... +t +--- +- - [1, 1] + - [5, 5] + - [11, 11] +... +t = {} for state, v in i:pairs({5, 5}, {iterator = 'GE'}) do table.insert(t, v) end +--- +... +t +--- +- - [5, 5] + - [11, 11] +... +t = {} for state, v in i:pairs({5, 5}, {iterator = 'GT'}) do table.insert(t, v) end +--- +... +t +--- +- - [11, 11] +... +t = {} for state, v in i:pairs({}, {iterator = 'LE'}) do table.insert(t, v) end +--- +... +t +--- +- - [11, 11] + - [5, 5] + - [1, 1] +... +t = {} for state, v in i:pairs({5, 5}, {iterator = 'LE'}) do table.insert(t, v) end +--- +... +t +--- +- - [5, 5] + - [1, 1] +... +t = {} for state, v in i:pairs({}, {iterator = 'LT'}) do table.insert(t, v) end +--- +... +t +--- +- - [11, 11] + - [5, 5] + - [1, 1] +... +t = {} for state, v in i:pairs({5, 5}, {iterator = 'LT'}) do table.insert(t, v) end +--- +... +t +--- +- - [1, 1] +... +i:drop() +--- +... +i = s:create_index('a') +--- +... +s:auto_increment{1} +--- +- [1, 1] +... +s:auto_increment{2} +--- +- [2, 2] +... +s:auto_increment{3} +--- +- [3, 3] +... +s:pairs(2, 'GE'):totable() +--- +- - [2, 2] + - [3, 3] +... +i:count({2}, 'GT') +--- +- 1 +... +i:drop() +--- +... +i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} }) +--- +... +s:replace{1} +--- +- [1] +... +s:replace{2} +--- +- [2] +... +s:delete{1} +--- +- [1] +... +s:delete{2} +--- +- [2] +... +s:select() +--- +- [] +... +i:drop() +--- +... +s:drop() +--- +... +test_run = require('test_run').new() +--- +... +utils = dofile('utils.lua') +--- +... +s = box.schema.space.create_ephemeral() +--- +... +idx = s:create_index('a') +--- +... +for i = 1, 13 do s:insert{ i, string.rep('x', i) } end +--- +... +s:len() +--- +- 13 +... +s:bsize() +--- +- 130 +... +utils.space_bsize(s) +--- +- 130 +... +for i = 1, 13, 2 do s:delete{ i } end +--- +... +s:len() +--- +- 6 +... +s:bsize() +--- +- 60 +... +utils.space_bsize(s) +--- +- 60 +... +for i = 2, 13, 2 do s:update( { i }, {{ ":", 2, i, 0, string.rep('y', i) }} ) end +--- +... +s:len() +--- +- 6 +... +s:bsize() +--- +- 102 +... +utils.space_bsize(s) +--- +- 102 +... +idx:drop() +--- +... +i = s:create_index('a', { type = 'tree', parts = {1, 'string'} }) +--- +... +s:insert({'1', "AAAA"}) +--- +- ['1', 'AAAA'] +... +s:insert({'2', "AAAA"}) +--- +- ['2', 'AAAA'] +... +s:insert({'3', "AAAA"}) +--- +- ['3', 'AAAA'] +... +s:insert({'4', "AAAA"}) +--- +- ['4', 'AAAA'] +... +i:select() +--- +- - ['1', 'AAAA'] + - ['2', 'AAAA'] + - ['3', 'AAAA'] + - ['4', 'AAAA'] +... +i:max('2') +--- +- ['2', 'AAAA'] +... +i:min('2') +--- +- ['2', 'AAAA'] +... +i:count('2') +--- +- 1 +... +i:max() +--- +- ['4', 'AAAA'] +... +i:min() +--- +- ['1', 'AAAA'] +... +i:count() +--- +- 4 +... +s:insert({'20', "AAAA"}) +--- +- ['20', 'AAAA'] +... +s:insert({'30', "AAAA"}) +--- +- ['30', 'AAAA'] +... +s:insert({'40', "AAAA"}) +--- +- ['40', 'AAAA'] +... +s:select() +--- +- - ['1', 'AAAA'] + - ['2', 'AAAA'] + - ['20', 'AAAA'] + - ['3', 'AAAA'] + - ['30', 'AAAA'] + - ['4', 'AAAA'] + - ['40', 'AAAA'] +... +i:max('15') +--- +... +i:min('15') +--- +... +s:count('15') +--- +- 0 +... +i:max() +--- +- ['40', 'AAAA'] +... +i:min() +--- +- ['1', 'AAAA'] +... +s:count() +--- +- 7 +... +s:insert({'-2', "AAAA"}) +--- +- ['-2', 'AAAA'] +... +s:insert({'-3', "AAAA"}) +--- +- ['-3', 'AAAA'] +... +s:insert({'-4', "AAAA"}) +--- +- ['-4', 'AAAA'] +... +i:select() +--- +- - ['-2', 'AAAA'] + - ['-3', 'AAAA'] + - ['-4', 'AAAA'] + - ['1', 'AAAA'] + - ['2', 'AAAA'] + - ['20', 'AAAA'] + - ['3', 'AAAA'] + - ['30', 'AAAA'] + - ['4', 'AAAA'] + - ['40', 'AAAA'] +... +i:max('0') +--- +... +i:min('0') +--- +... +i:count('0') +--- +- 0 +... +i:max() +--- +- ['40', 'AAAA'] +... +i:min() +--- +- ['-2', 'AAAA'] +... +i:count() +--- +- 10 +... +s:drop() +--- +... +test_run:cmd("restart server default") diff --git a/test/box/ephemeral_space.test.lua b/test/box/ephemeral_space.test.lua index 93211c6..4c2bf22 100644 --- a/test/box/ephemeral_space.test.lua +++ b/test/box/ephemeral_space.test.lua @@ -89,3 +89,243 @@ i:drop() i = s:create_index('a', {id = 10}) i = s:create_index('a', {type = 'bitset', parts = {1, 'unsigned', 2, 'unsigned'}}) + +-- Ephemeral space: methods + +s = box.schema.space.create_ephemeral({field_count = 3}) +i = s:create_index('a') + +s:insert{1} +s:insert{2,2,2} + +s:drop() + +s = box.schema.space.create_ephemeral() +i = s:create_index('a', { type = 'tree', parts = {1, 'string'} }) +s:insert{'1'} +s:get{'1'} +s:insert{'1'} +s:insert{1} +i:drop() + +i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} }) +s:insert{1} +s:get{1} +s:insert{1} +s:insert{'1'} +i:drop() + +i = s:create_index('a', { type = 'tree', parts = {1, 'string'} }) +s:replace{'1'} +s:get{'1'} +s:replace{'1'} +s:replace{1} +i:drop() + +i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} }) +s:replace{1} +s:get{1} +s:replace{1} +s:replace{'1'} +i:drop() + +i = s:create_index('a', { type = 'tree', parts = {1, 'string'} }) +s:replace{'1'} +s:get{'1'} +s:replace{'1'} +s:replace{1} +i:drop() + +i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} }) +s:upsert({1, 0}, {{'+', 2, 1}}) +s:get{1} +s:upsert({1, 0}, {{'+', 2, 1}}) +s:get{1} +s:upsert({1, 0}, {{'+', 1, 1}}) +s:get{1} +s:get{2} + +s:upsert({'1'}, {{'!', 2, 100}}) +s:upsert({1}, {{'a', 2, 100}}) + +i:drop() + +i = s:create_index('a') +s:insert{1, 2, 3, 4, 5} +s:update({1}, {{'#', 1, 1}}) +s:update({1}, {{'#', 1, "only one record please"}}) +i:drop() + +i = s:create_index('a') +s:insert{1, 0} +s:update(1, {{'+', 2, 10}}) +s:update(1, {{'+', 2, 15}}) +s:update(1, {{'-', 2, 5}}) +s:update(1, {{'-', 2, 20}}) +s:update(1, {{'|', 2, 0x9}}) +s:update(1, {{'|', 2, 0x6}}) +s:update(1, {{'&', 2, 0xabcde}}) +s:update(1, {{'&', 2, 0x2}}) +s:update(1, {{'^', 2, 0xa2}}) +s:update(1, {{'^', 2, 0xa2}}) +i:drop() + +i = s:create_index('a') +s:insert{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} +s:update({0}, {{'#', 42, 1}}) +s:update({0}, {{'#', 4, 'abirvalg'}}) +s:update({0}, {{'#', 2, 1}, {'#', 4, 2}, {'#', 6, 1}}) +s:update({0}, {{'#', 4, 3}}) +s:update({0}, {{'#', 5, 123456}}) +s:update({0}, {{'#', 3, 4294967295}}) +s:update({0}, {{'#', 2, 0}}) +i:drop() + + + +i = s:create_index('a', { type = 'tree', parts = {1, 'string'} }) +s:insert{'1'} +s:insert{'5'} +s:insert{'6'} +s:insert{'11'} +t = {} for state, v in i:pairs({}, {iterator = 'ALL'}) do table.insert(t, v) end +t +t = {} for state, v in i:pairs({}, {iterator = 'GE'}) do table.insert(t, v) end +t +t = {} for state, v in i:pairs('5', {iterator = 'GE'}) do table.insert(t, v) end +t +t = {} for state, v in i:pairs('5', {iterator = 'GT'}) do table.insert(t, v) end +t +t = {} for state, v in i:pairs({}, {iterator = 'LE'}) do table.insert(t, v) end +t +t = {} for state, v in i:pairs('5', {iterator = 'LE'}) do table.insert(t, v) end +t +t = {} for state, v in i:pairs({}, {iterator = 'LT'}) do table.insert(t, v) end +t +t = {} for state, v in i:pairs('5', {iterator = 'LT'}) do table.insert(t, v) end +t +i:drop() + +i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} }) +s:insert{1} +s:insert{5} +s:insert{11} +t = {} for state, v in i:pairs({}, {iterator = 'ALL'}) do table.insert(t, v) end +t +t = {} for state, v in i:pairs({}, {iterator = 'GE'}) do table.insert(t, v) end +t +t = {} for state, v in i:pairs(5, {iterator = 'GE'}) do table.insert(t, v) end +t +t = {} for state, v in i:pairs(5, {iterator = 'GT'}) do table.insert(t, v) end +t +t = {} for state, v in i:pairs({}, {iterator = 'LE'}) do table.insert(t, v) end +t +t = {} for state, v in i:pairs(5, {iterator = 'LE'}) do table.insert(t, v) end +t +t = {} for state, v in i:pairs({}, {iterator = 'LT'}) do table.insert(t, v) end +t +t = {} for state, v in i:pairs(5, {iterator = 'LT'}) do table.insert(t, v) end +t +i:drop() + +i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned', 2, 'unsigned'} }) +s:insert{1, 1} +s:insert{5, 5} +s:insert{11, 11} +t = {} for state, v in i:pairs({}, {iterator = 'ALL'}) do table.insert(t, v) end +t +t = {} for state, v in i:pairs({}, {iterator = 'GE'}) do table.insert(t, v) end +t +t = {} for state, v in i:pairs({5, 5}, {iterator = 'GE'}) do table.insert(t, v) end +t +t = {} for state, v in i:pairs({5, 5}, {iterator = 'GT'}) do table.insert(t, v) end +t +t = {} for state, v in i:pairs({}, {iterator = 'LE'}) do table.insert(t, v) end +t +t = {} for state, v in i:pairs({5, 5}, {iterator = 'LE'}) do table.insert(t, v) end +t +t = {} for state, v in i:pairs({}, {iterator = 'LT'}) do table.insert(t, v) end +t +t = {} for state, v in i:pairs({5, 5}, {iterator = 'LT'}) do table.insert(t, v) end +t +i:drop() + +i = s:create_index('a') +s:auto_increment{1} +s:auto_increment{2} +s:auto_increment{3} +s:pairs(2, 'GE'):totable() +i:count({2}, 'GT') +i:drop() + +i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} }) +s:replace{1} +s:replace{2} +s:delete{1} +s:delete{2} +s:select() +i:drop() +s:drop() + +test_run = require('test_run').new() +utils = dofile('utils.lua') + +s = box.schema.space.create_ephemeral() +idx = s:create_index('a') +for i = 1, 13 do s:insert{ i, string.rep('x', i) } end +s:len() +s:bsize() +utils.space_bsize(s) + +for i = 1, 13, 2 do s:delete{ i } end +s:len() +s:bsize() +utils.space_bsize(s) + +for i = 2, 13, 2 do s:update( { i }, {{ ":", 2, i, 0, string.rep('y', i) }} ) end +s:len() +s:bsize() +utils.space_bsize(s) +idx:drop() + +i = s:create_index('a', { type = 'tree', parts = {1, 'string'} }) +s:insert({'1', "AAAA"}) +s:insert({'2', "AAAA"}) +s:insert({'3', "AAAA"}) +s:insert({'4', "AAAA"}) + +i:select() +i:max('2') +i:min('2') +i:count('2') +i:max() +i:min() +i:count() + +s:insert({'20', "AAAA"}) +s:insert({'30', "AAAA"}) +s:insert({'40', "AAAA"}) + +s:select() +i:max('15') +i:min('15') +s:count('15') +i:max() +i:min() +s:count() + +s:insert({'-2', "AAAA"}) +s:insert({'-3', "AAAA"}) +s:insert({'-4', "AAAA"}) + +i:select() +i:max('0') +i:min('0') +i:count('0') +i:max() +i:min() +i:count() + +s:drop() + +test_run:cmd("restart server default") diff --git a/test/engine/iterator.result b/test/engine/iterator.result index f39a15a..9e39a32 100644 --- a/test/engine/iterator.result +++ b/test/engine/iterator.result @@ -4213,7 +4213,7 @@ s:replace{35} ... state, value = gen(param,state) --- -- error: 'builtin/box/schema.lua:1180: usage: next(param, state)' +- error: 'builtin/box/schema.lua:1182: usage: next(param, state)' ... value --- -- 2.7.4