[tarantool-patches] [PATCH 5/5] Methods for ephemeral space and its index

imeevma at tarantool.org imeevma at tarantool.org
Thu Jul 12 14:16:16 MSK 2018


This patch defines most methods for index of
ephemeral space and ephemeral space.

Closes #3375.
---
 src/box/box.cc                    |   62 +
 src/box/box.h                     |    9 +
 src/box/index.cc                  |  172 +
 src/box/index.h                   |  140 +
 src/box/lua/info.h                |    4 -
 src/box/lua/schema.lua            |  122 +
 src/box/lua/space.cc              |  396 +-
 test/box/ephemeral_space.result   | 7979 +++++++++++++++++++++++++++++++++++++
 test/box/ephemeral_space.test.lua | 1694 ++++++++
 9 files changed, 10573 insertions(+), 5 deletions(-)

diff --git a/src/box/box.cc b/src/box/box.cc
index 795e3ee..e825735 100644
--- a/src/box/box.cc
+++ b/src/box/box.cc
@@ -1171,6 +1171,68 @@ 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;
+
+	rmean_collect(rmean_box, IPROTO_SELECT, 1);
+
+	if (iterator < 0 || iterator >= iterator_type_MAX) {
+		diag_set(ClientError, ER_ILLEGAL_PARAMS,
+			 "Invalid iterator type");
+		diag_log();
+		return -1;
+	}
+
+	struct index *index = index_find(space, index_id);
+	if (index == NULL)
+		return -1;
+
+	enum iterator_type type = (enum iterator_type) iterator;
+	uint32_t part_count = key ? mp_decode_array(&key) : 0;
+	if (key_validate(index->def, type, key, part_count))
+		return -1;
+
+	ERROR_INJECT(ERRINJ_TESTING, {
+		diag_set(ClientError, ER_INJECTION, "ERRINJ_TESTING");
+		return -1;
+	});
+
+	struct iterator *it = index_create_iterator(index, type,
+						    key, part_count);
+	if (it == NULL)
+		return -1;
+
+	int rc = 0;
+	uint32_t found = 0;
+	struct tuple *tuple;
+	port_tuple_create(port);
+	while (found < limit) {
+		rc = iterator_next(it, &tuple);
+		if (rc != 0 || tuple == NULL)
+			break;
+		if (offset > 0) {
+			offset--;
+			continue;
+		}
+		rc = port_tuple_add(port, tuple);
+		if (rc != 0)
+			break;
+		found++;
+	}
+	iterator_delete(it);
+
+	if (rc != 0) {
+		port_destroy(port);
+		return -1;
+	}
+	return 0;
+}
+
+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 a00a842..30f447b 100644
--- a/src/box/box.h
+++ b/src/box/box.h
@@ -402,6 +402,15 @@ int
 box_process1(struct request *request, box_tuple_t **result);
 
 /**
+ * Select all tuples from ephemeral space.
+ */
+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);
+
+/**
  * Used to prepare request for inserting tuple into
  * ephemeral space and call box_process_rw().
  */
diff --git a/src/box/index.cc b/src/box/index.cc
index 188995e..d2d209f 100644
--- a/src/box/index.cc
+++ b/src/box/index.cc
@@ -354,6 +354,131 @@ box_index_count(uint32_t space_id, uint32_t index_id, int type,
 	return count;
 }
 
+ssize_t
+box_ephemeral_index_len(struct space *space, uint32_t index_id)
+{
+	struct index *index = space_index(space, index_id);
+	if (index == NULL)
+		return 0;
+	return index_size(index);
+}
+
+ssize_t
+box_ephemeral_index_bsize(struct space *space, uint32_t index_id)
+{
+	struct index *index = space_index(space, index_id);
+	if (index == NULL)
+		return 0;
+	return index_bsize(index);
+}
+
+int
+box_ephemeral_index_random(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_ephemeral_index_get(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;
+	rmean_collect(rmean_box, IPROTO_SELECT, 1);
+	if (*result != NULL)
+		tuple_bless(*result);
+	return 0;
+}
+
+int
+box_ephemeral_index_min(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_ephemeral_index_max(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_ephemeral_index_count(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 ************************************************/
@@ -408,6 +533,32 @@ box_iterator_free(box_iterator_t *it)
 	iterator_delete(it);
 }
 
+box_iterator_t *
+box_ephemeral_index_iterator(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);
+	if (it == NULL)
+		return NULL;
+	return it;
+}
+
 /* }}} */
 
 /* {{{ Other index functions */
@@ -435,6 +586,27 @@ box_index_compact(uint32_t space_id, uint32_t index_id)
 	return 0;
 }
 
+int
+box_ephemeral_index_stat(struct space *space, uint32_t index_id,
+			 struct info_handler *info)
+{
+	struct index *index = index_find(space, index_id);
+	if(index == NULL)
+		return -1;
+	index_stat(index, info);
+	return 0;
+}
+
+int
+box_ephemeral_index_compact(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 686e7a1..ffcb9fc 100644
--- a/src/box/index.h
+++ b/src/box/index.h
@@ -235,6 +235,146 @@ 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.
+ * If space have no index it returns 0.
+ * 
+ * \param space - ephemeral space.
+ * \param index_id - index identifier.
+ * \retval >= 0.
+ */
+ssize_t
+box_ephemeral_index_len(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 >= 0.
+ */
+ssize_t
+box_ephemeral_index_bsize(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_ephemeral_index_random(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_ephemeral_index_get(struct space *space, uint32_t index_id, const char *key,
+			const char *key_end, box_tuple_t **result);
+
+/**
+ * Return a 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_ephemeral_index_min(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.
+ */
+int
+box_ephemeral_index_max(struct space *space, uint32_t index_id, const char *key,
+			const char *key_end, box_tuple_t **result);
+
+/**
+ * Return a 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.
+ */
+ssize_t
+box_ephemeral_index_count(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_ephemeral_index_iterator(struct space *space, uint32_t index_id, int type,
+			     const char *key, const char *key_end);
+
+/**
+ * Index statistics.
+ * 
+ * \param space - ephemeral space.
+ * \param index_id - index identifier.
+ * \param info - info handler
+ * \retval -1 on error.
+ * \retval 0 on success.
+ */
+int
+box_ephemeral_index_stat(struct space *space, uint32_t index_id,
+			 struct info_handler *info);
+
+/**
+ * Trigger index compaction.
+ * 
+ * \param space - ephemeral space.
+ * \param index_id - index identifier.
+ * \retval -1 on error.
+ * \retval 0 on success.
+ */
+int
+box_ephemeral_index_compact(struct space *space, uint32_t index_id);
+
 struct iterator {
 	/**
 	 * Iterate to the next tuple.
diff --git a/src/box/lua/info.h b/src/box/lua/info.h
index 78cd9e6..bf4c613 100644
--- a/src/box/lua/info.h
+++ b/src/box/lua/info.h
@@ -49,8 +49,4 @@ luaT_info_handler_create(struct info_handler *h, struct lua_State *L);
 } /* extern "C" */
 #endif /* defined(__cplusplus) */
 
-#if defined(__cplusplus)
-} /* extern "C" */
-#endif /* defined(__cplusplus) */
-
 #endif /* INCLUDES_TARANTOOL_LUA_INFO_H */
diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua
index 0fe109a..24e033e 100644
--- a/src/box/lua/schema.lua
+++ b/src/box/lua/schema.lua
@@ -1689,6 +1689,70 @@ space_mt.frommap = box.internal.space.frommap
 space_mt.__index = space_mt
 
 -- Metatable for primary index of ephemeral space
+index_ephemeral_mt.len = function(index)
+    local ret = index_ephemeral_methods.len(index)
+    if ret == -1 then
+        box.error()
+    end
+    return tonumber(ret)
+end
+index_ephemeral_mt.bsize = function(index)
+    local ret = index_ephemeral_methods.bsize(index)
+    if ret == -1 then
+        box.error()
+    end
+    return tonumber(ret)
+end
+index_ephemeral_mt.__len = index_ephemeral_mt.len
+index_ephemeral_mt.min = function(index, key)
+    key = keify(key)
+    return index_ephemeral_methods.min(index, key);
+end
+index_ephemeral_mt.max = function(index, key)
+    key = keify(key)
+    return index_ephemeral_methods.max(index, key);
+end
+index_ephemeral_mt.random = function(index, rnd)
+    rnd = rnd or math.random()
+    return index_ephemeral_methods.random(index, rnd);
+end
+index_ephemeral_mt.pairs = function(index, key, opts)
+    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, itype, keymp);
+    return fun.wrap(iterator_gen_luac, keybuf,
+        ffi.gc(cdata, builtin.box_iterator_free))
+end
+index_ephemeral_mt.count = function(index, key, opts)
+    key = keify(key)
+    local itype = check_iterator_type(opts, #key == 0);
+    return index_ephemeral_methods.count(index, itype, key);
+end
+index_ephemeral_mt.select = function(index, key, opts)
+    local key = keify(key)
+    local iterator, offset, limit = check_select_opts(opts, #key == 0)
+    return index_ephemeral_methods.select(index, iterator, offset, limit, key)
+end
+index_ephemeral_mt.get = function(index, key)
+    key = keify(key)
+    return index_ephemeral_methods.get(index, key)
+end
+index_ephemeral_mt.update = function(index, key, ops)
+    key = keify(key)
+    return index_ephemeral_methods.update(index, key, ops)
+end
+index_ephemeral_mt.delete = function(index, key)
+    key = keify(key)
+    return index_ephemeral_methods.delete(index, key)
+end
+index_ephemeral_mt.stat = function(index)
+    return index_ephemeral_methods.stat(index)
+end
+index_ephemeral_mt.compact = function(index)
+    return index_ephemeral_methods.compact(index)
+end
 index_ephemeral_mt.drop = index_ephemeral_drop
 index_ephemeral_mt.__index = index_ephemeral_mt
 
@@ -1699,9 +1763,67 @@ end
 space_ephemeral_mt.run_triggers = function(ephemeral_space, yesno)
     builtin.space_run_triggers(ephemeral_space.space, yesno)
 end
+space_ephemeral_mt.auto_increment = function(ephemeral_space, tuple)
+    local max_tuple = check_primary_index(ephemeral_space):max()
+    local max = 0
+    if max_tuple ~= nil then
+        max = max_tuple[1]
+    end
+    table.insert(tuple, 1, max + 1)
+    return ephemeral_space:insert(tuple)
+end
+space_ephemeral_mt.insert = function(ephemeral_space, tuple)
+    return space_ephemeral_methods.insert(ephemeral_space, tuple);
+end
+space_ephemeral_mt.replace = function(ephemeral_space, tuple)
+    return space_ephemeral_methods.replace(ephemeral_space, tuple);
+end
+space_ephemeral_mt.upsert = function(ephemeral_space, tuple_key, ops, deprecated)
+    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 space_ephemeral_methods.upsert(ephemeral_space, tuple_key, ops);
+end
+space_ephemeral_mt.bsize = function(ephemeral_space)
+    if ephemeral_space == nil or ephemeral_space.space == nil then
+        box.error(box.error.NO_SUCH_SPACE, "ephemeral")
+    end
+    return builtin.space_bsize(ephemeral_space.space)
+end
+
+space_ephemeral_mt.pairs = function(ephemeral_space, key, opts)
+    local pk = ephemeral_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(ephemeral_space)
+    if ephemeral_space.index == nil or ephemeral_space.index[0] == nil then
+        return
+    end
+    local index = ephemeral_space.index[0]
+    local name = index.name
+    local options = index.options
+    index:drop()
+    ephemeral_space:create_index(name, options)
+end
 space_ephemeral_mt.frommap = space_ephemeral_methods.frommap
 space_ephemeral_mt.create_index = index_ephemeral_create
 space_ephemeral_mt.drop = box.schema.space.drop_ephemeral
+space_ephemeral_mt.put = space_ephemeral_methods.replace
+space_ephemeral_mt.get = index_ephemeral_mt.get
+space_ephemeral_mt.select = index_ephemeral_mt.select
+space_ephemeral_mt.update = index_ephemeral_mt.update
+space_ephemeral_mt.delete = index_ephemeral_mt.delete
+space_ephemeral_mt.count = index_ephemeral_mt.count
+space_ephemeral_mt.len = index_ephemeral_mt.len
 space_ephemeral_mt.__index = space_ephemeral_mt
 
 box.schema.index_mt = base_index_mt
diff --git a/src/box/lua/space.cc b/src/box/lua/space.cc
index 4374db9..81b9f3c 100644
--- a/src/box/lua/space.cc
+++ b/src/box/lua/space.cc
@@ -35,6 +35,8 @@
 #include "lua/trigger.h"
 #include "box/box.h"
 #include "box/lua/misc.h" /* lbox_encode_tuple_on_gc() */
+#include "box/info.h"
+#include "box/lua/info.h"
 
 extern "C" {
 	#include <lua.h>
@@ -51,8 +53,10 @@ extern "C" {
 #include "box/sequence.h"
 #include "box/coll_id_cache.h"
 #include "box/replication.h" /* GROUP_LOCAL */
+#include "box/port.h"
 
 static uint32_t CTID_STRUCT_SPACE_POINTER = 0;
+static uint32_t CTID_STRUCT_ITERATOR_REF = 0;
 
 /**
  * Trigger function for all spaces
@@ -704,6 +708,370 @@ lbox_index_drop_ephemeral(struct lua_State *L)
 }
 
 /**
+ * Insert tuple into ephemeral space.
+ * 
+ * @param Lua ephemeral space.
+ * @param Lua tuple.
+ * @retval not nil - tuple inserted.
+ * @retval nil - error, A reason is returned in
+ *         the second value.
+ */
+static int
+lbox_ephemeral_space_insert(lua_State *L)
+{
+	if (lua_gettop(L) != 2 || !lua_istable(L, 1))
+		return luaL_error(L, "Usage space:insert(tuple)");
+	struct space *space = (struct 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);
+}
+
+/**
+ * Replace tuple from ephemeral space.
+ * 
+ * @param Lua ephemeral space.
+ * @param Lua tuple - tuple to insert.
+ * @retval not nil - tuple replaced.
+ * @retval nil - error, A reason is returned in
+ *         the second value.
+ */
+static int
+lbox_ephemeral_space_replace(lua_State *L)
+{
+	if (lua_gettop(L) != 2 || !lua_istable(L, 1))
+		return luaL_error(L, "Usage space:replace(tuple)");
+	struct space *space = (struct 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);
+}
+
+/**
+ * Insert or update tuple from ephemeral space.
+ * 
+ * @param Lua ephemeral space.
+ * @param Lua tuple - key or tuple to insert.
+ * @param Lua tuple - operaions in case of update .
+ * @retval not nil - tuple upserted.
+ * @retval nil - error, A reason is returned in
+ *         the second value.
+ */
+static int
+lbox_ephemeral_space_upsert(lua_State *L)
+{
+	if (lua_gettop(L) != 3 || !lua_istable(L, 1) ||
+	    (lua_type(L, 2) != LUA_TTABLE && luaT_istuple(L, 2) == NULL) ||
+	    (lua_type(L, 3) != LUA_TTABLE && luaT_istuple(L, 3) == NULL))
+		return luaL_error(L, "Usage index:upsert(key, ops)");
+	struct space *space = (struct space *)lua_checkephemeralspace(L, 1);
+	size_t key_len;
+	const char *key = lbox_encode_tuple_on_gc(L, 2, &key_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, key, key + key_len,
+				 ops, ops + ops_len, 1, &result) != 0)
+		return luaT_error(L);
+	return luaT_pushtupleornil(L, result);
+}
+
+/**
+ * Return the number of element in the index.
+ * 
+ * @param Lua ephemeral space.
+ * @retval number - number of element in the index.
+ */
+static int
+lbox_ephemeral_index_len(struct lua_State *L)
+{
+	if (lua_gettop(L) != 1)
+		return luaL_error(L, "Usage: index:len()");
+	struct space *space = (struct space *)lua_checkephemeralspace(L, 1);
+	lua_pushnumber(L, box_ephemeral_index_len(space, 0));
+	return 1;
+}
+
+/**
+ * Return the number of bytes used in memory by the
+ * index.
+ * 
+ * @param Lua ephemeral space.
+ * @retval number - number of bytes used in memory
+ * by the index.
+ */
+static int
+lbox_ephemeral_index_bsize(struct lua_State *L)
+{
+	if (lua_gettop(L) != 1)
+		return luaL_error(L, "Usage: index:bsize()");
+	struct space *space = (struct space *)lua_checkephemeralspace(L, 1);
+	lua_pushnumber(L, box_ephemeral_index_bsize(space, 0));
+	return 1;
+}
+
+/**
+ * Return a random tuple from the index.
+ * 
+ * @param Lua ephemeral space.
+ * @param Lua number - seed.
+ * @retval tuple or nil.
+ */
+static int
+lbox_ephemeral_index_random(lua_State *L)
+{
+	if (lua_gettop(L) != 2 || !lua_istable(L, 1))
+		return luaL_error(L, "Usage index:random(seed)");
+	struct space *space = (struct space *)lua_checkephemeralspace(L, 1);
+	uint32_t rnd = luaL_checknumber (L, 2);
+	struct tuple *result;
+	if (box_ephemeral_index_random(space, 0, rnd, &result) != 0)
+		return luaT_error(L);
+	return luaT_pushtupleornil(L, result);
+}
+
+/**
+ * Return a tuple from the index by given key.
+ * 
+ * @param Lua ephemeral space.
+ * @param Lua tuple - key.
+ * @retval tuple or nil.
+ */
+static int
+lbox_ephemeral_index_get(lua_State *L)
+{
+	if (lua_gettop(L) != 2 || !lua_istable(L, 1))
+		return luaL_error(L, "Usage index:get(key)");
+	struct space *space = (struct space *)lua_checkephemeralspace(L, 1);
+	size_t key_len;
+	struct tuple *result;
+	const char *key = lbox_encode_tuple_on_gc(L, 2, &key_len);
+	if (box_ephemeral_index_get(space, 0, key, key + key_len, &result) != 0)
+		return luaT_error(L);
+	return luaT_pushtupleornil(L, result);
+}
+
+static inline void
+lbox_port_to_table(lua_State *L, struct port *port_base)
+{
+	struct port_tuple *port = port_tuple(port_base);
+	lua_createtable(L, port->size, 0);
+	struct port_tuple_entry *entry = port->first;
+	for (int i = 0 ; i < port->size; i++) {
+		luaT_pushtuple(L, entry->tuple);
+		lua_rawseti(L, -2, i + 1);
+		entry = entry->next;
+	}
+}
+
+static int
+lbox_ephemeral_index_select(lua_State *L)
+{
+	if (lua_gettop(L) != 5 || !lua_istable(L, 1)) {
+		return luaL_error(L, "Usage index:select(iterator, offset, "
+				  "limit, key)");
+	}
+	struct space *space = (struct 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 int
+lbox_ephemeral_index_iterator(lua_State *L)
+{
+	if (lua_gettop(L) != 3 || !lua_istable(L, 1)) {
+		return luaL_error(L, "Usage index:iterator(type, key)");
+	}
+	struct space *space = (struct space *)lua_checkephemeralspace(L, 1);
+	int iterator = luaL_checknumber(L, 2);
+	size_t mpkey_len;
+	/* Key encoded by Lua */
+	const char *mpkey = lua_tolstring(L, 3, &mpkey_len);
+	struct iterator *it = box_ephemeral_index_iterator(space, 0, 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;
+}
+
+/**
+ * Return a first (minimal) tuple from the index
+ * matched provided key.
+ * 
+ * @param Lua ephemeral space.
+ * @param Lua tuple - key.
+ * @retval tuple or nil.
+ */
+static int
+lbox_ephemeral_index_min(lua_State *L)
+{
+	if (lua_gettop(L) != 2 || !lua_istable(L, 1))
+		return luaL_error(L, "Usage index:min(key)");
+	struct space *space = (struct space *)lua_checkephemeralspace(L, 1);
+	size_t key_len;
+	struct tuple *result;
+	const char *key = lbox_encode_tuple_on_gc(L, 2, &key_len);
+	if (box_ephemeral_index_min(space, 0, key, key + key_len, &result) != 0)
+		return luaT_error(L);
+	return luaT_pushtupleornil(L, result);
+}
+
+/**
+ * Return a last (maximal) tuple from the index
+ * matched provided key.
+ * 
+ * @param Lua ephemeral space.
+ * @param Lua tuple - key.
+ * @retval tuple or nil.
+ */
+static int
+lbox_ephemeral_index_max(lua_State *L)
+{
+	if (lua_gettop(L) != 2 || !lua_istable(L, 1))
+		return luaL_error(L, "Usage index:max(key)");
+	struct space *space = (struct space *)lua_checkephemeralspace(L, 1);
+	size_t key_len;
+	struct tuple *result;
+	const char *key = lbox_encode_tuple_on_gc(L, 2, &key_len);
+	if (box_ephemeral_index_max(space, 0, key, key + key_len, &result) != 0)
+		return luaT_error(L);
+	return luaT_pushtupleornil(L, result);
+}
+
+/**
+ * Count the number of tuple matched the provided key.
+ * 
+ * @param Lua ephemeral space.
+ * @param Lua number - iterator type.
+ * @param Lua tuple - key.
+ * @retval tuple or nil.
+ */
+static int
+lbox_ephemeral_index_count(lua_State *L)
+{
+	if (lua_gettop(L) != 3 || !lua_istable(L, 1))
+		return luaL_error(L, "Usage index:count(type, key)");
+	struct space *space = (struct space *)lua_checkephemeralspace(L, 1);
+	uint32_t type = lua_tonumber(L, 2);
+	size_t key_len;
+	const char *key = lbox_encode_tuple_on_gc(L, 3, &key_len);
+	ssize_t count = box_ephemeral_index_count(space, 0, type, key,
+						  key + key_len);
+	if (count < 0)
+		return luaT_error(L);
+	lua_pushnumber(L, count);
+	return 1;
+}
+
+/**
+ * Index statistics.
+ * 
+ * @param Lua ephemeral space.
+ * @retval info handler.
+ */
+static int
+lbox_ephemeral_index_stat(lua_State *L)
+{
+	if (lua_gettop(L) != 1 || !lua_istable(L, 1))
+		return luaL_error(L, "Usage index:stat()");
+	struct space *space = (struct space *)lua_checkephemeralspace(L, 1);
+	struct info_handler info;
+	luaT_info_handler_create(&info, L);
+	if (box_ephemeral_index_stat(space, 0, &info) != 0)
+		return luaT_error(L);
+	return 1;
+}
+
+/**
+ * Run index compaction.
+ * 
+ * @param Lua ephemeral space.
+ */
+static int
+lbox_ephemeral_index_compact(lua_State *L)
+{
+	if (lua_gettop(L) != 1 || !lua_istable(L, 1))
+		return luaL_error(L, "Usage index:compact()");
+	struct space *space = (struct space *)lua_checkephemeralspace(L, 1);
+	if (box_ephemeral_index_compact(space, 0) != 0)
+		return luaT_error(L);
+	return 0;
+}
+
+/**
+ * Update tuple matched the provided key.
+ * 
+ * @param Lua ephemeral space.
+ * @param Lua tuple - key.
+ * @param Lua tuple - operaions in case of update .
+ * @retval tuple or nil.
+ */
+static int
+lbox_ephemeral_index_update(lua_State *L)
+{
+	if (lua_gettop(L) != 3 || !lua_istable(L, 1) ||
+	    (lua_type(L, 2) != LUA_TTABLE && luaT_istuple(L, 2) == NULL) ||
+	    (lua_type(L, 3) != LUA_TTABLE && luaT_istuple(L, 3) == NULL))
+		return luaL_error(L, "Usage index:update(key, ops)");
+	struct space *space = (struct space *)lua_checkephemeralspace(L, 1);
+	size_t key_len;
+	const char *key = lbox_encode_tuple_on_gc(L, 2, &key_len);
+	size_t ops_len;
+	const char *ops = lbox_encode_tuple_on_gc(L, 3, &ops_len);
+	struct tuple *result;
+	if (box_ephemeral_update(space, 0, key, key + key_len,
+				 ops, ops + ops_len, 1, &result) != 0)
+		return luaT_error(L);
+	return luaT_pushtupleornil(L, result);
+}
+
+/**
+ * Delete tuple matched the provided key.
+ * 
+ * @param Lua ephemeral space.
+ * @param Lua tuple - key.
+ * @retval tuple or nil.
+ */
+static int
+lbox_ephemeral_index_delete(lua_State *L)
+{
+	if (lua_gettop(L) != 2 || !lua_istable(L, 1) ||
+	    (lua_type(L, 2) != LUA_TTABLE && luaT_istuple(L, 2) == NULL))
+		return luaL_error(L, "Usage index:delete(key)");
+	struct space *space = (struct space *)lua_checkephemeralspace(L, 1);
+	size_t key_len;
+	const char *key = lbox_encode_tuple_on_gc(L, 2, &key_len);
+	struct tuple *result;
+	if (box_ephemeral_delete(space, 0, key, key + key_len, &result) != 0)
+		return luaT_error(L);
+	return luaT_pushtupleornil(L, result);
+}
+
+/**
  * Make a tuple or a table Lua object by map.
  * @param Lua space object.
  * @param Lua map table object.
@@ -771,9 +1139,13 @@ box_lua_space_init(struct lua_State *L)
 
 	int rc = luaL_cdef(L, "struct space;");
 	assert(rc == 0);
-	(void) rc;
 	CTID_STRUCT_SPACE_POINTER = luaL_ctypeid(L, "struct space *");
 	assert(CTID_STRUCT_SPACE_POINTER != 0);
+	rc = luaL_cdef(L, "struct iterator;");
+	assert(rc == 0);
+	CTID_STRUCT_ITERATOR_REF = luaL_ctypeid(L, "struct iterator&");
+	assert(CTID_STRUCT_ITERATOR_REF != 0);
+	(void) rc;
 
 	lua_getfield(L, LUA_GLOBALSINDEX, "box");
 	lua_newtable(L);
@@ -873,9 +1245,31 @@ box_lua_space_init(struct lua_State *L)
 	lua_pop(L, 1);
 	static const struct luaL_Reg space_ephemeral_lib[] = {
 		{"frommap", lbox_space_frommap_ephemeral},
+		{"insert", lbox_ephemeral_space_insert},
+		{"replace", lbox_ephemeral_space_replace},
+		{"upsert", lbox_ephemeral_space_upsert},
 		{NULL, NULL}
 	};
 	luaL_register(L, "box.schema.space_ephemeral_methods",
 		      space_ephemeral_lib);
 	lua_pop(L, 1);
+	static const struct luaL_Reg index_ephemeral_lib[] = {
+		{"len", lbox_ephemeral_index_len},
+		{"bsize", lbox_ephemeral_index_bsize},
+		{"random", lbox_ephemeral_index_random},
+		{"get", lbox_ephemeral_index_get},
+		{"min", lbox_ephemeral_index_min},
+		{"max", lbox_ephemeral_index_max},
+		{"count", lbox_ephemeral_index_count},
+		{"iterator", lbox_ephemeral_index_iterator},
+		{"stat", lbox_ephemeral_index_stat},
+		{"compact", lbox_ephemeral_index_compact},
+		{"update", lbox_ephemeral_index_update},
+		{"delete", lbox_ephemeral_index_delete},
+		{"select", lbox_ephemeral_index_select},
+		{NULL, NULL}
+	};
+	luaL_register(L, "box.schema.index_ephemeral_methods",
+		      index_ephemeral_lib);
+	lua_pop(L, 1);
 }
diff --git a/test/box/ephemeral_space.result b/test/box/ephemeral_space.result
index d958ffe..0f80fa4 100644
--- a/test/box/ephemeral_space.result
+++ b/test/box/ephemeral_space.result
@@ -1,3 +1,6 @@
+test_run = require('test_run').new()
+---
+...
 -- Ephemeral space: creation and dropping.
 -- Simple creation.
 s = box.schema.space.create_ephemeral()
@@ -480,3 +483,7979 @@ s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {dummy = true})
 s:drop()
 ---
 ...
+-- Ephemeral space: methods: insert
+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}
+---
+- error: Tuple field count 2 does not match space field count 3
+...
+s:insert{3,3,3}
+---
+- [3, 3, 3]
+...
+s:insert{4,4,4,4}
+---
+- error: Tuple field count 4 does not match space field count 3
+...
+s:drop()
+---
+...
+s = box.schema.space.create_ephemeral()
+---
+...
+i = s:create_index('a', { type = 'tree', parts = {1, 'string'} })
+---
+...
+for key = 1, 10 do s:insert({tostring(key)}) end
+---
+...
+t = {}
+---
+...
+for key = 1, 10 do table.insert(t, s:get({tostring(key)})) end
+---
+...
+t
+---
+- - ['1']
+  - ['2']
+  - ['3']
+  - ['4']
+  - ['5']
+  - ['6']
+  - ['7']
+  - ['8']
+  - ['9']
+  - ['10']
+...
+s:insert({tostring(7)})
+---
+- error: Duplicate key exists in unique index 'a' in space 'ephemeral'
+...
+s:drop()
+---
+...
+s = box.schema.space.create_ephemeral()
+---
+...
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} })
+---
+...
+for key = 1, 10 do s:insert({key}) end
+---
+...
+t = {}
+---
+...
+for key = 1, 10 do table.insert(t, s:get({key})) end
+---
+...
+t
+---
+- - [1]
+  - [2]
+  - [3]
+  - [4]
+  - [5]
+  - [6]
+  - [7]
+  - [8]
+  - [9]
+  - [10]
+...
+s:insert({7})
+---
+- error: Duplicate key exists in unique index 'a' in space 'ephemeral'
+...
+s:drop()
+---
+...
+s = box.schema.space.create_ephemeral()
+---
+...
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned', 2, 'unsigned'} })
+---
+...
+for key = 1, 10 do s:insert({key, key}) end
+---
+...
+t = {}
+---
+...
+for key = 1, 10 do table.insert(t, s:get({key, key})) end
+---
+...
+t
+---
+- - [1, 1]
+  - [2, 2]
+  - [3, 3]
+  - [4, 4]
+  - [5, 5]
+  - [6, 6]
+  - [7, 7]
+  - [8, 8]
+  - [9, 9]
+  - [10, 10]
+...
+s:insert({7, 7})
+---
+- error: Duplicate key exists in unique index 'a' in space 'ephemeral'
+...
+s:drop()
+---
+...
+s = box.schema.space.create_ephemeral()
+---
+...
+i = s:create_index('a', { type = 'tree', parts = {1, 'string'} })
+---
+...
+for key = 1, 10 do s:insert({tostring(key)}) end
+---
+...
+t = {}
+---
+...
+for key = 1, 10 do table.insert(t, s:get({tostring(key)})) end
+---
+...
+t
+---
+- - ['1']
+  - ['2']
+  - ['3']
+  - ['4']
+  - ['5']
+  - ['6']
+  - ['7']
+  - ['8']
+  - ['9']
+  - ['10']
+...
+s:insert(box.tuple.new{tostring(7)})
+---
+- error: Duplicate key exists in unique index 'a' in space 'ephemeral'
+...
+s:drop()
+---
+...
+s = box.schema.space.create_ephemeral()
+---
+...
+index = s:create_index('a')
+---
+...
+s:insert(1)
+---
+- error: Tuple/Key must be MsgPack array
+...
+s:insert(1, 2)
+---
+- error: Tuple/Key must be MsgPack array
+...
+s:insert(1, 2, 3)
+---
+- error: Tuple/Key must be MsgPack array
+...
+s:insert{1}
+---
+- [1]
+...
+s:insert{2, 3}
+---
+- [2, 3]
+...
+tmp = s:delete(1, 2, 3)
+---
+...
+s:select{}
+---
+- - [2, 3]
+...
+s:drop()
+---
+...
+s = box.schema.space.create_ephemeral()
+---
+...
+i = s:create_index('a', { type = 'tree', parts = {3, 'unsigned'} })
+---
+...
+s:insert{1}
+---
+- error: Tuple field count 1 is less than required by space format or defined indexes
+    (expected at least 3)
+...
+s:insert{2, 3}
+---
+- error: Tuple field count 2 is less than required by space format or defined indexes
+    (expected at least 3)
+...
+s:insert{4, 5, 6}
+---
+- [4, 5, 6]
+...
+s:drop()
+---
+...
+s = box.schema.space.create_ephemeral()
+---
+...
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} })
+---
+...
+s:insert{"1"}
+---
+- error: 'Tuple field 1 type does not match one required by operation: expected unsigned'
+...
+s:drop()
+---
+...
+s = box.schema.space.create_ephemeral()
+---
+...
+i = s:create_index('a', { type = 'tree', parts = {1, 'string'} })
+---
+...
+s:insert{1}
+---
+- error: 'Tuple field 1 type does not match one required by operation: expected string'
+...
+s:drop()
+---
+...
+-- Ephemeral space: methods: replace
+s = box.schema.space.create_ephemeral({field_count = 3})
+---
+...
+i = s:create_index('a')
+---
+...
+s:replace{1}
+---
+- error: Tuple field count 1 does not match space field count 3
+...
+s:replace{2, 2}
+---
+- error: Tuple field count 2 does not match space field count 3
+...
+s:replace{3, 3, 3}
+---
+- [3, 3, 3]
+...
+s:replace{4, 4, 4, 4}
+---
+- error: Tuple field count 4 does not match space field count 3
+...
+s:drop()
+---
+...
+s = box.schema.space.create_ephemeral()
+---
+...
+i = s:create_index('a', { type = 'tree', parts = {1, 'string'} })
+---
+...
+for key = 1, 10 do s:replace({tostring(key)}) end
+---
+...
+t = {}
+---
+...
+for key = 1, 10 do table.insert(t, s:get({tostring(key)})) end
+---
+...
+t
+---
+- - ['1']
+  - ['2']
+  - ['3']
+  - ['4']
+  - ['5']
+  - ['6']
+  - ['7']
+  - ['8']
+  - ['9']
+  - ['10']
+...
+_ = s:replace({tostring(7)})
+---
+...
+s:drop()
+---
+...
+s = box.schema.space.create_ephemeral()
+---
+...
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} })
+---
+...
+for key = 1, 10 do s:replace({key}) end
+---
+...
+t = {}
+---
+...
+for key = 1, 10 do table.insert(t, s:get({key})) end
+---
+...
+t
+---
+- - [1]
+  - [2]
+  - [3]
+  - [4]
+  - [5]
+  - [6]
+  - [7]
+  - [8]
+  - [9]
+  - [10]
+...
+_ = s:replace({7})
+---
+...
+s:drop()
+---
+...
+s = box.schema.space.create_ephemeral()
+---
+...
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned', 2, 'unsigned'} })
+---
+...
+for key = 1, 10 do s:replace({key, key}) end
+---
+...
+t = {}
+---
+...
+for key = 1, 10 do table.insert(t, s:get({key, key})) end
+---
+...
+t
+---
+- - [1, 1]
+  - [2, 2]
+  - [3, 3]
+  - [4, 4]
+  - [5, 5]
+  - [6, 6]
+  - [7, 7]
+  - [8, 8]
+  - [9, 9]
+  - [10, 10]
+...
+s:replace({7, 7})
+---
+- [7, 7]
+...
+s:drop()
+---
+...
+s = box.schema.space.create_ephemeral()
+---
+...
+i = s:create_index('a', { type = 'tree', parts = {1, 'string'} })
+---
+...
+for key = 1, 10 do s:replace({tostring(key)}) end
+---
+...
+t = {}
+---
+...
+for key = 1, 10 do table.insert(t, s:get({tostring(key)})) end
+---
+...
+t
+---
+- - ['1']
+  - ['2']
+  - ['3']
+  - ['4']
+  - ['5']
+  - ['6']
+  - ['7']
+  - ['8']
+  - ['9']
+  - ['10']
+...
+s:replace(box.tuple.new{tostring(7)})
+---
+- ['7']
+...
+s:drop()
+---
+...
+s = box.schema.space.create_ephemeral()
+---
+...
+index = s:create_index('a')
+---
+...
+s:replace(1)
+---
+- error: Tuple/Key must be MsgPack array
+...
+s:replace(1, 2)
+---
+- error: Tuple/Key must be MsgPack array
+...
+s:replace(1, 2, 3)
+---
+- error: Tuple/Key must be MsgPack array
+...
+s:replace{1}
+---
+- [1]
+...
+s:replace{2, 3}
+---
+- [2, 3]
+...
+tmp = s:delete(1, 2, 3)
+---
+...
+s:select{}
+---
+- - [2, 3]
+...
+s:drop()
+---
+...
+s = box.schema.space.create_ephemeral()
+---
+...
+i = s:create_index('a', { type = 'tree', parts = {3, 'unsigned'} })
+---
+...
+s:replace{1}
+---
+- error: Tuple field count 1 is less than required by space format or defined indexes
+    (expected at least 3)
+...
+s:replace{2, 3}
+---
+- error: Tuple field count 2 is less than required by space format or defined indexes
+    (expected at least 3)
+...
+s:replace{4, 5, 6}
+---
+- [4, 5, 6]
+...
+s:drop()
+---
+...
+s = box.schema.space.create_ephemeral()
+---
+...
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} })
+---
+...
+s:replace{"1"}
+---
+- error: 'Tuple field 1 type does not match one required by operation: expected unsigned'
+...
+s:drop()
+---
+...
+s = box.schema.space.create_ephemeral()
+---
+...
+i = s:create_index('a', { type = 'tree', parts = {1, 'string'} })
+---
+...
+s:replace{1}
+---
+- error: 'Tuple field 1 type does not match one required by operation: expected string'
+...
+s:drop()
+---
+...
+-- Ephemeral space: methods: upsert
+s = box.schema.space.create_ephemeral()
+---
+...
+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 = box.schema.space.create_ephemeral()
+---
+...
+i = s:create_index('a', { type = 'tree', parts = {1, 'string'} })
+---
+...
+for key = 1, 10 do s:upsert({tostring(key), 0}, {{'+', 2, 1}}) end
+---
+...
+t = {}
+---
+...
+for key = 1, 10 do table.insert(t, s:get({tostring(key)})) end
+---
+...
+t
+---
+- - ['1', 0]
+  - ['2', 0]
+  - ['3', 0]
+  - ['4', 0]
+  - ['5', 0]
+  - ['6', 0]
+  - ['7', 0]
+  - ['8', 0]
+  - ['9', 0]
+  - ['10', 0]
+...
+for key = 1, 10 do s:upsert({tostring(key), 0}, {{'+', 2, 10}}) end
+---
+...
+t = {}
+---
+...
+for key = 1, 10 do table.insert(t, s:get({tostring(key)})) end
+---
+...
+t
+---
+- - ['1', 10]
+  - ['2', 10]
+  - ['3', 10]
+  - ['4', 10]
+  - ['5', 10]
+  - ['6', 10]
+  - ['7', 10]
+  - ['8', 10]
+  - ['9', 10]
+  - ['10', 10]
+...
+for key = 1, 10 do s:delete({tostring(key)}) end
+---
+...
+for key = 1, 10 do s:upsert({tostring(key), 0}, {{'+', 2, 1}, {'=', 3, key}}) end
+---
+...
+t = {}
+---
+...
+for key = 1, 10 do table.insert(t, s:get({tostring(key)})) end
+---
+...
+t
+---
+- - ['1', 0]
+  - ['2', 0]
+  - ['3', 0]
+  - ['4', 0]
+  - ['5', 0]
+  - ['6', 0]
+  - ['7', 0]
+  - ['8', 0]
+  - ['9', 0]
+  - ['10', 0]
+...
+s:drop()
+---
+...
+s = box.schema.space.create_ephemeral()
+---
+...
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} })
+---
+...
+for key = 1, 10 do s:upsert({key, 0}, {{'+', 2, 1}}) end
+---
+...
+t = {}
+---
+...
+for key = 1, 10 do table.insert(t, s:get({key})) end
+---
+...
+t
+---
+- - [1, 0]
+  - [2, 0]
+  - [3, 0]
+  - [4, 0]
+  - [5, 0]
+  - [6, 0]
+  - [7, 0]
+  - [8, 0]
+  - [9, 0]
+  - [10, 0]
+...
+for key = 1, 10 do s:upsert({key, 0}, {{'+', 2, 10}}) end
+---
+...
+t = {}
+---
+...
+for key = 1, 10 do table.insert(t, s:get({key})) end
+---
+...
+t
+---
+- - [1, 10]
+  - [2, 10]
+  - [3, 10]
+  - [4, 10]
+  - [5, 10]
+  - [6, 10]
+  - [7, 10]
+  - [8, 10]
+  - [9, 10]
+  - [10, 10]
+...
+for key = 1, 10 do s:delete({key}) end
+---
+...
+for key = 1, 10 do s:upsert({key, 0}, {{'+', 2, 1}, {'=', 3, key}}) end
+---
+...
+t = {}
+---
+...
+for key = 1, 10 do table.insert(t, s:get({key})) end
+---
+...
+t
+---
+- - [1, 0]
+  - [2, 0]
+  - [3, 0]
+  - [4, 0]
+  - [5, 0]
+  - [6, 0]
+  - [7, 0]
+  - [8, 0]
+  - [9, 0]
+  - [10, 0]
+...
+s:drop()
+---
+...
+s = box.schema.space.create_ephemeral()
+---
+...
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned', 2, 'unsigned'} })
+---
+...
+for key = 1, 10 do s:upsert({key, key, 0}, {{'+', 3, 1}}) end
+---
+...
+t = {}
+---
+...
+for key = 1, 10 do table.insert(t, s:get({key, key})) end
+---
+...
+t
+---
+- - [1, 1, 0]
+  - [2, 2, 0]
+  - [3, 3, 0]
+  - [4, 4, 0]
+  - [5, 5, 0]
+  - [6, 6, 0]
+  - [7, 7, 0]
+  - [8, 8, 0]
+  - [9, 9, 0]
+  - [10, 10, 0]
+...
+for key = 1, 10 do s:upsert({key, key, 0}, {{'+', 3, 10}}) end
+---
+...
+t = {}
+---
+...
+for key = 1, 10 do table.insert(t, s:get({key, key})) end
+---
+...
+t
+---
+- - [1, 1, 10]
+  - [2, 2, 10]
+  - [3, 3, 10]
+  - [4, 4, 10]
+  - [5, 5, 10]
+  - [6, 6, 10]
+  - [7, 7, 10]
+  - [8, 8, 10]
+  - [9, 9, 10]
+  - [10, 10, 10]
+...
+for key = 1, 10 do s:delete({key, key}) end
+---
+...
+for key = 1, 10 do s:upsert({key, key, 0}, {{'+', 3, 1}, {'=', 4, key}}) end
+---
+...
+t = {}
+---
+...
+for key = 1, 10 do table.insert(t, s:get({key, key})) end
+---
+...
+t
+---
+- - [1, 1, 0]
+  - [2, 2, 0]
+  - [3, 3, 0]
+  - [4, 4, 0]
+  - [5, 5, 0]
+  - [6, 6, 0]
+  - [7, 7, 0]
+  - [8, 8, 0]
+  - [9, 9, 0]
+  - [10, 10, 0]
+...
+s:drop()
+---
+...
+test_run:cmd("setopt delimiter ';'");
+---
+- true
+...
+function less(a, b)
+    if type(a[2]) ~= type(b[2]) then
+        return type(a[2]) < type(b[2])
+    end
+    if a[2] == b[2] then
+        return a[1] < b[1]
+    end
+    if type(a[2]) == 'boolean' then
+        return a[2] == false and b[2] == true
+    end
+    return a[2] < b[2]
+end;
+---
+...
+test_run:cmd("setopt delimiter ''");
+---
+- true
+...
+function sort(t) table.sort(t, less) return t end
+---
+...
+-- upsert default tuple constraint
+s = box.schema.space.create_ephemeral()
+---
+...
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned', 2, 'unsigned'} })
+---
+...
+s:upsert({0, 'key', 0}, {{'+', 3, 1}})
+---
+- error: 'Tuple field 2 type does not match one required by operation: expected unsigned'
+...
+s:drop()
+---
+...
+-- upsert primary key modify (skipped)
+s = box.schema.space.create_ephemeral()
+---
+...
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} })
+---
+...
+s:upsert({0, 0}, {{'+', 1, 1}, {'+', 2, 1}})
+---
+...
+s:get({0})
+---
+- [0, 0]
+...
+s:drop()
+---
+...
+-- upsert with box.tuple.new
+s = box.schema.space.create_ephemeral()
+---
+...
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} })
+---
+...
+s:upsert({0, 0}, {{'+', 1, 1}, {'+', 2, 1}})
+---
+...
+s:get({0})
+---
+- [0, 0]
+...
+s:drop()
+---
+...
+s = box.schema.space.create_ephemeral()
+---
+...
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned', 2, 'unsigned'} })
+---
+...
+for key = 1, 10 do s:upsert(box.tuple.new{key, key, 0}, box.tuple.new{{'+', 3, 1}}) end
+---
+...
+t = {}
+---
+...
+for key = 1, 10 do table.insert(t, s:get({key, key})) end
+---
+...
+t
+---
+- - [1, 1, 0]
+  - [2, 2, 0]
+  - [3, 3, 0]
+  - [4, 4, 0]
+  - [5, 5, 0]
+  - [6, 6, 0]
+  - [7, 7, 0]
+  - [8, 8, 0]
+  - [9, 9, 0]
+  - [10, 10, 0]
+...
+for key = 1, 10 do s:upsert(box.tuple.new{key, key, 0}, box.tuple.new{{'+', 3, 10}}) end
+---
+...
+t = {}
+---
+...
+for key = 1, 10 do table.insert(t, s:get({key, key})) end
+---
+...
+t
+---
+- - [1, 1, 10]
+  - [2, 2, 10]
+  - [3, 3, 10]
+  - [4, 4, 10]
+  - [5, 5, 10]
+  - [6, 6, 10]
+  - [7, 7, 10]
+  - [8, 8, 10]
+  - [9, 9, 10]
+  - [10, 10, 10]
+...
+for key = 1, 10 do s:delete({key, key}) end
+---
+...
+for key = 1, 10 do s:upsert(box.tuple.new{key, key, 0}, box.tuple.new{{'+', 3, 1}, {'=', 4, key}}) end
+---
+...
+t = {}
+---
+...
+for key = 1, 10 do table.insert(t, s:get({key, key})) end
+---
+...
+t
+---
+- - [1, 1, 0]
+  - [2, 2, 0]
+  - [3, 3, 0]
+  - [4, 4, 0]
+  - [5, 5, 0]
+  - [6, 6, 0]
+  - [7, 7, 0]
+  - [8, 8, 0]
+  - [9, 9, 0]
+  - [10, 10, 0]
+...
+s:drop()
+---
+...
+s = box.schema.space.create_ephemeral()
+---
+...
+i = s:create_index('a')
+---
+...
+s:upsert({0, 0}, {{'+', 2, 2}})
+---
+...
+s:select{0}
+---
+- - [0, 0]
+...
+tmp = s:delete{0}
+---
+...
+s:upsert({0, 0, 0}, {{'+', 2, 2}})
+---
+...
+s:select{0}
+---
+- - [0, 0, 0]
+...
+tmp = s:delete{0}
+---
+...
+s:upsert({0}, {{'+', 2, 2}})
+---
+...
+s:select{0}
+---
+- - [0]
+...
+s:replace{0, 1, 2, 4}
+---
+- [0, 1, 2, 4]
+...
+s:upsert({0, 0, "you will not see it"}, {{'+', 2, 2}})
+---
+...
+s:select{0}
+---
+- - [0, 3, 2, 4]
+...
+s:replace{0, -0x4000000000000000ll}
+---
+- [0, -4611686018427387904]
+...
+s:upsert({0}, {{'+', 2, -0x4000000000000001ll}})  -- overflow
+---
+...
+s:select{0}
+---
+- - [0, -4611686018427387904]
+...
+s:replace{0, "thing"}
+---
+- [0, 'thing']
+...
+s:upsert({0, "nothing"}, {{'+', 2, 2}})
+---
+...
+s:select{0}
+---
+- - [0, 'thing']
+...
+tmp = s:delete{0}
+---
+...
+s:upsert({0, "thing"}, {{'+', 2, 2}})
+---
+...
+s:select{0}
+---
+- - [0, 'thing']
+...
+s:replace{0, 1, 2}
+---
+- [0, 1, 2]
+...
+s:upsert({0}, {{'!', 42, 42}})
+---
+...
+s:select{0}
+---
+- - [0, 1, 2]
+...
+s:upsert({0}, {{'#', 42, 42}})
+---
+...
+s:select{0}
+---
+- - [0, 1, 2]
+...
+s:upsert({0}, {{'=', 42, 42}})
+---
+...
+s:select{}
+---
+- - [0, 1, 2]
+...
+s:replace{0, 1.5}
+---
+- [0, 1.5]
+...
+s:select{}
+---
+- - [0, 1.5]
+...
+s:upsert({0}, {{'|', 1, 255}})
+---
+...
+s:select{0}
+---
+- - [0, 1.5]
+...
+s:replace{0, 1.5}
+---
+- [0, 1.5]
+...
+s:replace{0, 'something to splice'}
+---
+- [0, 'something to splice']
+...
+s:upsert({0}, {{':', 2, 1, 4, 'no'}})
+---
+...
+s:select{0}
+---
+- - [0, 'nothing to splice']
+...
+s:upsert({0}, {{':', 2, 1, 2, 'every'}})
+---
+...
+s:select{0}
+---
+- - [0, 'everything to splice']
+...
+s:upsert({0}, {{':', 2, -100, 2, 'every'}})
+---
+...
+s:select{0}
+---
+- - [0, 'everything to splice']
+...
+s:drop()
+---
+...
+s = box.schema.space.create_ephemeral({ field_count = 1 })
+---
+...
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} })
+---
+...
+s:insert({1})
+---
+- [1]
+...
+s:select{}
+---
+- - [1]
+...
+s:upsert({2, 2}, {{'+', 2, 1}})
+---
+- error: Tuple field count 2 does not match space field count 1
+...
+s:select{}
+---
+- - [1]
+...
+s:drop()
+---
+...
+s = box.schema.space.create_ephemeral({ field_count = 2 })
+---
+...
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} })
+---
+...
+s:insert({1, 1})
+---
+- [1, 1]
+...
+s:select{}
+---
+- - [1, 1]
+...
+s:upsert({2, 2, 2}, {{'+', 3, 1}})
+---
+- error: Tuple field count 3 does not match space field count 2
+...
+s:upsert({3, 3}, {{'+', 2, 1}})
+---
+...
+s:select{}
+---
+- - [1, 1]
+  - [3, 3]
+...
+s:drop()
+---
+...
+test_run:cmd("setopt delimiter ';'")
+---
+- true
+...
+function anything_to_string(tab)
+    if tab == nil then
+        return 'nil'
+    end
+    local str = '['
+    local first_route = true
+    local t = 0
+    for k,f in pairs(tab) do
+        if not first_route then str = str .. ',' end
+        first_route = false
+        t = t + 1
+        if k ~= t then
+            str = str .. k .. '='
+        end
+        if type(f) == 'string' then
+            str = str .. "'" .. f .. "'"
+        elseif type (f) == 'number' then
+            str = str .. tostring(f)
+        elseif type (f) == 'table' or type (f) == 'cdata' then
+            str = str .. anything_to_string(f)
+        else
+            str = str .. '?'
+        end
+    end
+    str = str .. ']'
+    return str
+end;
+---
+...
+function things_equal(var1, var2)
+    local type1 = type(var1) == 'cdata' and 'table' or type(var1)
+    local type2 = type(var2) == 'cdata' and 'table' or type(var2)
+    if type1 ~= type2 then
+        return false
+    end
+    if type1 ~= 'table' then
+        return var1 == var2
+    end
+    for k,v in pairs(var1) do
+        if not things_equal(v, var2[k]) then
+            return false
+        end
+    end
+    for k,v in pairs(var2) do
+        if not things_equal(v, var1[k]) then
+            return false
+        end
+    end
+    return true
+end;
+---
+...
+function copy_thing(t)
+    if type(t) ~= 'table' then
+        return t
+    end
+    local res = {}
+    for k,v in pairs(t) do
+        res[copy_thing(k)] = copy_thing(v)
+    end
+    return res
+end;
+---
+...
+function test(space, key_tuple, ops, expect)
+    space:upsert(key_tuple, ops)
+    if (things_equal(space:select{}, expect)) then
+        return 'upsert('.. anything_to_string(key_tuple) .. ', ' ..
+                anything_to_string(ops) .. ', '  ..
+                ') OK ' .. anything_to_string(space:select{})
+    end
+    return 'upsert('.. anything_to_string(key_tuple) .. ', ' ..
+            anything_to_string(ops) .. ', ' ..
+            ') FAILED, got ' .. anything_to_string(space:select{}) ..
+            ' expected ' .. anything_to_string(expect)
+end;
+---
+...
+test_run:cmd("setopt delimiter ''");
+---
+- true
+...
+s = box.schema.space.create_ephemeral()
+---
+...
+i = s:create_index('a', { type = 'tree', parts = {1, 'string'} })
+---
+...
+s:upsert({1}, {{'!', 2, 100}}) -- must fail on checking tuple
+---
+- error: 'Tuple field 1 type does not match one required by operation: expected string'
+...
+s:upsert({'a'}, {{'a', 2, 100}}) -- must fail on checking ops
+---
+- error: Unknown UPDATE operation
+...
+s:upsert({'a'}, {{'!', 2, 'ups1'}}) -- 'fast' upsert via insert in one index
+---
+...
+s:upsert({'a', 'b'}, {{'!', 2, 'ups2'}}) -- 'fast' upsert via update in one index
+---
+...
+s:select{}
+---
+- - ['a', 'ups2']
+...
+s:drop()
+---
+...
+s = box.schema.space.create_ephemeral()
+---
+...
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} })
+---
+...
+s:insert({1, 1, 1})
+---
+- [1, 1, 1]
+...
+s:insert({2, 2, 2})
+---
+- [2, 2, 2]
+...
+s:insert({3, 3, 3})
+---
+- [3, 3, 3]
+...
+s:select{}
+---
+- - [1, 1, 1]
+  - [2, 2, 2]
+  - [3, 3, 3]
+...
+s:upsert({2, 18, 76}, {})
+---
+...
+s:upsert({4, 4, 4}, {})
+---
+...
+s:select{}
+---
+- - [1, 1, 1]
+  - [2, 2, 2]
+  - [3, 3, 3]
+  - [4, 4, 4]
+...
+s:drop()
+---
+...
+s = box.schema.space.create_ephemeral()
+---
+...
+i = s:create_index('a')
+---
+...
+t = {1, '1', 1, 'qwerty'}
+---
+...
+s:insert(t)
+---
+- [1, '1', 1, 'qwerty']
+...
+-- all good operations, one op, equivalent to update
+test(s, t, {{'+', 3, 5}}, {{1, '1', 6, 'qwerty'}})
+---
+- upsert([1,'1',1,'qwerty'], [['+',3,5]], ) OK [[1,'1',6,'qwerty']]
+...
+test(s, t, {{'-', 3, 3}}, {{1, '1', 3, 'qwerty'}})
+---
+- upsert([1,'1',1,'qwerty'], [['-',3,3]], ) OK [[1,'1',3,'qwerty']]
+...
+test(s, t, {{'&', 3, 5}}, {{1, '1', 1, 'qwerty'}})
+---
+- upsert([1,'1',1,'qwerty'], [['&',3,5]], ) OK [[1,'1',1,'qwerty']]
+...
+test(s, t, {{'|', 3, 8}}, {{1, '1', 9, 'qwerty'}})
+---
+- upsert([1,'1',1,'qwerty'], [['|',3,8]], ) OK [[1,'1',9,'qwerty']]
+...
+test(s, t, {{'^', 3, 12}}, {{1, '1', 5, 'qwerty'}})
+---
+- upsert([1,'1',1,'qwerty'], [['^',3,12]], ) OK [[1,'1',5,'qwerty']]
+...
+test(s, t, {{':', 4, 2, 4, "uer"}}, {{1, '1', 5, 'query'}})
+---
+- upsert([1,'1',1,'qwerty'], [[':',4,2,4,'uer']], ) OK [[1,'1',5,'query']]
+...
+test(s, t, {{'!', 4, 'answer'}}, {{1, '1', 5, 'answer', 'query'}})
+---
+- upsert([1,'1',1,'qwerty'], [['!',4,'answer']], ) OK [[1,'1',5,'answer','query']]
+...
+test(s, t, {{'#', 5, 1}}, {{1, '1', 5, 'answer'}})
+---
+- upsert([1,'1',1,'qwerty'], [['#',5,1]], ) OK [[1,'1',5,'answer']]
+...
+test(s, t, {{'!', -1, 1}}, {{1, '1', 5, 'answer', 1}})
+---
+- upsert([1,'1',1,'qwerty'], [['!',-1,1]], ) OK [[1,'1',5,'answer',1]]
+...
+test(s, t, {{'!', -1, 2}}, {{1, '1', 5, 'answer', 1, 2}})
+---
+- upsert([1,'1',1,'qwerty'], [['!',-1,2]], ) OK [[1,'1',5,'answer',1,2]]
+...
+test(s, t, {{'!', -1, 3}}, {{1, '1', 5, 'answer', 1, 2 ,3}})
+---
+- upsert([1,'1',1,'qwerty'], [['!',-1,3]], ) OK [[1,'1',5,'answer',1,2,3]]
+...
+test(s, t, {{'#', 5, 100500}}, {{1, '1', 5, 'answer'}})
+---
+- upsert([1,'1',1,'qwerty'], [['#',5,100500]], ) OK [[1,'1',5,'answer']]
+...
+test(s, t, {{'=', 4, 'qwerty'}}, {{1, '1', 5, 'qwerty'}})
+---
+- upsert([1,'1',1,'qwerty'], [['=',4,'qwerty']], ) OK [[1,'1',5,'qwerty']]
+...
+-- same check for negative posistion
+test(s, t, {{'+', -2, 5}}, {{1, '1', 10, 'qwerty'}})
+---
+- upsert([1,'1',1,'qwerty'], [['+',-2,5]], ) OK [[1,'1',10,'qwerty']]
+...
+test(s, t, {{'-', -2, 3}}, {{1, '1', 7, 'qwerty'}})
+---
+- upsert([1,'1',1,'qwerty'], [['-',-2,3]], ) OK [[1,'1',7,'qwerty']]
+...
+test(s, t, {{'&', -2, 5}}, {{1, '1', 5, 'qwerty'}})
+---
+- upsert([1,'1',1,'qwerty'], [['&',-2,5]], ) OK [[1,'1',5,'qwerty']]
+...
+test(s, t, {{'|', -2, 8}}, {{1, '1', 13, 'qwerty'}})
+---
+- upsert([1,'1',1,'qwerty'], [['|',-2,8]], ) OK [[1,'1',13,'qwerty']]
+...
+test(s, t, {{'^', -2, 12}}, {{1, '1', 1, 'qwerty'}})
+---
+- upsert([1,'1',1,'qwerty'], [['^',-2,12]], ) OK [[1,'1',1,'qwerty']]
+...
+test(s, t, {{':', -1, 2, 4, "uer"}}, {{1, '1', 1, 'query'}})
+---
+- upsert([1,'1',1,'qwerty'], [[':',-1,2,4,'uer']], ) OK [[1,'1',1,'query']]
+...
+test(s, t, {{'!', -2, 'answer'}}, {{1, '1', 1, 'answer', 'query'}})
+---
+- upsert([1,'1',1,'qwerty'], [['!',-2,'answer']], ) OK [[1,'1',1,'answer','query']]
+...
+test(s, t, {{'#', -1, 1}}, {{1, '1', 1, 'answer'}})
+---
+- upsert([1,'1',1,'qwerty'], [['#',-1,1]], ) OK [[1,'1',1,'answer']]
+...
+test(s, t, {{'=', -1, 'answer!'}}, {{1, '1', 1, 'answer!'}})
+---
+- upsert([1,'1',1,'qwerty'], [['=',-1,'answer!']], ) OK [[1,'1',1,'answer!']]
+...
+-- selective test for good multiple ops
+test(s, t, {{'+', 3, 2}, {'!', 4, 42}}, {{1, '1', 3, 42, 'answer!'}})
+---
+- upsert([1,'1',1,'qwerty'], [['+',3,2],['!',4,42]], ) OK [[1,'1',3,42,'answer!']]
+...
+test(s, t, {{'!', 1, 666}, {'#', 1, 1}, {'+', 3, 2}}, {{1, '1', 5, 42, 'answer!'}})
+---
+- upsert([1,'1',1,'qwerty'], [['!',1,666],['#',1,1],['+',3,2]], ) OK [[1,'1',5,42,'answer!']]
+...
+test(s, t, {{'!', 3, 43}, {'+', 4, 2}}, {{1, '1', 43, 7, 42, 'answer!'}})
+---
+- upsert([1,'1',1,'qwerty'], [['!',3,43],['+',4,2]], ) OK [[1,'1',43,7,42,'answer!']]
+...
+test(s, t, {{'#', 3, 2}, {'=', 3, 1}, {'=', 4, '1'}}, {{1, '1', 1, '1'}})
+---
+- upsert([1,'1',1,'qwerty'], [['#',3,2],['=',3,1],['=',4,'1']], ) OK [[1,'1',1,'1']]
+...
+-- all bad operations, one op, equivalent to update but error is supressed
+test(s, t, {{'+', 4, 3}}, {{1, '1', 1, '1'}})
+---
+- upsert([1,'1',1,'qwerty'], [['+',4,3]], ) OK [[1,'1',1,'1']]
+...
+test(s, t, {{'-', 4, 3}}, {{1, '1', 1, '1'}})
+---
+- upsert([1,'1',1,'qwerty'], [['-',4,3]], ) OK [[1,'1',1,'1']]
+...
+test(s, t, {{'&', 4, 1}}, {{1, '1', 1, '1'}})
+---
+- upsert([1,'1',1,'qwerty'], [['&',4,1]], ) OK [[1,'1',1,'1']]
+...
+test(s, t, {{'|', 4, 1}}, {{1, '1', 1, '1'}})
+---
+- upsert([1,'1',1,'qwerty'], [['|',4,1]], ) OK [[1,'1',1,'1']]
+...
+test(s, t, {{'^', 4, 1}}, {{1, '1', 1, '1'}})
+---
+- upsert([1,'1',1,'qwerty'], [['^',4,1]], ) OK [[1,'1',1,'1']]
+...
+test(s, t, {{':', 3, 2, 4, "uer"}}, {{1, '1', 1, '1'}})
+---
+- upsert([1,'1',1,'qwerty'], [[':',3,2,4,'uer']], ) OK [[1,'1',1,'1']]
+...
+test(s, t, {{'!', 18, 'answer'}}, {{1, '1', 1, '1'}})
+---
+- upsert([1,'1',1,'qwerty'], [['!',18,'answer']], ) OK [[1,'1',1,'1']]
+...
+test(s, t, {{'#', 18, 1}}, {{1, '1', 1, '1'}})
+---
+- upsert([1,'1',1,'qwerty'], [['#',18,1]], ) OK [[1,'1',1,'1']]
+...
+test(s, t, {{'=', 18, 'qwerty'}}, {{1, '1', 1, '1'}})
+---
+- upsert([1,'1',1,'qwerty'], [['=',18,'qwerty']], ) OK [[1,'1',1,'1']]
+...
+-- selective test for good/bad multiple ops mix
+test(s, t, {{'+', 3, 1}, {'+', 4, 1}}, {{1, '1', 2, '1'}})
+---
+- upsert([1,'1',1,'qwerty'], [['+',3,1],['+',4,1]], ) OK [[1,'1',2,'1']]
+...
+test(s, t, {{'-', 4, 1}, {'-', 3, 1}}, {{1, '1', 1, '1'}})
+---
+- upsert([1,'1',1,'qwerty'], [['-',4,1],['-',3,1]], ) OK [[1,'1',1,'1']]
+...
+test(s, t, {{'#', 18, 1}, {'|', 3, 14}, {'!', 18, '!'}}, {{1, '1', 15, '1'}})
+---
+- upsert([1,'1',1,'qwerty'], [['#',18,1],['|',3,14],['!',18,'!']], ) OK [[1,'1',15,'1']]
+...
+test(s, t, {{'^', 42, 42}, {':', 1, 1, 1, ''}, {'^', 3, 8}}, {{1, '1', 7, '1'}})
+---
+- upsert([1,'1',1,'qwerty'], [['^',42,42],[':',1,1,1,''],['^',3,8]], ) OK [[1,'1',7,'1']]
+...
+test(s, t, {{'&', 3, 1}, {'&', 2, 1}, {'&', 4, 1}}, {{1, '1', 1, '1'}})
+---
+- upsert([1,'1',1,'qwerty'], [['&',3,1],['&',2,1],['&',4,1]], ) OK [[1,'1',1,'1']]
+...
+-- broken ops must raise an exception and discarded
+'dump ' .. anything_to_string(s:select{})
+---
+- dump [[1,'1',1,'1']]
+...
+test(s, t, {{'&', 'a', 3}, {'+', 3, 3}}, {{1, '1', 1, '1'}})
+---
+- error: Illegal parameters, field id must be a number
+...
+test(s, t, {{'+', 3, 3}, {'&', 3, 'a'}}, {{1, '1', 1, '1'}})
+---
+- error: 'Argument type in operation ''&'' on field 3 does not match field type: expected
+    a positive integer'
+...
+test(s, t, {{'+', 3}, {'&', 3, 'a'}}, {{1, '1', 1, '1'}})
+---
+- error: Unknown UPDATE operation
+...
+test(s, t, {{':', 3, 3}}, {{1, '1', 1, '1'}})
+---
+- error: Unknown UPDATE operation
+...
+test(s, t, {{':', 3, 3, 3}}, {{1, '1', 1, '1'}})
+---
+- error: Unknown UPDATE operation
+...
+test(s, t, {{'?', 3, 3}}, {{1, '1', 1, '1'}})
+---
+- error: Unknown UPDATE operation
+...
+'dump ' .. anything_to_string(s:select{})
+---
+- dump [[1,'1',1,'1']]
+...
+-- -- ignoring ops for insert upsert
+test(s, {2, '2', 2, '2'}, {{}}, {{1, '1', 1, '1'}})
+---
+- error: Illegal parameters, update operation must be an array {op,..}, got empty
+    array
+...
+test(s, {3, '3', 3, '3'}, {{'+', 3, 3}}, {{1, '1', 1, '1'}, {3, '3', 3, '3'}})
+---
+- upsert([3,'3',3,'3'], [['+',3,3]], ) OK [[1,'1',1,'1'],[3,'3',3,'3']]
+...
+t[1] = 1
+---
+...
+test(s, t, {{'+', 3, 3}, {'+', 4, 3}}, {{1, '1', 4, '1'}, {3, '3', 3, '3'}})
+---
+- upsert([1,'1',1,'qwerty'], [['+',3,3],['+',4,3]], ) OK [[1,'1',4,'1'],[3,'3',3,'3']]
+...
+t[1] = 2
+---
+...
+test(s, t, {{'-', 4, 1}}, {{1, '1', 4, '1'}, {2, '1', 1, 'qwerty'}, {3, '3', 3, '3'}})
+---
+- upsert([2,'1',1,'qwerty'], [['-',4,1]], ) OK [[1,'1',4,'1'],[2,'1',1,'qwerty'],[3,'3',3,'3']]
+...
+t[1] = 3
+---
+...
+test(s, t, {{':', 3, 3, 3, ''}, {'|', 3, 4}}, {{1, '1', 4, '1'}, {2, '1', 1, 'qwerty'}, {3, '3', 7, '3'}})
+---
+- upsert([3,'1',1,'qwerty'], [[':',3,3,3,''],['|',3,4]], ) OK [[1,'1',4,'1'],[2,'1',1,'qwerty'],[3,'3',7,'3']]
+...
+s:drop()
+---
+...
+s = box.schema.space.create_ephemeral()
+---
+...
+i = s:create_index('a')
+---
+...
+s:replace({1, 1, 1})
+---
+- [1, 1, 1]
+...
+box.snapshot()
+---
+- ok
+...
+s:upsert({1, 1}, {{'+', 2, 2}})
+---
+...
+s:upsert({1, 1}, {{'+', 3, 4}})
+---
+...
+s:select()
+---
+- - [1, 3, 5]
+...
+s:drop()
+---
+...
+s = box.schema.space.create_ephemeral()
+---
+...
+s:upsert({1}, {})
+---
+- error: 'No index #0 is defined in space ''ephemeral'''
+...
+s:drop()
+---
+...
+s = box.schema.space.create_ephemeral()
+---
+...
+i = s:create_index('a', {parts = {1, 'unsigned', 3, 'unsigned'}})
+---
+...
+s:upsert({100, 100, 100}, {{'+', 2, 200}})
+---
+...
+s:upsert({200, 100, 200}, {{'+', 2, 300}})
+---
+...
+s:upsert({300, 100, 300}, {{'+', 2, 400}})
+---
+...
+i:select{}
+---
+- - [100, 100, 100]
+  - [200, 100, 200]
+  - [300, 100, 300]
+...
+s:drop()
+---
+...
+s = box.schema.space.create_ephemeral()
+---
+...
+i = s:create_index('a', {parts = {2, 'unsigned', 3, 'unsigned'}})
+---
+...
+s:upsert({100, 100, 100}, {{'+', 1, 200}})
+---
+...
+s:upsert({200, 100, 200}, {{'+', 1, 300}})
+---
+...
+s:upsert({300, 100, 300}, {{'+', 1, 400}})
+---
+...
+i:select{}
+---
+- - [100, 100, 100]
+  - [200, 100, 200]
+  - [300, 100, 300]
+...
+s:drop()
+---
+...
+s = box.schema.space.create_ephemeral()
+---
+...
+i = s:create_index('a', {parts = {3, 'unsigned', 2, 'unsigned'}})
+---
+...
+s:upsert({100, 100, 100}, {{'+', 1, 200}})
+---
+...
+s:upsert({200, 100, 200}, {{'+', 1, 300}})
+---
+...
+s:upsert({300, 100, 300}, {{'+', 1, 400}})
+---
+...
+i:select{}
+---
+- - [100, 100, 100]
+  - [200, 100, 200]
+  - [300, 100, 300]
+...
+s:drop()
+---
+...
+-- Ephemeral index: methods: update
+s = box.schema.space.create_ephemeral()
+---
+...
+-- test delete field
+i = s:create_index('a')
+---
+...
+s:insert{1000001, 1000002, 1000003, 1000004, 1000005}
+---
+- [1000001, 1000002, 1000003, 1000004, 1000005]
+...
+s:update({1000001}, {{'#', 1, 1}})
+---
+- error: Attempt to modify a tuple field which is part of index 'a' in space 'ephemeral'
+...
+s:update({1000001}, {{'#', 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()
+---
+...
+-- test arithmetic
+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()
+---
+...
+-- test delete multiple fields
+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()
+---
+...
+-- test insert field
+i = s:create_index('a')
+---
+...
+s:insert{1, 3, 6, 9}
+---
+- [1, 3, 6, 9]
+...
+s:update({1}, {{'!', 2, 2}})
+---
+- [1, 2, 3, 6, 9]
+...
+s:update({1}, {{'!', 4, 4}, {'!', 4, 5}, {'!', 5, 7}, {'!', 5, 8}})
+---
+- [1, 2, 3, 5, 8, 7, 4, 6, 9]
+...
+s:update({1}, {{'!', 10, 10}, {'!', 10, 11}, {'!', 10, 12}})
+---
+- [1, 2, 3, 5, 8, 7, 4, 6, 9, 12, 11, 10]
+...
+i:drop()
+---
+...
+i = s:create_index('a')
+---
+...
+s:insert{1, 'tuple'}
+---
+- [1, 'tuple']
+...
+s:update({1}, {{'#', 2, 1}, {'!', 2, 'inserted tuple'}, {'=', 3, 'set tuple'}})
+---
+- [1, 'inserted tuple', 'set tuple']
+...
+i:drop()
+---
+...
+i = s:create_index('a')
+---
+...
+s:insert{1, 'tuple'}
+---
+- [1, 'tuple']
+...
+s:update({1}, {{'=', 2, 'set tuple'}, {'!', 2, 'inserted tuple'}, {'#', 3, 1}})
+---
+- [1, 'inserted tuple']
+...
+s:update({1}, {{'!', 1, 3}, {'!', 1, 2}})
+---
+- error: Attempt to modify a tuple field which is part of index 'a' in space 'ephemeral'
+...
+i:drop()
+---
+...
+-- test update's assign opearations
+i = s:create_index('a')
+---
+...
+s:replace{1, 'field string value'}
+---
+- [1, 'field string value']
+...
+s:update({1}, {{'=', 2, 'new field string value'}, {'=', 3, 42}, {'=', 4, 0xdeadbeef}})
+---
+- [1, 'new field string value', 42, 3735928559]
+...
+-- test multiple update opearations on the same field
+s:update({1}, {{'+', 3, 16}, {'&', 4, 0xffff0000}, {'|', 4, 0x0000a0a0}, {'^', 4, 0xffff00aa}})
+---
+- error: 'Field 4 UPDATE error: double update of the same field'
+...
+-- test update splice operation
+s:replace{1953719668, 'something to splice'}
+---
+- [1953719668, 'something to splice']
+...
+s:update(1953719668, {{':', 2, 1, 4, 'no'}})
+---
+- [1953719668, 'nothing to splice']
+...
+s:update(1953719668, {{':', 2, 1, 2, 'every'}})
+---
+- [1953719668, 'everything to splice']
+...
+-- check an incorrect offset
+s:update(1953719668, {{':', 2, 100, 2, 'every'}})
+---
+- [1953719668, 'everything to spliceevery']
+...
+s:update(1953719668, {{':', 2, -100, 2, 'every'}})
+---
+- error: 'SPLICE error on field 2: offset is out of bound'
+...
+i:drop()
+---
+...
+i = s:create_index('a')
+---
+...
+s:insert{1953719668, 'hello', 'october', '20th'}:unpack()
+---
+- 1953719668
+- hello
+- october
+- 20th
+...
+i:drop()
+---
+...
+i = s:create_index('a')
+---
+...
+s:insert{1953719668, 'hello world'}
+---
+- [1953719668, 'hello world']
+...
+s:update(1953719668, {{'=', 2, 'bye, world'}})
+---
+- [1953719668, 'bye, world']
+...
+s:delete{1953719668}
+---
+- [1953719668, 'bye, world']
+...
+s:replace({10, 'abcde'})
+---
+- [10, 'abcde']
+...
+s:update(10,  {{':', 2, 0, 0, '!'}})
+---
+- error: 'SPLICE error on field 2: offset is out of bound'
+...
+s:update(10,  {{':', 2, 1, 0, '('}})
+---
+- [10, '(abcde']
+...
+s:update(10,  {{':', 2, 2, 0, '({'}})
+---
+- [10, '(({abcde']
+...
+s:update(10,  {{':', 2, -1, 0, ')'}})
+---
+- [10, '(({abcde)']
+...
+s:update(10,  {{':', 2, -2, 0, '})'}})
+---
+- [10, '(({abcde}))']
+...
+-- test update delete operations
+s:update({1}, {{'#', 4, 1}, {'#', 3, 1}})
+---
+...
+-- test update insert operations
+s:update({1}, {{'!', 2, 1}, {'!', 2, 2}, {'!', 2, 3}, {'!', 2, 4}})
+---
+...
+-- s:update: zero field
+s:insert{48}
+---
+- [48]
+...
+s:update(48, {{'=', 0, 'hello'}})
+---
+- error: Field 0 was not found in the tuple
+...
+-- s:update: push/pop fields
+s:insert{1684234849}
+---
+- [1684234849]
+...
+s:update({1684234849}, {{'#', 2, 1}})
+---
+- error: Field 2 was not found in the tuple
+...
+s:update({1684234849}, {{'!', -1, 'push1'}})
+---
+- [1684234849, 'push1']
+...
+s:update({1684234849}, {{'!', -1, 'push2'}})
+---
+- [1684234849, 'push1', 'push2']
+...
+s:update({1684234849}, {{'!', -1, 'push3'}})
+---
+- [1684234849, 'push1', 'push2', 'push3']
+...
+s:update({1684234849}, {{'#', 2, 1}, {'!', -1, 'swap1'}})
+---
+- [1684234849, 'push2', 'push3', 'swap1']
+...
+s:update({1684234849}, {{'#', 2, 1}, {'!', -1, 'swap2'}})
+---
+- [1684234849, 'push3', 'swap1', 'swap2']
+...
+s:update({1684234849}, {{'#', 2, 1}, {'!', -1, 'swap3'}})
+---
+- [1684234849, 'swap1', 'swap2', 'swap3']
+...
+s:update({1684234849}, {{'#', -1, 1}, {'!', -1, 'noop1'}})
+---
+- [1684234849, 'swap1', 'swap2', 'noop1']
+...
+s:update({1684234849}, {{'#', -1, 1}, {'!', -1, 'noop2'}})
+---
+- [1684234849, 'swap1', 'swap2', 'noop2']
+...
+s:update({1684234849}, {{'#', -1, 1}, {'!', -1, 'noop3'}})
+---
+- [1684234849, 'swap1', 'swap2', 'noop3']
+...
+--
+-- negative indexes
+--
+box.tuple.new({1, 2, 3, 4, 5}):update({{'!', 0, 'Test'}})
+---
+- error: Field 0 was not found in the tuple
+...
+box.tuple.new({1, 2, 3, 4, 5}):update({{'!', -1, 'Test'}})
+---
+- [1, 2, 3, 4, 5, 'Test']
+...
+box.tuple.new({1, 2, 3, 4, 5}):update({{'!', -3, 'Test'}})
+---
+- [1, 2, 3, 'Test', 4, 5]
+...
+box.tuple.new({1, 2, 3, 4, 5}):update({{'!', -5, 'Test'}})
+---
+- [1, 'Test', 2, 3, 4, 5]
+...
+box.tuple.new({1, 2, 3, 4, 5}):update({{'!', -6, 'Test'}})
+---
+- ['Test', 1, 2, 3, 4, 5]
+...
+box.tuple.new({1, 2, 3, 4, 5}):update({{'!', -7, 'Test'}})
+---
+- error: Field -7 was not found in the tuple
+...
+box.tuple.new({1, 2, 3, 4, 5}):update({{'!', -100500, 'Test'}})
+---
+- error: Field -100500 was not found in the tuple
+...
+box.tuple.new({1, 2, 3, 4, 5}):update({{'=', 0, 'Test'}})
+---
+- error: Field 0 was not found in the tuple
+...
+box.tuple.new({1, 2, 3, 4, 5}):update({{'=', -1, 'Test'}})
+---
+- [1, 2, 3, 4, 'Test']
+...
+box.tuple.new({1, 2, 3, 4, 5}):update({{'=', -3, 'Test'}})
+---
+- [1, 2, 'Test', 4, 5]
+...
+box.tuple.new({1, 2, 3, 4, 5}):update({{'=', -5, 'Test'}})
+---
+- ['Test', 2, 3, 4, 5]
+...
+box.tuple.new({1, 2, 3, 4, 5}):update({{'=', -6, 'Test'}})
+---
+- error: Field -6 was not found in the tuple
+...
+box.tuple.new({1, 2, 3, 4, 5}):update({{'=', -100500, 'Test'}})
+---
+- error: Field -100500 was not found in the tuple
+...
+box.tuple.new({1, 2, 3, 4, 5}):update({{'+', 0, 100}})
+---
+- error: Field 0 was not found in the tuple
+...
+box.tuple.new({1, 2, 3, 4, 5}):update({{'+', -1, 100}})
+---
+- [1, 2, 3, 4, 105]
+...
+box.tuple.new({1, 2, 3, 4, 5}):update({{'+', -3, 100}})
+---
+- [1, 2, 103, 4, 5]
+...
+box.tuple.new({1, 2, 3, 4, 5}):update({{'+', -5, 100}})
+---
+- [101, 2, 3, 4, 5]
+...
+box.tuple.new({1, 2, 3, 4, 5}):update({{'+', -6, 100}})
+---
+- error: Field -6 was not found in the tuple
+...
+box.tuple.new({1, 2, 3, 4, 5}):update({{'+', -100500, 100}})
+---
+- error: Field -100500 was not found in the tuple
+...
+box.tuple.new({1, 2, 3, 4, 5}):update({{'|', 0, 100}})
+---
+- error: Field 0 was not found in the tuple
+...
+box.tuple.new({1, 2, 3, 4, 5}):update({{'|', -1, 100}})
+---
+- [1, 2, 3, 4, 101]
+...
+box.tuple.new({1, 2, 3, 4, 5}):update({{'|', -3, 100}})
+---
+- [1, 2, 103, 4, 5]
+...
+box.tuple.new({1, 2, 3, 4, 5}):update({{'|', -5, 100}})
+---
+- [101, 2, 3, 4, 5]
+...
+box.tuple.new({1, 2, 3, 4, 5}):update({{'|', -6, 100}})
+---
+- error: Field -6 was not found in the tuple
+...
+box.tuple.new({1, 2, 3, 4, 5}):update({{'|', -100500, 100}})
+---
+- error: Field -100500 was not found in the tuple
+...
+box.tuple.new({1, 2, 3, 4, 5}):update({{'#', 0, 1}})
+---
+- error: Field 0 was not found in the tuple
+...
+box.tuple.new({1, 2, 3, 4, 5}):update({{'#', -1, 1}})
+---
+- [1, 2, 3, 4]
+...
+box.tuple.new({1, 2, 3, 4, 5}):update({{'#', -3, 1}})
+---
+- [1, 2, 4, 5]
+...
+box.tuple.new({1, 2, 3, 4, 5}):update({{'#', -5, 1}})
+---
+- [2, 3, 4, 5]
+...
+box.tuple.new({1, 2, 3, 4, 5}):update({{'#', -6, 1}})
+---
+- error: Field -6 was not found in the tuple
+...
+box.tuple.new({1, 2, 3, 4, 5}):update({{'#', -100500, 1}})
+---
+- error: Field -100500 was not found in the tuple
+...
+s:drop()
+---
+...
+s = box.schema.space.create_ephemeral()
+---
+...
+i = s:create_index('a')
+---
+...
+s:insert{1, 2, 3}
+---
+- [1, 2, 3]
+...
+s:update({1})
+---
+- error: Usage index:update(key, ops)
+...
+s:update({1}, {'=', 1, 1})
+---
+- error: Illegal parameters, update operation must be an array {op,..}
+...
+s:drop()
+---
+...
+ffi = require('ffi')
+---
+...
+s = box.schema.space.create_ephemeral()
+---
+...
+i = s:create_index('a')
+---
+...
+s:insert{0, -1}
+---
+- [0, -1]
+...
+-- + --
+s:update({0}, {{'+', 2, "a"}}) -- err
+---
+- error: 'Argument type in operation ''+'' on field 2 does not match field type: expected
+    a number'
+...
+s:update({0}, {{'+', 2, 10}}) -- neg(ative) + pos(itive) = pos(itive) 9
+---
+- [0, 9]
+...
+s:update({0}, {{'+', 2, 5}}) -- pos + pos = pos 14
+---
+- [0, 14]
+...
+s:update({0}, {{'+', 2, -4}}) -- pos + neg = pos 10
+---
+- [0, 10]
+...
+s:update({0}, {{'+', 2, -22}}) -- pos + neg = neg -12
+---
+- [0, -12]
+...
+s:update({0}, {{'+', 2, -3}}) -- neg + neg = neg -15
+---
+- [0, -15]
+...
+s:update({0}, {{'+', 2, 7}}) -- neg + pos = neg -8
+---
+- [0, -8]
+...
+-- - --
+s:update({0}, {{'-', 2, "a"}}) -- err
+---
+- error: 'Argument type in operation ''-'' on field 2 does not match field type: expected
+    a number'
+...
+s:update({0}, {{'-', 2, 16}}) -- neg(ative) - pos(itive) = neg(ative) -24
+---
+- [0, -24]
+...
+s:update({0}, {{'-', 2, -4}}) -- neg - neg = neg 20
+---
+- [0, -20]
+...
+s:update({0}, {{'-', 2, -32}}) -- neg - neg = pos 12
+---
+- [0, 12]
+...
+s:update({0}, {{'-', 2, 3}}) -- pos - pos = pos 9
+---
+- [0, 9]
+...
+s:update({0}, {{'-', 2, -5}}) -- pos - neg = pos 14
+---
+- [0, 14]
+...
+s:update({0}, {{'-', 2, 17}}) -- pos - pos = neg -3
+---
+- [0, -3]
+...
+-- bit --
+s:replace{0, 0} -- 0
+---
+- [0, 0]
+...
+s:update({0}, {{'|', 2, 24}}) -- 24
+---
+- [0, 24]
+...
+s:update({0}, {{'|', 2, 2}}) -- 26
+---
+- [0, 26]
+...
+s:update({0}, {{'&', 2, 50}}) -- 18
+---
+- [0, 18]
+...
+s:update({0}, {{'^', 2, 6}}) -- 20
+---
+- [0, 20]
+...
+s:update({0}, {{'|', 2, -1}}) -- err
+---
+- error: 'Argument type in operation ''|'' on field 2 does not match field type: expected
+    a positive integer'
+...
+s:update({0}, {{'&', 2, -1}}) -- err
+---
+- error: 'Argument type in operation ''&'' on field 2 does not match field type: expected
+    a positive integer'
+...
+s:update({0}, {{'^', 2, -1}}) -- err
+---
+- error: 'Argument type in operation ''^'' on field 2 does not match field type: expected
+    a positive integer'
+...
+s:replace{0, -1} -- -1
+---
+- [0, -1]
+...
+s:update({0}, {{'|', 2, 2}}) -- err
+---
+- error: 'Argument type in operation ''|'' on field 2 does not match field type: expected
+    a positive integer'
+...
+s:update({0}, {{'&', 2, 40}}) -- err
+---
+- error: 'Argument type in operation ''&'' on field 2 does not match field type: expected
+    a positive integer'
+...
+s:update({0}, {{'^', 2, 6}}) -- err
+---
+- error: 'Argument type in operation ''^'' on field 2 does not match field type: expected
+    a positive integer'
+...
+s:replace{0, 1.5} -- 1.5
+---
+- [0, 1.5]
+...
+s:update({0}, {{'|', 2, 2}}) -- err
+---
+- error: 'Argument type in operation ''|'' on field 2 does not match field type: expected
+    a positive integer'
+...
+s:update({0}, {{'&', 2, 40}}) -- err
+---
+- error: 'Argument type in operation ''&'' on field 2 does not match field type: expected
+    a positive integer'
+...
+s:update({0}, {{'^', 2, 6}}) -- err
+---
+- error: 'Argument type in operation ''^'' on field 2 does not match field type: expected
+    a positive integer'
+...
+-- double
+s:replace{0, 5} -- 5
+---
+- [0, 5]
+...
+s:update({0}, {{'+', 2, 1.5}}) -- int + double = double 6.5
+---
+- [0, 6.5]
+...
+s:update({0}, {{'|', 2, 2}}) -- err (double!)
+---
+- error: 'Argument type in operation ''|'' on field 2 does not match field type: expected
+    a positive integer'
+...
+s:update({0}, {{'-', 2, 0.5}}) -- double - double = double 6
+---
+- [0, 6]
+...
+s:update({0}, {{'+', 2, 1.5}}) -- double + double = double 7.5
+---
+- [0, 7.5]
+...
+-- float
+s:replace{0, ffi.new("float", 1.5)} -- 1.5
+---
+- [0, 1.5]
+...
+s:update({0}, {{'+', 2, 2}}) -- float + int = float 3.5
+---
+- [0, 3.5]
+...
+s:update({0}, {{'+', 2, ffi.new("float", 3.5)}}) -- float + int = float 7
+---
+- [0, 7]
+...
+s:update({0}, {{'|', 2, 2}}) -- err (float!)
+---
+- error: 'Argument type in operation ''|'' on field 2 does not match field type: expected
+    a positive integer'
+...
+s:update({0}, {{'-', 2, ffi.new("float", 1.5)}}) -- float - float = float 5.5
+---
+- [0, 5.5]
+...
+s:update({0}, {{'+', 2, ffi.new("float", 3.5)}}) -- float + float = float 9
+---
+- [0, 9]
+...
+s:update({0}, {{'-', 2, ffi.new("float", 9)}}) -- float + float = float 0
+---
+- [0, 0]
+...
+s:update({0}, {{'+', 2, ffi.new("float", 1.2)}}) -- float + float = float 1.2
+---
+- [0, 1.2000000476837]
+...
+-- overflow --
+s:replace{0, 0xfffffffffffffffeull}
+---
+- [0, 18446744073709551614]
+...
+s:update({0}, {{'+', 2, 1}}) -- ok
+---
+- [0, 18446744073709551615]
+...
+s:update({0}, {{'+', 2, 1}}) -- overflow
+---
+- error: Integer overflow when performing '+' operation on field 2
+...
+s:update({0}, {{'+', 2, 100500}}) -- overflow
+---
+- error: Integer overflow when performing '+' operation on field 2
+...
+s:replace{0, 1}
+---
+- [0, 1]
+...
+s:update({0}, {{'+', 2, 0xffffffffffffffffull}})  -- overflow
+---
+- error: Integer overflow when performing '+' operation on field 2
+...
+s:replace{0, -1}
+---
+- [0, -1]
+...
+s:update({0}, {{'+', 2, 0xffffffffffffffffull}})  -- ok
+---
+- [0, 18446744073709551614]
+...
+s:replace{0, 0}
+---
+- [0, 0]
+...
+s:update({0}, {{'-', 2, 0x7fffffffffffffffull}})  -- ok
+---
+- [0, -9223372036854775807]
+...
+s:replace{0, -1}
+---
+- [0, -1]
+...
+s:update({0}, {{'-', 2, 0x7fffffffffffffffull}})  -- ok
+---
+- [0, -9223372036854775808]
+...
+s:replace{0, -2}
+---
+- [0, -2]
+...
+s:update({0}, {{'-', 2, 0x7fffffffffffffffull}})  -- overflow
+---
+- error: Integer overflow when performing '-' operation on field 2
+...
+s:replace{0, 1}
+---
+- [0, 1]
+...
+s:update({0}, {{'-', 2, 0xffffffffffffffffull}})  -- overflow
+---
+- error: Integer overflow when performing '-' operation on field 2
+...
+s:replace{0, 0xffffffffffffffefull}
+---
+- [0, 18446744073709551599]
+...
+s:update({0}, {{'-', 2, -16}})  -- ok
+---
+- [0, 18446744073709551615]
+...
+s:update({0}, {{'-', 2, -16}})  -- overflow
+---
+- error: Integer overflow when performing '-' operation on field 2
+...
+s:replace{0, -0x4000000000000000ll}
+---
+- [0, -4611686018427387904]
+...
+s:update({0}, {{'+', 2, -0x4000000000000000ll}})  -- ok
+---
+- [0, -9223372036854775808]
+...
+s:replace{0, -0x4000000000000000ll}
+---
+- [0, -4611686018427387904]
+...
+s:update({0}, {{'+', 2, -0x4000000000000001ll}})  -- overflow
+---
+- error: Integer overflow when performing '+' operation on field 2
+...
+-- some wrong updates --
+s:update({0}, 0)
+---
+- error: Usage index:update(key, ops)
+...
+s:update({0}, {'+', 2, 2})
+---
+- error: Illegal parameters, update operation must be an array {op,..}
+...
+s:update({0}, {{}})
+---
+- error: Illegal parameters, update operation must be an array {op,..}, got empty
+    array
+...
+s:update({0}, {{'+'}})
+---
+- error: Unknown UPDATE operation
+...
+s:update({0}, {{'+', 0}})
+---
+- error: Unknown UPDATE operation
+...
+s:update({0}, {{'+', '+', '+'}})
+---
+- error: Illegal parameters, field id must be a number
+...
+s:update({0}, {{0, 0, 0}})
+---
+- error: Illegal parameters, update operation name must be a string
+...
+ops = {}
+---
+...
+for i = 1,10 do table.insert(ops, {'=', 2, '1234567890'}) end
+---
+...
+s:upsert({0}, ops)
+---
+...
+s:get{0}
+---
+- [0, '1234567890']
+...
+s:update({0}, {})
+---
+- [0, '1234567890']
+...
+map = setmetatable({}, { __serialize = 'map' })
+---
+...
+t = box.tuple.new({1, 2, 3})
+---
+...
+s:replace({1, 2, 3})
+---
+- [1, 2, 3]
+...
+t:update({{'=', 3, map}})
+---
+- [1, 2, {}]
+...
+s:update(1, {{'=', 3, map}})
+---
+- [1, 2, {}]
+...
+s:drop()
+---
+...
+-- Ephemeral index: methods: pairs, count, select (iterators)
+-- iterator (str)
+s = box.schema.space.create_ephemeral()
+---
+...
+i = s:create_index('a', { type = 'tree', parts = {1, 'string'} })
+---
+...
+for key = 1, 100 do s:replace({tostring(key)}) end
+---
+...
+t = {} for state, v in i:pairs({}, {iterator = 'ALL'}) do table.insert(t, v) end
+---
+...
+t
+---
+- - ['1']
+  - ['10']
+  - ['100']
+  - ['11']
+  - ['12']
+  - ['13']
+  - ['14']
+  - ['15']
+  - ['16']
+  - ['17']
+  - ['18']
+  - ['19']
+  - ['2']
+  - ['20']
+  - ['21']
+  - ['22']
+  - ['23']
+  - ['24']
+  - ['25']
+  - ['26']
+  - ['27']
+  - ['28']
+  - ['29']
+  - ['3']
+  - ['30']
+  - ['31']
+  - ['32']
+  - ['33']
+  - ['34']
+  - ['35']
+  - ['36']
+  - ['37']
+  - ['38']
+  - ['39']
+  - ['4']
+  - ['40']
+  - ['41']
+  - ['42']
+  - ['43']
+  - ['44']
+  - ['45']
+  - ['46']
+  - ['47']
+  - ['48']
+  - ['49']
+  - ['5']
+  - ['50']
+  - ['51']
+  - ['52']
+  - ['53']
+  - ['54']
+  - ['55']
+  - ['56']
+  - ['57']
+  - ['58']
+  - ['59']
+  - ['6']
+  - ['60']
+  - ['61']
+  - ['62']
+  - ['63']
+  - ['64']
+  - ['65']
+  - ['66']
+  - ['67']
+  - ['68']
+  - ['69']
+  - ['7']
+  - ['70']
+  - ['71']
+  - ['72']
+  - ['73']
+  - ['74']
+  - ['75']
+  - ['76']
+  - ['77']
+  - ['78']
+  - ['79']
+  - ['8']
+  - ['80']
+  - ['81']
+  - ['82']
+  - ['83']
+  - ['84']
+  - ['85']
+  - ['86']
+  - ['87']
+  - ['88']
+  - ['89']
+  - ['9']
+  - ['90']
+  - ['91']
+  - ['92']
+  - ['93']
+  - ['94']
+  - ['95']
+  - ['96']
+  - ['97']
+  - ['98']
+  - ['99']
+...
+t = {} for state, v in i:pairs({}, {iterator = 'GE'}) do table.insert(t, v) end
+---
+...
+t
+---
+- - ['1']
+  - ['10']
+  - ['100']
+  - ['11']
+  - ['12']
+  - ['13']
+  - ['14']
+  - ['15']
+  - ['16']
+  - ['17']
+  - ['18']
+  - ['19']
+  - ['2']
+  - ['20']
+  - ['21']
+  - ['22']
+  - ['23']
+  - ['24']
+  - ['25']
+  - ['26']
+  - ['27']
+  - ['28']
+  - ['29']
+  - ['3']
+  - ['30']
+  - ['31']
+  - ['32']
+  - ['33']
+  - ['34']
+  - ['35']
+  - ['36']
+  - ['37']
+  - ['38']
+  - ['39']
+  - ['4']
+  - ['40']
+  - ['41']
+  - ['42']
+  - ['43']
+  - ['44']
+  - ['45']
+  - ['46']
+  - ['47']
+  - ['48']
+  - ['49']
+  - ['5']
+  - ['50']
+  - ['51']
+  - ['52']
+  - ['53']
+  - ['54']
+  - ['55']
+  - ['56']
+  - ['57']
+  - ['58']
+  - ['59']
+  - ['6']
+  - ['60']
+  - ['61']
+  - ['62']
+  - ['63']
+  - ['64']
+  - ['65']
+  - ['66']
+  - ['67']
+  - ['68']
+  - ['69']
+  - ['7']
+  - ['70']
+  - ['71']
+  - ['72']
+  - ['73']
+  - ['74']
+  - ['75']
+  - ['76']
+  - ['77']
+  - ['78']
+  - ['79']
+  - ['8']
+  - ['80']
+  - ['81']
+  - ['82']
+  - ['83']
+  - ['84']
+  - ['85']
+  - ['86']
+  - ['87']
+  - ['88']
+  - ['89']
+  - ['9']
+  - ['90']
+  - ['91']
+  - ['92']
+  - ['93']
+  - ['94']
+  - ['95']
+  - ['96']
+  - ['97']
+  - ['98']
+  - ['99']
+...
+t = {} for state, v in i:pairs(tostring(44), {iterator = 'GE'}) do table.insert(t, v) end
+---
+...
+t
+---
+- - ['44']
+  - ['45']
+  - ['46']
+  - ['47']
+  - ['48']
+  - ['49']
+  - ['5']
+  - ['50']
+  - ['51']
+  - ['52']
+  - ['53']
+  - ['54']
+  - ['55']
+  - ['56']
+  - ['57']
+  - ['58']
+  - ['59']
+  - ['6']
+  - ['60']
+  - ['61']
+  - ['62']
+  - ['63']
+  - ['64']
+  - ['65']
+  - ['66']
+  - ['67']
+  - ['68']
+  - ['69']
+  - ['7']
+  - ['70']
+  - ['71']
+  - ['72']
+  - ['73']
+  - ['74']
+  - ['75']
+  - ['76']
+  - ['77']
+  - ['78']
+  - ['79']
+  - ['8']
+  - ['80']
+  - ['81']
+  - ['82']
+  - ['83']
+  - ['84']
+  - ['85']
+  - ['86']
+  - ['87']
+  - ['88']
+  - ['89']
+  - ['9']
+  - ['90']
+  - ['91']
+  - ['92']
+  - ['93']
+  - ['94']
+  - ['95']
+  - ['96']
+  - ['97']
+  - ['98']
+  - ['99']
+...
+t = {} for state, v in i:pairs(tostring(44), {iterator = 'GT'}) do table.insert(t, v) end
+---
+...
+t
+---
+- - ['45']
+  - ['46']
+  - ['47']
+  - ['48']
+  - ['49']
+  - ['5']
+  - ['50']
+  - ['51']
+  - ['52']
+  - ['53']
+  - ['54']
+  - ['55']
+  - ['56']
+  - ['57']
+  - ['58']
+  - ['59']
+  - ['6']
+  - ['60']
+  - ['61']
+  - ['62']
+  - ['63']
+  - ['64']
+  - ['65']
+  - ['66']
+  - ['67']
+  - ['68']
+  - ['69']
+  - ['7']
+  - ['70']
+  - ['71']
+  - ['72']
+  - ['73']
+  - ['74']
+  - ['75']
+  - ['76']
+  - ['77']
+  - ['78']
+  - ['79']
+  - ['8']
+  - ['80']
+  - ['81']
+  - ['82']
+  - ['83']
+  - ['84']
+  - ['85']
+  - ['86']
+  - ['87']
+  - ['88']
+  - ['89']
+  - ['9']
+  - ['90']
+  - ['91']
+  - ['92']
+  - ['93']
+  - ['94']
+  - ['95']
+  - ['96']
+  - ['97']
+  - ['98']
+  - ['99']
+...
+t = {} for state, v in i:pairs({}, {iterator = 'LE'}) do table.insert(t, v) end
+---
+...
+t
+---
+- - ['99']
+  - ['98']
+  - ['97']
+  - ['96']
+  - ['95']
+  - ['94']
+  - ['93']
+  - ['92']
+  - ['91']
+  - ['90']
+  - ['9']
+  - ['89']
+  - ['88']
+  - ['87']
+  - ['86']
+  - ['85']
+  - ['84']
+  - ['83']
+  - ['82']
+  - ['81']
+  - ['80']
+  - ['8']
+  - ['79']
+  - ['78']
+  - ['77']
+  - ['76']
+  - ['75']
+  - ['74']
+  - ['73']
+  - ['72']
+  - ['71']
+  - ['70']
+  - ['7']
+  - ['69']
+  - ['68']
+  - ['67']
+  - ['66']
+  - ['65']
+  - ['64']
+  - ['63']
+  - ['62']
+  - ['61']
+  - ['60']
+  - ['6']
+  - ['59']
+  - ['58']
+  - ['57']
+  - ['56']
+  - ['55']
+  - ['54']
+  - ['53']
+  - ['52']
+  - ['51']
+  - ['50']
+  - ['5']
+  - ['49']
+  - ['48']
+  - ['47']
+  - ['46']
+  - ['45']
+  - ['44']
+  - ['43']
+  - ['42']
+  - ['41']
+  - ['40']
+  - ['4']
+  - ['39']
+  - ['38']
+  - ['37']
+  - ['36']
+  - ['35']
+  - ['34']
+  - ['33']
+  - ['32']
+  - ['31']
+  - ['30']
+  - ['3']
+  - ['29']
+  - ['28']
+  - ['27']
+  - ['26']
+  - ['25']
+  - ['24']
+  - ['23']
+  - ['22']
+  - ['21']
+  - ['20']
+  - ['2']
+  - ['19']
+  - ['18']
+  - ['17']
+  - ['16']
+  - ['15']
+  - ['14']
+  - ['13']
+  - ['12']
+  - ['11']
+  - ['100']
+  - ['10']
+  - ['1']
+...
+t = {} for state, v in i:pairs(tostring(77), {iterator = 'LE'}) do table.insert(t, v) end
+---
+...
+t
+---
+- - ['77']
+  - ['76']
+  - ['75']
+  - ['74']
+  - ['73']
+  - ['72']
+  - ['71']
+  - ['70']
+  - ['7']
+  - ['69']
+  - ['68']
+  - ['67']
+  - ['66']
+  - ['65']
+  - ['64']
+  - ['63']
+  - ['62']
+  - ['61']
+  - ['60']
+  - ['6']
+  - ['59']
+  - ['58']
+  - ['57']
+  - ['56']
+  - ['55']
+  - ['54']
+  - ['53']
+  - ['52']
+  - ['51']
+  - ['50']
+  - ['5']
+  - ['49']
+  - ['48']
+  - ['47']
+  - ['46']
+  - ['45']
+  - ['44']
+  - ['43']
+  - ['42']
+  - ['41']
+  - ['40']
+  - ['4']
+  - ['39']
+  - ['38']
+  - ['37']
+  - ['36']
+  - ['35']
+  - ['34']
+  - ['33']
+  - ['32']
+  - ['31']
+  - ['30']
+  - ['3']
+  - ['29']
+  - ['28']
+  - ['27']
+  - ['26']
+  - ['25']
+  - ['24']
+  - ['23']
+  - ['22']
+  - ['21']
+  - ['20']
+  - ['2']
+  - ['19']
+  - ['18']
+  - ['17']
+  - ['16']
+  - ['15']
+  - ['14']
+  - ['13']
+  - ['12']
+  - ['11']
+  - ['100']
+  - ['10']
+  - ['1']
+...
+t = {} for state, v in i:pairs({}, {iterator = 'LT'}) do table.insert(t, v) end
+---
+...
+t
+---
+- - ['99']
+  - ['98']
+  - ['97']
+  - ['96']
+  - ['95']
+  - ['94']
+  - ['93']
+  - ['92']
+  - ['91']
+  - ['90']
+  - ['9']
+  - ['89']
+  - ['88']
+  - ['87']
+  - ['86']
+  - ['85']
+  - ['84']
+  - ['83']
+  - ['82']
+  - ['81']
+  - ['80']
+  - ['8']
+  - ['79']
+  - ['78']
+  - ['77']
+  - ['76']
+  - ['75']
+  - ['74']
+  - ['73']
+  - ['72']
+  - ['71']
+  - ['70']
+  - ['7']
+  - ['69']
+  - ['68']
+  - ['67']
+  - ['66']
+  - ['65']
+  - ['64']
+  - ['63']
+  - ['62']
+  - ['61']
+  - ['60']
+  - ['6']
+  - ['59']
+  - ['58']
+  - ['57']
+  - ['56']
+  - ['55']
+  - ['54']
+  - ['53']
+  - ['52']
+  - ['51']
+  - ['50']
+  - ['5']
+  - ['49']
+  - ['48']
+  - ['47']
+  - ['46']
+  - ['45']
+  - ['44']
+  - ['43']
+  - ['42']
+  - ['41']
+  - ['40']
+  - ['4']
+  - ['39']
+  - ['38']
+  - ['37']
+  - ['36']
+  - ['35']
+  - ['34']
+  - ['33']
+  - ['32']
+  - ['31']
+  - ['30']
+  - ['3']
+  - ['29']
+  - ['28']
+  - ['27']
+  - ['26']
+  - ['25']
+  - ['24']
+  - ['23']
+  - ['22']
+  - ['21']
+  - ['20']
+  - ['2']
+  - ['19']
+  - ['18']
+  - ['17']
+  - ['16']
+  - ['15']
+  - ['14']
+  - ['13']
+  - ['12']
+  - ['11']
+  - ['100']
+  - ['10']
+  - ['1']
+...
+t = {} for state, v in i:pairs(tostring(77), {iterator = 'LT'}) do table.insert(t, v) end
+---
+...
+t
+---
+- - ['76']
+  - ['75']
+  - ['74']
+  - ['73']
+  - ['72']
+  - ['71']
+  - ['70']
+  - ['7']
+  - ['69']
+  - ['68']
+  - ['67']
+  - ['66']
+  - ['65']
+  - ['64']
+  - ['63']
+  - ['62']
+  - ['61']
+  - ['60']
+  - ['6']
+  - ['59']
+  - ['58']
+  - ['57']
+  - ['56']
+  - ['55']
+  - ['54']
+  - ['53']
+  - ['52']
+  - ['51']
+  - ['50']
+  - ['5']
+  - ['49']
+  - ['48']
+  - ['47']
+  - ['46']
+  - ['45']
+  - ['44']
+  - ['43']
+  - ['42']
+  - ['41']
+  - ['40']
+  - ['4']
+  - ['39']
+  - ['38']
+  - ['37']
+  - ['36']
+  - ['35']
+  - ['34']
+  - ['33']
+  - ['32']
+  - ['31']
+  - ['30']
+  - ['3']
+  - ['29']
+  - ['28']
+  - ['27']
+  - ['26']
+  - ['25']
+  - ['24']
+  - ['23']
+  - ['22']
+  - ['21']
+  - ['20']
+  - ['2']
+  - ['19']
+  - ['18']
+  - ['17']
+  - ['16']
+  - ['15']
+  - ['14']
+  - ['13']
+  - ['12']
+  - ['11']
+  - ['100']
+  - ['10']
+  - ['1']
+...
+s:drop()
+---
+...
+-- iterator (num)
+s = box.schema.space.create_ephemeral()
+---
+...
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} })
+---
+...
+for key = 1, 100 do s:replace({key}) end
+---
+...
+t = {} for state, v in i:pairs({}, {iterator = 'ALL'}) do table.insert(t, v) end
+---
+...
+t
+---
+- - [1]
+  - [2]
+  - [3]
+  - [4]
+  - [5]
+  - [6]
+  - [7]
+  - [8]
+  - [9]
+  - [10]
+  - [11]
+  - [12]
+  - [13]
+  - [14]
+  - [15]
+  - [16]
+  - [17]
+  - [18]
+  - [19]
+  - [20]
+  - [21]
+  - [22]
+  - [23]
+  - [24]
+  - [25]
+  - [26]
+  - [27]
+  - [28]
+  - [29]
+  - [30]
+  - [31]
+  - [32]
+  - [33]
+  - [34]
+  - [35]
+  - [36]
+  - [37]
+  - [38]
+  - [39]
+  - [40]
+  - [41]
+  - [42]
+  - [43]
+  - [44]
+  - [45]
+  - [46]
+  - [47]
+  - [48]
+  - [49]
+  - [50]
+  - [51]
+  - [52]
+  - [53]
+  - [54]
+  - [55]
+  - [56]
+  - [57]
+  - [58]
+  - [59]
+  - [60]
+  - [61]
+  - [62]
+  - [63]
+  - [64]
+  - [65]
+  - [66]
+  - [67]
+  - [68]
+  - [69]
+  - [70]
+  - [71]
+  - [72]
+  - [73]
+  - [74]
+  - [75]
+  - [76]
+  - [77]
+  - [78]
+  - [79]
+  - [80]
+  - [81]
+  - [82]
+  - [83]
+  - [84]
+  - [85]
+  - [86]
+  - [87]
+  - [88]
+  - [89]
+  - [90]
+  - [91]
+  - [92]
+  - [93]
+  - [94]
+  - [95]
+  - [96]
+  - [97]
+  - [98]
+  - [99]
+  - [100]
+...
+t = {} for state, v in i:pairs({}, {iterator = 'GE'}) do table.insert(t, v) end
+---
+...
+t
+---
+- - [1]
+  - [2]
+  - [3]
+  - [4]
+  - [5]
+  - [6]
+  - [7]
+  - [8]
+  - [9]
+  - [10]
+  - [11]
+  - [12]
+  - [13]
+  - [14]
+  - [15]
+  - [16]
+  - [17]
+  - [18]
+  - [19]
+  - [20]
+  - [21]
+  - [22]
+  - [23]
+  - [24]
+  - [25]
+  - [26]
+  - [27]
+  - [28]
+  - [29]
+  - [30]
+  - [31]
+  - [32]
+  - [33]
+  - [34]
+  - [35]
+  - [36]
+  - [37]
+  - [38]
+  - [39]
+  - [40]
+  - [41]
+  - [42]
+  - [43]
+  - [44]
+  - [45]
+  - [46]
+  - [47]
+  - [48]
+  - [49]
+  - [50]
+  - [51]
+  - [52]
+  - [53]
+  - [54]
+  - [55]
+  - [56]
+  - [57]
+  - [58]
+  - [59]
+  - [60]
+  - [61]
+  - [62]
+  - [63]
+  - [64]
+  - [65]
+  - [66]
+  - [67]
+  - [68]
+  - [69]
+  - [70]
+  - [71]
+  - [72]
+  - [73]
+  - [74]
+  - [75]
+  - [76]
+  - [77]
+  - [78]
+  - [79]
+  - [80]
+  - [81]
+  - [82]
+  - [83]
+  - [84]
+  - [85]
+  - [86]
+  - [87]
+  - [88]
+  - [89]
+  - [90]
+  - [91]
+  - [92]
+  - [93]
+  - [94]
+  - [95]
+  - [96]
+  - [97]
+  - [98]
+  - [99]
+  - [100]
+...
+t = {} for state, v in i:pairs(44, {iterator = 'GE'}) do table.insert(t, v) end
+---
+...
+t
+---
+- - [44]
+  - [45]
+  - [46]
+  - [47]
+  - [48]
+  - [49]
+  - [50]
+  - [51]
+  - [52]
+  - [53]
+  - [54]
+  - [55]
+  - [56]
+  - [57]
+  - [58]
+  - [59]
+  - [60]
+  - [61]
+  - [62]
+  - [63]
+  - [64]
+  - [65]
+  - [66]
+  - [67]
+  - [68]
+  - [69]
+  - [70]
+  - [71]
+  - [72]
+  - [73]
+  - [74]
+  - [75]
+  - [76]
+  - [77]
+  - [78]
+  - [79]
+  - [80]
+  - [81]
+  - [82]
+  - [83]
+  - [84]
+  - [85]
+  - [86]
+  - [87]
+  - [88]
+  - [89]
+  - [90]
+  - [91]
+  - [92]
+  - [93]
+  - [94]
+  - [95]
+  - [96]
+  - [97]
+  - [98]
+  - [99]
+  - [100]
+...
+t = {} for state, v in i:pairs(44, {iterator = 'GT'}) do table.insert(t, v) end
+---
+...
+t
+---
+- - [45]
+  - [46]
+  - [47]
+  - [48]
+  - [49]
+  - [50]
+  - [51]
+  - [52]
+  - [53]
+  - [54]
+  - [55]
+  - [56]
+  - [57]
+  - [58]
+  - [59]
+  - [60]
+  - [61]
+  - [62]
+  - [63]
+  - [64]
+  - [65]
+  - [66]
+  - [67]
+  - [68]
+  - [69]
+  - [70]
+  - [71]
+  - [72]
+  - [73]
+  - [74]
+  - [75]
+  - [76]
+  - [77]
+  - [78]
+  - [79]
+  - [80]
+  - [81]
+  - [82]
+  - [83]
+  - [84]
+  - [85]
+  - [86]
+  - [87]
+  - [88]
+  - [89]
+  - [90]
+  - [91]
+  - [92]
+  - [93]
+  - [94]
+  - [95]
+  - [96]
+  - [97]
+  - [98]
+  - [99]
+  - [100]
+...
+t = {} for state, v in i:pairs({}, {iterator = 'LE'}) do table.insert(t, v) end
+---
+...
+t
+---
+- - [100]
+  - [99]
+  - [98]
+  - [97]
+  - [96]
+  - [95]
+  - [94]
+  - [93]
+  - [92]
+  - [91]
+  - [90]
+  - [89]
+  - [88]
+  - [87]
+  - [86]
+  - [85]
+  - [84]
+  - [83]
+  - [82]
+  - [81]
+  - [80]
+  - [79]
+  - [78]
+  - [77]
+  - [76]
+  - [75]
+  - [74]
+  - [73]
+  - [72]
+  - [71]
+  - [70]
+  - [69]
+  - [68]
+  - [67]
+  - [66]
+  - [65]
+  - [64]
+  - [63]
+  - [62]
+  - [61]
+  - [60]
+  - [59]
+  - [58]
+  - [57]
+  - [56]
+  - [55]
+  - [54]
+  - [53]
+  - [52]
+  - [51]
+  - [50]
+  - [49]
+  - [48]
+  - [47]
+  - [46]
+  - [45]
+  - [44]
+  - [43]
+  - [42]
+  - [41]
+  - [40]
+  - [39]
+  - [38]
+  - [37]
+  - [36]
+  - [35]
+  - [34]
+  - [33]
+  - [32]
+  - [31]
+  - [30]
+  - [29]
+  - [28]
+  - [27]
+  - [26]
+  - [25]
+  - [24]
+  - [23]
+  - [22]
+  - [21]
+  - [20]
+  - [19]
+  - [18]
+  - [17]
+  - [16]
+  - [15]
+  - [14]
+  - [13]
+  - [12]
+  - [11]
+  - [10]
+  - [9]
+  - [8]
+  - [7]
+  - [6]
+  - [5]
+  - [4]
+  - [3]
+  - [2]
+  - [1]
+...
+t = {} for state, v in i:pairs(77, {iterator = 'LE'}) do table.insert(t, v) end
+---
+...
+t
+---
+- - [77]
+  - [76]
+  - [75]
+  - [74]
+  - [73]
+  - [72]
+  - [71]
+  - [70]
+  - [69]
+  - [68]
+  - [67]
+  - [66]
+  - [65]
+  - [64]
+  - [63]
+  - [62]
+  - [61]
+  - [60]
+  - [59]
+  - [58]
+  - [57]
+  - [56]
+  - [55]
+  - [54]
+  - [53]
+  - [52]
+  - [51]
+  - [50]
+  - [49]
+  - [48]
+  - [47]
+  - [46]
+  - [45]
+  - [44]
+  - [43]
+  - [42]
+  - [41]
+  - [40]
+  - [39]
+  - [38]
+  - [37]
+  - [36]
+  - [35]
+  - [34]
+  - [33]
+  - [32]
+  - [31]
+  - [30]
+  - [29]
+  - [28]
+  - [27]
+  - [26]
+  - [25]
+  - [24]
+  - [23]
+  - [22]
+  - [21]
+  - [20]
+  - [19]
+  - [18]
+  - [17]
+  - [16]
+  - [15]
+  - [14]
+  - [13]
+  - [12]
+  - [11]
+  - [10]
+  - [9]
+  - [8]
+  - [7]
+  - [6]
+  - [5]
+  - [4]
+  - [3]
+  - [2]
+  - [1]
+...
+t = {} for state, v in i:pairs({}, {iterator = 'LT'}) do table.insert(t, v) end
+---
+...
+t
+---
+- - [100]
+  - [99]
+  - [98]
+  - [97]
+  - [96]
+  - [95]
+  - [94]
+  - [93]
+  - [92]
+  - [91]
+  - [90]
+  - [89]
+  - [88]
+  - [87]
+  - [86]
+  - [85]
+  - [84]
+  - [83]
+  - [82]
+  - [81]
+  - [80]
+  - [79]
+  - [78]
+  - [77]
+  - [76]
+  - [75]
+  - [74]
+  - [73]
+  - [72]
+  - [71]
+  - [70]
+  - [69]
+  - [68]
+  - [67]
+  - [66]
+  - [65]
+  - [64]
+  - [63]
+  - [62]
+  - [61]
+  - [60]
+  - [59]
+  - [58]
+  - [57]
+  - [56]
+  - [55]
+  - [54]
+  - [53]
+  - [52]
+  - [51]
+  - [50]
+  - [49]
+  - [48]
+  - [47]
+  - [46]
+  - [45]
+  - [44]
+  - [43]
+  - [42]
+  - [41]
+  - [40]
+  - [39]
+  - [38]
+  - [37]
+  - [36]
+  - [35]
+  - [34]
+  - [33]
+  - [32]
+  - [31]
+  - [30]
+  - [29]
+  - [28]
+  - [27]
+  - [26]
+  - [25]
+  - [24]
+  - [23]
+  - [22]
+  - [21]
+  - [20]
+  - [19]
+  - [18]
+  - [17]
+  - [16]
+  - [15]
+  - [14]
+  - [13]
+  - [12]
+  - [11]
+  - [10]
+  - [9]
+  - [8]
+  - [7]
+  - [6]
+  - [5]
+  - [4]
+  - [3]
+  - [2]
+  - [1]
+...
+t = {} for state, v in i:pairs(77, {iterator = 'LT'}) do table.insert(t, v) end
+---
+...
+t
+---
+- - [76]
+  - [75]
+  - [74]
+  - [73]
+  - [72]
+  - [71]
+  - [70]
+  - [69]
+  - [68]
+  - [67]
+  - [66]
+  - [65]
+  - [64]
+  - [63]
+  - [62]
+  - [61]
+  - [60]
+  - [59]
+  - [58]
+  - [57]
+  - [56]
+  - [55]
+  - [54]
+  - [53]
+  - [52]
+  - [51]
+  - [50]
+  - [49]
+  - [48]
+  - [47]
+  - [46]
+  - [45]
+  - [44]
+  - [43]
+  - [42]
+  - [41]
+  - [40]
+  - [39]
+  - [38]
+  - [37]
+  - [36]
+  - [35]
+  - [34]
+  - [33]
+  - [32]
+  - [31]
+  - [30]
+  - [29]
+  - [28]
+  - [27]
+  - [26]
+  - [25]
+  - [24]
+  - [23]
+  - [22]
+  - [21]
+  - [20]
+  - [19]
+  - [18]
+  - [17]
+  - [16]
+  - [15]
+  - [14]
+  - [13]
+  - [12]
+  - [11]
+  - [10]
+  - [9]
+  - [8]
+  - [7]
+  - [6]
+  - [5]
+  - [4]
+  - [3]
+  - [2]
+  - [1]
+...
+s:drop()
+---
+...
+-- iterator multi-part (num, num)
+s = box.schema.space.create_ephemeral()
+---
+...
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned', 2, 'unsigned'} })
+---
+...
+for key = 1, 100 do s:replace({key, key}) end
+---
+...
+t = {} for state, v in i:pairs({}, {iterator = 'ALL'}) do table.insert(t, v) end
+---
+...
+t
+---
+- - [1, 1]
+  - [2, 2]
+  - [3, 3]
+  - [4, 4]
+  - [5, 5]
+  - [6, 6]
+  - [7, 7]
+  - [8, 8]
+  - [9, 9]
+  - [10, 10]
+  - [11, 11]
+  - [12, 12]
+  - [13, 13]
+  - [14, 14]
+  - [15, 15]
+  - [16, 16]
+  - [17, 17]
+  - [18, 18]
+  - [19, 19]
+  - [20, 20]
+  - [21, 21]
+  - [22, 22]
+  - [23, 23]
+  - [24, 24]
+  - [25, 25]
+  - [26, 26]
+  - [27, 27]
+  - [28, 28]
+  - [29, 29]
+  - [30, 30]
+  - [31, 31]
+  - [32, 32]
+  - [33, 33]
+  - [34, 34]
+  - [35, 35]
+  - [36, 36]
+  - [37, 37]
+  - [38, 38]
+  - [39, 39]
+  - [40, 40]
+  - [41, 41]
+  - [42, 42]
+  - [43, 43]
+  - [44, 44]
+  - [45, 45]
+  - [46, 46]
+  - [47, 47]
+  - [48, 48]
+  - [49, 49]
+  - [50, 50]
+  - [51, 51]
+  - [52, 52]
+  - [53, 53]
+  - [54, 54]
+  - [55, 55]
+  - [56, 56]
+  - [57, 57]
+  - [58, 58]
+  - [59, 59]
+  - [60, 60]
+  - [61, 61]
+  - [62, 62]
+  - [63, 63]
+  - [64, 64]
+  - [65, 65]
+  - [66, 66]
+  - [67, 67]
+  - [68, 68]
+  - [69, 69]
+  - [70, 70]
+  - [71, 71]
+  - [72, 72]
+  - [73, 73]
+  - [74, 74]
+  - [75, 75]
+  - [76, 76]
+  - [77, 77]
+  - [78, 78]
+  - [79, 79]
+  - [80, 80]
+  - [81, 81]
+  - [82, 82]
+  - [83, 83]
+  - [84, 84]
+  - [85, 85]
+  - [86, 86]
+  - [87, 87]
+  - [88, 88]
+  - [89, 89]
+  - [90, 90]
+  - [91, 91]
+  - [92, 92]
+  - [93, 93]
+  - [94, 94]
+  - [95, 95]
+  - [96, 96]
+  - [97, 97]
+  - [98, 98]
+  - [99, 99]
+  - [100, 100]
+...
+t = {} for state, v in i:pairs({}, {iterator = 'GE'}) do table.insert(t, v) end
+---
+...
+t
+---
+- - [1, 1]
+  - [2, 2]
+  - [3, 3]
+  - [4, 4]
+  - [5, 5]
+  - [6, 6]
+  - [7, 7]
+  - [8, 8]
+  - [9, 9]
+  - [10, 10]
+  - [11, 11]
+  - [12, 12]
+  - [13, 13]
+  - [14, 14]
+  - [15, 15]
+  - [16, 16]
+  - [17, 17]
+  - [18, 18]
+  - [19, 19]
+  - [20, 20]
+  - [21, 21]
+  - [22, 22]
+  - [23, 23]
+  - [24, 24]
+  - [25, 25]
+  - [26, 26]
+  - [27, 27]
+  - [28, 28]
+  - [29, 29]
+  - [30, 30]
+  - [31, 31]
+  - [32, 32]
+  - [33, 33]
+  - [34, 34]
+  - [35, 35]
+  - [36, 36]
+  - [37, 37]
+  - [38, 38]
+  - [39, 39]
+  - [40, 40]
+  - [41, 41]
+  - [42, 42]
+  - [43, 43]
+  - [44, 44]
+  - [45, 45]
+  - [46, 46]
+  - [47, 47]
+  - [48, 48]
+  - [49, 49]
+  - [50, 50]
+  - [51, 51]
+  - [52, 52]
+  - [53, 53]
+  - [54, 54]
+  - [55, 55]
+  - [56, 56]
+  - [57, 57]
+  - [58, 58]
+  - [59, 59]
+  - [60, 60]
+  - [61, 61]
+  - [62, 62]
+  - [63, 63]
+  - [64, 64]
+  - [65, 65]
+  - [66, 66]
+  - [67, 67]
+  - [68, 68]
+  - [69, 69]
+  - [70, 70]
+  - [71, 71]
+  - [72, 72]
+  - [73, 73]
+  - [74, 74]
+  - [75, 75]
+  - [76, 76]
+  - [77, 77]
+  - [78, 78]
+  - [79, 79]
+  - [80, 80]
+  - [81, 81]
+  - [82, 82]
+  - [83, 83]
+  - [84, 84]
+  - [85, 85]
+  - [86, 86]
+  - [87, 87]
+  - [88, 88]
+  - [89, 89]
+  - [90, 90]
+  - [91, 91]
+  - [92, 92]
+  - [93, 93]
+  - [94, 94]
+  - [95, 95]
+  - [96, 96]
+  - [97, 97]
+  - [98, 98]
+  - [99, 99]
+  - [100, 100]
+...
+t = {} for state, v in i:pairs({44, 44}, {iterator = 'GE'}) do table.insert(t, v) end
+---
+...
+t
+---
+- - [44, 44]
+  - [45, 45]
+  - [46, 46]
+  - [47, 47]
+  - [48, 48]
+  - [49, 49]
+  - [50, 50]
+  - [51, 51]
+  - [52, 52]
+  - [53, 53]
+  - [54, 54]
+  - [55, 55]
+  - [56, 56]
+  - [57, 57]
+  - [58, 58]
+  - [59, 59]
+  - [60, 60]
+  - [61, 61]
+  - [62, 62]
+  - [63, 63]
+  - [64, 64]
+  - [65, 65]
+  - [66, 66]
+  - [67, 67]
+  - [68, 68]
+  - [69, 69]
+  - [70, 70]
+  - [71, 71]
+  - [72, 72]
+  - [73, 73]
+  - [74, 74]
+  - [75, 75]
+  - [76, 76]
+  - [77, 77]
+  - [78, 78]
+  - [79, 79]
+  - [80, 80]
+  - [81, 81]
+  - [82, 82]
+  - [83, 83]
+  - [84, 84]
+  - [85, 85]
+  - [86, 86]
+  - [87, 87]
+  - [88, 88]
+  - [89, 89]
+  - [90, 90]
+  - [91, 91]
+  - [92, 92]
+  - [93, 93]
+  - [94, 94]
+  - [95, 95]
+  - [96, 96]
+  - [97, 97]
+  - [98, 98]
+  - [99, 99]
+  - [100, 100]
+...
+t = {} for state, v in i:pairs({44, 44}, {iterator = 'GT'}) do table.insert(t, v) end
+---
+...
+t
+---
+- - [45, 45]
+  - [46, 46]
+  - [47, 47]
+  - [48, 48]
+  - [49, 49]
+  - [50, 50]
+  - [51, 51]
+  - [52, 52]
+  - [53, 53]
+  - [54, 54]
+  - [55, 55]
+  - [56, 56]
+  - [57, 57]
+  - [58, 58]
+  - [59, 59]
+  - [60, 60]
+  - [61, 61]
+  - [62, 62]
+  - [63, 63]
+  - [64, 64]
+  - [65, 65]
+  - [66, 66]
+  - [67, 67]
+  - [68, 68]
+  - [69, 69]
+  - [70, 70]
+  - [71, 71]
+  - [72, 72]
+  - [73, 73]
+  - [74, 74]
+  - [75, 75]
+  - [76, 76]
+  - [77, 77]
+  - [78, 78]
+  - [79, 79]
+  - [80, 80]
+  - [81, 81]
+  - [82, 82]
+  - [83, 83]
+  - [84, 84]
+  - [85, 85]
+  - [86, 86]
+  - [87, 87]
+  - [88, 88]
+  - [89, 89]
+  - [90, 90]
+  - [91, 91]
+  - [92, 92]
+  - [93, 93]
+  - [94, 94]
+  - [95, 95]
+  - [96, 96]
+  - [97, 97]
+  - [98, 98]
+  - [99, 99]
+  - [100, 100]
+...
+t = {} for state, v in i:pairs({}, {iterator = 'LE'}) do table.insert(t, v) end
+---
+...
+t
+---
+- - [100, 100]
+  - [99, 99]
+  - [98, 98]
+  - [97, 97]
+  - [96, 96]
+  - [95, 95]
+  - [94, 94]
+  - [93, 93]
+  - [92, 92]
+  - [91, 91]
+  - [90, 90]
+  - [89, 89]
+  - [88, 88]
+  - [87, 87]
+  - [86, 86]
+  - [85, 85]
+  - [84, 84]
+  - [83, 83]
+  - [82, 82]
+  - [81, 81]
+  - [80, 80]
+  - [79, 79]
+  - [78, 78]
+  - [77, 77]
+  - [76, 76]
+  - [75, 75]
+  - [74, 74]
+  - [73, 73]
+  - [72, 72]
+  - [71, 71]
+  - [70, 70]
+  - [69, 69]
+  - [68, 68]
+  - [67, 67]
+  - [66, 66]
+  - [65, 65]
+  - [64, 64]
+  - [63, 63]
+  - [62, 62]
+  - [61, 61]
+  - [60, 60]
+  - [59, 59]
+  - [58, 58]
+  - [57, 57]
+  - [56, 56]
+  - [55, 55]
+  - [54, 54]
+  - [53, 53]
+  - [52, 52]
+  - [51, 51]
+  - [50, 50]
+  - [49, 49]
+  - [48, 48]
+  - [47, 47]
+  - [46, 46]
+  - [45, 45]
+  - [44, 44]
+  - [43, 43]
+  - [42, 42]
+  - [41, 41]
+  - [40, 40]
+  - [39, 39]
+  - [38, 38]
+  - [37, 37]
+  - [36, 36]
+  - [35, 35]
+  - [34, 34]
+  - [33, 33]
+  - [32, 32]
+  - [31, 31]
+  - [30, 30]
+  - [29, 29]
+  - [28, 28]
+  - [27, 27]
+  - [26, 26]
+  - [25, 25]
+  - [24, 24]
+  - [23, 23]
+  - [22, 22]
+  - [21, 21]
+  - [20, 20]
+  - [19, 19]
+  - [18, 18]
+  - [17, 17]
+  - [16, 16]
+  - [15, 15]
+  - [14, 14]
+  - [13, 13]
+  - [12, 12]
+  - [11, 11]
+  - [10, 10]
+  - [9, 9]
+  - [8, 8]
+  - [7, 7]
+  - [6, 6]
+  - [5, 5]
+  - [4, 4]
+  - [3, 3]
+  - [2, 2]
+  - [1, 1]
+...
+t = {} for state, v in i:pairs({77, 77}, {iterator = 'LE'}) do table.insert(t, v) end
+---
+...
+t
+---
+- - [77, 77]
+  - [76, 76]
+  - [75, 75]
+  - [74, 74]
+  - [73, 73]
+  - [72, 72]
+  - [71, 71]
+  - [70, 70]
+  - [69, 69]
+  - [68, 68]
+  - [67, 67]
+  - [66, 66]
+  - [65, 65]
+  - [64, 64]
+  - [63, 63]
+  - [62, 62]
+  - [61, 61]
+  - [60, 60]
+  - [59, 59]
+  - [58, 58]
+  - [57, 57]
+  - [56, 56]
+  - [55, 55]
+  - [54, 54]
+  - [53, 53]
+  - [52, 52]
+  - [51, 51]
+  - [50, 50]
+  - [49, 49]
+  - [48, 48]
+  - [47, 47]
+  - [46, 46]
+  - [45, 45]
+  - [44, 44]
+  - [43, 43]
+  - [42, 42]
+  - [41, 41]
+  - [40, 40]
+  - [39, 39]
+  - [38, 38]
+  - [37, 37]
+  - [36, 36]
+  - [35, 35]
+  - [34, 34]
+  - [33, 33]
+  - [32, 32]
+  - [31, 31]
+  - [30, 30]
+  - [29, 29]
+  - [28, 28]
+  - [27, 27]
+  - [26, 26]
+  - [25, 25]
+  - [24, 24]
+  - [23, 23]
+  - [22, 22]
+  - [21, 21]
+  - [20, 20]
+  - [19, 19]
+  - [18, 18]
+  - [17, 17]
+  - [16, 16]
+  - [15, 15]
+  - [14, 14]
+  - [13, 13]
+  - [12, 12]
+  - [11, 11]
+  - [10, 10]
+  - [9, 9]
+  - [8, 8]
+  - [7, 7]
+  - [6, 6]
+  - [5, 5]
+  - [4, 4]
+  - [3, 3]
+  - [2, 2]
+  - [1, 1]
+...
+t = {} for state, v in i:pairs({}, {iterator = 'LT'}) do table.insert(t, v) end
+---
+...
+t
+---
+- - [100, 100]
+  - [99, 99]
+  - [98, 98]
+  - [97, 97]
+  - [96, 96]
+  - [95, 95]
+  - [94, 94]
+  - [93, 93]
+  - [92, 92]
+  - [91, 91]
+  - [90, 90]
+  - [89, 89]
+  - [88, 88]
+  - [87, 87]
+  - [86, 86]
+  - [85, 85]
+  - [84, 84]
+  - [83, 83]
+  - [82, 82]
+  - [81, 81]
+  - [80, 80]
+  - [79, 79]
+  - [78, 78]
+  - [77, 77]
+  - [76, 76]
+  - [75, 75]
+  - [74, 74]
+  - [73, 73]
+  - [72, 72]
+  - [71, 71]
+  - [70, 70]
+  - [69, 69]
+  - [68, 68]
+  - [67, 67]
+  - [66, 66]
+  - [65, 65]
+  - [64, 64]
+  - [63, 63]
+  - [62, 62]
+  - [61, 61]
+  - [60, 60]
+  - [59, 59]
+  - [58, 58]
+  - [57, 57]
+  - [56, 56]
+  - [55, 55]
+  - [54, 54]
+  - [53, 53]
+  - [52, 52]
+  - [51, 51]
+  - [50, 50]
+  - [49, 49]
+  - [48, 48]
+  - [47, 47]
+  - [46, 46]
+  - [45, 45]
+  - [44, 44]
+  - [43, 43]
+  - [42, 42]
+  - [41, 41]
+  - [40, 40]
+  - [39, 39]
+  - [38, 38]
+  - [37, 37]
+  - [36, 36]
+  - [35, 35]
+  - [34, 34]
+  - [33, 33]
+  - [32, 32]
+  - [31, 31]
+  - [30, 30]
+  - [29, 29]
+  - [28, 28]
+  - [27, 27]
+  - [26, 26]
+  - [25, 25]
+  - [24, 24]
+  - [23, 23]
+  - [22, 22]
+  - [21, 21]
+  - [20, 20]
+  - [19, 19]
+  - [18, 18]
+  - [17, 17]
+  - [16, 16]
+  - [15, 15]
+  - [14, 14]
+  - [13, 13]
+  - [12, 12]
+  - [11, 11]
+  - [10, 10]
+  - [9, 9]
+  - [8, 8]
+  - [7, 7]
+  - [6, 6]
+  - [5, 5]
+  - [4, 4]
+  - [3, 3]
+  - [2, 2]
+  - [1, 1]
+...
+t = {} for state, v in i:pairs({77, 77}, {iterator = 'LT'}) do table.insert(t, v) end
+---
+...
+t
+---
+- - [76, 76]
+  - [75, 75]
+  - [74, 74]
+  - [73, 73]
+  - [72, 72]
+  - [71, 71]
+  - [70, 70]
+  - [69, 69]
+  - [68, 68]
+  - [67, 67]
+  - [66, 66]
+  - [65, 65]
+  - [64, 64]
+  - [63, 63]
+  - [62, 62]
+  - [61, 61]
+  - [60, 60]
+  - [59, 59]
+  - [58, 58]
+  - [57, 57]
+  - [56, 56]
+  - [55, 55]
+  - [54, 54]
+  - [53, 53]
+  - [52, 52]
+  - [51, 51]
+  - [50, 50]
+  - [49, 49]
+  - [48, 48]
+  - [47, 47]
+  - [46, 46]
+  - [45, 45]
+  - [44, 44]
+  - [43, 43]
+  - [42, 42]
+  - [41, 41]
+  - [40, 40]
+  - [39, 39]
+  - [38, 38]
+  - [37, 37]
+  - [36, 36]
+  - [35, 35]
+  - [34, 34]
+  - [33, 33]
+  - [32, 32]
+  - [31, 31]
+  - [30, 30]
+  - [29, 29]
+  - [28, 28]
+  - [27, 27]
+  - [26, 26]
+  - [25, 25]
+  - [24, 24]
+  - [23, 23]
+  - [22, 22]
+  - [21, 21]
+  - [20, 20]
+  - [19, 19]
+  - [18, 18]
+  - [17, 17]
+  - [16, 16]
+  - [15, 15]
+  - [14, 14]
+  - [13, 13]
+  - [12, 12]
+  - [11, 11]
+  - [10, 10]
+  - [9, 9]
+  - [8, 8]
+  - [7, 7]
+  - [6, 6]
+  - [5, 5]
+  - [4, 4]
+  - [3, 3]
+  - [2, 2]
+  - [1, 1]
+...
+s:drop()
+---
+...
+-- iterator with tuple.new
+s = box.schema.space.create_ephemeral()
+---
+...
+i = s:create_index('a', { type = 'tree', parts = {1, 'string'} })
+---
+...
+for key = 1, 100 do s:replace({tostring(key)}) end
+---
+...
+t = {} for state, v in i:pairs(box.tuple.new{}, {iterator = 'ALL'}) do table.insert(t, v) end
+---
+...
+t
+---
+- - ['1']
+  - ['10']
+  - ['100']
+  - ['11']
+  - ['12']
+  - ['13']
+  - ['14']
+  - ['15']
+  - ['16']
+  - ['17']
+  - ['18']
+  - ['19']
+  - ['2']
+  - ['20']
+  - ['21']
+  - ['22']
+  - ['23']
+  - ['24']
+  - ['25']
+  - ['26']
+  - ['27']
+  - ['28']
+  - ['29']
+  - ['3']
+  - ['30']
+  - ['31']
+  - ['32']
+  - ['33']
+  - ['34']
+  - ['35']
+  - ['36']
+  - ['37']
+  - ['38']
+  - ['39']
+  - ['4']
+  - ['40']
+  - ['41']
+  - ['42']
+  - ['43']
+  - ['44']
+  - ['45']
+  - ['46']
+  - ['47']
+  - ['48']
+  - ['49']
+  - ['5']
+  - ['50']
+  - ['51']
+  - ['52']
+  - ['53']
+  - ['54']
+  - ['55']
+  - ['56']
+  - ['57']
+  - ['58']
+  - ['59']
+  - ['6']
+  - ['60']
+  - ['61']
+  - ['62']
+  - ['63']
+  - ['64']
+  - ['65']
+  - ['66']
+  - ['67']
+  - ['68']
+  - ['69']
+  - ['7']
+  - ['70']
+  - ['71']
+  - ['72']
+  - ['73']
+  - ['74']
+  - ['75']
+  - ['76']
+  - ['77']
+  - ['78']
+  - ['79']
+  - ['8']
+  - ['80']
+  - ['81']
+  - ['82']
+  - ['83']
+  - ['84']
+  - ['85']
+  - ['86']
+  - ['87']
+  - ['88']
+  - ['89']
+  - ['9']
+  - ['90']
+  - ['91']
+  - ['92']
+  - ['93']
+  - ['94']
+  - ['95']
+  - ['96']
+  - ['97']
+  - ['98']
+  - ['99']
+...
+t = {} for state, v in i:pairs(box.tuple.new{}, {iterator = 'GE'}) do table.insert(t, v) end
+---
+...
+t
+---
+- - ['1']
+  - ['10']
+  - ['100']
+  - ['11']
+  - ['12']
+  - ['13']
+  - ['14']
+  - ['15']
+  - ['16']
+  - ['17']
+  - ['18']
+  - ['19']
+  - ['2']
+  - ['20']
+  - ['21']
+  - ['22']
+  - ['23']
+  - ['24']
+  - ['25']
+  - ['26']
+  - ['27']
+  - ['28']
+  - ['29']
+  - ['3']
+  - ['30']
+  - ['31']
+  - ['32']
+  - ['33']
+  - ['34']
+  - ['35']
+  - ['36']
+  - ['37']
+  - ['38']
+  - ['39']
+  - ['4']
+  - ['40']
+  - ['41']
+  - ['42']
+  - ['43']
+  - ['44']
+  - ['45']
+  - ['46']
+  - ['47']
+  - ['48']
+  - ['49']
+  - ['5']
+  - ['50']
+  - ['51']
+  - ['52']
+  - ['53']
+  - ['54']
+  - ['55']
+  - ['56']
+  - ['57']
+  - ['58']
+  - ['59']
+  - ['6']
+  - ['60']
+  - ['61']
+  - ['62']
+  - ['63']
+  - ['64']
+  - ['65']
+  - ['66']
+  - ['67']
+  - ['68']
+  - ['69']
+  - ['7']
+  - ['70']
+  - ['71']
+  - ['72']
+  - ['73']
+  - ['74']
+  - ['75']
+  - ['76']
+  - ['77']
+  - ['78']
+  - ['79']
+  - ['8']
+  - ['80']
+  - ['81']
+  - ['82']
+  - ['83']
+  - ['84']
+  - ['85']
+  - ['86']
+  - ['87']
+  - ['88']
+  - ['89']
+  - ['9']
+  - ['90']
+  - ['91']
+  - ['92']
+  - ['93']
+  - ['94']
+  - ['95']
+  - ['96']
+  - ['97']
+  - ['98']
+  - ['99']
+...
+t = {} for state, v in i:pairs(box.tuple.new(tostring(44)), {iterator = 'GE'}) do table.insert(t, v) end
+---
+...
+t
+---
+- - ['44']
+  - ['45']
+  - ['46']
+  - ['47']
+  - ['48']
+  - ['49']
+  - ['5']
+  - ['50']
+  - ['51']
+  - ['52']
+  - ['53']
+  - ['54']
+  - ['55']
+  - ['56']
+  - ['57']
+  - ['58']
+  - ['59']
+  - ['6']
+  - ['60']
+  - ['61']
+  - ['62']
+  - ['63']
+  - ['64']
+  - ['65']
+  - ['66']
+  - ['67']
+  - ['68']
+  - ['69']
+  - ['7']
+  - ['70']
+  - ['71']
+  - ['72']
+  - ['73']
+  - ['74']
+  - ['75']
+  - ['76']
+  - ['77']
+  - ['78']
+  - ['79']
+  - ['8']
+  - ['80']
+  - ['81']
+  - ['82']
+  - ['83']
+  - ['84']
+  - ['85']
+  - ['86']
+  - ['87']
+  - ['88']
+  - ['89']
+  - ['9']
+  - ['90']
+  - ['91']
+  - ['92']
+  - ['93']
+  - ['94']
+  - ['95']
+  - ['96']
+  - ['97']
+  - ['98']
+  - ['99']
+...
+t = {} for state, v in i:pairs(box.tuple.new(tostring(44)), {iterator = 'GT'}) do table.insert(t, v) end
+---
+...
+t
+---
+- - ['45']
+  - ['46']
+  - ['47']
+  - ['48']
+  - ['49']
+  - ['5']
+  - ['50']
+  - ['51']
+  - ['52']
+  - ['53']
+  - ['54']
+  - ['55']
+  - ['56']
+  - ['57']
+  - ['58']
+  - ['59']
+  - ['6']
+  - ['60']
+  - ['61']
+  - ['62']
+  - ['63']
+  - ['64']
+  - ['65']
+  - ['66']
+  - ['67']
+  - ['68']
+  - ['69']
+  - ['7']
+  - ['70']
+  - ['71']
+  - ['72']
+  - ['73']
+  - ['74']
+  - ['75']
+  - ['76']
+  - ['77']
+  - ['78']
+  - ['79']
+  - ['8']
+  - ['80']
+  - ['81']
+  - ['82']
+  - ['83']
+  - ['84']
+  - ['85']
+  - ['86']
+  - ['87']
+  - ['88']
+  - ['89']
+  - ['9']
+  - ['90']
+  - ['91']
+  - ['92']
+  - ['93']
+  - ['94']
+  - ['95']
+  - ['96']
+  - ['97']
+  - ['98']
+  - ['99']
+...
+t = {} for state, v in i:pairs(box.tuple.new{}, {iterator = 'LE'}) do table.insert(t, v) end
+---
+...
+t
+---
+- - ['99']
+  - ['98']
+  - ['97']
+  - ['96']
+  - ['95']
+  - ['94']
+  - ['93']
+  - ['92']
+  - ['91']
+  - ['90']
+  - ['9']
+  - ['89']
+  - ['88']
+  - ['87']
+  - ['86']
+  - ['85']
+  - ['84']
+  - ['83']
+  - ['82']
+  - ['81']
+  - ['80']
+  - ['8']
+  - ['79']
+  - ['78']
+  - ['77']
+  - ['76']
+  - ['75']
+  - ['74']
+  - ['73']
+  - ['72']
+  - ['71']
+  - ['70']
+  - ['7']
+  - ['69']
+  - ['68']
+  - ['67']
+  - ['66']
+  - ['65']
+  - ['64']
+  - ['63']
+  - ['62']
+  - ['61']
+  - ['60']
+  - ['6']
+  - ['59']
+  - ['58']
+  - ['57']
+  - ['56']
+  - ['55']
+  - ['54']
+  - ['53']
+  - ['52']
+  - ['51']
+  - ['50']
+  - ['5']
+  - ['49']
+  - ['48']
+  - ['47']
+  - ['46']
+  - ['45']
+  - ['44']
+  - ['43']
+  - ['42']
+  - ['41']
+  - ['40']
+  - ['4']
+  - ['39']
+  - ['38']
+  - ['37']
+  - ['36']
+  - ['35']
+  - ['34']
+  - ['33']
+  - ['32']
+  - ['31']
+  - ['30']
+  - ['3']
+  - ['29']
+  - ['28']
+  - ['27']
+  - ['26']
+  - ['25']
+  - ['24']
+  - ['23']
+  - ['22']
+  - ['21']
+  - ['20']
+  - ['2']
+  - ['19']
+  - ['18']
+  - ['17']
+  - ['16']
+  - ['15']
+  - ['14']
+  - ['13']
+  - ['12']
+  - ['11']
+  - ['100']
+  - ['10']
+  - ['1']
+...
+t = {} for state, v in i:pairs(box.tuple.new(tostring(77)), {iterator = 'LE'}) do table.insert(t, v) end
+---
+...
+t
+---
+- - ['77']
+  - ['76']
+  - ['75']
+  - ['74']
+  - ['73']
+  - ['72']
+  - ['71']
+  - ['70']
+  - ['7']
+  - ['69']
+  - ['68']
+  - ['67']
+  - ['66']
+  - ['65']
+  - ['64']
+  - ['63']
+  - ['62']
+  - ['61']
+  - ['60']
+  - ['6']
+  - ['59']
+  - ['58']
+  - ['57']
+  - ['56']
+  - ['55']
+  - ['54']
+  - ['53']
+  - ['52']
+  - ['51']
+  - ['50']
+  - ['5']
+  - ['49']
+  - ['48']
+  - ['47']
+  - ['46']
+  - ['45']
+  - ['44']
+  - ['43']
+  - ['42']
+  - ['41']
+  - ['40']
+  - ['4']
+  - ['39']
+  - ['38']
+  - ['37']
+  - ['36']
+  - ['35']
+  - ['34']
+  - ['33']
+  - ['32']
+  - ['31']
+  - ['30']
+  - ['3']
+  - ['29']
+  - ['28']
+  - ['27']
+  - ['26']
+  - ['25']
+  - ['24']
+  - ['23']
+  - ['22']
+  - ['21']
+  - ['20']
+  - ['2']
+  - ['19']
+  - ['18']
+  - ['17']
+  - ['16']
+  - ['15']
+  - ['14']
+  - ['13']
+  - ['12']
+  - ['11']
+  - ['100']
+  - ['10']
+  - ['1']
+...
+t = {} for state, v in i:pairs(box.tuple.new{}, {iterator = 'LT'}) do table.insert(t, v) end
+---
+...
+t
+---
+- - ['99']
+  - ['98']
+  - ['97']
+  - ['96']
+  - ['95']
+  - ['94']
+  - ['93']
+  - ['92']
+  - ['91']
+  - ['90']
+  - ['9']
+  - ['89']
+  - ['88']
+  - ['87']
+  - ['86']
+  - ['85']
+  - ['84']
+  - ['83']
+  - ['82']
+  - ['81']
+  - ['80']
+  - ['8']
+  - ['79']
+  - ['78']
+  - ['77']
+  - ['76']
+  - ['75']
+  - ['74']
+  - ['73']
+  - ['72']
+  - ['71']
+  - ['70']
+  - ['7']
+  - ['69']
+  - ['68']
+  - ['67']
+  - ['66']
+  - ['65']
+  - ['64']
+  - ['63']
+  - ['62']
+  - ['61']
+  - ['60']
+  - ['6']
+  - ['59']
+  - ['58']
+  - ['57']
+  - ['56']
+  - ['55']
+  - ['54']
+  - ['53']
+  - ['52']
+  - ['51']
+  - ['50']
+  - ['5']
+  - ['49']
+  - ['48']
+  - ['47']
+  - ['46']
+  - ['45']
+  - ['44']
+  - ['43']
+  - ['42']
+  - ['41']
+  - ['40']
+  - ['4']
+  - ['39']
+  - ['38']
+  - ['37']
+  - ['36']
+  - ['35']
+  - ['34']
+  - ['33']
+  - ['32']
+  - ['31']
+  - ['30']
+  - ['3']
+  - ['29']
+  - ['28']
+  - ['27']
+  - ['26']
+  - ['25']
+  - ['24']
+  - ['23']
+  - ['22']
+  - ['21']
+  - ['20']
+  - ['2']
+  - ['19']
+  - ['18']
+  - ['17']
+  - ['16']
+  - ['15']
+  - ['14']
+  - ['13']
+  - ['12']
+  - ['11']
+  - ['100']
+  - ['10']
+  - ['1']
+...
+t = {} for state, v in i:pairs(box.tuple.new(tostring(77)), {iterator = 'LT'}) do table.insert(t, v) end
+---
+...
+t
+---
+- - ['76']
+  - ['75']
+  - ['74']
+  - ['73']
+  - ['72']
+  - ['71']
+  - ['70']
+  - ['7']
+  - ['69']
+  - ['68']
+  - ['67']
+  - ['66']
+  - ['65']
+  - ['64']
+  - ['63']
+  - ['62']
+  - ['61']
+  - ['60']
+  - ['6']
+  - ['59']
+  - ['58']
+  - ['57']
+  - ['56']
+  - ['55']
+  - ['54']
+  - ['53']
+  - ['52']
+  - ['51']
+  - ['50']
+  - ['5']
+  - ['49']
+  - ['48']
+  - ['47']
+  - ['46']
+  - ['45']
+  - ['44']
+  - ['43']
+  - ['42']
+  - ['41']
+  - ['40']
+  - ['4']
+  - ['39']
+  - ['38']
+  - ['37']
+  - ['36']
+  - ['35']
+  - ['34']
+  - ['33']
+  - ['32']
+  - ['31']
+  - ['30']
+  - ['3']
+  - ['29']
+  - ['28']
+  - ['27']
+  - ['26']
+  - ['25']
+  - ['24']
+  - ['23']
+  - ['22']
+  - ['21']
+  - ['20']
+  - ['2']
+  - ['19']
+  - ['18']
+  - ['17']
+  - ['16']
+  - ['15']
+  - ['14']
+  - ['13']
+  - ['12']
+  - ['11']
+  - ['100']
+  - ['10']
+  - ['1']
+...
+s:drop()
+---
+...
+s = box.schema.space.create_ephemeral()
+---
+...
+i = s:create_index('a', { type = 'tree', parts = {1, 'string'} })
+---
+...
+i:pairs({}, {iterator = 666 })
+---
+- error: Illegal parameters, Invalid iterator type
+...
+s = box.schema.space.create_ephemeral()
+---
+...
+i = s:create_index('a')
+---
+...
+s:replace({1})
+---
+- [1]
+...
+s:replace({2})
+---
+- [2]
+...
+s:replace({3})
+---
+- [3]
+...
+s:replace({4})
+---
+- [4]
+...
+s:pairs(2, { iterator = 'GE' }):totable()
+---
+- - [2]
+  - [3]
+  - [4]
+...
+s:drop()
+---
+...
+s = box.schema.space.create_ephemeral()
+---
+...
+i = s:create_index('a')
+---
+...
+s:auto_increment{1}
+---
+- [1, 1]
+...
+s:auto_increment{2}
+---
+- [2, 2]
+...
+s:auto_increment{3}
+---
+- [3, 3]
+...
+s:auto_increment{4}
+---
+- [4, 4]
+...
+s:auto_increment{5}
+---
+- [5, 5]
+...
+s:pairs(3, 'GE'):totable()
+---
+- - [3, 3]
+  - [4, 4]
+  - [5, 5]
+...
+i:pairs(3, 'GE'):totable()
+---
+- - [3, 3]
+  - [4, 4]
+  - [5, 5]
+...
+s:pairs(3, {iterator = 'GE' }):totable()
+---
+- - [3, 3]
+  - [4, 4]
+  - [5, 5]
+...
+i:pairs(3, {iterator = 'GE' }):totable()
+---
+- - [3, 3]
+  - [4, 4]
+  - [5, 5]
+...
+s:pairs(3, 'EQ'):totable()
+---
+- - [3, 3]
+...
+i:pairs(3, 'EQ'):totable()
+---
+- - [3, 3]
+...
+s:pairs(3, {iterator = 'EQ' }):totable()
+---
+- - [3, 3]
+...
+i:pairs(3, {iterator = 'EQ' }):totable()
+---
+- - [3, 3]
+...
+s:pairs(3, 'GT'):totable()
+---
+- - [4, 4]
+  - [5, 5]
+...
+i:pairs(3, 'GT'):totable()
+---
+- - [4, 4]
+  - [5, 5]
+...
+s:pairs(3, {iterator = 'GT' }):totable()
+---
+- - [4, 4]
+  - [5, 5]
+...
+i:pairs(3, {iterator = 'GT' }):totable()
+---
+- - [4, 4]
+  - [5, 5]
+...
+i:select({3}, 'LE')
+---
+- - [3, 3]
+  - [2, 2]
+  - [1, 1]
+...
+s:select({3}, 'LE')
+---
+- - [3, 3]
+  - [2, 2]
+  - [1, 1]
+...
+i:count({3}, 'GT')
+---
+- 2
+...
+s:count({3}, 'GT')
+---
+- 2
+...
+s:drop()
+---
+...
+-- implement lazy iterator positioning
+s = box.schema.space.create_ephemeral()
+---
+...
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned', 2, 'unsigned'} })
+---
+...
+for i = 1,3 do for j = 1,3 do s:replace{i, j} end end
+---
+...
+itr1,itr2,itr3 = s:pairs{2}
+---
+...
+_ = s:replace{1, 4}
+---
+...
+r = {}
+---
+...
+for k,v in itr1,itr2,itr3 do table.insert(r, v) end
+---
+...
+r
+---
+- - [2, 1]
+  - [2, 2]
+  - [2, 3]
+...
+itr1,itr2,itr3 = s:pairs({2}, {iterator = 'GE'})
+---
+...
+_ = s:replace{1, 5}
+---
+...
+r = {}
+---
+...
+for k,v in itr1,itr2,itr3 do table.insert(r, v) end
+---
+...
+r
+---
+- - [2, 1]
+  - [2, 2]
+  - [2, 3]
+  - [3, 1]
+  - [3, 2]
+  - [3, 3]
+...
+itr1,itr2,itr3 = s:pairs({2}, {iterator = 'REQ'})
+---
+...
+s:replace{2, 4}
+---
+- [2, 4]
+...
+r = {}
+---
+...
+for k,v in itr1,itr2,itr3 do table.insert(r, v) end
+---
+...
+r
+---
+- - [2, 4]
+  - [2, 3]
+  - [2, 2]
+  - [2, 1]
+...
+r = nil
+---
+...
+s:drop()
+---
+...
+-- make tree iterators stable
+s = box.schema.space.create_ephemeral()
+---
+...
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} })
+---
+...
+for i = 1,10 do s:replace{i} end
+---
+...
+r = {}
+---
+...
+for k,v in s:pairs{} do table.insert(r, v[1]) s:delete(v[1]) end
+---
+...
+r
+---
+- - 1
+  - 2
+  - 3
+  - 4
+  - 5
+  - 6
+  - 7
+  - 8
+  - 9
+  - 10
+...
+s:select{}
+---
+- []
+...
+for i = 1,10 do s:replace{i} end
+---
+...
+r = {}
+---
+...
+for k,v in s:pairs({}, {iterator = 'REQ'}) do table.insert(r, v[1]) s:delete(v[1]) end
+---
+...
+r
+---
+- - 10
+  - 9
+  - 8
+  - 7
+  - 6
+  - 5
+  - 4
+  - 3
+  - 2
+  - 1
+...
+s:select{}
+---
+- []
+...
+s:drop()
+---
+...
+s = box.schema.space.create_ephemeral()
+---
+...
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned', 2, 'unsigned'} })
+---
+...
+for i = 1,3 do for j = 1,3 do s:replace{i, j} end end
+---
+...
+r = {}
+---
+...
+for k,v in s:pairs{2} do table.insert(r, v) s:delete{v[1], v[2]} end
+---
+...
+r
+---
+- - [2, 1]
+  - [2, 2]
+  - [2, 3]
+...
+s:select{}
+---
+- - [1, 1]
+  - [1, 2]
+  - [1, 3]
+  - [3, 1]
+  - [3, 2]
+  - [3, 3]
+...
+for i = 1,3 do for j = 1,3 do s:replace{i, j} end end
+---
+...
+r = {}
+---
+...
+for k,v in s:pairs({3}, {iterator = 'REQ'}) do table.insert(r, v) s:delete{v[1], v[2]} end
+---
+...
+r
+---
+- - [3, 3]
+  - [3, 2]
+  - [3, 1]
+...
+s:select{}
+---
+- - [1, 1]
+  - [1, 2]
+  - [1, 3]
+  - [2, 1]
+  - [2, 2]
+  - [2, 3]
+...
+r = nil
+---
+...
+s:drop()
+---
+...
+s = box.schema.space.create_ephemeral()
+---
+...
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} })
+---
+...
+s:replace{10} s:replace{20} s:replace{30} s:replace{40} s:replace{50} s:replace{60}
+---
+...
+gen,param,state = i:pairs({25})
+---
+...
+s:replace{25}
+---
+- [25]
+...
+state, value = gen(param,state)
+---
+...
+value
+---
+- [25]
+...
+state, value = gen(param,state)
+---
+...
+value
+---
+- null
+...
+gen,param,state = i:pairs({35})
+---
+...
+state, value = gen(param,state)
+---
+...
+value
+---
+- null
+...
+s:replace{35}
+---
+- [35]
+...
+state, value = gen(param,state)
+---
+- error: 'builtin/box/schema.lua:1247: usage: next(state)'
+...
+value
+---
+- null
+...
+s:drop()
+---
+...
+s = box.schema.space.create_ephemeral()
+---
+...
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} })
+---
+...
+s:replace{10} s:replace{20} s:replace{30} s:replace{40} s:replace{50} s:replace{60}
+---
+...
+gen,param,state = i:pairs({30}, {iterator = 'GE'})
+---
+...
+state, value = gen(param, state)
+---
+...
+value
+---
+- [30]
+...
+s:replace{0}
+---
+- [0]
+...
+state, value = gen(param, state)
+---
+...
+value
+---
+- [40]
+...
+s:replace{42}
+---
+- [42]
+...
+state, value = gen(param, state)
+---
+...
+value
+---
+- [42]
+...
+s:replace{80}
+---
+- [80]
+...
+state, value = gen(param, state)
+---
+...
+value
+---
+- [50]
+...
+s:replace{15}
+---
+- [15]
+...
+state, value = gen(param, state)
+---
+...
+value
+---
+- [60]
+...
+state, value = gen(param, state)
+---
+...
+value
+---
+- [80]
+...
+state, value = gen(param, state)
+---
+...
+state
+---
+- null
+...
+value
+---
+- null
+...
+s:drop()
+---
+...
+s = box.schema.space.create_ephemeral()
+---
+...
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} })
+---
+...
+s:replace{10} s:replace{20} s:replace{30} s:replace{40} s:replace{50} s:replace{60}
+---
+...
+gen,param,state = i:pairs({40}, {iterator = 'LE'})
+---
+...
+state, value = gen(param, state)
+---
+...
+value
+---
+- [40]
+...
+s:replace{0}
+---
+- [0]
+...
+state, value = gen(param, state)
+---
+...
+value
+---
+- [30]
+...
+s:replace{15}
+---
+- [15]
+...
+state, value = gen(param, state)
+---
+...
+value
+---
+- [20]
+...
+s:replace{42}
+---
+- [42]
+...
+state, value = gen(param, state)
+---
+...
+value
+---
+- [15]
+...
+s:replace{32}
+---
+- [32]
+...
+state, value = gen(param, state)
+---
+...
+value
+---
+- [10]
+...
+s:replace{80}
+---
+- [80]
+...
+state, value = gen(param, state)
+---
+...
+value
+---
+- [0]
+...
+state, value = gen(param, state)
+---
+...
+state
+---
+- null
+...
+value
+---
+- null
+...
+s:drop()
+---
+...
+s = box.schema.space.create_ephemeral()
+---
+...
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} })
+---
+...
+s:replace{10} s:replace{20} s:replace{30} s:replace{40} s:replace{50} s:replace{60}
+---
+...
+gen,param,state = i:pairs({28}, {iterator = 'GE'})
+---
+...
+s:replace{0}
+---
+- [0]
+...
+state, value = gen(param, state)
+---
+...
+value
+---
+- [30]
+...
+s:replace{15}
+---
+- [15]
+...
+state, value = gen(param, state)
+---
+...
+value
+---
+- [40]
+...
+s:replace{42}
+---
+- [42]
+...
+state, value = gen(param, state)
+---
+...
+value
+---
+- [42]
+...
+s:replace{32}
+---
+- [32]
+...
+state, value = gen(param, state)
+---
+...
+value
+---
+- [50]
+...
+s:replace{80}
+---
+- [80]
+...
+state, value = gen(param, state)
+---
+...
+value
+---
+- [60]
+...
+state, value = gen(param, state)
+---
+...
+value
+---
+- [80]
+...
+gen(param, state)
+---
+- null
+...
+-- test iterator dummy function, invoked when it's out of bounds
+gen(param, state)
+---
+- null
+...
+s:drop()
+---
+...
+s = box.schema.space.create_ephemeral()
+---
+...
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} })
+---
+...
+s:replace{10} s:replace{20} s:replace{30} s:replace{40} s:replace{50} s:replace{60}
+---
+...
+gen,param,state = i:pairs({42}, {iterator = 'LE'})
+---
+...
+s:replace{0}
+---
+- [0]
+...
+state, value = gen(param, state)
+---
+...
+value
+---
+- [40]
+...
+s:replace{42}
+---
+- [42]
+...
+state, value = gen(param, state)
+---
+...
+value
+---
+- [30]
+...
+s:replace{15}
+---
+- [15]
+...
+state, value = gen(param, state)
+---
+...
+value
+---
+- [20]
+...
+s:replace{32}
+---
+- [32]
+...
+state, value = gen(param, state)
+---
+...
+value
+---
+- [15]
+...
+s:replace{80}
+---
+- [80]
+...
+state, value = gen(param, state)
+---
+...
+value
+---
+- [10]
+...
+state, value = gen(param, state)
+---
+...
+value
+---
+- [0]
+...
+gen(param, state)
+---
+- null
+...
+-- test iterator dummy function, invoked when it's out of bounds
+gen(param, state)
+---
+- null
+...
+s:drop()
+---
+...
+s = box.schema.space.create_ephemeral()
+---
+...
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} })
+---
+...
+s:replace{10} s:replace{20} s:replace{30} s:replace{40} s:replace{50} s:replace{60}
+---
+...
+gen,param,state = i:pairs({20}, {iterator = 'GT'})
+---
+...
+state, value = gen(param, state)
+---
+...
+value
+---
+- [30]
+...
+s:replace{0}
+---
+- [0]
+...
+state, value = gen(param, state)
+---
+...
+value
+---
+- [40]
+...
+s:replace{42}
+---
+- [42]
+...
+state, value = gen(param, state)
+---
+...
+value
+---
+- [42]
+...
+s:replace{80}
+---
+- [80]
+...
+state, value = gen(param, state)
+---
+...
+value
+---
+- [50]
+...
+s:replace{15}
+---
+- [15]
+...
+state, value = gen(param, state)
+---
+...
+value
+---
+- [60]
+...
+state, value = gen(param, state)
+---
+...
+value
+---
+- [80]
+...
+gen(param, state)
+---
+- null
+...
+-- test iterator dummy function, invoked when it's out of bounds
+gen(param, state)
+---
+- null
+...
+s:drop()
+---
+...
+s = box.schema.space.create_ephemeral()
+---
+...
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} })
+---
+...
+s:replace{10} s:replace{20} s:replace{30} s:replace{40} s:replace{50} s:replace{60}
+---
+...
+gen,param,state = i:pairs({50}, {iterator = 'LT'})
+---
+...
+state, value = gen(param, state)
+---
+...
+value
+---
+- [40]
+...
+s:replace{0}
+---
+- [0]
+...
+state, value = gen(param, state)
+---
+...
+value
+---
+- [30]
+...
+s:replace{15}
+---
+- [15]
+...
+state, value = gen(param, state)
+---
+...
+value
+---
+- [20]
+...
+s:replace{42}
+---
+- [42]
+...
+state, value = gen(param, state)
+---
+...
+value
+---
+- [15]
+...
+s:replace{32}
+---
+- [32]
+...
+state, value = gen(param, state)
+---
+...
+value
+---
+- [10]
+...
+s:replace{80}
+---
+- [80]
+...
+state, value = gen(param, state)
+---
+...
+value
+---
+- [0]
+...
+gen(param, state)
+---
+- null
+...
+-- test iterator dummy function, invoked when it's out of bounds
+gen(param, state)
+---
+- null
+...
+s:drop()
+---
+...
+s = box.schema.space.create_ephemeral()
+---
+...
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} })
+---
+...
+s:replace{10} s:replace{20} s:replace{30} s:replace{40} s:replace{50} s:replace{60}
+---
+...
+gen,param,state = i:pairs({28}, {iterator = 'GT'})
+---
+...
+s:replace{0}
+---
+- [0]
+...
+state, value = gen(param, state)
+---
+...
+value
+---
+- [30]
+...
+s:replace{15}
+---
+- [15]
+...
+state, value = gen(param, state)
+---
+...
+value
+---
+- [40]
+...
+s:replace{42}
+---
+- [42]
+...
+state, value = gen(param, state)
+---
+...
+value
+---
+- [42]
+...
+s:replace{32}
+---
+- [32]
+...
+state, value = gen(param, state)
+---
+...
+value
+---
+- [50]
+...
+s:replace{80}
+---
+- [80]
+...
+state, value = gen(param, state)
+---
+...
+value
+---
+- [60]
+...
+state, value = gen(param, state)
+---
+...
+value
+---
+- [80]
+...
+gen(param, state)
+---
+- null
+...
+-- test iterator dummy function, invoked when it's out of bounds
+gen(param, state)
+---
+- null
+...
+s:drop()
+---
+...
+s = box.schema.space.create_ephemeral()
+---
+...
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} })
+---
+...
+s:replace{10} s:replace{20} s:replace{30} s:replace{40} s:replace{50} s:replace{60}
+---
+...
+gen,param,state = i:pairs({42}, {iterator = 'LT'})
+---
+...
+s:replace{0}
+---
+- [0]
+...
+state, value = gen(param, state)
+---
+...
+value
+---
+- [40]
+...
+s:replace{42}
+---
+- [42]
+...
+state, value = gen(param, state)
+---
+...
+value
+---
+- [30]
+...
+s:replace{15}
+---
+- [15]
+...
+state, value = gen(param, state)
+---
+...
+value
+---
+- [20]
+...
+s:replace{32}
+---
+- [32]
+...
+state, value = gen(param, state)
+---
+...
+value
+---
+- [15]
+...
+s:replace{80}
+---
+- [80]
+...
+state, value = gen(param, state)
+---
+...
+value
+---
+- [10]
+...
+state, value = gen(param, state)
+---
+...
+value
+---
+- [0]
+...
+gen(param, state)
+---
+- null
+...
+-- test iterator dummy function, invoked when it's out of bounds
+gen(param, state)
+---
+- null
+...
+s:drop()
+---
+...
+-- Ephemeral space: methods: delete
+-- delete (str)
+s = box.schema.space.create_ephemeral()
+---
+...
+i = s:create_index('a', { type = 'tree', parts = {1, 'string'} })
+---
+...
+for key = 1, 10 do s:replace({tostring(key)}) end
+---
+...
+t = {}
+---
+...
+for key = 1, 10 do table.insert(t, s:get({tostring(key)})) end
+---
+...
+t
+---
+- - ['1']
+  - ['2']
+  - ['3']
+  - ['4']
+  - ['5']
+  - ['6']
+  - ['7']
+  - ['8']
+  - ['9']
+  - ['10']
+...
+for key = 1, 10 do s:delete({tostring(key)}) end
+---
+...
+for key = 1, 10 do assert(s:get({tostring(key)}) == nil) end
+---
+...
+s:delete({tostring(7)})
+---
+...
+s:drop()
+---
+...
+-- delete (num)
+s = box.schema.space.create_ephemeral()
+---
+...
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} })
+---
+...
+for key = 1, 10 do s:replace({key}) end
+---
+...
+t = {}
+---
+...
+for key = 1, 10 do table.insert(t, s:get({key})) end
+---
+...
+t
+---
+- - [1]
+  - [2]
+  - [3]
+  - [4]
+  - [5]
+  - [6]
+  - [7]
+  - [8]
+  - [9]
+  - [10]
+...
+for key = 1, 10 do s:delete({key}) end
+---
+...
+for key = 1, 10 do assert(s:get({key}) == nil) end
+---
+...
+s:delete({7})
+---
+...
+s:drop()
+---
+...
+-- delete multi-part (num, num)
+s = box.schema.space.create_ephemeral()
+---
+...
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned', 2, 'unsigned'} })
+---
+...
+for key = 1, 10 do s:replace({key, key}) end
+---
+...
+t = {}
+---
+...
+for key = 1, 10 do table.insert(t, s:get({key, key})) end
+---
+...
+t
+---
+- - [1, 1]
+  - [2, 2]
+  - [3, 3]
+  - [4, 4]
+  - [5, 5]
+  - [6, 6]
+  - [7, 7]
+  - [8, 8]
+  - [9, 9]
+  - [10, 10]
+...
+for key = 1, 10 do s:delete({key, key}) end
+---
+...
+for key = 1, 10 do assert(s:get({key, key}) == nil) end
+---
+...
+s:delete({7, 7})
+---
+...
+s:drop()
+---
+...
+-- delete (str)
+s = box.schema.space.create_ephemeral()
+---
+...
+i = s:create_index('a', { type = 'tree', parts = {1, 'string'} })
+---
+...
+for key = 1, 10 do s:replace({tostring(key)}) end
+---
+...
+t = {}
+---
+...
+for key = 1, 10 do table.insert(t, s:get({tostring(key)})) end
+---
+...
+t
+---
+- - ['1']
+  - ['2']
+  - ['3']
+  - ['4']
+  - ['5']
+  - ['6']
+  - ['7']
+  - ['8']
+  - ['9']
+  - ['10']
+...
+for key = 1, 10 do s:delete(box.tuple.new{tostring(key)}) end
+---
+...
+for key = 1, 10 do assert(s:get({tostring(key)}) == nil) end
+---
+...
+s:delete(box.tuple.new{tostring(7)})
+---
+...
+s:drop()
+---
+...
+-- Ephemeral space: methods: bsize, len
+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
+...
+s:drop()
+---
+...
+s = box.schema.space.create_ephemeral()
+---
+...
+i = s:create_index('a', { type = 'tree', parts = {1, 'INTEGER'} })
+---
+...
+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']
+...
+i:select()
+---
+- - [1, 'AAAA']
+  - [2, 'AAAA']
+  - [3, 'AAAA']
+  - [4, 'AAAA']
+  - [20, 'AAAA']
+  - [30, 'AAAA']
+  - [40, 'AAAA']
+...
+i:max(15)
+---
+...
+i:min(15)
+---
+...
+i:count(15)
+---
+- 0
+...
+i:max()
+---
+- [40, 'AAAA']
+...
+i:min()
+---
+- [1, 'AAAA']
+...
+i:count()
+---
+- 7
+...
+s:insert({-2, "AAAA"})
+---
+- [-2, 'AAAA']
+...
+s:insert({-3, "AAAA"})
+---
+- [-3, 'AAAA']
+...
+s:insert({-4, "AAAA"})
+---
+- [-4, 'AAAA']
+...
+i:select()
+---
+- - [-4, 'AAAA']
+  - [-3, 'AAAA']
+  - [-2, 'AAAA']
+  - [1, 'AAAA']
+  - [2, 'AAAA']
+  - [3, 'AAAA']
+  - [4, 'AAAA']
+  - [20, 'AAAA']
+  - [30, 'AAAA']
+  - [40, 'AAAA']
+...
+i:max(0)
+---
+...
+i:min(0)
+---
+...
+i:count(0)
+---
+- 0
+...
+i:max()
+---
+- [40, 'AAAA']
+...
+i:min()
+---
+- [-4, 'AAAA']
+...
+i:count()
+---
+- 10
+...
+s:drop()
+---
+...
+-- number type
+s = box.schema.space.create_ephemeral()
+---
+...
+i = s:create_index('a', { type = 'tree', parts = {1, 'number'} })
+---
+...
+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']
+...
+i:select()
+---
+- - [1, 'AAAA']
+  - [2, 'AAAA']
+  - [3, 'AAAA']
+  - [4, 'AAAA']
+  - [20, 'AAAA']
+  - [30, 'AAAA']
+  - [40, 'AAAA']
+...
+i:max(15)
+---
+...
+i:min(15)
+---
+...
+i:count(15)
+---
+- 0
+...
+i:max()
+---
+- [40, 'AAAA']
+...
+i:min()
+---
+- [1, 'AAAA']
+...
+i:count()
+---
+- 7
+...
+s:insert({-2, "AAAA"})
+---
+- [-2, 'AAAA']
+...
+s:insert({-3, "AAAA"})
+---
+- [-3, 'AAAA']
+...
+s:insert({-4, "AAAA"})
+---
+- [-4, 'AAAA']
+...
+i:select()
+---
+- - [-4, 'AAAA']
+  - [-3, 'AAAA']
+  - [-2, 'AAAA']
+  - [1, 'AAAA']
+  - [2, 'AAAA']
+  - [3, 'AAAA']
+  - [4, 'AAAA']
+  - [20, 'AAAA']
+  - [30, 'AAAA']
+  - [40, 'AAAA']
+...
+i:max(0)
+---
+...
+i:min(0)
+---
+...
+i:count(0)
+---
+- 0
+...
+i:max()
+---
+- [40, 'AAAA']
+...
+i:min()
+---
+- [-4, 'AAAA']
+...
+i:count()
+---
+- 10
+...
+s:insert({1.5, "AAAA"})
+---
+- [1.5, 'AAAA']
+...
+s:insert({2.5, "AAAA"})
+---
+- [2.5, 'AAAA']
+...
+s:insert({3.5, "AAAA"})
+---
+- [3.5, 'AAAA']
+...
+s:insert({4.5, "AAAA"})
+---
+- [4.5, 'AAAA']
+...
+i:select()
+---
+- - [-4, 'AAAA']
+  - [-3, 'AAAA']
+  - [-2, 'AAAA']
+  - [1, 'AAAA']
+  - [1.5, 'AAAA']
+  - [2, 'AAAA']
+  - [2.5, 'AAAA']
+  - [3, 'AAAA']
+  - [3.5, 'AAAA']
+  - [4, 'AAAA']
+  - [4.5, 'AAAA']
+  - [20, 'AAAA']
+  - [30, 'AAAA']
+  - [40, 'AAAA']
+...
+i:max(1)
+---
+- [1, 'AAAA']
+...
+i:min(1)
+---
+- [1, 'AAAA']
+...
+i:count(1)
+---
+- 1
+...
+i:max()
+---
+- [40, 'AAAA']
+...
+i:min()
+---
+- [-4, 'AAAA']
+...
+i:count()
+---
+- 14
+...
+s:drop()
+---
+...
+-- str type
+s = box.schema.space.create_ephemeral()
+---
+...
+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']
+...
+i:select()
+---
+- - ['1', 'AAAA']
+  - ['2', 'AAAA']
+  - ['20', 'AAAA']
+  - ['3', 'AAAA']
+  - ['30', 'AAAA']
+  - ['4', 'AAAA']
+  - ['40', 'AAAA']
+...
+i:max('15')
+---
+...
+i:min('15')
+---
+...
+i:count('15')
+---
+- 0
+...
+i:max()
+---
+- ['40', 'AAAA']
+...
+i:min()
+---
+- ['1', 'AAAA']
+...
+i: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()
+---
+...
+-- num type
+s = box.schema.space.create_ephemeral()
+---
+...
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} })
+---
+...
+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']
+...
+i:select()
+---
+- - [1, 'AAAA']
+  - [2, 'AAAA']
+  - [3, 'AAAA']
+  - [4, 'AAAA']
+  - [20, 'AAAA']
+  - [30, 'AAAA']
+  - [40, 'AAAA']
+...
+i:max(15)
+---
+...
+i:min(15)
+---
+...
+i:count(15)
+---
+- 0
+...
+i:max()
+---
+- [40, 'AAAA']
+...
+i:min()
+---
+- [1, 'AAAA']
+...
+i:count()
+---
+- 7
+...
+s:drop()
+---
+...
+-- scalar type
+s = box.schema.space.create_ephemeral()
+---
+...
+i = s:create_index('a', { type = 'tree', parts = {1, 'scalar'} })
+---
+...
+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']
+...
+i:select()
+---
+- - [1, 'AAAA']
+  - [2, 'AAAA']
+  - [3, 'AAAA']
+  - [4, 'AAAA']
+  - [20, 'AAAA']
+  - [30, 'AAAA']
+  - [40, 'AAAA']
+...
+i:max(15)
+---
+...
+i:min(15)
+---
+...
+i:count(15)
+---
+- 0
+...
+i:max()
+---
+- [40, 'AAAA']
+...
+i:min()
+---
+- [1, 'AAAA']
+...
+i:count()
+---
+- 7
+...
+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']
+  - [20, 'AAAA']
+  - [30, 'AAAA']
+  - [40, 'AAAA']
+  - ['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()
+---
+- 11
+...
+s:insert({'20', "AAAA"})
+---
+- ['20', 'AAAA']
+...
+s:insert({'30', "AAAA"})
+---
+- ['30', 'AAAA']
+...
+s:insert({'40', "AAAA"})
+---
+- ['40', 'AAAA']
+...
+i:select()
+---
+- - [1, 'AAAA']
+  - [2, 'AAAA']
+  - [3, 'AAAA']
+  - [4, 'AAAA']
+  - [20, 'AAAA']
+  - [30, 'AAAA']
+  - [40, 'AAAA']
+  - ['1', 'AAAA']
+  - ['2', 'AAAA']
+  - ['20', 'AAAA']
+  - ['3', 'AAAA']
+  - ['30', 'AAAA']
+  - ['4', 'AAAA']
+  - ['40', 'AAAA']
+...
+i:max('15')
+---
+...
+i:min('15')
+---
+...
+i:count('15')
+---
+- 0
+...
+i:max()
+---
+- ['40', 'AAAA']
+...
+i:min()
+---
+- [1, 'AAAA']
+...
+i:count()
+---
+- 14
+...
+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']
+  - [20, 'AAAA']
+  - [30, 'AAAA']
+  - [40, 'AAAA']
+  - ['-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()
+---
+- [1, 'AAAA']
+...
+i:count()
+---
+- 17
+...
+s:insert({-2, "AAAA"})
+---
+- [-2, 'AAAA']
+...
+s:insert({-3, "AAAA"})
+---
+- [-3, 'AAAA']
+...
+s:insert({-4, "AAAA"})
+---
+- [-4, 'AAAA']
+...
+i:select()
+---
+- - [-4, 'AAAA']
+  - [-3, 'AAAA']
+  - [-2, 'AAAA']
+  - [1, 'AAAA']
+  - [2, 'AAAA']
+  - [3, 'AAAA']
+  - [4, 'AAAA']
+  - [20, 'AAAA']
+  - [30, 'AAAA']
+  - [40, 'AAAA']
+  - ['-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()
+---
+- [-4, 'AAAA']
+...
+i:count()
+---
+- 20
+...
+s:drop()
+---
+...
+-- multi filed indices
+-- scalar int
+s = box.schema.space.create_ephemeral()
+---
+...
+i = s:create_index('a', { type = 'tree', parts = {1, 'scalar', 2, 'INTEGER'} })
+---
+...
+s:insert({1, 1})
+---
+- [1, 1]
+...
+s:insert({1, 2})
+---
+- [1, 2]
+...
+s:insert({1, 3})
+---
+- [1, 3]
+...
+s:insert({1, -4})
+---
+- [1, -4]
+...
+i:select()
+---
+- - [1, -4]
+  - [1, 1]
+  - [1, 2]
+  - [1, 3]
+...
+i:max({1})
+---
+- [1, 3]
+...
+i:min({1})
+---
+- [1, -4]
+...
+i:count({1})
+---
+- 4
+...
+i:max({1, 0})
+---
+...
+i:min({1, 1})
+---
+- [1, 1]
+...
+i:count({1})
+---
+- 4
+...
+i:max()
+---
+- [1, 3]
+...
+i:min()
+---
+- [1, -4]
+...
+i:count()
+---
+- 4
+...
+s:insert({2, 1})
+---
+- [2, 1]
+...
+s:insert({2, 2})
+---
+- [2, 2]
+...
+s:insert({2, 3})
+---
+- [2, 3]
+...
+s:insert({2, -4})
+---
+- [2, -4]
+...
+i:select()
+---
+- - [1, -4]
+  - [1, 1]
+  - [1, 2]
+  - [1, 3]
+  - [2, -4]
+  - [2, 1]
+  - [2, 2]
+  - [2, 3]
+...
+i:max({2})
+---
+- [2, 3]
+...
+i:min({2})
+---
+- [2, -4]
+...
+i:count({2})
+---
+- 4
+...
+i:max({2, 0})
+---
+...
+i:min({2, 1})
+---
+- [2, 1]
+...
+i:count({2})
+---
+- 4
+...
+i:max()
+---
+- [2, 3]
+...
+i:min()
+---
+- [1, -4]
+...
+i:count()
+---
+- 8
+...
+s:drop()
+---
+...
+-- scalar str
+s = box.schema.space.create_ephemeral()
+---
+...
+i = s:create_index('a', { type = 'tree', parts = {1, 'scalar', 2, 'string'} })
+---
+...
+s:insert({1, '1'})
+---
+- [1, '1']
+...
+s:insert({1, '2'})
+---
+- [1, '2']
+...
+s:insert({1, '3'})
+---
+- [1, '3']
+...
+s:insert({1, '-4'})
+---
+- [1, '-4']
+...
+i:select()
+---
+- - [1, '-4']
+  - [1, '1']
+  - [1, '2']
+  - [1, '3']
+...
+i:max({1})
+---
+- [1, '3']
+...
+i:min({1})
+---
+- [1, '-4']
+...
+i:count({1})
+---
+- 4
+...
+i:max({1, '0'})
+---
+...
+i:min({1, '1'})
+---
+- [1, '1']
+...
+i:count({1})
+---
+- 4
+...
+i:max()
+---
+- [1, '3']
+...
+i:min()
+---
+- [1, '-4']
+...
+i:count()
+---
+- 4
+...
+s:insert({2, '1'})
+---
+- [2, '1']
+...
+s:insert({2, '2'})
+---
+- [2, '2']
+...
+s:insert({2, '3'})
+---
+- [2, '3']
+...
+s:insert({2, '-4'})
+---
+- [2, '-4']
+...
+i:select()
+---
+- - [1, '-4']
+  - [1, '1']
+  - [1, '2']
+  - [1, '3']
+  - [2, '-4']
+  - [2, '1']
+  - [2, '2']
+  - [2, '3']
+...
+i:max({2})
+---
+- [2, '3']
+...
+i:min({2})
+---
+- [2, '-4']
+...
+i:count({2})
+---
+- 4
+...
+i:max({2, '0'})
+---
+...
+i:min({2, '1'})
+---
+- [2, '1']
+...
+i:count({2})
+---
+- 4
+...
+i:max()
+---
+- [2, '3']
+...
+i:min()
+---
+- [1, '-4']
+...
+i:count()
+---
+- 8
+...
+s:drop()
+---
+...
+-- min max count after many inserts
+string = require('string')
+---
+...
+s = box.schema.space.create_ephemeral()
+---
+...
+i = s:create_index('a', { type = 'tree', parts = {1, 'scalar'} })
+---
+...
+long_string = string.rep('A', 650)
+---
+...
+for i = 1, 1000 do s:insert({i, long_string}) end
+---
+...
+i:max({100})
+---
+- [100, 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA']
+...
+i:max({700})
+---
+- [700, 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA']
+...
+i:min({100})
+---
+- [100, 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA']
+...
+i:min({700})
+---
+- [700, 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA']
+...
+i:count({2})
+---
+- 1
+...
+i:max()
+---
+- [1000, 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA']
+...
+i:min()
+---
+- [1, 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA']
+...
+i:count()
+---
+- 1000
+...
+s:drop()
+---
+...
+s = box.schema.space.create_ephemeral()
+---
+...
+i = s:create_index('a', { type = 'tree', parts = {1, 'scalar', 2, 'INTEGER'} })
+---
+...
+for i = 1, 1000 do s:insert({i % 10, i, long_string}) end
+---
+...
+i:max({1, 100})
+---
+...
+i:max({2, 700})
+---
+...
+i:max({3})
+---
+- [3, 993, 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA']
+...
+i:min({1, 10})
+---
+...
+i:min({1, 700})
+---
+...
+i:min({3})
+---
+- [3, 3, 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA']
+...
+i:count({2})
+---
+- 100
+...
+i:max()
+---
+- [9, 999, 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA']
+...
+i:min()
+---
+- [0, 10, 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA']
+...
+i:count()
+---
+- 1000
+...
+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 211014c..ee03810 100644
--- a/test/box/ephemeral_space.test.lua
+++ b/test/box/ephemeral_space.test.lua
@@ -1,3 +1,4 @@
+test_run = require('test_run').new()
 -- Ephemeral space: creation and dropping.
 
 -- Simple creation.
@@ -168,3 +169,1696 @@ s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {table = false})
 s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = box.NULL})
 s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {dummy = true})
 s:drop()
+
+
+-- Ephemeral space: methods: insert
+s = box.schema.space.create_ephemeral({field_count = 3})
+i = s:create_index('a')
+s:insert{1}
+s:insert{2,2}
+s:insert{3,3,3}
+s:insert{4,4,4,4}
+s:drop()
+
+s = box.schema.space.create_ephemeral()
+i = s:create_index('a', { type = 'tree', parts = {1, 'string'} })
+for key = 1, 10 do s:insert({tostring(key)}) end
+t = {}
+for key = 1, 10 do table.insert(t, s:get({tostring(key)})) end
+t
+s:insert({tostring(7)})
+s:drop()
+
+s = box.schema.space.create_ephemeral()
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} })
+for key = 1, 10 do s:insert({key}) end
+t = {}
+for key = 1, 10 do table.insert(t, s:get({key})) end
+t
+s:insert({7})
+s:drop()
+
+s = box.schema.space.create_ephemeral()
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned', 2, 'unsigned'} })
+for key = 1, 10 do s:insert({key, key}) end
+t = {}
+for key = 1, 10 do table.insert(t, s:get({key, key})) end
+t
+s:insert({7, 7})
+s:drop()
+
+s = box.schema.space.create_ephemeral()
+i = s:create_index('a', { type = 'tree', parts = {1, 'string'} })
+for key = 1, 10 do s:insert({tostring(key)}) end
+t = {}
+for key = 1, 10 do table.insert(t, s:get({tostring(key)})) end
+t
+s:insert(box.tuple.new{tostring(7)})
+s:drop()
+
+s = box.schema.space.create_ephemeral()
+index = s:create_index('a')
+s:insert(1)
+s:insert(1, 2)
+s:insert(1, 2, 3)
+s:insert{1}
+s:insert{2, 3}
+tmp = s:delete(1, 2, 3)
+s:select{}
+s:drop()
+
+s = box.schema.space.create_ephemeral()
+i = s:create_index('a', { type = 'tree', parts = {3, 'unsigned'} })
+s:insert{1}
+s:insert{2, 3}
+s:insert{4, 5, 6}
+s:drop()
+
+s = box.schema.space.create_ephemeral()
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} })
+s:insert{"1"}
+s:drop()
+
+s = box.schema.space.create_ephemeral()
+i = s:create_index('a', { type = 'tree', parts = {1, 'string'} })
+s:insert{1}
+s:drop()
+
+
+-- Ephemeral space: methods: replace
+s = box.schema.space.create_ephemeral({field_count = 3})
+i = s:create_index('a')
+s:replace{1}
+s:replace{2, 2}
+s:replace{3, 3, 3}
+s:replace{4, 4, 4, 4}
+s:drop()
+
+s = box.schema.space.create_ephemeral()
+i = s:create_index('a', { type = 'tree', parts = {1, 'string'} })
+for key = 1, 10 do s:replace({tostring(key)}) end
+t = {}
+for key = 1, 10 do table.insert(t, s:get({tostring(key)})) end
+t
+_ = s:replace({tostring(7)})
+s:drop()
+
+s = box.schema.space.create_ephemeral()
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} })
+for key = 1, 10 do s:replace({key}) end
+t = {}
+for key = 1, 10 do table.insert(t, s:get({key})) end
+t
+_ = s:replace({7})
+s:drop()
+
+s = box.schema.space.create_ephemeral()
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned', 2, 'unsigned'} })
+for key = 1, 10 do s:replace({key, key}) end
+t = {}
+for key = 1, 10 do table.insert(t, s:get({key, key})) end
+t
+s:replace({7, 7})
+s:drop()
+
+s = box.schema.space.create_ephemeral()
+i = s:create_index('a', { type = 'tree', parts = {1, 'string'} })
+for key = 1, 10 do s:replace({tostring(key)}) end
+t = {}
+for key = 1, 10 do table.insert(t, s:get({tostring(key)})) end
+t
+s:replace(box.tuple.new{tostring(7)})
+s:drop()
+
+s = box.schema.space.create_ephemeral()
+index = s:create_index('a')
+s:replace(1)
+s:replace(1, 2)
+s:replace(1, 2, 3)
+s:replace{1}
+s:replace{2, 3}
+tmp = s:delete(1, 2, 3)
+s:select{}
+s:drop()
+
+s = box.schema.space.create_ephemeral()
+i = s:create_index('a', { type = 'tree', parts = {3, 'unsigned'} })
+s:replace{1}
+s:replace{2, 3}
+s:replace{4, 5, 6}
+s:drop()
+
+s = box.schema.space.create_ephemeral()
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} })
+s:replace{"1"}
+s:drop()
+
+s = box.schema.space.create_ephemeral()
+i = s:create_index('a', { type = 'tree', parts = {1, 'string'} })
+s:replace{1}
+s:drop()
+
+
+-- Ephemeral space: methods: upsert
+
+s = box.schema.space.create_ephemeral()
+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 = box.schema.space.create_ephemeral()
+i = s:create_index('a', { type = 'tree', parts = {1, 'string'} })
+for key = 1, 10 do s:upsert({tostring(key), 0}, {{'+', 2, 1}}) end
+t = {}
+for key = 1, 10 do table.insert(t, s:get({tostring(key)})) end
+t
+for key = 1, 10 do s:upsert({tostring(key), 0}, {{'+', 2, 10}}) end
+t = {}
+for key = 1, 10 do table.insert(t, s:get({tostring(key)})) end
+t
+for key = 1, 10 do s:delete({tostring(key)}) end
+for key = 1, 10 do s:upsert({tostring(key), 0}, {{'+', 2, 1}, {'=', 3, key}}) end
+t = {}
+for key = 1, 10 do table.insert(t, s:get({tostring(key)})) end
+t
+s:drop()
+
+s = box.schema.space.create_ephemeral()
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} })
+for key = 1, 10 do s:upsert({key, 0}, {{'+', 2, 1}}) end
+t = {}
+for key = 1, 10 do table.insert(t, s:get({key})) end
+t
+for key = 1, 10 do s:upsert({key, 0}, {{'+', 2, 10}}) end
+t = {}
+for key = 1, 10 do table.insert(t, s:get({key})) end
+t
+for key = 1, 10 do s:delete({key}) end
+for key = 1, 10 do s:upsert({key, 0}, {{'+', 2, 1}, {'=', 3, key}}) end
+t = {}
+for key = 1, 10 do table.insert(t, s:get({key})) end
+t
+s:drop()
+
+s = box.schema.space.create_ephemeral()
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned', 2, 'unsigned'} })
+for key = 1, 10 do s:upsert({key, key, 0}, {{'+', 3, 1}}) end
+t = {}
+for key = 1, 10 do table.insert(t, s:get({key, key})) end
+t
+for key = 1, 10 do s:upsert({key, key, 0}, {{'+', 3, 10}}) end
+t = {}
+for key = 1, 10 do table.insert(t, s:get({key, key})) end
+t
+for key = 1, 10 do s:delete({key, key}) end
+for key = 1, 10 do s:upsert({key, key, 0}, {{'+', 3, 1}, {'=', 4, key}}) end
+t = {}
+for key = 1, 10 do table.insert(t, s:get({key, key})) end
+t
+s:drop()
+
+test_run:cmd("setopt delimiter ';'");
+function less(a, b)
+    if type(a[2]) ~= type(b[2]) then
+        return type(a[2]) < type(b[2])
+    end
+    if a[2] == b[2] then
+        return a[1] < b[1]
+    end
+    if type(a[2]) == 'boolean' then
+        return a[2] == false and b[2] == true
+    end
+    return a[2] < b[2]
+end;
+test_run:cmd("setopt delimiter ''");
+function sort(t) table.sort(t, less) return t end
+
+-- upsert default tuple constraint
+s = box.schema.space.create_ephemeral()
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned', 2, 'unsigned'} })
+s:upsert({0, 'key', 0}, {{'+', 3, 1}})
+s:drop()
+
+-- upsert primary key modify (skipped)
+s = box.schema.space.create_ephemeral()
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} })
+s:upsert({0, 0}, {{'+', 1, 1}, {'+', 2, 1}})
+s:get({0})
+s:drop()
+
+-- upsert with box.tuple.new
+s = box.schema.space.create_ephemeral()
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} })
+s:upsert({0, 0}, {{'+', 1, 1}, {'+', 2, 1}})
+s:get({0})
+s:drop()
+
+s = box.schema.space.create_ephemeral()
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned', 2, 'unsigned'} })
+for key = 1, 10 do s:upsert(box.tuple.new{key, key, 0}, box.tuple.new{{'+', 3, 1}}) end
+t = {}
+for key = 1, 10 do table.insert(t, s:get({key, key})) end
+t
+for key = 1, 10 do s:upsert(box.tuple.new{key, key, 0}, box.tuple.new{{'+', 3, 10}}) end
+t = {}
+for key = 1, 10 do table.insert(t, s:get({key, key})) end
+t
+for key = 1, 10 do s:delete({key, key}) end
+for key = 1, 10 do s:upsert(box.tuple.new{key, key, 0}, box.tuple.new{{'+', 3, 1}, {'=', 4, key}}) end
+t = {}
+for key = 1, 10 do table.insert(t, s:get({key, key})) end
+t
+s:drop()
+
+s = box.schema.space.create_ephemeral()
+i = s:create_index('a')
+s:upsert({0, 0}, {{'+', 2, 2}})
+s:select{0}
+tmp = s:delete{0}
+s:upsert({0, 0, 0}, {{'+', 2, 2}})
+s:select{0}
+tmp = s:delete{0}
+s:upsert({0}, {{'+', 2, 2}})
+s:select{0}
+s:replace{0, 1, 2, 4}
+s:upsert({0, 0, "you will not see it"}, {{'+', 2, 2}})
+s:select{0}
+s:replace{0, -0x4000000000000000ll}
+s:upsert({0}, {{'+', 2, -0x4000000000000001ll}})  -- overflow
+s:select{0}
+s:replace{0, "thing"}
+s:upsert({0, "nothing"}, {{'+', 2, 2}})
+s:select{0}
+tmp = s:delete{0}
+s:upsert({0, "thing"}, {{'+', 2, 2}})
+s:select{0}
+s:replace{0, 1, 2}
+s:upsert({0}, {{'!', 42, 42}})
+s:select{0}
+s:upsert({0}, {{'#', 42, 42}})
+s:select{0}
+s:upsert({0}, {{'=', 42, 42}})
+s:select{}
+s:replace{0, 1.5}
+s:select{}
+s:upsert({0}, {{'|', 1, 255}})
+s:select{0}
+s:replace{0, 1.5}
+s:replace{0, 'something to splice'}
+s:upsert({0}, {{':', 2, 1, 4, 'no'}})
+s:select{0}
+s:upsert({0}, {{':', 2, 1, 2, 'every'}})
+s:select{0}
+s:upsert({0}, {{':', 2, -100, 2, 'every'}})
+s:select{0}
+s:drop()
+
+s = box.schema.space.create_ephemeral({ field_count = 1 })
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} })
+s:insert({1})
+s:select{}
+s:upsert({2, 2}, {{'+', 2, 1}})
+s:select{}
+s:drop()
+
+s = box.schema.space.create_ephemeral({ field_count = 2 })
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} })
+s:insert({1, 1})
+s:select{}
+s:upsert({2, 2, 2}, {{'+', 3, 1}})
+s:upsert({3, 3}, {{'+', 2, 1}})
+s:select{}
+s:drop()
+
+test_run:cmd("setopt delimiter ';'")
+function anything_to_string(tab)
+    if tab == nil then
+        return 'nil'
+    end
+    local str = '['
+    local first_route = true
+    local t = 0
+    for k,f in pairs(tab) do
+        if not first_route then str = str .. ',' end
+        first_route = false
+        t = t + 1
+        if k ~= t then
+            str = str .. k .. '='
+        end
+        if type(f) == 'string' then
+            str = str .. "'" .. f .. "'"
+        elseif type (f) == 'number' then
+            str = str .. tostring(f)
+        elseif type (f) == 'table' or type (f) == 'cdata' then
+            str = str .. anything_to_string(f)
+        else
+            str = str .. '?'
+        end
+    end
+    str = str .. ']'
+    return str
+end;
+
+function things_equal(var1, var2)
+    local type1 = type(var1) == 'cdata' and 'table' or type(var1)
+    local type2 = type(var2) == 'cdata' and 'table' or type(var2)
+    if type1 ~= type2 then
+        return false
+    end
+    if type1 ~= 'table' then
+        return var1 == var2
+    end
+    for k,v in pairs(var1) do
+        if not things_equal(v, var2[k]) then
+            return false
+        end
+    end
+    for k,v in pairs(var2) do
+        if not things_equal(v, var1[k]) then
+            return false
+        end
+    end
+    return true
+end;
+
+function copy_thing(t)
+    if type(t) ~= 'table' then
+        return t
+    end
+    local res = {}
+    for k,v in pairs(t) do
+        res[copy_thing(k)] = copy_thing(v)
+    end
+    return res
+end;
+
+function test(space, key_tuple, ops, expect)
+    space:upsert(key_tuple, ops)
+    if (things_equal(space:select{}, expect)) then
+        return 'upsert('.. anything_to_string(key_tuple) .. ', ' ..
+                anything_to_string(ops) .. ', '  ..
+                ') OK ' .. anything_to_string(space:select{})
+    end
+    return 'upsert('.. anything_to_string(key_tuple) .. ', ' ..
+            anything_to_string(ops) .. ', ' ..
+            ') FAILED, got ' .. anything_to_string(space:select{}) ..
+            ' expected ' .. anything_to_string(expect)
+end;
+test_run:cmd("setopt delimiter ''");
+
+s = box.schema.space.create_ephemeral()
+i = s:create_index('a', { type = 'tree', parts = {1, 'string'} })
+s:upsert({1}, {{'!', 2, 100}}) -- must fail on checking tuple
+s:upsert({'a'}, {{'a', 2, 100}}) -- must fail on checking ops
+s:upsert({'a'}, {{'!', 2, 'ups1'}}) -- 'fast' upsert via insert in one index
+s:upsert({'a', 'b'}, {{'!', 2, 'ups2'}}) -- 'fast' upsert via update in one index
+s:select{}
+s:drop()
+
+s = box.schema.space.create_ephemeral()
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} })
+s:insert({1, 1, 1})
+s:insert({2, 2, 2})
+s:insert({3, 3, 3})
+s:select{}
+s:upsert({2, 18, 76}, {})
+s:upsert({4, 4, 4}, {})
+s:select{}
+s:drop()
+
+s = box.schema.space.create_ephemeral()
+i = s:create_index('a')
+
+t = {1, '1', 1, 'qwerty'}
+s:insert(t)
+
+-- all good operations, one op, equivalent to update
+test(s, t, {{'+', 3, 5}}, {{1, '1', 6, 'qwerty'}})
+test(s, t, {{'-', 3, 3}}, {{1, '1', 3, 'qwerty'}})
+test(s, t, {{'&', 3, 5}}, {{1, '1', 1, 'qwerty'}})
+test(s, t, {{'|', 3, 8}}, {{1, '1', 9, 'qwerty'}})
+test(s, t, {{'^', 3, 12}}, {{1, '1', 5, 'qwerty'}})
+test(s, t, {{':', 4, 2, 4, "uer"}}, {{1, '1', 5, 'query'}})
+test(s, t, {{'!', 4, 'answer'}}, {{1, '1', 5, 'answer', 'query'}})
+test(s, t, {{'#', 5, 1}}, {{1, '1', 5, 'answer'}})
+test(s, t, {{'!', -1, 1}}, {{1, '1', 5, 'answer', 1}})
+test(s, t, {{'!', -1, 2}}, {{1, '1', 5, 'answer', 1, 2}})
+test(s, t, {{'!', -1, 3}}, {{1, '1', 5, 'answer', 1, 2 ,3}})
+test(s, t, {{'#', 5, 100500}}, {{1, '1', 5, 'answer'}})
+test(s, t, {{'=', 4, 'qwerty'}}, {{1, '1', 5, 'qwerty'}})
+
+-- same check for negative posistion
+test(s, t, {{'+', -2, 5}}, {{1, '1', 10, 'qwerty'}})
+test(s, t, {{'-', -2, 3}}, {{1, '1', 7, 'qwerty'}})
+test(s, t, {{'&', -2, 5}}, {{1, '1', 5, 'qwerty'}})
+test(s, t, {{'|', -2, 8}}, {{1, '1', 13, 'qwerty'}})
+test(s, t, {{'^', -2, 12}}, {{1, '1', 1, 'qwerty'}})
+test(s, t, {{':', -1, 2, 4, "uer"}}, {{1, '1', 1, 'query'}})
+test(s, t, {{'!', -2, 'answer'}}, {{1, '1', 1, 'answer', 'query'}})
+test(s, t, {{'#', -1, 1}}, {{1, '1', 1, 'answer'}})
+test(s, t, {{'=', -1, 'answer!'}}, {{1, '1', 1, 'answer!'}})
+
+-- selective test for good multiple ops
+test(s, t, {{'+', 3, 2}, {'!', 4, 42}}, {{1, '1', 3, 42, 'answer!'}})
+test(s, t, {{'!', 1, 666}, {'#', 1, 1}, {'+', 3, 2}}, {{1, '1', 5, 42, 'answer!'}})
+test(s, t, {{'!', 3, 43}, {'+', 4, 2}}, {{1, '1', 43, 7, 42, 'answer!'}})
+test(s, t, {{'#', 3, 2}, {'=', 3, 1}, {'=', 4, '1'}}, {{1, '1', 1, '1'}})
+
+-- all bad operations, one op, equivalent to update but error is supressed
+test(s, t, {{'+', 4, 3}}, {{1, '1', 1, '1'}})
+test(s, t, {{'-', 4, 3}}, {{1, '1', 1, '1'}})
+test(s, t, {{'&', 4, 1}}, {{1, '1', 1, '1'}})
+test(s, t, {{'|', 4, 1}}, {{1, '1', 1, '1'}})
+test(s, t, {{'^', 4, 1}}, {{1, '1', 1, '1'}})
+test(s, t, {{':', 3, 2, 4, "uer"}}, {{1, '1', 1, '1'}})
+test(s, t, {{'!', 18, 'answer'}}, {{1, '1', 1, '1'}})
+test(s, t, {{'#', 18, 1}}, {{1, '1', 1, '1'}})
+test(s, t, {{'=', 18, 'qwerty'}}, {{1, '1', 1, '1'}})
+
+-- selective test for good/bad multiple ops mix
+test(s, t, {{'+', 3, 1}, {'+', 4, 1}}, {{1, '1', 2, '1'}})
+test(s, t, {{'-', 4, 1}, {'-', 3, 1}}, {{1, '1', 1, '1'}})
+test(s, t, {{'#', 18, 1}, {'|', 3, 14}, {'!', 18, '!'}}, {{1, '1', 15, '1'}})
+test(s, t, {{'^', 42, 42}, {':', 1, 1, 1, ''}, {'^', 3, 8}}, {{1, '1', 7, '1'}})
+test(s, t, {{'&', 3, 1}, {'&', 2, 1}, {'&', 4, 1}}, {{1, '1', 1, '1'}})
+
+-- broken ops must raise an exception and discarded
+'dump ' .. anything_to_string(s:select{})
+test(s, t, {{'&', 'a', 3}, {'+', 3, 3}}, {{1, '1', 1, '1'}})
+test(s, t, {{'+', 3, 3}, {'&', 3, 'a'}}, {{1, '1', 1, '1'}})
+test(s, t, {{'+', 3}, {'&', 3, 'a'}}, {{1, '1', 1, '1'}})
+test(s, t, {{':', 3, 3}}, {{1, '1', 1, '1'}})
+test(s, t, {{':', 3, 3, 3}}, {{1, '1', 1, '1'}})
+test(s, t, {{'?', 3, 3}}, {{1, '1', 1, '1'}})
+'dump ' .. anything_to_string(s:select{})
+
+-- -- ignoring ops for insert upsert
+test(s, {2, '2', 2, '2'}, {{}}, {{1, '1', 1, '1'}})
+test(s, {3, '3', 3, '3'}, {{'+', 3, 3}}, {{1, '1', 1, '1'}, {3, '3', 3, '3'}})
+
+t[1] = 1
+test(s, t, {{'+', 3, 3}, {'+', 4, 3}}, {{1, '1', 4, '1'}, {3, '3', 3, '3'}})
+t[1] = 2
+test(s, t, {{'-', 4, 1}}, {{1, '1', 4, '1'}, {2, '1', 1, 'qwerty'}, {3, '3', 3, '3'}})
+t[1] = 3
+test(s, t, {{':', 3, 3, 3, ''}, {'|', 3, 4}}, {{1, '1', 4, '1'}, {2, '1', 1, 'qwerty'}, {3, '3', 7, '3'}})
+
+s:drop()
+
+s = box.schema.space.create_ephemeral()
+i = s:create_index('a')
+s:replace({1, 1, 1})
+box.snapshot()
+s:upsert({1, 1}, {{'+', 2, 2}})
+s:upsert({1, 1}, {{'+', 3, 4}})
+s:select()
+
+s:drop()
+
+s = box.schema.space.create_ephemeral()
+s:upsert({1}, {})
+s:drop()
+
+s = box.schema.space.create_ephemeral()
+i = s:create_index('a', {parts = {1, 'unsigned', 3, 'unsigned'}})
+s:upsert({100, 100, 100}, {{'+', 2, 200}})
+s:upsert({200, 100, 200}, {{'+', 2, 300}})
+s:upsert({300, 100, 300}, {{'+', 2, 400}})
+i:select{}
+s:drop()
+
+s = box.schema.space.create_ephemeral()
+i = s:create_index('a', {parts = {2, 'unsigned', 3, 'unsigned'}})
+s:upsert({100, 100, 100}, {{'+', 1, 200}})
+s:upsert({200, 100, 200}, {{'+', 1, 300}})
+s:upsert({300, 100, 300}, {{'+', 1, 400}})
+i:select{}
+s:drop()
+
+s = box.schema.space.create_ephemeral()
+i = s:create_index('a', {parts = {3, 'unsigned', 2, 'unsigned'}})
+s:upsert({100, 100, 100}, {{'+', 1, 200}})
+s:upsert({200, 100, 200}, {{'+', 1, 300}})
+s:upsert({300, 100, 300}, {{'+', 1, 400}})
+i:select{}
+s:drop()
+
+
+-- Ephemeral index: methods: update
+
+s = box.schema.space.create_ephemeral()
+
+-- test delete field
+i = s:create_index('a')
+s:insert{1000001, 1000002, 1000003, 1000004, 1000005}
+s:update({1000001}, {{'#', 1, 1}})
+s:update({1000001}, {{'#', 1, "only one record please"}})
+i:drop()
+
+-- test arithmetic
+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()
+
+-- test delete multiple fields
+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()
+
+-- test insert field
+i = s:create_index('a')
+s:insert{1, 3, 6, 9}
+s:update({1}, {{'!', 2, 2}})
+s:update({1}, {{'!', 4, 4}, {'!', 4, 5}, {'!', 5, 7}, {'!', 5, 8}})
+s:update({1}, {{'!', 10, 10}, {'!', 10, 11}, {'!', 10, 12}})
+i:drop()
+
+i = s:create_index('a')
+s:insert{1, 'tuple'}
+s:update({1}, {{'#', 2, 1}, {'!', 2, 'inserted tuple'}, {'=', 3, 'set tuple'}})
+i:drop()
+
+i = s:create_index('a')
+s:insert{1, 'tuple'}
+s:update({1}, {{'=', 2, 'set tuple'}, {'!', 2, 'inserted tuple'}, {'#', 3, 1}})
+s:update({1}, {{'!', 1, 3}, {'!', 1, 2}})
+i:drop()
+
+-- test update's assign opearations
+i = s:create_index('a')
+s:replace{1, 'field string value'}
+s:update({1}, {{'=', 2, 'new field string value'}, {'=', 3, 42}, {'=', 4, 0xdeadbeef}})
+
+-- test multiple update opearations on the same field
+s:update({1}, {{'+', 3, 16}, {'&', 4, 0xffff0000}, {'|', 4, 0x0000a0a0}, {'^', 4, 0xffff00aa}})
+
+-- test update splice operation
+s:replace{1953719668, 'something to splice'}
+s:update(1953719668, {{':', 2, 1, 4, 'no'}})
+s:update(1953719668, {{':', 2, 1, 2, 'every'}})
+
+-- check an incorrect offset
+s:update(1953719668, {{':', 2, 100, 2, 'every'}})
+s:update(1953719668, {{':', 2, -100, 2, 'every'}})
+i:drop()
+
+i = s:create_index('a')
+s:insert{1953719668, 'hello', 'october', '20th'}:unpack()
+i:drop()
+
+i = s:create_index('a')
+s:insert{1953719668, 'hello world'}
+s:update(1953719668, {{'=', 2, 'bye, world'}})
+s:delete{1953719668}
+
+s:replace({10, 'abcde'})
+s:update(10,  {{':', 2, 0, 0, '!'}})
+s:update(10,  {{':', 2, 1, 0, '('}})
+s:update(10,  {{':', 2, 2, 0, '({'}})
+s:update(10,  {{':', 2, -1, 0, ')'}})
+s:update(10,  {{':', 2, -2, 0, '})'}})
+
+-- test update delete operations
+s:update({1}, {{'#', 4, 1}, {'#', 3, 1}})
+
+-- test update insert operations
+s:update({1}, {{'!', 2, 1}, {'!', 2, 2}, {'!', 2, 3}, {'!', 2, 4}})
+
+-- s:update: zero field
+s:insert{48}
+s:update(48, {{'=', 0, 'hello'}})
+
+-- s:update: push/pop fields
+s:insert{1684234849}
+s:update({1684234849}, {{'#', 2, 1}})
+s:update({1684234849}, {{'!', -1, 'push1'}})
+s:update({1684234849}, {{'!', -1, 'push2'}})
+s:update({1684234849}, {{'!', -1, 'push3'}})
+s:update({1684234849}, {{'#', 2, 1}, {'!', -1, 'swap1'}})
+s:update({1684234849}, {{'#', 2, 1}, {'!', -1, 'swap2'}})
+s:update({1684234849}, {{'#', 2, 1}, {'!', -1, 'swap3'}})
+s:update({1684234849}, {{'#', -1, 1}, {'!', -1, 'noop1'}})
+s:update({1684234849}, {{'#', -1, 1}, {'!', -1, 'noop2'}})
+s:update({1684234849}, {{'#', -1, 1}, {'!', -1, 'noop3'}})
+
+--
+-- negative indexes
+--
+
+box.tuple.new({1, 2, 3, 4, 5}):update({{'!', 0, 'Test'}})
+box.tuple.new({1, 2, 3, 4, 5}):update({{'!', -1, 'Test'}})
+box.tuple.new({1, 2, 3, 4, 5}):update({{'!', -3, 'Test'}})
+box.tuple.new({1, 2, 3, 4, 5}):update({{'!', -5, 'Test'}})
+box.tuple.new({1, 2, 3, 4, 5}):update({{'!', -6, 'Test'}})
+box.tuple.new({1, 2, 3, 4, 5}):update({{'!', -7, 'Test'}})
+box.tuple.new({1, 2, 3, 4, 5}):update({{'!', -100500, 'Test'}})
+
+box.tuple.new({1, 2, 3, 4, 5}):update({{'=', 0, 'Test'}})
+box.tuple.new({1, 2, 3, 4, 5}):update({{'=', -1, 'Test'}})
+box.tuple.new({1, 2, 3, 4, 5}):update({{'=', -3, 'Test'}})
+box.tuple.new({1, 2, 3, 4, 5}):update({{'=', -5, 'Test'}})
+box.tuple.new({1, 2, 3, 4, 5}):update({{'=', -6, 'Test'}})
+box.tuple.new({1, 2, 3, 4, 5}):update({{'=', -100500, 'Test'}})
+
+box.tuple.new({1, 2, 3, 4, 5}):update({{'+', 0, 100}})
+box.tuple.new({1, 2, 3, 4, 5}):update({{'+', -1, 100}})
+box.tuple.new({1, 2, 3, 4, 5}):update({{'+', -3, 100}})
+box.tuple.new({1, 2, 3, 4, 5}):update({{'+', -5, 100}})
+box.tuple.new({1, 2, 3, 4, 5}):update({{'+', -6, 100}})
+box.tuple.new({1, 2, 3, 4, 5}):update({{'+', -100500, 100}})
+
+box.tuple.new({1, 2, 3, 4, 5}):update({{'|', 0, 100}})
+box.tuple.new({1, 2, 3, 4, 5}):update({{'|', -1, 100}})
+box.tuple.new({1, 2, 3, 4, 5}):update({{'|', -3, 100}})
+box.tuple.new({1, 2, 3, 4, 5}):update({{'|', -5, 100}})
+box.tuple.new({1, 2, 3, 4, 5}):update({{'|', -6, 100}})
+box.tuple.new({1, 2, 3, 4, 5}):update({{'|', -100500, 100}})
+
+box.tuple.new({1, 2, 3, 4, 5}):update({{'#', 0, 1}})
+box.tuple.new({1, 2, 3, 4, 5}):update({{'#', -1, 1}})
+box.tuple.new({1, 2, 3, 4, 5}):update({{'#', -3, 1}})
+box.tuple.new({1, 2, 3, 4, 5}):update({{'#', -5, 1}})
+box.tuple.new({1, 2, 3, 4, 5}):update({{'#', -6, 1}})
+box.tuple.new({1, 2, 3, 4, 5}):update({{'#', -100500, 1}})
+s:drop()
+
+s = box.schema.space.create_ephemeral()
+i = s:create_index('a')
+s:insert{1, 2, 3}
+s:update({1})
+s:update({1}, {'=', 1, 1})
+s:drop()
+
+ffi = require('ffi')
+s = box.schema.space.create_ephemeral()
+i = s:create_index('a')
+
+s:insert{0, -1}
+-- + --
+s:update({0}, {{'+', 2, "a"}}) -- err
+s:update({0}, {{'+', 2, 10}}) -- neg(ative) + pos(itive) = pos(itive) 9
+s:update({0}, {{'+', 2, 5}}) -- pos + pos = pos 14
+s:update({0}, {{'+', 2, -4}}) -- pos + neg = pos 10
+s:update({0}, {{'+', 2, -22}}) -- pos + neg = neg -12
+s:update({0}, {{'+', 2, -3}}) -- neg + neg = neg -15
+s:update({0}, {{'+', 2, 7}}) -- neg + pos = neg -8
+-- - --
+s:update({0}, {{'-', 2, "a"}}) -- err
+s:update({0}, {{'-', 2, 16}}) -- neg(ative) - pos(itive) = neg(ative) -24
+s:update({0}, {{'-', 2, -4}}) -- neg - neg = neg 20
+s:update({0}, {{'-', 2, -32}}) -- neg - neg = pos 12
+s:update({0}, {{'-', 2, 3}}) -- pos - pos = pos 9
+s:update({0}, {{'-', 2, -5}}) -- pos - neg = pos 14
+s:update({0}, {{'-', 2, 17}}) -- pos - pos = neg -3
+-- bit --
+s:replace{0, 0} -- 0
+s:update({0}, {{'|', 2, 24}}) -- 24
+s:update({0}, {{'|', 2, 2}}) -- 26
+s:update({0}, {{'&', 2, 50}}) -- 18
+s:update({0}, {{'^', 2, 6}}) -- 20
+s:update({0}, {{'|', 2, -1}}) -- err
+s:update({0}, {{'&', 2, -1}}) -- err
+s:update({0}, {{'^', 2, -1}}) -- err
+s:replace{0, -1} -- -1
+s:update({0}, {{'|', 2, 2}}) -- err
+s:update({0}, {{'&', 2, 40}}) -- err
+s:update({0}, {{'^', 2, 6}}) -- err
+s:replace{0, 1.5} -- 1.5
+s:update({0}, {{'|', 2, 2}}) -- err
+s:update({0}, {{'&', 2, 40}}) -- err
+s:update({0}, {{'^', 2, 6}}) -- err
+-- double
+s:replace{0, 5} -- 5
+s:update({0}, {{'+', 2, 1.5}}) -- int + double = double 6.5
+s:update({0}, {{'|', 2, 2}}) -- err (double!)
+s:update({0}, {{'-', 2, 0.5}}) -- double - double = double 6
+s:update({0}, {{'+', 2, 1.5}}) -- double + double = double 7.5
+-- float
+s:replace{0, ffi.new("float", 1.5)} -- 1.5
+s:update({0}, {{'+', 2, 2}}) -- float + int = float 3.5
+s:update({0}, {{'+', 2, ffi.new("float", 3.5)}}) -- float + int = float 7
+s:update({0}, {{'|', 2, 2}}) -- err (float!)
+s:update({0}, {{'-', 2, ffi.new("float", 1.5)}}) -- float - float = float 5.5
+s:update({0}, {{'+', 2, ffi.new("float", 3.5)}}) -- float + float = float 9
+s:update({0}, {{'-', 2, ffi.new("float", 9)}}) -- float + float = float 0
+s:update({0}, {{'+', 2, ffi.new("float", 1.2)}}) -- float + float = float 1.2
+-- overflow --
+s:replace{0, 0xfffffffffffffffeull}
+s:update({0}, {{'+', 2, 1}}) -- ok
+s:update({0}, {{'+', 2, 1}}) -- overflow
+s:update({0}, {{'+', 2, 100500}}) -- overflow
+s:replace{0, 1}
+s:update({0}, {{'+', 2, 0xffffffffffffffffull}})  -- overflow
+s:replace{0, -1}
+s:update({0}, {{'+', 2, 0xffffffffffffffffull}})  -- ok
+s:replace{0, 0}
+s:update({0}, {{'-', 2, 0x7fffffffffffffffull}})  -- ok
+s:replace{0, -1}
+s:update({0}, {{'-', 2, 0x7fffffffffffffffull}})  -- ok
+s:replace{0, -2}
+s:update({0}, {{'-', 2, 0x7fffffffffffffffull}})  -- overflow
+s:replace{0, 1}
+s:update({0}, {{'-', 2, 0xffffffffffffffffull}})  -- overflow
+s:replace{0, 0xffffffffffffffefull}
+s:update({0}, {{'-', 2, -16}})  -- ok
+s:update({0}, {{'-', 2, -16}})  -- overflow
+s:replace{0, -0x4000000000000000ll}
+s:update({0}, {{'+', 2, -0x4000000000000000ll}})  -- ok
+s:replace{0, -0x4000000000000000ll}
+s:update({0}, {{'+', 2, -0x4000000000000001ll}})  -- overflow
+-- some wrong updates --
+s:update({0}, 0)
+s:update({0}, {'+', 2, 2})
+s:update({0}, {{}})
+s:update({0}, {{'+'}})
+s:update({0}, {{'+', 0}})
+s:update({0}, {{'+', '+', '+'}})
+s:update({0}, {{0, 0, 0}})
+
+ops = {}
+for i = 1,10 do table.insert(ops, {'=', 2, '1234567890'}) end
+s:upsert({0}, ops)
+
+s:get{0}
+s:update({0}, {})
+
+map = setmetatable({}, { __serialize = 'map' })
+t = box.tuple.new({1, 2, 3})
+s:replace({1, 2, 3})
+
+t:update({{'=', 3, map}})
+s:update(1, {{'=', 3, map}})
+
+s:drop()
+
+-- Ephemeral index: methods: pairs, count, select (iterators)
+
+-- iterator (str)
+s = box.schema.space.create_ephemeral()
+i = s:create_index('a', { type = 'tree', parts = {1, 'string'} })
+for key = 1, 100 do s:replace({tostring(key)}) end
+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(tostring(44), {iterator = 'GE'}) do table.insert(t, v) end
+t
+t = {} for state, v in i:pairs(tostring(44), {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(tostring(77), {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(tostring(77), {iterator = 'LT'}) do table.insert(t, v) end
+t
+s:drop()
+
+-- iterator (num)
+s = box.schema.space.create_ephemeral()
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} })
+for key = 1, 100 do s:replace({key}) end
+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(44, {iterator = 'GE'}) do table.insert(t, v) end
+t
+t = {} for state, v in i:pairs(44, {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(77, {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(77, {iterator = 'LT'}) do table.insert(t, v) end
+t
+s:drop()
+
+-- iterator multi-part (num, num)
+s = box.schema.space.create_ephemeral()
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned', 2, 'unsigned'} })
+for key = 1, 100 do s:replace({key, key}) end
+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({44, 44}, {iterator = 'GE'}) do table.insert(t, v) end
+t
+t = {} for state, v in i:pairs({44, 44}, {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({77, 77}, {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({77, 77}, {iterator = 'LT'}) do table.insert(t, v) end
+t
+s:drop()
+
+-- iterator with tuple.new
+s = box.schema.space.create_ephemeral()
+i = s:create_index('a', { type = 'tree', parts = {1, 'string'} })
+for key = 1, 100 do s:replace({tostring(key)}) end
+t = {} for state, v in i:pairs(box.tuple.new{}, {iterator = 'ALL'}) do table.insert(t, v) end
+t
+t = {} for state, v in i:pairs(box.tuple.new{}, {iterator = 'GE'}) do table.insert(t, v) end
+t
+t = {} for state, v in i:pairs(box.tuple.new(tostring(44)), {iterator = 'GE'}) do table.insert(t, v) end
+t
+t = {} for state, v in i:pairs(box.tuple.new(tostring(44)), {iterator = 'GT'}) do table.insert(t, v) end
+t
+t = {} for state, v in i:pairs(box.tuple.new{}, {iterator = 'LE'}) do table.insert(t, v) end
+t
+t = {} for state, v in i:pairs(box.tuple.new(tostring(77)), {iterator = 'LE'}) do table.insert(t, v) end
+t
+t = {} for state, v in i:pairs(box.tuple.new{}, {iterator = 'LT'}) do table.insert(t, v) end
+t
+t = {} for state, v in i:pairs(box.tuple.new(tostring(77)), {iterator = 'LT'}) do table.insert(t, v) end
+t
+s:drop()
+
+s = box.schema.space.create_ephemeral()
+i = s:create_index('a', { type = 'tree', parts = {1, 'string'} })
+i:pairs({}, {iterator = 666 })
+
+s = box.schema.space.create_ephemeral()
+i = s:create_index('a')
+s:replace({1})
+s:replace({2})
+s:replace({3})
+s:replace({4})
+s:pairs(2, { iterator = 'GE' }):totable()
+s:drop()
+
+s = box.schema.space.create_ephemeral()
+i = s:create_index('a')
+s:auto_increment{1}
+s:auto_increment{2}
+s:auto_increment{3}
+s:auto_increment{4}
+s:auto_increment{5}
+
+s:pairs(3, 'GE'):totable()
+i:pairs(3, 'GE'):totable()
+s:pairs(3, {iterator = 'GE' }):totable()
+i:pairs(3, {iterator = 'GE' }):totable()
+
+s:pairs(3, 'EQ'):totable()
+i:pairs(3, 'EQ'):totable()
+s:pairs(3, {iterator = 'EQ' }):totable()
+i:pairs(3, {iterator = 'EQ' }):totable()
+
+s:pairs(3, 'GT'):totable()
+i:pairs(3, 'GT'):totable()
+s:pairs(3, {iterator = 'GT' }):totable()
+i:pairs(3, {iterator = 'GT' }):totable()
+
+i:select({3}, 'LE')
+s:select({3}, 'LE')
+
+i:count({3}, 'GT')
+s:count({3}, 'GT')
+
+s:drop()
+
+-- implement lazy iterator positioning
+s = box.schema.space.create_ephemeral()
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned', 2, 'unsigned'} })
+for i = 1,3 do for j = 1,3 do s:replace{i, j} end end
+
+itr1,itr2,itr3 = s:pairs{2}
+_ = s:replace{1, 4}
+r = {}
+for k,v in itr1,itr2,itr3 do table.insert(r, v) end
+r
+
+itr1,itr2,itr3 = s:pairs({2}, {iterator = 'GE'})
+_ = s:replace{1, 5}
+r = {}
+for k,v in itr1,itr2,itr3 do table.insert(r, v) end
+r
+
+itr1,itr2,itr3 = s:pairs({2}, {iterator = 'REQ'})
+s:replace{2, 4}
+r = {}
+for k,v in itr1,itr2,itr3 do table.insert(r, v) end
+r
+
+r = nil
+s:drop()
+
+-- make tree iterators stable
+s = box.schema.space.create_ephemeral()
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} })
+
+for i = 1,10 do s:replace{i} end
+r = {}
+for k,v in s:pairs{} do table.insert(r, v[1]) s:delete(v[1]) end
+r
+s:select{}
+
+for i = 1,10 do s:replace{i} end
+r = {}
+for k,v in s:pairs({}, {iterator = 'REQ'}) do table.insert(r, v[1]) s:delete(v[1]) end
+r
+s:select{}
+
+s:drop()
+
+s = box.schema.space.create_ephemeral()
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned', 2, 'unsigned'} })
+
+for i = 1,3 do for j = 1,3 do s:replace{i, j} end end
+r = {}
+for k,v in s:pairs{2} do table.insert(r, v) s:delete{v[1], v[2]} end
+r
+s:select{}
+
+for i = 1,3 do for j = 1,3 do s:replace{i, j} end end
+r = {}
+for k,v in s:pairs({3}, {iterator = 'REQ'}) do table.insert(r, v) s:delete{v[1], v[2]} end
+r
+s:select{}
+
+r = nil
+s:drop()
+
+s = box.schema.space.create_ephemeral()
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} })
+s:replace{10} s:replace{20} s:replace{30} s:replace{40} s:replace{50} s:replace{60}
+
+gen,param,state = i:pairs({25})
+s:replace{25}
+state, value = gen(param,state)
+value
+state, value = gen(param,state)
+value
+
+gen,param,state = i:pairs({35})
+state, value = gen(param,state)
+value
+s:replace{35}
+state, value = gen(param,state)
+value
+
+s:drop()
+
+s = box.schema.space.create_ephemeral()
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} })
+s:replace{10} s:replace{20} s:replace{30} s:replace{40} s:replace{50} s:replace{60}
+
+gen,param,state = i:pairs({30}, {iterator = 'GE'})
+state, value = gen(param, state)
+value
+s:replace{0}
+state, value = gen(param, state)
+value
+s:replace{42}
+state, value = gen(param, state)
+value
+s:replace{80}
+state, value = gen(param, state)
+value
+s:replace{15}
+state, value = gen(param, state)
+value
+state, value = gen(param, state)
+value
+state, value = gen(param, state)
+state
+value
+
+s:drop()
+
+s = box.schema.space.create_ephemeral()
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} })
+s:replace{10} s:replace{20} s:replace{30} s:replace{40} s:replace{50} s:replace{60}
+
+gen,param,state = i:pairs({40}, {iterator = 'LE'})
+state, value = gen(param, state)
+value
+s:replace{0}
+state, value = gen(param, state)
+value
+s:replace{15}
+state, value = gen(param, state)
+value
+s:replace{42}
+state, value = gen(param, state)
+value
+s:replace{32}
+state, value = gen(param, state)
+value
+s:replace{80}
+state, value = gen(param, state)
+value
+state, value = gen(param, state)
+state
+value
+
+s:drop()
+
+s = box.schema.space.create_ephemeral()
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} })
+s:replace{10} s:replace{20} s:replace{30} s:replace{40} s:replace{50} s:replace{60}
+
+gen,param,state = i:pairs({28}, {iterator = 'GE'})
+s:replace{0}
+state, value = gen(param, state)
+value
+s:replace{15}
+state, value = gen(param, state)
+value
+s:replace{42}
+state, value = gen(param, state)
+value
+s:replace{32}
+state, value = gen(param, state)
+value
+s:replace{80}
+state, value = gen(param, state)
+value
+state, value = gen(param, state)
+value
+gen(param, state)
+-- test iterator dummy function, invoked when it's out of bounds
+gen(param, state)
+
+s:drop()
+
+s = box.schema.space.create_ephemeral()
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} })
+s:replace{10} s:replace{20} s:replace{30} s:replace{40} s:replace{50} s:replace{60}
+
+gen,param,state = i:pairs({42}, {iterator = 'LE'})
+s:replace{0}
+state, value = gen(param, state)
+value
+s:replace{42}
+state, value = gen(param, state)
+value
+s:replace{15}
+state, value = gen(param, state)
+value
+s:replace{32}
+state, value = gen(param, state)
+value
+s:replace{80}
+state, value = gen(param, state)
+value
+state, value = gen(param, state)
+value
+gen(param, state)
+-- test iterator dummy function, invoked when it's out of bounds
+gen(param, state)
+
+s:drop()
+
+s = box.schema.space.create_ephemeral()
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} })
+s:replace{10} s:replace{20} s:replace{30} s:replace{40} s:replace{50} s:replace{60}
+
+gen,param,state = i:pairs({20}, {iterator = 'GT'})
+state, value = gen(param, state)
+value
+s:replace{0}
+state, value = gen(param, state)
+value
+s:replace{42}
+state, value = gen(param, state)
+value
+s:replace{80}
+state, value = gen(param, state)
+value
+s:replace{15}
+state, value = gen(param, state)
+value
+state, value = gen(param, state)
+value
+gen(param, state)
+-- test iterator dummy function, invoked when it's out of bounds
+gen(param, state)
+
+s:drop()
+
+s = box.schema.space.create_ephemeral()
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} })
+s:replace{10} s:replace{20} s:replace{30} s:replace{40} s:replace{50} s:replace{60}
+
+gen,param,state = i:pairs({50}, {iterator = 'LT'})
+state, value = gen(param, state)
+value
+s:replace{0}
+state, value = gen(param, state)
+value
+s:replace{15}
+state, value = gen(param, state)
+value
+s:replace{42}
+state, value = gen(param, state)
+value
+s:replace{32}
+state, value = gen(param, state)
+value
+s:replace{80}
+state, value = gen(param, state)
+value
+gen(param, state)
+-- test iterator dummy function, invoked when it's out of bounds
+gen(param, state)
+
+s:drop()
+
+s = box.schema.space.create_ephemeral()
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} })
+s:replace{10} s:replace{20} s:replace{30} s:replace{40} s:replace{50} s:replace{60}
+
+gen,param,state = i:pairs({28}, {iterator = 'GT'})
+s:replace{0}
+state, value = gen(param, state)
+value
+s:replace{15}
+state, value = gen(param, state)
+value
+s:replace{42}
+state, value = gen(param, state)
+value
+s:replace{32}
+state, value = gen(param, state)
+value
+s:replace{80}
+state, value = gen(param, state)
+value
+state, value = gen(param, state)
+value
+gen(param, state)
+-- test iterator dummy function, invoked when it's out of bounds
+gen(param, state)
+
+s:drop()
+
+s = box.schema.space.create_ephemeral()
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} })
+s:replace{10} s:replace{20} s:replace{30} s:replace{40} s:replace{50} s:replace{60}
+
+gen,param,state = i:pairs({42}, {iterator = 'LT'})
+s:replace{0}
+state, value = gen(param, state)
+value
+s:replace{42}
+state, value = gen(param, state)
+value
+s:replace{15}
+state, value = gen(param, state)
+value
+s:replace{32}
+state, value = gen(param, state)
+value
+s:replace{80}
+state, value = gen(param, state)
+value
+state, value = gen(param, state)
+value
+gen(param, state)
+-- test iterator dummy function, invoked when it's out of bounds
+gen(param, state)
+
+s:drop()
+
+
+-- Ephemeral space: methods: delete
+
+-- delete (str)
+s = box.schema.space.create_ephemeral()
+i = s:create_index('a', { type = 'tree', parts = {1, 'string'} })
+for key = 1, 10 do s:replace({tostring(key)}) end
+t = {}
+for key = 1, 10 do table.insert(t, s:get({tostring(key)})) end
+t
+for key = 1, 10 do s:delete({tostring(key)}) end
+for key = 1, 10 do assert(s:get({tostring(key)}) == nil) end
+
+s:delete({tostring(7)})
+s:drop()
+
+-- delete (num)
+s = box.schema.space.create_ephemeral()
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} })
+for key = 1, 10 do s:replace({key}) end
+t = {}
+for key = 1, 10 do table.insert(t, s:get({key})) end
+t
+for key = 1, 10 do s:delete({key}) end
+for key = 1, 10 do assert(s:get({key}) == nil) end
+s:delete({7})
+s:drop()
+
+-- delete multi-part (num, num)
+s = box.schema.space.create_ephemeral()
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned', 2, 'unsigned'} })
+for key = 1, 10 do s:replace({key, key}) end
+t = {}
+for key = 1, 10 do table.insert(t, s:get({key, key})) end
+t
+for key = 1, 10 do s:delete({key, key}) end
+for key = 1, 10 do assert(s:get({key, key}) == nil) end
+s:delete({7, 7})
+s:drop()
+
+-- delete (str)
+s = box.schema.space.create_ephemeral()
+i = s:create_index('a', { type = 'tree', parts = {1, 'string'} })
+for key = 1, 10 do s:replace({tostring(key)}) end
+t = {}
+for key = 1, 10 do table.insert(t, s:get({tostring(key)})) end
+t
+for key = 1, 10 do s:delete(box.tuple.new{tostring(key)}) end
+for key = 1, 10 do assert(s:get({tostring(key)}) == nil) end
+
+s:delete(box.tuple.new{tostring(7)})
+s:drop()
+
+-- Ephemeral space: methods: bsize, len
+
+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)
+
+s:drop()
+
+s = box.schema.space.create_ephemeral()
+i = s:create_index('a', { type = 'tree', parts = {1, 'INTEGER'} })
+
+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"})
+
+i:select()
+i:max(15)
+i:min(15)
+i:count(15)
+i:max()
+i:min()
+i: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()
+
+-- number type
+
+s = box.schema.space.create_ephemeral()
+i = s:create_index('a', { type = 'tree', parts = {1, 'number'} })
+
+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"})
+
+i:select()
+i:max(15)
+i:min(15)
+i:count(15)
+i:max()
+i:min()
+i: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:insert({1.5, "AAAA"})
+s:insert({2.5, "AAAA"})
+s:insert({3.5, "AAAA"})
+s:insert({4.5, "AAAA"})
+
+i:select()
+i:max(1)
+i:min(1)
+i:count(1)
+i:max()
+i:min()
+i:count()
+
+s:drop()
+
+-- str type
+
+s = box.schema.space.create_ephemeral()
+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"})
+
+i:select()
+i:max('15')
+i:min('15')
+i:count('15')
+i:max()
+i:min()
+i: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()
+
+-- num type
+
+s = box.schema.space.create_ephemeral()
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} })
+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"})
+
+i:select()
+i:max(15)
+i:min(15)
+i:count(15)
+i:max()
+i:min()
+i:count()
+
+s:drop()
+
+-- scalar type
+
+s = box.schema.space.create_ephemeral()
+i = s:create_index('a', { type = 'tree', parts = {1, 'scalar'} })
+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"})
+
+i:select()
+i:max(15)
+i:min(15)
+i:count(15)
+i:max()
+i:min()
+i:count()
+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"})
+
+i:select()
+i:max('15')
+i:min('15')
+i:count('15')
+i:max()
+i:min()
+i: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: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()
+
+-- multi filed indices
+
+-- scalar int
+s = box.schema.space.create_ephemeral()
+i = s:create_index('a', { type = 'tree', parts = {1, 'scalar', 2, 'INTEGER'} })
+
+s:insert({1, 1})
+s:insert({1, 2})
+s:insert({1, 3})
+s:insert({1, -4})
+
+i:select()
+i:max({1})
+i:min({1})
+i:count({1})
+i:max({1, 0})
+i:min({1, 1})
+i:count({1})
+i:max()
+i:min()
+i:count()
+
+s:insert({2, 1})
+s:insert({2, 2})
+s:insert({2, 3})
+s:insert({2, -4})
+
+i:select()
+i:max({2})
+i:min({2})
+i:count({2})
+i:max({2, 0})
+i:min({2, 1})
+i:count({2})
+i:max()
+i:min()
+i:count()
+
+s:drop()
+
+-- scalar str
+s = box.schema.space.create_ephemeral()
+i = s:create_index('a', { type = 'tree', parts = {1, 'scalar', 2, 'string'} })
+
+s:insert({1, '1'})
+s:insert({1, '2'})
+s:insert({1, '3'})
+s:insert({1, '-4'})
+
+i:select()
+i:max({1})
+i:min({1})
+i:count({1})
+i:max({1, '0'})
+i:min({1, '1'})
+i:count({1})
+i:max()
+i:min()
+i:count()
+
+s:insert({2, '1'})
+s:insert({2, '2'})
+s:insert({2, '3'})
+s:insert({2, '-4'})
+
+i:select()
+i:max({2})
+i:min({2})
+i:count({2})
+i:max({2, '0'})
+i:min({2, '1'})
+i:count({2})
+i:max()
+i:min()
+i:count()
+
+s:drop()
+
+-- min max count after many inserts
+
+string = require('string')
+
+s = box.schema.space.create_ephemeral()
+i = s:create_index('a', { type = 'tree', parts = {1, 'scalar'} })
+
+long_string = string.rep('A', 650)
+for i = 1, 1000 do s:insert({i, long_string}) end
+
+i:max({100})
+i:max({700})
+i:min({100})
+i:min({700})
+i:count({2})
+i:max()
+i:min()
+i:count()
+
+s:drop()
+
+s = box.schema.space.create_ephemeral()
+i = s:create_index('a', { type = 'tree', parts = {1, 'scalar', 2, 'INTEGER'} })
+
+for i = 1, 1000 do s:insert({i % 10, i, long_string}) end
+
+i:max({1, 100})
+i:max({2, 700})
+i:max({3})
+i:min({1, 10})
+i:min({1, 700})
+i:min({3})
+i:count({2})
+i:max()
+i:min()
+i:count()
+
+s:drop()
+
+test_run:cmd("restart server default")
-- 
2.7.4





More information about the Tarantool-patches mailing list