diff --git a/cmake/rpm.cmake b/cmake/rpm.cmake
index 75247c85aa7bac7519dfcf64feec6622ed32308e..45e53f85c3cff7444bf25dea17f1f206c036fb7c 100644
--- a/cmake/rpm.cmake
+++ b/cmake/rpm.cmake
@@ -14,9 +14,9 @@ if (RPMBUILD)
         OUTPUT_VARIABLE RELEASE
         OUTPUT_STRIP_TRAILING_WHITESPACE)
 
-    set (SCL_VERSION "1.0"  CACHE STRING "" FORCE)
-    set (SCL_RELEASE "1"    CACHE STRING "" FORCE)
-    set (SCL_TARANTOOL "16" CACHE STRING "" FORCE)
+    set (SCL_VERSION "1.0"         CACHE STRING "" FORCE)
+    set (SCL_RELEASE "1"           CACHE STRING "" FORCE)
+    set (SCL_TARANTOOL "mailru-16" CACHE STRING "" FORCE)
 
     set (RPM_PACKAGE_VERSION ${VERSION} CACHE STRING "" FORCE)
     set (RPM_PACKAGE_RELEASE ${RELEASE} CACHE STRING "" FORCE)
diff --git a/extra/rpm/tarantool-scl.rpm.spec b/extra/rpm/tarantool-scl.rpm.spec
index 5b71713268e0a39d04b8cb0f5359d9d4415615ae..4c08ae6215ebed170c0a23f01277b134638a9668 100644
--- a/extra/rpm/tarantool-scl.rpm.spec
+++ b/extra/rpm/tarantool-scl.rpm.spec
@@ -1,4 +1,4 @@
-%global scl 16
+%global scl mailru-16
 
 %define _source_filedigest_algorithm 0
 %define _binary_filedigest_algorithm 0
@@ -6,8 +6,6 @@
 %global _scl_prefix /opt/tarantool
 %scl_package
 
-%global scl_name tarantool-%scl
-
 BuildRequires: scl-utils-build
 BuildRequires: iso-codes
 
diff --git a/extra/rpm/tarantool.rpm.spec.in b/extra/rpm/tarantool.rpm.spec.in
index 171ab495307dc976dc13c17204e4b9d06bc3f952..c7dc52640d1b37615eae9ed193d86b62b21d364b 100644
--- a/extra/rpm/tarantool.rpm.spec.in
+++ b/extra/rpm/tarantool.rpm.spec.in
@@ -1,8 +1,9 @@
 ####################################################
 ################# MACROS AND DEFAULTS ##############
 ####################################################
-%{?scl:%{?scl_package:%scl_package tarantool}}
-%global scl_runtime tarantool-15-runtime
+
+%{?scl:%global _scl_prefix /opt/tarantool}
+%{?scl:%scl_package tarantool}
 
 %define _source_filedigest_algorithm 0
 %define _binary_filedigest_algorithm 0
diff --git a/src/box/alter.cc b/src/box/alter.cc
index 784e6c8948b5df0f3abdd052b68ba8f6566867b7..6899fee6b92f53c7c14bfc88f6302fe13aba1a8c 100644
--- a/src/box/alter.cc
+++ b/src/box/alter.cc
@@ -307,8 +307,7 @@ alter_space_commit(struct trigger *trigger, void * /* event */)
 			space_swap_index(alter->old_space,
 					 alter->new_space,
 					 index_id(old_index),
-					 index_id(new_index),
-					 false);
+					 index_id(new_index));
 		}
 	}
 	/*
@@ -626,7 +625,8 @@ ModifyIndex::commit(struct alter_space *alter)
 {
 	/* Move the old index to the new place but preserve */
 	space_swap_index(alter->old_space, alter->new_space,
-			 old_key_def->iid, new_key_def->iid, true);
+			 old_key_def->iid, new_key_def->iid);
+	key_def_copy(old_key_def, new_key_def);
 }
 
 ModifyIndex::~ModifyIndex()
diff --git a/src/box/key_def.h b/src/box/key_def.h
index bdbd9d87a4a4b0051cbbfafdb574c9d30bc1e7cc..96028f7a5bd91451121899f1786ebd6706e6049a 100644
--- a/src/box/key_def.h
+++ b/src/box/key_def.h
@@ -137,6 +137,23 @@ key_def_dup(struct key_def *def)
 	return dup;
 }
 
+/**
+ * Copy one key def into another, preserving the membership
+ * in rlist. This only works for key defs with equal number of
+ * parts.
+ */
+static inline void
+key_def_copy(struct key_def *to, const struct key_def *from)
+{
+	struct rlist save_link = to->link;
+	int part_count = (to->part_count < from->part_count ?
+			  to->part_count : from->part_count);
+	size_t size  = (sizeof(struct key_def) +
+			sizeof(struct key_part) * part_count);
+	memcpy(to, from, size);
+	to->link = save_link;
+}
+
 /**
  * Set a single key part in a key def.
  * @pre part_no < part_count
diff --git a/src/box/lua/call.cc b/src/box/lua/call.cc
index f0f6b38e9593d0b8814a30d740dbd516ce3e816f..0ed7344ca48cfbd31dfc91edd7d3d1650c79fc7e 100644
--- a/src/box/lua/call.cc
+++ b/src/box/lua/call.cc
@@ -361,6 +361,7 @@ lbox_update(lua_State *L)
 	struct request *request = lbox_request_create(L, IPROTO_UPDATE,
 						      3, 4);
 	/* Ignore index_id for now */
+	request->field_base = 1; /* field ids are one-indexed */
 	box_process(port_lua_create(L), request);
 	return lua_gettop(L) - 4;
 }
diff --git a/src/box/lua/index.cc b/src/box/lua/index.cc
index 48088402c26fdd0fffc0eea9c8269f2fa2461b36..44341362e890f0b77e4540e009ae7191381febc0 100644
--- a/src/box/lua/index.cc
+++ b/src/box/lua/index.cc
@@ -39,91 +39,32 @@
 /** {{{ box.index Lua library: access to spaces and indexes
  */
 
-static const char *indexlib_name = "box.index";
-
-/* Index userdata. */
-struct lbox_index
-{
-	Index *index;
-	/* space id. */
-	uint32_t id;
-	/* index id. */
-	uint32_t iid;
-	/* space cache version at the time of push. */
-	int sc_version;
-};
-
-static Index *
-lua_checkindex(struct lua_State *L, int i)
+static inline Index *
+check_index(uint32_t space_id, uint32_t index_id)
 {
-	struct lbox_index *index =
-		(struct lbox_index *) luaL_checkudata(L, i, indexlib_name);
-	assert(index != NULL);
-	if (index->sc_version != sc_version) {
-		index->index = index_find(space_cache_find(index->id),
-					  index->iid);
-		index->sc_version = sc_version;
-	}
-	return index->index;
+	struct space *space = space_cache_find(space_id);
+	space_check_access(space, PRIV_R);
+	return index_find(space, index_id);
 }
 
-static int
-lbox_index_bind(struct lua_State *L)
+size_t
+boxffi_index_len(uint32_t space_id, uint32_t index_id)
 {
-	uint32_t id = (uint32_t) luaL_checkint(L, 1); /* get space id */
-	uint32_t iid = (uint32_t) luaL_checkint(L, 2); /* get index id in */
-	/* locate the appropriate index */
-	struct space *space = space_cache_find(id);
-	Index *i = index_find(space, iid);
-
-	/* create a userdata object */
-	struct lbox_index *index = (struct lbox_index *)
-		lua_newuserdata(L, sizeof(struct lbox_index));
-	index->id = id;
-	index->iid = iid;
-	index->sc_version = sc_version;
-	index->index = i;
-	/* set userdata object metatable to indexlib */
-	luaL_getmetatable(L, indexlib_name);
-	lua_setmetatable(L, -2);
-
-	return 1;
-}
-
-static int
-lbox_index_tostring(struct lua_State *L)
-{
-	Index *index = lua_checkindex(L, 1);
-	lua_pushfstring(L, " index %d", (int) index_id(index));
-	return 1;
-}
-
-static int
-lbox_index_len(struct lua_State *L)
-{
-	Index *index = lua_checkindex(L, 1);
-	lua_pushinteger(L, index->size());
-	return 1;
-}
-
-static int
-lbox_index_part_count(struct lua_State *L)
-{
-	Index *index = lua_checkindex(L, 1);
-	lua_pushinteger(L, index->key_def->part_count);
-	return 1;
+	try {
+		return check_index(space_id, index_id)->size();
+	} catch (Exception *) {
+		return (size_t) -1; /* handled by box.raise() in Lua */
+	}
 }
 
-static int
-lbox_index_random(struct lua_State *L)
+struct tuple *
+boxffi_index_random(uint32_t space_id, uint32_t index_id, uint32_t rnd)
 {
-	if (lua_gettop(L) != 2 || lua_isnil(L, 2))
-		luaL_error(L, "Usage: index:random((uint32) rnd)");
-
-	Index *index = lua_checkindex(L, 1);
-	uint32_t rnd = lua_tointeger(L, 2);
-	lbox_pushtuple(L, index->random(rnd));
-	return 1;
+	try {
+		return check_index(space_id, index_id)->random(rnd);
+	}  catch (Exception *) {
+		return (struct tuple *) -1; /* handled by box.raise() in Lua */
+	}
 }
 
 static void
@@ -148,9 +89,7 @@ boxffi_index_iterator(uint32_t space_id, uint32_t index_id, int type,
 	struct iterator *it = NULL;
 	enum iterator_type itype = (enum iterator_type) type;
 	try {
-		struct space *space = space_cache_find(space_id);
-		space_check_access(space, PRIV_R);
-		Index *index = index_find(space, index_id);
+		Index *index = check_index(space_id, index_id);
 		assert(mp_typeof(*key) == MP_ARRAY); /* checked by Lua */
 		uint32_t part_count = mp_decode_array(&key);
 		key_validate(index->key_def, itype, key, part_count);
@@ -170,22 +109,11 @@ boxffi_index_iterator(uint32_t space_id, uint32_t index_id, int type,
 void
 box_lua_index_init(struct lua_State *L)
 {
-	static const struct luaL_reg lbox_index_meta[] = {
-		{"__tostring", lbox_index_tostring},
-		{"__len", lbox_index_len},
-		{"part_count", lbox_index_part_count},
-		{"random", lbox_index_random},
-		{NULL, NULL}
-	};
-
 	static const struct luaL_reg indexlib [] = {
-		{"bind", lbox_index_bind},
 		{NULL, NULL}
 	};
 
-
 	/* box.index */
-	luaL_register_type(L, indexlib_name, lbox_index_meta);
 	luaL_register(L, "box.index", indexlib);
 	box_index_init_iterator_types(L, -2);
 	lua_pop(L, 1);
diff --git a/src/box/lua/index.h b/src/box/lua/index.h
index 757ed9e4d59fd3db2bab7ddf7fa3ccf21076db60..2ea1a53e68237d1815a6a921171f153bd0c73658 100644
--- a/src/box/lua/index.h
+++ b/src/box/lua/index.h
@@ -29,6 +29,7 @@
  * SUCH DAMAGE.
  */
 
+#include <stddef.h>
 #include <stdint.h>
 
 struct lua_State;
@@ -41,6 +42,12 @@ box_lua_index_init(struct lua_State *L);
 extern "C" {
 #endif /* defined(__cplusplus) */
 
+size_t
+boxffi_index_len(uint32_t space_id, uint32_t index_id);
+
+struct tuple *
+boxffi_index_random(uint32_t space_id, uint32_t index_id, uint32_t rnd);
+
 struct iterator *
 boxffi_index_iterator(uint32_t space_id, uint32_t index_id, int type,
 		      const char *key);
diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua
index 0a38b7ac3ec4681eb37edb69f53af7f9b742eca7..f7c10d78e9daf0ccb1f0f578444007d092195830 100644
--- a/src/box/lua/schema.lua
+++ b/src/box/lua/schema.lua
@@ -17,6 +17,10 @@ ffi.cdef[[
         void (*free)(struct iterator *);
         void (*close)(struct iterator *);
     };
+    size_t
+    boxffi_index_len(uint32_t space_id, uint32_t index_id);
+    struct tuple *
+    boxffi_index_random(uint32_t space_id, uint32_t index_id, uint32_t rnd);
     struct iterator *
     boxffi_index_iterator(uint32_t space_id, uint32_t index_id, int type,
                   const char *key);
@@ -124,7 +128,7 @@ box.schema.space.drop = function(space_id)
 end
 box.schema.space.rename = function(space_id, space_name)
     local _space = box.space[box.schema.SPACE_ID]
-    _space:update(space_id, {{"=", 2, space_name}})
+    _space:update(space_id, {{"=", 3, space_name}})
 end
 
 box.schema.index = {}
@@ -175,7 +179,7 @@ box.schema.index.drop = function(space_id, index_id)
 end
 box.schema.index.rename = function(space_id, index_id, name)
     local _index = box.space[box.schema.INDEX_ID]
-    _index:update({space_id, index_id}, {{"=", 2, name}})
+    _index:update({space_id, index_id}, {{"=", 3, name}})
 end
 box.schema.index.alter = function(space_id, index_id, options)
     if box.space[space_id] == nil then
@@ -210,10 +214,10 @@ box.schema.index.alter = function(space_id, index_id, options)
                 table.insert(ops, {'=', field_no, value})
             end
         end
-        add_op(options.id, 1)
-        add_op(options.name, 2)
-        add_op(options.type, 3)
-        add_op(options.unique, 4)
+        add_op(options.id, 2)
+        add_op(options.name, 3)
+        add_op(options.type, 4)
+        add_op(options.unique, 5)
         _index:update({space_id, index_id}, ops)
         return
     end
@@ -305,7 +309,14 @@ local port_t = ffi.typeof('struct port *')
 function box.schema.space.bless(space)
     local index_mt = {}
     -- __len and __index
-    index_mt.len = function(index) return #index.idx end
+    index_mt.len = function(index)
+        local ret = builtin.boxffi_index_len(index.space.id, index.id)
+        if ret == -1 then
+            box.raise()
+        end
+        return tonumber(ret)
+    end
+    index_mt.__len = index_mt.len -- Lua 5.2 compatibility
     index_mt.__newindex = function(table, index)
         return error('Attempt to modify a read-only table') end
     index_mt.__index = index_mt
@@ -332,7 +343,17 @@ function box.schema.space.bless(space)
             return
         end
     end
-    index_mt.random = function(index, rnd) return index.idx:random(rnd) end
+    index_mt.random = function(index, rnd)
+        rnd = rnd or math.random()
+        local tuple = builtin.boxffi_index_random(index.space.id, index.id, rnd)
+        if tuple == ffi.cast('void *', -1) then
+            box.raise() -- error
+        elseif tuple ~= nil then
+            return box.tuple.bless(tuple)
+        else
+            return nil
+        end
+    end
     -- iteration
     index_mt.pairs = function(index, key, opts)
         local pkey, pkey_end = msgpackffi.encode_tuple(key)
@@ -373,7 +394,7 @@ function box.schema.space.bless(space)
         end
 
         if key == nil or type(key) == "table" and #key == 0 then
-            return #index.idx
+            return index:len()
         end
 
         local state, tuple
@@ -516,7 +537,7 @@ function box.schema.space.bless(space)
     --
     space_mt.inc = function(space, key)
         local key = keify(key)
-        local cnt_index = #key
+        local cnt_index = #key + 1
         local tuple
         while true do
             tuple = space:update(key, {{'+', cnt_index, 1}})
@@ -526,7 +547,7 @@ function box.schema.space.bless(space)
             tuple = space:insert(data)
             if tuple ~= nil then break end
         end
-        return tuple[cnt_index + 1]
+        return tuple[cnt_index]
     end
 
     --
@@ -536,15 +557,15 @@ function box.schema.space.bless(space)
     --
     space_mt.dec = function(space, key)
         local key = keify(key)
-        local cnt_index = #key
+        local cnt_index = #key + 1
         local tuple = space:get(key)
         if tuple == nil then return 0 end
-        if tuple[cnt_index + 1] == 1 then
+        if tuple[cnt_index] == 1 then
             space:delete(key)
             return 0
         else
             tuple = space:update(key, {{'-', cnt_index, 1}})
-            return tuple[cnt_index + 1]
+            return tuple[cnt_index]
         end
     end
 
@@ -564,7 +585,7 @@ function box.schema.space.bless(space)
         end
         check_index(space, 0)
         local pk = space.index[0]
-        while #pk.idx > 0 do
+        while pk:len() > 0 do
             local state, t
             for state, t in pk:pairs() do
                 local key = {}
@@ -597,7 +618,6 @@ function box.schema.space.bless(space)
     if type(space.index) == 'table' and space.enabled then
         for j, index in pairs(space.index) do
             if type(j) == 'number' then
-                rawset(index, 'idx', box.index.bind(space.id, j))
                 setmetatable(index, index_mt)
             end
         end
@@ -709,7 +729,7 @@ box.schema.user.passwd = function(new_password)
     auth_mech_list = {}
     auth_mech_list["chap-sha1"] = box.schema.user.password(new_password)
     require('session').su('admin')
-    _user:update({uid}, {{"=", 4, auth_mech_list}})
+    _user:update({uid}, {{"=", 5, auth_mech_list}})
     require('session').su(uid)
 end
 
@@ -787,7 +807,7 @@ box.schema.user.revoke = function(user_name, privilege, object_type, object_name
     local old_privilege = tuple[5]
     if old_privilege ~= privilege then
         privilege = bit.band(old_privilege, bit.bnot(privilege))
-        _priv:update({uid, object_type, oid}, { "=", 4, privilege})
+        _priv:update({uid, object_type, oid}, { "=", 5, privilege})
     else
         _priv:delete{uid, object_type, oid}
     end
diff --git a/src/box/lua/tuple.cc b/src/box/lua/tuple.cc
index e09cbf54c3d6136b034564815fbe8f2d1923544a..e6e1918b321026f8551ead191fe89599ef06305b 100644
--- a/src/box/lua/tuple.cc
+++ b/src/box/lua/tuple.cc
@@ -231,12 +231,17 @@ lbox_tuple_transform(struct lua_State *L)
 
 	uint32_t field_count = tuple_field_count(tuple);
 	/* validate offset and len */
-	if (offset < 0) {
+	if (offset == 0) {
+		luaL_error(L, "tuple.transform(): offset is out of bound");
+	} else if (offset < 0) {
 		if (-offset > field_count)
 			luaL_error(L, "tuple.transform(): offset is out of bound");
 		offset += field_count;
-	} else if (offset > field_count) {
-		offset = field_count;
+	} else {
+		--offset; /* offset is one-indexed */
+		if (offset > field_count) {
+			offset = field_count;
+		}
 	}
 	if (len < 0)
 		luaL_error(L, "tuple.transform(): len is negative");
@@ -285,7 +290,8 @@ lbox_tuple_transform(struct lua_State *L)
 	struct tuple *new_tuple = tuple_update(tuple_format_ber,
 					       tuple_update_region_alloc,
 					       &fiber()->gc,
-					       tuple, tbuf_str(b), tbuf_end(b));
+					       tuple, tbuf_str(b), tbuf_end(b),
+					       0);
 	lbox_pushtuple(L, new_tuple);
 	return 1;
 }
diff --git a/src/box/lua/tuple.lua b/src/box/lua/tuple.lua
index f731fa31514df9a8d241d07b015ec4b7cdbed570..f452f009f52b4d89869fd8bb1545e1d7c5db4f78 100644
--- a/src/box/lua/tuple.lua
+++ b/src/box/lua/tuple.lua
@@ -117,7 +117,7 @@ local function tuple_find(tuple, offset, val)
         offset = 0
     end
     local r = tuple:pairs(offset):index(val)
-    return r ~= nil and offset + r - 1 or nil -- tuple is zero-indexed
+    return r ~= nil and offset + r or nil
 end
 
 local function tuple_findall(tuple, offset, val)
@@ -126,7 +126,7 @@ local function tuple_findall(tuple, offset, val)
         offset = 0
     end
     return tuple:pairs(offset):indexes(val)
-        :map(function(i) return offset + i - 1 end) -- tuple is zero-indexed
+        :map(function(i) return offset + i end)
         :totable()
 end
 
diff --git a/src/box/request.cc b/src/box/request.cc
index 01e76b5e3b4af6e8d260ab9f597ddea6d66c9310..759f3bbb754102825374f882da6f42aa5b1fd0b2 100644
--- a/src/box/request.cc
+++ b/src/box/request.cc
@@ -123,7 +123,8 @@ execute_update(struct request *request, struct txn *txn,
 					       region_alloc_cb,
 					       &fiber()->gc,
 					       old_tuple, request->tuple,
-					       request->tuple_end);
+					       request->tuple_end,
+					       request->field_base);
 	TupleGuard guard(new_tuple);
 	space_validate_tuple(space, new_tuple);
 	txn_replace(txn, space, old_tuple, new_tuple, DUP_INSERT);
diff --git a/src/box/request.h b/src/box/request.h
index 46acbcd1177c8a16c6c767af27193b76a86a39ab..6a188098f70710a83187df2371265f688cb618ff 100644
--- a/src/box/request.h
+++ b/src/box/request.h
@@ -54,12 +54,14 @@ struct request
 	uint32_t offset;
 	uint32_t limit;
 	uint32_t iterator;
-	/* Search key or proc name. */
+	/** Search key or proc name. */
 	const char *key;
 	const char *key_end;
-	/* Insert/replace tuple or proc argument or update operations. */
+	/** Insert/replace tuple or proc argument or update operations. */
 	const char *tuple;
 	const char *tuple_end;
+	/** Base field offset, e.g. 0 for C and 1 for Lua */
+	int field_base;
 
 	request_execute_f execute;
 };
diff --git a/src/box/space.cc b/src/box/space.cc
index 7312c87062e4e31c9220f809fe0bb58db74958e4..ebfc0681cec0734606dba2be327f52d0a7b3551c 100644
--- a/src/box/space.cc
+++ b/src/box/space.cc
@@ -325,18 +325,12 @@ space_dump_def(const struct space *space, struct rlist *key_list)
 }
 
 void
-space_swap_index(struct space *lhs, struct space *rhs, uint32_t lhs_id,
-		 uint32_t rhs_id, bool keep_key_def)
+space_swap_index(struct space *lhs, struct space *rhs,
+		 uint32_t lhs_id, uint32_t rhs_id)
 {
 	Index *tmp = lhs->index_map[lhs_id];
 	lhs->index_map[lhs_id] = rhs->index_map[rhs_id];
 	rhs->index_map[rhs_id] = tmp;
-	if (keep_key_def) {
-		struct key_def *tmp = lhs->index_map[lhs_id]->key_def;
-		lhs->index_map[lhs_id]->key_def =
-			rhs->index_map[rhs_id]->key_def;
-		rhs->index_map[rhs_id]->key_def = tmp;
-	}
 }
 
 extern "C" void
diff --git a/src/box/space.h b/src/box/space.h
index 923c0d6cb1af4d5095dc744019debeb0d1e676d9..73bf6ce8f1e5846e9200a2697385e305df63d8fb 100644
--- a/src/box/space.h
+++ b/src/box/space.h
@@ -239,8 +239,8 @@ space_dump_def(const struct space *space, struct rlist *key_list);
  * making sure the old index doesn't leak.
  */
 void
-space_swap_index(struct space *lhs, struct space *rhs, uint32_t lhs_id,
-		 uint32_t rhs_id, bool keep_key_def);
+space_swap_index(struct space *lhs, struct space *rhs,
+		 uint32_t lhs_id, uint32_t rhs_id);
 
 /** Rebuild index map in a space after a series of swap index. */
 void
diff --git a/src/box/tuple.cc b/src/box/tuple.cc
index f6dca523f782d48d4b8ba73455fbcde0faee642f..383304acec6c9566ef2e31e66ebb717eb5a4a030 100644
--- a/src/box/tuple.cc
+++ b/src/box/tuple.cc
@@ -380,13 +380,13 @@ struct tuple *
 tuple_update(struct tuple_format *format,
 	     void *(*region_alloc)(void *, size_t), void *alloc_ctx,
 	     const struct tuple *old_tuple, const char *expr,
-	     const char *expr_end)
+	     const char *expr_end, int field_base)
 {
 	uint32_t new_size = 0;
 	const char *new_data = tuple_update_execute(region_alloc, alloc_ctx,
 					expr, expr_end, old_tuple->data,
 					old_tuple->data + old_tuple->bsize,
-					&new_size);
+					&new_size, field_base);
 
 	/* Allocate a new tuple. */
 	assert(mp_typeof(*new_data) == MP_ARRAY);
diff --git a/src/box/tuple.h b/src/box/tuple.h
index 571a7ea33c47594ab137f144f62d0227511c54b4..b07a3e4db7f11f3515e790424d45f67e800ecd3a 100644
--- a/src/box/tuple.h
+++ b/src/box/tuple.h
@@ -388,7 +388,7 @@ struct tuple *
 tuple_update(struct tuple_format *new_format,
 	     void *(*region_alloc)(void *, size_t), void *alloc_ctx,
 	     const struct tuple *old_tuple,
-	     const char *expr, const char *expr_end);
+	     const char *expr, const char *expr_end, int field_base);
 
 /**
  * @brief Compare two tuple fields using using field type definition
diff --git a/src/box/tuple_update.cc b/src/box/tuple_update.cc
index 5e9a83b6ac1d2edd6eb2543bcdc9dd6faef18340..5ce86d636581b876feafb8e420c475ca775e9dd7 100644
--- a/src/box/tuple_update.cc
+++ b/src/box/tuple_update.cc
@@ -294,9 +294,9 @@ do_op_arith(struct tuple_update *update, struct update_op *op,
 	case '+': arg->val += val; break;
 	case '&': arg->val &= val; break;
 	case '^': arg->val ^= val; break;
-	case '|': arg->val |= val;
-	case '-':
-	default:  arg->val = val - arg->val; break;
+	case '|': arg->val |= val; break;
+	case '-': arg->val = val - arg->val; break;
+	default: assert(false); /* checked by update_read_ops */
 	}
 	op->new_field_len = mp_sizeof_uint(arg->val);
 }
@@ -547,7 +547,7 @@ update_write_tuple(struct tuple_update *update, char *buffer, char *buffer_end)
 
 static void
 update_read_ops(struct tuple_update *update, const char *expr,
-		const char *expr_end)
+		const char *expr_end, int field_base)
 {
 	/* number of operations */
 	update->op_count = mp_decode_array(&expr);
@@ -599,6 +599,14 @@ update_read_ops(struct tuple_update *update, const char *expr,
 		if (args != op->meta->args)
 			tnt_raise(ClientError, ER_UNKNOWN_UPDATE_OP);
 		op->field_no = mp_read_int(&expr, "expected a field no (integer)");
+		if (op->field_no != UINT32_MAX) {
+			/* Check that field_no is not zero for Lua (base = 1) */
+			if (op->field_no < field_base) {
+				tnt_raise(ClientError, ER_NO_SUCH_FIELD,
+					  op->field_no);
+			}
+			op->field_no -= field_base;
+		}
 		op->meta->do_op(update, op, &expr);
 	}
 
@@ -611,7 +619,7 @@ const char *
 tuple_update_execute(region_alloc_func alloc, void *alloc_ctx,
 		     const char *expr,const char *expr_end,
 		     const char *old_data, const char *old_data_end,
-		     uint32_t *p_tuple_len)
+		     uint32_t *p_tuple_len, int field_base)
 {
 	struct tuple_update *update = (struct tuple_update *)
 			alloc(alloc_ctx, sizeof(*update));
@@ -621,7 +629,7 @@ tuple_update_execute(region_alloc_func alloc, void *alloc_ctx,
 	update->alloc_ctx = alloc_ctx;
 
 	update_create_rope(update, old_data, old_data_end);
-	update_read_ops(update, expr, expr_end);
+	update_read_ops(update, expr, expr_end, field_base);
 	uint32_t tuple_len = update_calc_tuple_length(update);
 
 	char *buffer = (char *) alloc(alloc_ctx, tuple_len);
diff --git a/src/box/tuple_update.h b/src/box/tuple_update.h
index acfd304f5212e22a58b4969acc8ca3220673cb6a..17348c70cda4b831461e2cfd3061b9a961c966e9 100644
--- a/src/box/tuple_update.h
+++ b/src/box/tuple_update.h
@@ -43,6 +43,6 @@ const char *
 tuple_update_execute(region_alloc_func alloc, void *alloc_ctx,
 		     const char *expr,const char *expr_end,
 		     const char *old_data, const char *old_data_end,
-		     uint32_t *p_new_size);
+		     uint32_t *p_new_size, int field_base);
 
 #endif /* TARANTOOL_BOX_TUPLE_UPDATE_H_INCLUDED */
diff --git a/src/ffisyms.cc b/src/ffisyms.cc
index 3edf46520bce0bfa02a23f6ff7b293bd89319401..20178da7a68522efa88f3cbd0123567831be06b1 100644
--- a/src/ffisyms.cc
+++ b/src/ffisyms.cc
@@ -25,6 +25,8 @@ void *ffi_symbols[] = {
 	(void *) tuple_seek,
 	(void *) tuple_next,
 	(void *) tuple_ref,
+	(void *) boxffi_index_len,
+	(void *) boxffi_index_random,
 	(void *) boxffi_index_iterator,
 	(void *) port_ffi_create,
 	(void *) port_ffi_destroy,
diff --git a/src/tarantool.cc b/src/tarantool.cc
index 54427961a44fe46cfe547af7c8f35ae753d56ccb..fe1dcb54851b53b4c57b3f6e5456c2b0a1cc1bfd 100644
--- a/src/tarantool.cc
+++ b/src/tarantool.cc
@@ -538,6 +538,7 @@ main(int argc, char **argv)
 	 */
 	__libc_stack_end = (void*) &argv;
 #endif
+	start_time = ev_time();
 	/* set locale to make iswXXXX function work */
 	if (setlocale(LC_CTYPE, "en_US.UTF-8") == NULL)
 		fprintf(stderr, "Failed to set locale to en_US.UTF-8\n");
@@ -650,7 +651,6 @@ main(int argc, char **argv)
 		if (start_loop) {
 			say_crit("entering the event loop");
 			ev_now_update(loop());
-			start_time = ev_now(loop());
 			signal_start();
 			ev_run(loop(), 0);
 		}
diff --git a/test/big/lua.result b/test/big/lua.result
index 66fd9b16a053ca4a0d94b1a05d7ee2ac005b82c7..9476a6d404ef1dfbe36db1bd2a5fbd6fee436353 100644
--- a/test/big/lua.result
+++ b/test/big/lua.result
@@ -591,15 +591,15 @@ space:create_index('primary', { type = 'hash', parts = {1, 'str'}, unique = true
 t = space:insert{'1', '2', '3', '4', '5', '6', '7'}
 ---
 ...
-t:transform(7, 0, '8', '9', '100')
+t:transform(8, 0, '8', '9', '100')
 ---
 - ['1', '2', '3', '4', '5', '6', '7', '8', '9', '100']
 ...
-t:transform(0, 1)
+t:transform(1, 1)
 ---
 - ['2', '3', '4', '5', '6', '7']
 ...
-t:transform(1, 4)
+t:transform(2, 4)
 ---
 - ['1', '6', '7']
 ...
@@ -611,7 +611,7 @@ t:transform(-3, 2)
 ---
 - ['1', '2', '3', '4', '7']
 ...
-t:transform(0, 0, 'A')
+t:transform(1, 0, 'A')
 ---
 - ['A', '1', '2', '3', '4', '5', '6', '7']
 ...
@@ -619,7 +619,7 @@ t:transform(-1, 0, 'A')
 ---
 - ['1', '2', '3', '4', '5', '6', 'A', '7']
 ...
-t:transform(0, 1, 'A')
+t:transform(1, 1, 'A')
 ---
 - ['A', '2', '3', '4', '5', '6', '7']
 ...
@@ -627,19 +627,19 @@ t:transform(-1, 1, 'B')
 ---
 - ['1', '2', '3', '4', '5', '6', 'B']
 ...
-t:transform(0, 2, 'C')
+t:transform(1, 2, 'C')
 ---
 - ['C', '3', '4', '5', '6', '7']
 ...
-t:transform(2, 0, 'hello')
+t:transform(3, 0, 'hello')
 ---
 - ['1', '2', 'hello', '3', '4', '5', '6', '7']
 ...
-t:transform(0, -1, 'C')
+t:transform(1, -1, 'C')
 ---
 - error: 'tuple.transform(): len is negative'
 ...
-t:transform(0, 100)
+t:transform(1, 100)
 ---
 - []
 ...
@@ -647,15 +647,15 @@ t:transform(-100, 1)
 ---
 - error: 'tuple.transform(): offset is out of bound'
 ...
-t:transform(0, 3, 1, 2, 3)
+t:transform(1, 3, 1, 2, 3)
 ---
 - [1, 2, 3, '4', '5', '6', '7']
 ...
-t:transform(3, 1, tonumber64(4))
+t:transform(4, 1, tonumber64(4))
 ---
 - ['1', '2', '3', 4, '5', '6', '7']
 ...
-t:transform(0, 1, {})
+t:transform(1, 1, {})
 ---
 - [[], '2', '3', '4', '5', '6', '7']
 ...
@@ -675,7 +675,7 @@ tab = {}; for i=1,n,1 do table.insert(tab, i) end
 t = box.tuple.new(tab)
 ---
 ...
-t:transform(0, n - 1)
+t:transform(1, n - 1)
 ---
 - [2000]
 ...
@@ -691,19 +691,19 @@ t = space:insert{'A', '2', '3', '4', '3', '2', '5', '6', '3', '7'}
 ...
 t:find('2')
 ---
-- 1
+- 2
 ...
 t:find('4')
 ---
-- 3
+- 4
 ...
 t:find('5')
 ---
-- 6
+- 7
 ...
 t:find('A')
 ---
-- 0
+- 1
 ...
 t:find('0')
 ---
@@ -711,18 +711,18 @@ t:find('0')
 ...
 t:findall('A')
 ---
-- - 0
+- - 1
 ...
 t:findall('2')
 ---
-- - 1
-  - 5
+- - 2
+  - 6
 ...
 t:findall('3')
 ---
-- - 2
-  - 4
-  - 8
+- - 3
+  - 5
+  - 9
 ...
 t:findall('0')
 ---
@@ -730,7 +730,7 @@ t:findall('0')
 ...
 t:find(2, '2')
 ---
-- 5
+- 6
 ...
 t:find(89, '2')
 ---
@@ -738,24 +738,24 @@ t:find(89, '2')
 ...
 t:findall(4, '3')
 ---
-- - 4
-  - 8
+- - 5
+  - 9
 ...
 t = space:insert{'Z', '2', 2, 3, tonumber64(2)}
 ---
 ...
 t:find(2)
 ---
-- 2
+- 3
 ...
 t:findall(tonumber64(2))
 ---
-- - 2
-  - 4
+- - 3
+  - 5
 ...
 t:find('2')
 ---
-- 1
+- 2
 ...
 space:drop()
 ---
diff --git a/test/big/lua.test.lua b/test/big/lua.test.lua
index 8ec7bbd81fbbd7fc4272a35320e8e744d62691e1..1a2531d4d123f8446f3b8253f56b5195fe8c7e1e 100644
--- a/test/big/lua.test.lua
+++ b/test/big/lua.test.lua
@@ -212,23 +212,23 @@ space:drop()
 space = box.schema.create_space('tweedledum')
 space:create_index('primary', { type = 'hash', parts = {1, 'str'}, unique = true })
 t = space:insert{'1', '2', '3', '4', '5', '6', '7'}
-t:transform(7, 0, '8', '9', '100')
-t:transform(0, 1)
-t:transform(1, 4)
+t:transform(8, 0, '8', '9', '100')
+t:transform(1, 1)
+t:transform(2, 4)
 t:transform(-1, 1)
 t:transform(-3, 2)
-t:transform(0, 0, 'A')
+t:transform(1, 0, 'A')
 t:transform(-1, 0, 'A')
-t:transform(0, 1, 'A')
+t:transform(1, 1, 'A')
 t:transform(-1, 1, 'B')
-t:transform(0, 2, 'C')
-t:transform(2, 0, 'hello')
-t:transform(0, -1, 'C')
-t:transform(0, 100)
+t:transform(1, 2, 'C')
+t:transform(3, 0, 'hello')
+t:transform(1, -1, 'C')
+t:transform(1, 100)
 t:transform(-100, 1)
-t:transform(0, 3, 1, 2, 3)
-t:transform(3, 1, tonumber64(4))
-t:transform(0, 1, {})
+t:transform(1, 3, 1, 2, 3)
+t:transform(4, 1, tonumber64(4))
+t:transform(1, 1, {})
 space:truncate()
 
 --
@@ -239,7 +239,7 @@ space:truncate()
 n = 2000
 tab = {}; for i=1,n,1 do table.insert(tab, i) end
 t = box.tuple.new(tab)
-t:transform(0, n - 1)
+t:transform(1, n - 1)
 t = nil
 
 --
diff --git a/test/big/lua/push.lua b/test/big/lua/push.lua
index 423946433eb56c6e8600193f91ff254f9298233f..154126f8e1b1cf9a998495cd7363f2ce5243ac52 100644
--- a/test/big/lua/push.lua
+++ b/test/big/lua/push.lua
@@ -8,9 +8,9 @@ function push_collection(space, size, cid, ...)
 	if #append == 0 then
 		return tuple
 	end
-	tuple = tuple:transform( #tuple, 0, unpack( append ) )
+	tuple = tuple:transform( #tuple + 1, 0, unpack( append ) )
 	if #tuple - 1 > tonumber(size) then
-		tuple = tuple:transform( 1, #tuple - 1 - tonumber(size) )
+		tuple = tuple:transform( 2, #tuple - 1 - tonumber(size) )
 	end
 	return space:replace{tuple:unpack()}
 end
diff --git a/test/big/tree_pk_multipart.result b/test/big/tree_pk_multipart.result
index 42fdc0a697043e948bd72f7174401b2f295d06a1..e5653c703b8d6cc6178eae9d5bb522e9d29f41fa 100644
--- a/test/big/tree_pk_multipart.result
+++ b/test/big/tree_pk_multipart.result
@@ -267,11 +267,11 @@ space:delete{'Vincent', 'The Wolf!', 0}
 ---
 - ['Vincent', 'The Wolf!', 0, 'A please would be nice.']
 ...
-space:update({'Vincent', 'The Wolf!', 1}, {{ '=', 0, 'Updated' }, {'=', 4, 'New'}})
+space:update({'Vincent', 'The Wolf!', 1}, {{ '=', 1, 'Updated' }, {'=', 5, 'New'}})
 ---
 - ['Updated', 'The Wolf!', 1, 'I said a please would be nice.', 'New']
 ...
-space:update({'Updated', 'The Wolf!', 1}, {{ '=', 0, 'Vincent'}, { '#', 4, 1 }})
+space:update({'Updated', 'The Wolf!', 1}, {{ '=', 1, 'Vincent'}, { '#', 5, 1 }})
 ---
 - ['Vincent', 'The Wolf!', 1, 'I said a please would be nice.']
 ...
@@ -309,11 +309,11 @@ space:delete{'The Wolf!', 'Vincent', 1, 'Come again?'}
 --
 -- Update test
 --
-space:update({'The Wolf!', 'Vincent', 1}, {{'=', 3, '<ooops>'}})
+space:update({'The Wolf!', 'Vincent', 1}, {{'=', 4, '<ooops>'}})
 ---
 - ['The Wolf!', 'Vincent', 1, '<ooops>']
 ...
-space:update({'Vincent', 'The Wolf!', 1}, {{'=', 3, '<ooops>'}})
+space:update({'Vincent', 'The Wolf!', 1}, {{'=', 4, '<ooops>'}})
 ---
 - ['Vincent', 'The Wolf!', 1, '<ooops>']
 ...
@@ -335,16 +335,16 @@ space.index['primary']:select({'The Wolf!', 'Vincent'})
       help`s not appreciated then lotsa luck, gentlemen.']
 ...
 -- try to update a nonexistent message
-space:update({'Vincent', 'The Wolf!', 3}, {{'=', 3, '<ooops>'}})
+space:update({'Vincent', 'The Wolf!', 4}, {{'=', 4, '<ooops>'}})
 ---
 ...
 -- try to update patrial defined key
-space:update({'Vincent', 'The Wolf!'}, {{'=', 3, '<ooops>'}})
+space:update({'Vincent', 'The Wolf!'}, {{'=', 4, '<ooops>'}})
 ---
 - error: Invalid key part count in an exact match (expected 3, got 2)
 ...
 -- try to update by invalid key
-space:update({'The Wolf!', 'Vincent', 1, 'Come again?'}, {{'=', 3, '<ooops>'}})
+space:update({'The Wolf!', 'Vincent', 1, 'Come again?'}, {{'=', 4, '<ooops>'}})
 ---
 - error: Invalid key part count in an exact match (expected 3, got 4)
 ...
diff --git a/test/big/tree_pk_multipart.test.lua b/test/big/tree_pk_multipart.test.lua
index ec12c7a9f5bbf620137c70ca6603410580de4796..d80454726878658c5438228ad9f634fbcec4b39d 100644
--- a/test/big/tree_pk_multipart.test.lua
+++ b/test/big/tree_pk_multipart.test.lua
@@ -78,8 +78,8 @@ space:delete{'The Wolf!', 'Vincent', 0}
 space:delete{'The Wolf!', 'Vincent', 3}
 space:delete{'Vincent', 'The Wolf!', 0}
 
-space:update({'Vincent', 'The Wolf!', 1}, {{ '=', 0, 'Updated' }, {'=', 4, 'New'}})
-space:update({'Updated', 'The Wolf!', 1}, {{ '=', 0, 'Vincent'}, { '#', 4, 1 }})
+space:update({'Vincent', 'The Wolf!', 1}, {{ '=', 1, 'Updated' }, {'=', 5, 'New'}})
+space:update({'Updated', 'The Wolf!', 1}, {{ '=', 1, 'Vincent'}, { '#', 5, 1 }})
 -- Checking Vincent's last messages
 space.index['primary']:select({'Vincent', 'The Wolf!'})
 -- Checking The Wolf's last messages
@@ -95,8 +95,8 @@ space:delete{'The Wolf!', 'Vincent', 1, 'Come again?'}
 --
 -- Update test
 --
-space:update({'The Wolf!', 'Vincent', 1}, {{'=', 3, '<ooops>'}})
-space:update({'Vincent', 'The Wolf!', 1}, {{'=', 3, '<ooops>'}})
+space:update({'The Wolf!', 'Vincent', 1}, {{'=', 4, '<ooops>'}})
+space:update({'Vincent', 'The Wolf!', 1}, {{'=', 4, '<ooops>'}})
 
 -- Checking Vincent's last messages
 space.index['primary']:select({'Vincent', 'The Wolf!'})
@@ -104,11 +104,11 @@ space.index['primary']:select({'Vincent', 'The Wolf!'})
 space.index['primary']:select({'The Wolf!', 'Vincent'})
 
 -- try to update a nonexistent message
-space:update({'Vincent', 'The Wolf!', 3}, {{'=', 3, '<ooops>'}})
+space:update({'Vincent', 'The Wolf!', 4}, {{'=', 4, '<ooops>'}})
 -- try to update patrial defined key
-space:update({'Vincent', 'The Wolf!'}, {{'=', 3, '<ooops>'}})
+space:update({'Vincent', 'The Wolf!'}, {{'=', 4, '<ooops>'}})
 -- try to update by invalid key
-space:update({'The Wolf!', 'Vincent', 1, 'Come again?'}, {{'=', 3, '<ooops>'}})
+space:update({'The Wolf!', 'Vincent', 1, 'Come again?'}, {{'=', 4, '<ooops>'}})
 space:len()
 space:truncate()
 space:len()
diff --git a/test/box/alter.result b/test/box/alter.result
index ef6754c69c7a155f917d8534b6702d0597033256..1d454079d4b400085b3c122fb1ee105c30f1e6c0 100644
--- a/test/box/alter.result
+++ b/test/box/alter.result
@@ -69,11 +69,11 @@ _space:delete{_index.id}
 --
 -- Can't change properties of a space
 --
-_space:update({_space.id}, {{'+', 0, 1}})
+_space:update({_space.id}, {{'+', 1, 1}})
 ---
 - error: 'Can''t modify space 280: space id is immutable'
 ...
-_space:update({_space.id}, {{'+', 0, 2}})
+_space:update({_space.id}, {{'+', 1, 2}})
 ---
 - error: 'Can''t modify space 280: space id is immutable'
 ...
@@ -114,7 +114,7 @@ space:replace{0, 0}
 ---
 - error: 'No index #0 is defined in space 321'
 ...
-space:update({0}, {{'+', 0, 1}})
+space:update({0}, {{'+', 1, 1}})
 ---
 - error: 'No index #0 is defined in space 321'
 ...
@@ -340,3 +340,34 @@ box.schema.create_space('auto_original', {id = auto.id})
 auto:drop()
 ---
 ...
+-- ------------------------------------------------------------------
+-- gh-281 Crash after rename + replace + delete with multi-part index
+-- ------------------------------------------------------------------
+s = box.schema.create_space('space')
+---
+...
+s:create_index('primary', {unique = true, parts = {1, 'NUM', 2, 'STR'}})
+---
+...
+s:insert{1, 'a'}
+---
+- [1, 'a']
+...
+box.space.space.index.primary:rename('secondary')
+---
+...
+box.space.space:replace{1,'The rain in Spain'}
+---
+- [1, 'The rain in Spain']
+...
+box.space.space:delete{1,'The rain in Spain'}
+---
+- [1, 'The rain in Spain']
+...
+box.space.space:select{}
+---
+- - [1, 'a']
+...
+s:drop()
+---
+...
diff --git a/test/box/alter.test.lua b/test/box/alter.test.lua
index aa815766d579a7a4afa61937d5eac4a47939588c..5a2345ed07d9e26d1e7ba94c07cfa175930d8a9c 100644
--- a/test/box/alter.test.lua
+++ b/test/box/alter.test.lua
@@ -33,8 +33,8 @@ _space:delete{_index.id}
 --
 -- Can't change properties of a space
 --
-_space:update({_space.id}, {{'+', 0, 1}})
-_space:update({_space.id}, {{'+', 0, 2}})
+_space:update({_space.id}, {{'+', 1, 1}})
+_space:update({_space.id}, {{'+', 1, 2}})
 --
 -- Create a space
 --
@@ -50,7 +50,7 @@ space.index[0]
 space:select{0}
 space:insert{0, 0}
 space:replace{0, 0}
-space:update({0}, {{'+', 0, 1}})
+space:update({0}, {{'+', 1, 1}})
 space:delete{0}
 t = _space:delete{space.id}
 space_deleted = box.space[t[1]]
@@ -127,3 +127,15 @@ box.schema.space.drop('auto')
 auto2
 box.schema.create_space('auto_original', {id = auto.id})
 auto:drop()
+
+-- ------------------------------------------------------------------
+-- gh-281 Crash after rename + replace + delete with multi-part index
+-- ------------------------------------------------------------------
+s = box.schema.create_space('space')
+s:create_index('primary', {unique = true, parts = {1, 'NUM', 2, 'STR'}})
+s:insert{1, 'a'}
+box.space.space.index.primary:rename('secondary')
+box.space.space:replace{1,'The rain in Spain'}
+box.space.space:delete{1,'The rain in Spain'}
+box.space.space:select{}
+s:drop()
diff --git a/test/box/alter_limits.result b/test/box/alter_limits.result
index 6a2a8374f64d69dd3df56cdc7afa87bec3192dc4..f03a38d38630c715a302a785cab82b37fb0201f8 100644
--- a/test/box/alter_limits.result
+++ b/test/box/alter_limits.result
@@ -145,7 +145,7 @@ s:delete{0}
 ---
 - error: 'No index #0 is defined in space 512'
 ...
-s:update(0, {{"=", 0, 0}})
+s:update(0, {{"=", 1, 0}})
 ---
 - error: 'No index #0 is defined in space 512'
 ...
@@ -257,7 +257,7 @@ FIELD_COUNT = 4
 ---
 ...
 -- increase field_count -- error
-box.space['_space']:update(s.id, {{"=", FIELD_COUNT, 3}})
+box.space['_space']:update(s.id, {{"=", FIELD_COUNT + 1, 3}})
 ---
 - error: 'Can''t modify space 512: can not change field count on a non-empty space'
 ...
@@ -266,12 +266,12 @@ s:select{}
 - - [1, 2]
 ...
 -- decrease field_count - error
-box.space['_space']:update(s.id, {{"=", FIELD_COUNT, 1}})
+box.space['_space']:update(s.id, {{"=", FIELD_COUNT + 1, 1}})
 ---
 - error: 'Can''t modify space 512: can not change field count on a non-empty space'
 ...
 -- remove field_count - ok
-box.space['_space']:update(s.id, {{"=", FIELD_COUNT, 0}})
+box.space['_space']:update(s.id, {{"=", FIELD_COUNT + 1, 0}})
 ---
 - [512, 1, 'test', 'memtx', 0, '']
 ...
@@ -280,7 +280,7 @@ s:select{}
 - - [1, 2]
 ...
 -- increase field_count - error
-box.space['_space']:update(s.id, {{"=", FIELD_COUNT, 3}})
+box.space['_space']:update(s.id, {{"=", FIELD_COUNT + 1, 3}})
 ---
 - error: 'Can''t modify space 512: can not change field count on a non-empty space'
 ...
@@ -292,7 +292,7 @@ s:select{}
 - []
 ...
 -- set field_count of an empty space
-box.space['_space']:update(s.id, {{"=", FIELD_COUNT, 3}})
+box.space['_space']:update(s.id, {{"=", FIELD_COUNT + 1, 3}})
 ---
 - [512, 1, 'test', 'memtx', 3, '']
 ...
@@ -639,7 +639,6 @@ s.index.primary
     fieldno: 1
   id: 0
   type: HASH
-  idx: ' index 0'
   space:
     index:
       0: *0
@@ -779,7 +778,7 @@ s.index.primary:select{}
   - ['Homevideo', 2011]
   - ['No such movie', 999]
 ...
-box.space['_index']:update({s.id, s.index.year.id}, {{"=", 7, 'num'}})
+box.space['_index']:update({s.id, s.index.year.id}, {{"=", 8, 'num'}})
 ---
 - [512, 1, 'year', 'tree', 0, 1, 1, 'num']
 ...
diff --git a/test/box/alter_limits.test.lua b/test/box/alter_limits.test.lua
index 8bad5a54ebfaf5e03d29275874dbadd7c3d304aa..1406fec524dd76b1c4e1e354fdb551d12712eb1f 100644
--- a/test/box/alter_limits.test.lua
+++ b/test/box/alter_limits.test.lua
@@ -52,7 +52,7 @@ s = box.schema.create_space('tweedledum')
 s:insert{0}
 s:select{}
 s:delete{0}
-s:update(0, {{"=", 0, 0}})
+s:update(0, {{"=", 1, 0}})
 s:insert{0}
 s.index[0]
 s:truncate()
@@ -91,19 +91,19 @@ s:select{}
 FIELD_COUNT = 4
 -- increase field_count -- error
 
-box.space['_space']:update(s.id, {{"=", FIELD_COUNT, 3}})
+box.space['_space']:update(s.id, {{"=", FIELD_COUNT + 1, 3}})
 s:select{}
 -- decrease field_count - error
-box.space['_space']:update(s.id, {{"=", FIELD_COUNT, 1}})
+box.space['_space']:update(s.id, {{"=", FIELD_COUNT + 1, 1}})
 -- remove field_count - ok
-box.space['_space']:update(s.id, {{"=", FIELD_COUNT, 0}})
+box.space['_space']:update(s.id, {{"=", FIELD_COUNT + 1, 0}})
 s:select{}
 -- increase field_count - error
-box.space['_space']:update(s.id, {{"=", FIELD_COUNT, 3}})
+box.space['_space']:update(s.id, {{"=", FIELD_COUNT + 1, 3}})
 s:truncate()
 s:select{}
 -- set field_count of an empty space
-box.space['_space']:update(s.id, {{"=", FIELD_COUNT, 3}})
+box.space['_space']:update(s.id, {{"=", FIELD_COUNT + 1, 3}})
 s:select{}
 -- field_count actually works
 s:insert{3, 4}
@@ -270,7 +270,7 @@ s:create_index('nodups', { type = 'tree', unique=true, parts = { 2, 'num'} })
 -- change of non-unique index to unique: same effect
 s.index.year:alter({unique=true})
 s.index.primary:select{}
-box.space['_index']:update({s.id, s.index.year.id}, {{"=", 7, 'num'}})
+box.space['_index']:update({s.id, s.index.year.id}, {{"=", 8, 'num'}})
 -- ambiguous field type
 s:create_index('str', { type = 'tree', unique =  false, parts = { 2, 'str'}})
 -- create index on a non-existing field
diff --git a/test/box/call.result b/test/box/call.result
index 0f05df40b861b10ba1de97bd29cb8ee46debc8bd..85128be8445e3e45f84b9430bcbb8533f011b301 100644
--- a/test/box/call.result
+++ b/test/box/call.result
@@ -326,7 +326,7 @@ call myinsert(3, 'old', 2)
     errcode: ER_TUPLE_FOUND
     errmsg: Duplicate key exists in unique index 0
 ...
-space:update({3}, {{'=', 0, 4}, {'=', 1, 'new'}})
+space:update({3}, {{'=', 1, 4}, {'=', 2, 'new'}})
 ---
 - [4, 'new', 2]
 ...
@@ -338,11 +338,11 @@ call space:select(4)
 ---
 - [4, 'new', 2]
 ...
-space:update({4}, {{'+', 2, 1}})
+space:update({4}, {{'+', 3, 1}})
 ---
 - [4, 'new', 3]
 ...
-space:update({4}, {{'-', 2, 1}})
+space:update({4}, {{'-', 3, 1}})
 ---
 - [4, 'new', 2]
 ...
diff --git a/test/box/call.test.py b/test/box/call.test.py
index 0cccbc9dc57de1cd81f63ecdf0389f3edc499553..30e9c12d2e6d87f64c0aa47120685540dad931af 100644
--- a/test/box/call.test.py
+++ b/test/box/call.test.py
@@ -108,11 +108,11 @@ sql("call space:delete(2)")
 sql("call myinsert(3, 'old', 2)")
 # test that insert produces a duplicate key error
 sql("call myinsert(3, 'old', 2)")
-admin("space:update({3}, {{'=', 0, 4}, {'=', 1, 'new'}})")
+admin("space:update({3}, {{'=', 1, 4}, {'=', 2, 'new'}})")
 sql("call space:get(4)")
 sql("call space:select(4)")
-admin("space:update({4}, {{'+', 2, 1}})")
-admin("space:update({4}, {{'-', 2, 1}})")
+admin("space:update({4}, {{'+', 3, 1}})")
+admin("space:update({4}, {{'-', 3, 1}})")
 sql("call space:get(4)")
 sql("call space:select(4)")
 admin("function field_x(key, field_index) return space:get(key)[field_index] end")
diff --git a/test/box/errinj.result b/test/box/errinj.result
index 98730488624ee0fe13b87b8b8bd779456c619633..b12dcf3861ef1132049fb4c0361740b3e3b92460 100644
--- a/test/box/errinj.result
+++ b/test/box/errinj.result
@@ -65,7 +65,7 @@ errinj.set("ERRINJ_WAL_IO", true)
 ---
 - ok
 ...
-space:update(1, {{'=', 0, 2}})
+space:update(1, {{'=', 1, 2}})
 ---
 - error: Failed to write to disk
 ...
@@ -107,7 +107,7 @@ errinj.set("ERRINJ_WAL_ROTATE", true)
 ---
 - ok
 ...
-space:update(1, {{'=', 0, 2}})
+space:update(1, {{'=', 1, 2}})
 ---
 - error: Failed to write to disk
 ...
@@ -122,7 +122,7 @@ errinj.set("ERRINJ_WAL_ROTATE", false)
 ---
 - ok
 ...
-space:update(1, {{'=', 0, 2}})
+space:update(1, {{'=', 1, 2}})
 ---
 - [2]
 ...
diff --git a/test/box/errinj.test.lua b/test/box/errinj.test.lua
index cfcdc98e19523f50413d37c729028267fddfc373..45bebb3f53f9415929048e0466303c325c8ef814 100644
--- a/test/box/errinj.test.lua
+++ b/test/box/errinj.test.lua
@@ -18,7 +18,7 @@ space:get{1}
 errinj.set("ERRINJ_WAL_IO", false)
 space:insert{1}
 errinj.set("ERRINJ_WAL_IO", true)
-space:update(1, {{'=', 0, 2}})
+space:update(1, {{'=', 1, 2}})
 space:get{1}
 space:get{2}
 errinj.set("ERRINJ_WAL_IO", false)
@@ -31,11 +31,11 @@ space:get{1}
 errinj.set("ERRINJ_WAL_ROTATE", false)
 space:insert{1}
 errinj.set("ERRINJ_WAL_ROTATE", true)
-space:update(1, {{'=', 0, 2}})
+space:update(1, {{'=', 1, 2}})
 space:get{1}
 space:get{2}
 errinj.set("ERRINJ_WAL_ROTATE", false)
-space:update(1, {{'=', 0, 2}})
+space:update(1, {{'=', 1, 2}})
 space:get{1}
 space:get{2}
 errinj.set("ERRINJ_WAL_ROTATE", true)
diff --git a/test/box/fiber.result b/test/box/fiber.result
index fa4ab8e60ff2347455204a5683a5c2c9e527cfbb..a6f61ceb1fc557816885a27ae5dd08a148906269 100644
--- a/test/box/fiber.result
+++ b/test/box/fiber.result
@@ -143,26 +143,26 @@ space:insert{1953719668, 'old', 1684234849}
 ---
 - error: Duplicate key exists in unique index 0
 ...
-space:update(1953719668, {{'=', 0, 1936941424}, {'=', 1, 'new'}})
+space:update(1953719668, {{'=', 1, 1936941424}, {'=', 2, 'new'}})
 ---
 - [1936941424, 'new', 1684234849]
 ...
-space:update(1234567890, {{'+', 2, 1}})
+space:update(1234567890, {{'+', 3, 1}})
 ---
 ...
-space:update(1936941424, {{'+', 2, 1}})
+space:update(1936941424, {{'+', 3, 1}})
 ---
 - [1936941424, 'new', 1684234850]
 ...
-space:update(1936941424, {{'-', 2, 1}})
+space:update(1936941424, {{'-', 3, 1}})
 ---
 - [1936941424, 'new', 1684234849]
 ...
-space:update(1936941424, {{'-', 2, 1}})
+space:update(1936941424, {{'-', 3, 1}})
 ---
 - [1936941424, 'new', 1684234848]
 ...
-space:update(1936941424, {{'+', 2, 1}})
+space:update(1936941424, {{'+', 3, 1}})
 ---
 - [1936941424, 'new', 1684234849]
 ...
@@ -191,7 +191,7 @@ space:insert{1953719668, 'hello world'}
 ---
 - [1953719668, 'hello world']
 ...
-space:update(1953719668, {{'=', 1, 'bye, world'}})
+space:update(1953719668, {{'=', 2, 'bye, world'}})
 ---
 - [1953719668, 'bye, world']
 ...
diff --git a/test/box/fiber.test.lua b/test/box/fiber.test.lua
index 08d422aeac79b01dbfb5c65ef56ac3d693958ce5..87c8b1a03f84d3eca36ce9f838cb52365285a128 100644
--- a/test/box/fiber.test.lua
+++ b/test/box/fiber.test.lua
@@ -42,12 +42,12 @@ space:delete{1667655012}
 space:insert{1953719668, 'old', 1684234849}
 -- test that insert produces a duplicate key error
 space:insert{1953719668, 'old', 1684234849}
-space:update(1953719668, {{'=', 0, 1936941424}, {'=', 1, 'new'}})
-space:update(1234567890, {{'+', 2, 1}})
-space:update(1936941424, {{'+', 2, 1}})
-space:update(1936941424, {{'-', 2, 1}})
-space:update(1936941424, {{'-', 2, 1}})
-space:update(1936941424, {{'+', 2, 1}})
+space:update(1953719668, {{'=', 1, 1936941424}, {'=', 2, 'new'}})
+space:update(1234567890, {{'+', 3, 1}})
+space:update(1936941424, {{'+', 3, 1}})
+space:update(1936941424, {{'-', 3, 1}})
+space:update(1936941424, {{'-', 3, 1}})
+space:update(1936941424, {{'+', 3, 1}})
 space:delete{1936941424}
 -- must be read-only
 
@@ -56,7 +56,7 @@ space:insert{1684234849}
 space:delete{1953719668}
 space:delete{1684234849}
 space:insert{1953719668, 'hello world'}
-space:update(1953719668, {{'=', 1, 'bye, world'}})
+space:update(1953719668, {{'=', 2, 'bye, world'}})
 space:delete{1953719668}
 -- test tuple iterators
 t = space:insert{1953719668}
diff --git a/test/box/lua/fifo.lua b/test/box/lua/fifo.lua
index b181c7c3b3cf438c054b3d2a8f69050062cb2681..ae1cd835b41daa11bba8824d5eb24babb1fa40ea 100644
--- a/test/box/lua/fifo.lua
+++ b/test/box/lua/fifo.lua
@@ -23,7 +23,7 @@ function fifo_push(space, name, val)
     elseif bottom == top then
         bottom = bottom + 1
     end
-    return space:update({name}, {{'=', 1, top}, {'=', 2, bottom }, {'=', top - 1, val}})
+    return space:update({name}, {{'=', 2, top}, {'=', 3, bottom }, {'=', top, val}})
 end
 function fifo_top(space, name)
     fifo = find_or_create_fifo(space, name)
diff --git a/test/box/net.box.result b/test/box/net.box.result
index 6b99b180672543749b26b18180c883119902ffc4..6adeae9833e6ad5dcdc9199c128828bb152a57cd 100644
--- a/test/box/net.box.result
+++ b/test/box/net.box.result
@@ -213,7 +213,7 @@ type(foo)
 ---
 - table
 ...
-space:update(123, {{'=', 1, 'test1-updated'}})
+space:update(123, {{'=', 2, 'test1-updated'}})
 ---
 - [123, 'test1-updated', 'test2']
 ...
diff --git a/test/box/net.box.test.lua b/test/box/net.box.test.lua
index b983d359f1b4df267b6dd3cdfd81f2a7e89b3a72..b4f98eed617fa03a11fece3f82d05f262cb6e2db 100644
--- a/test/box/net.box.test.lua
+++ b/test/box/net.box.test.lua
@@ -66,7 +66,7 @@ slf, foo = require('box.internal').call_loadproc('box.net.self:select')
 type(slf)
 type(foo)
 
-space:update(123, {{'=', 1, 'test1-updated'}})
+space:update(123, {{'=', 2, 'test1-updated'}})
 remote:update(space.id, 123, {{'=', 2, 'test2-updated'}})
 
 space:insert{123, 'test1', 'test2'}
diff --git a/test/box/schema.result b/test/box/schema.result
index c647cae51c2aa943ab4818a011b0032b9676bc5a..6a658d3f0c727fca7b2f6a0295bf92f45e104053 100644
--- a/test/box/schema.result
+++ b/test/box/schema.result
@@ -16,69 +16,6 @@ t
   - 'name: tweedledum'
   - 'field_count: 0'
 ...
-box.space[300] = 1
----
-...
-box.index.bind('abc', 'cde')
----
-- error: 'bad argument #1 to ''?'' (number expected, got string)'
-...
-box.index.bind(1, 2)
----
-- error: Space 1 does not exist
-...
-box.index.bind(0, 1)
----
-- error: 'No index #1 is defined in space 0'
-...
-box.index.bind(0, 0)
----
-- ' index 0'
-...
-#box.index.bind(0,0)
----
-- 0
-...
-#box.space[0].index[0].idx
----
-- 0
-...
-space:insert{1953719668}
----
-- [1953719668]
-...
-space:insert{1684234849}
----
-- [1684234849]
-...
-#box.index.bind(0,0)
----
-- 2
-...
-#box.space[0].index[0].idx
----
-- 2
-...
-space:delete{1953719668}
----
-- [1953719668]
-...
-#box.index.bind(0,0)
----
-- 1
-...
-space:delete{1684234849}
----
-- [1684234849]
-...
-#box.space[0].index[0].idx
----
-- 0
-...
-#box.index.bind(0,0)
----
-- 0
-...
 space:drop()
 ---
 ...
diff --git a/test/box/schema.test.lua b/test/box/schema.test.lua
index 364ba133f9bd608dca49235fb535386f0deaee13..9e589c1a24f032dd7c6a5ca620b006dfd3d60497 100644
--- a/test/box/schema.test.lua
+++ b/test/box/schema.test.lua
@@ -3,21 +3,5 @@ space:create_index('primary', { type = 'hash' })
 
 t = {} for k,v in pairs(box.space[0]) do if type(v) ~= 'table' and type(v) ~= 'function' then table.insert(t, k..': '..tostring(v)) end end
 t
-box.space[300] = 1
-box.index.bind('abc', 'cde')
-box.index.bind(1, 2)
-box.index.bind(0, 1)
-box.index.bind(0, 0)
-#box.index.bind(0,0)
-#box.space[0].index[0].idx
-space:insert{1953719668}
-space:insert{1684234849}
-#box.index.bind(0,0)
-#box.space[0].index[0].idx
-space:delete{1953719668}
-#box.index.bind(0,0)
-space:delete{1684234849}
-#box.space[0].index[0].idx
-#box.index.bind(0,0)
 
 space:drop()
diff --git a/test/box/temp_spaces.result b/test/box/temp_spaces.result
index 54cdd138b5b4ac55f0f1e6a1b463fc72078c8fe5..1fc7150176400cefce9d3ed93b79b199f4de1253 100644
--- a/test/box/temp_spaces.result
+++ b/test/box/temp_spaces.result
@@ -1,6 +1,6 @@
 -- temporary spaces
 -- not a temporary
-FLAGS = 5
+FLAGS = 6
 ---
 ...
 s = box.schema.create_space('t', { temporary = true })
@@ -63,7 +63,7 @@ box.space[box.schema.SPACE_ID]:update(s.id, {{'=', FLAGS, ''}})
 ...
 --# stop server default
 --# start server default
-FLAGS = 5
+FLAGS = 6
 ---
 ...
 s = box.space.t
@@ -79,7 +79,7 @@ s.temporary
 ...
 box.space[box.schema.SPACE_ID]:update(s.id, {{'=', FLAGS, 'no-temporary'}})
 ---
-- [512, 1, 't', 'memtx', 0, 'no-temporary']
+- [512, 1, 't', 'memtx', 0, 'no-temporary', 'temporary']
 ...
 s.temporary
 ---
@@ -87,7 +87,7 @@ s.temporary
 ...
 box.space[box.schema.SPACE_ID]:update(s.id, {{'=', FLAGS, ',:asfda:temporary'}})
 ---
-- [512, 1, 't', 'memtx', 0, ',:asfda:temporary']
+- [512, 1, 't', 'memtx', 0, ',:asfda:temporary', 'temporary']
 ...
 s.temporary
 ---
@@ -95,7 +95,7 @@ s.temporary
 ...
 box.space[box.schema.SPACE_ID]:update(s.id, {{'=', FLAGS, 'a,b,c,d,e'}})
 ---
-- [512, 1, 't', 'memtx', 0, 'a,b,c,d,e']
+- [512, 1, 't', 'memtx', 0, 'a,b,c,d,e', 'temporary']
 ...
 s.temporary
 ---
@@ -103,7 +103,7 @@ s.temporary
 ...
 box.space[box.schema.SPACE_ID]:update(s.id, {{'=', FLAGS, 'temporary'}})
 ---
-- [512, 1, 't', 'memtx', 0, 'temporary']
+- [512, 1, 't', 'memtx', 0, 'temporary', 'temporary']
 ...
 s.temporary
 ---
@@ -118,7 +118,7 @@ s:insert{1, 2, 3}
 ...
 box.space[box.schema.SPACE_ID]:update(s.id, {{'=', FLAGS, 'temporary'}})
 ---
-- [512, 1, 't', 'memtx', 0, 'temporary']
+- [512, 1, 't', 'memtx', 0, 'temporary', 'temporary']
 ...
 box.space[box.schema.SPACE_ID]:update(s.id, {{'=', FLAGS, 'no-temporary'}})
 ---
@@ -130,7 +130,7 @@ s:delete{1}
 ...
 box.space[box.schema.SPACE_ID]:update(s.id, {{'=', FLAGS, 'no-temporary'}})
 ---
-- [512, 1, 't', 'memtx', 0, 'no-temporary']
+- [512, 1, 't', 'memtx', 0, 'no-temporary', 'temporary']
 ...
 s:drop()
 ---
diff --git a/test/box/temp_spaces.test.lua b/test/box/temp_spaces.test.lua
index 56830b91a3ec2c2b3736d6bee81b6c001f616555..82e4953fd05227aa035fbfc3d3db6560104a0a5f 100644
--- a/test/box/temp_spaces.test.lua
+++ b/test/box/temp_spaces.test.lua
@@ -1,6 +1,6 @@
 -- temporary spaces
 -- not a temporary
-FLAGS = 5
+FLAGS = 6
 s = box.schema.create_space('t', { temporary = true })
 s.temporary
 s:drop()
@@ -27,7 +27,7 @@ box.space[box.schema.SPACE_ID]:update(s.id, {{'=', FLAGS, ''}})
 
 --# stop server default
 --# start server default
-FLAGS = 5
+FLAGS = 6
 
 s = box.space.t
 s:len()
diff --git a/test/box/tuple.result b/test/box/tuple.result
index f0387de5fc6012b0005b8fcf499a13f0eb07836f..7989466aab480dd255e6ce96a08d6f57025c6b45 100644
--- a/test/box/tuple.result
+++ b/test/box/tuple.result
@@ -602,15 +602,15 @@ t = box.tuple.new({'a','b','c','a', -1, 0, 1, 2, true, 9223372036854775807ULL,
 ...
 t:find('a')
 ---
-- 0
+- 1
 ...
 t:find(1, 'a')
 ---
-- 3
+- 4
 ...
 t:find('c')
 ---
-- 2
+- 3
 ...
 t:find('xxxxx')
 ---
@@ -622,12 +622,12 @@ t:find(1, 'xxxxx')
 ...
 t:findall('a')
 ---
-- - 0
-  - 3
+- - 1
+  - 4
 ...
 t:findall(1, 'a')
 ---
-- - 3
+- - 4
 ...
 t:findall('xxxxx')
 ---
@@ -658,83 +658,83 @@ t:findall(100, 'xxxxx')
 ---
 t:find(2)
 ---
-- 7
+- 8
 ...
 t:findall(2)
 ---
-- - 7
+- - 8
 ...
 t:find(2ULL)
 ---
-- 7
+- 8
 ...
 t:findall(2ULL)
 ---
-- - 7
+- - 8
 ...
 t:find(2LL)
 ---
-- 7
+- 8
 ...
 t:findall(2LL)
 ---
-- - 7
+- - 8
 ...
 t:find(2)
 ---
-- 7
+- 8
 ...
 t:findall(2)
 ---
-- - 7
+- - 8
 ...
 t:find(-1)
 ---
-- 4
+- 5
 ...
 t:findall(-1)
 ---
-- - 4
+- - 5
 ...
 t:find(-1LL)
 ---
-- 4
+- 5
 ...
 t:findall(-1LL)
 ---
-- - 4
+- - 5
 ...
 t:find(true)
 ---
-- 8
+- 9
 ...
 t:findall(true)
 ---
-- - 8
+- - 9
 ...
 t:find(9223372036854775807LL)
 ---
-- 9
+- 10
 ...
 t:findall(9223372036854775807LL)
 ---
-- - 9
+- - 10
 ...
 t:find(9223372036854775807ULL)
 ---
-- 9
+- 10
 ...
 t:findall(9223372036854775807ULL)
 ---
-- - 9
+- - 10
 ...
 t:find(-9223372036854775807LL)
 ---
-- 10
+- 11
 ...
 t:findall(-9223372036854775807LL)
 ---
-- - 10
+- - 11
 ...
 --------------------------------------------------------------------------------
 -- test msgpack.encode + tuple
diff --git a/test/box/update.result b/test/box/update.result
index 55e0a8fe226c4b3de3121ca067352f943660893b..89329cfdc859fbcf7cde378a8bdfc83f2dbf0077 100644
--- a/test/box/update.result
+++ b/test/box/update.result
@@ -9,29 +9,77 @@ s:insert{1000001, 1000002, 1000003, 1000004, 1000005}
 ---
 - [1000001, 1000002, 1000003, 1000004, 1000005]
 ...
-s:update({1000001}, {{'#', 0, 1}})
+s:update({1000001}, {{'#', 1, 1}})
 ---
 - [1000002, 1000003, 1000004, 1000005]
 ...
-s:update({1000002}, {{'#', 0, 1}})
+s:update({1000002}, {{'#', 1, 1}})
 ---
 - [1000003, 1000004, 1000005]
 ...
-s:update({1000003}, {{'#', 0, 1}})
+s:update({1000003}, {{'#', 1, 1}})
 ---
 - [1000004, 1000005]
 ...
-s:update({1000004}, {{'#', 0, 1}})
+s:update({1000004}, {{'#', 1, 1}})
 ---
 - [1000005]
 ...
-s:update({1000005}, {{'#', 0, 1}})
+s:update({1000005}, {{'#', 1, 1}})
 ---
 - error: Tuple field count 0 is less than required by a defined index (expected 1)
 ...
 s:truncate()
 ---
 ...
+-- test arithmetic
+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]
+...
+s:truncate()
+---
+...
 -- test delete multiple fields
 s:insert{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
 ---
@@ -39,30 +87,30 @@ s:insert{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
+- error: Field 41 was not found in the tuple
 ...
-s:update({0}, {{'#', 3, 'abirvalg'}})
+s:update({0}, {{'#', 4, 'abirvalg'}})
 ---
 - error: 'Argument type in operation on field 3 does not match field type: expected
     a UINT'
 ...
-s:update({0}, {{'#', 1, 1}, {'#', 3, 2}, {'#', 5, 1}})
+s:update({0}, {{'#', 2, 1}, {'#', 4, 2}, {'#', 6, 1}})
 ---
 - [0, 2, 3, 6, 7, 9, 10, 11, 12, 13, 14, 15]
 ...
-s:update({0}, {{'#', 3, 3}})
+s:update({0}, {{'#', 4, 3}})
 ---
 - [0, 2, 3, 10, 11, 12, 13, 14, 15]
 ...
-s:update({0}, {{'#', 4, 123456}})
+s:update({0}, {{'#', 5, 123456}})
 ---
 - [0, 2, 3, 10]
 ...
-s:update({0}, {{'#', 2, 4294967295}})
+s:update({0}, {{'#', 3, 4294967295}})
 ---
 - [0, 2]
 ...
-s:update({0}, {{'#', 1, 0}})
+s:update({0}, {{'#', 2, 0}})
 ---
 - error: 'Field 1 UPDATE error: cannot delete 0 fields'
 ...
@@ -74,15 +122,15 @@ s:insert{1, 3, 6, 9}
 ---
 - [1, 3, 6, 9]
 ...
-s:update({1}, {{'!', 1, 2}})
+s:update({1}, {{'!', 2, 2}})
 ---
 - [1, 2, 3, 6, 9]
 ...
-s:update({1}, {{'!', 3, 4}, {'!', 3, 5}, {'!', 4, 7}, {'!', 4, 8}})
+s:update({1}, {{'!', 4, 4}, {'!', 4, 5}, {'!', 5, 7}, {'!', 5, 8}})
 ---
 - [1, 2, 3, 5, 8, 7, 4, 6, 9]
 ...
-s:update({1}, {{'!', 9, 10}, {'!', 9, 11}, {'!', 9, 12}})
+s:update({1}, {{'!', 10, 10}, {'!', 10, 11}, {'!', 10, 12}})
 ---
 - [1, 2, 3, 5, 8, 7, 4, 6, 9, 12, 11, 10]
 ...
@@ -93,7 +141,7 @@ s:insert{1, 'tuple'}
 ---
 - [1, 'tuple']
 ...
-s:update({1}, {{'#', 1, 1}, {'!', 1, 'inserted tuple'}, {'=', 2, 'set tuple'}})
+s:update({1}, {{'#', 2, 1}, {'!', 2, 'inserted tuple'}, {'=', 3, 'set tuple'}})
 ---
 - [1, 'inserted tuple', 'set tuple']
 ...
@@ -104,11 +152,11 @@ s:insert{1, 'tuple'}
 ---
 - [1, 'tuple']
 ...
-s:update({1}, {{'=', 1, 'set tuple'}, {'!', 1, 'inserted tuple'}, {'#', 2, 1}})
+s:update({1}, {{'=', 2, 'set tuple'}, {'!', 2, 'inserted tuple'}, {'#', 3, 1}})
 ---
 - [1, 'inserted tuple']
 ...
-s:update({1}, {{'!', 0, 3}, {'!', 0, 2}})
+s:update({1}, {{'!', 1, 3}, {'!', 1, 2}})
 ---
 - [2, 3, 1, 'inserted tuple']
 ...
@@ -120,17 +168,17 @@ s:replace{1, 'field string value'}
 ---
 - [1, 'field string value']
 ...
-s:update({1}, {{'=', 1, 'new field string value'}, {'=', 2, 42}, {'=', 3, 0xdeadbeef}})
+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}, {{'+', 2, 16}, {'&', 3, 0xffff0000}, {'|', 3, 0x0000a0a0}, {'^', 3, 0xffff00aa}})
+s:update({1}, {{'+', 3, 16}, {'&', 4, 0xffff0000}, {'|', 4, 0x0000a0a0}, {'^', 4, 0xffff00aa}})
 ---
 - error: 'Field 3 UPDATE error: double update of the same field'
 ...
 -- test update splice operation
-s:update({1}, {{':', 1, 0, 3, 'the newest'}})
+s:update({1}, {{':', 2, 0, 3, 'the newest'}})
 ---
 - [1, 'the newest field string value', 42, 3735928559]
 ...
@@ -138,20 +186,20 @@ s:replace{1953719668, 'something to splice'}
 ---
 - [1953719668, 'something to splice']
 ...
-s:update(1953719668, {{':', 1, 0, 4, 'no'}})
+s:update(1953719668, {{':', 2, 0, 4, 'no'}})
 ---
 - [1953719668, 'nothing to splice']
 ...
-s:update(1953719668, {{':', 1, 0, 2, 'every'}})
+s:update(1953719668, {{':', 2, 0, 2, 'every'}})
 ---
 - [1953719668, 'everything to splice']
 ...
 -- check an incorrect offset
-s:update(1953719668, {{':', 1, 100, 2, 'every'}})
+s:update(1953719668, {{':', 2, 100, 2, 'every'}})
 ---
 - [1953719668, 'everything to spliceevery']
 ...
-s:update(1953719668, {{':', 1, -100, 2, 'every'}})
+s:update(1953719668, {{':', 2, -100, 2, 'every'}})
 ---
 - error: 'Field SPLICE error: offset is out of bound'
 ...
@@ -172,7 +220,7 @@ s:insert{1953719668, 'hello world'}
 ---
 - [1953719668, 'hello world']
 ...
-s:update(1953719668, {{'=', 1, 'bye, world'}})
+s:update(1953719668, {{'=', 2, 'bye, world'}})
 ---
 - [1953719668, 'bye, world']
 ...
@@ -181,12 +229,24 @@ s:delete{1953719668}
 - [1953719668, 'bye, world']
 ...
 -- test update delete operations
-s:update({1}, {{'#', 3, 1}, {'#', 2, 1}})
+s:update({1}, {{'#', 4, 1}, {'#', 3, 1}})
 ---
 ...
 -- test update insert operations
-s:update({1}, {{'!', 1, 1}, {'!', 1, 2}, {'!', 1, 3}, {'!', 1, 4}})
+s:update({1}, {{'!', 2, 1}, {'!', 2, 2}, {'!', 2, 3}, {'!', 2, 4}})
+---
+...
+s:truncate()
+---
+...
+-- s:update: zero field
+s:insert{48}
+---
+- [48]
+...
+s:update(48, {{'=', 0, 'hello'}})
 ---
+- error: Field 0 was not found in the tuple
 ...
 s:truncate()
 ---
@@ -196,7 +256,7 @@ s:insert{1684234849}
 ---
 - [1684234849]
 ...
-s:update({1684234849}, {{'#', 1, 1}})
+s:update({1684234849}, {{'#', 2, 1}})
 ---
 - error: Field 1 was not found in the tuple
 ...
@@ -212,15 +272,15 @@ s:update({1684234849}, {{'=', -1, 'push3'}})
 ---
 - [1684234849, 'push1', 'push2', 'push3']
 ...
-s:update({1684234849}, {{'#', 1, 1}, {'=', -1, 'swap1'}})
+s:update({1684234849}, {{'#', 2, 1}, {'=', -1, 'swap1'}})
 ---
 - [1684234849, 'push2', 'push3', 'swap1']
 ...
-s:update({1684234849}, {{'#', 1, 1}, {'=', -1, 'swap2'}})
+s:update({1684234849}, {{'#', 2, 1}, {'=', -1, 'swap2'}})
 ---
 - [1684234849, 'push3', 'swap1', 'swap2']
 ...
-s:update({1684234849}, {{'#', 1, 1}, {'=', -1, 'swap3'}})
+s:update({1684234849}, {{'#', 2, 1}, {'=', -1, 'swap3'}})
 ---
 - [1684234849, 'swap1', 'swap2', 'swap3']
 ...
diff --git a/test/box/update.test.lua b/test/box/update.test.lua
index 4d532c3a0a04928340c549ea3a2def192d6ff699..4eec6c4af720c3daef27ce1222fa05a0212e4821 100644
--- a/test/box/update.test.lua
+++ b/test/box/update.test.lua
@@ -3,78 +3,97 @@ s:create_index('pk')
 
 -- test delete field
 s:insert{1000001, 1000002, 1000003, 1000004, 1000005}
-s:update({1000001}, {{'#', 0, 1}})
-s:update({1000002}, {{'#', 0, 1}})
-s:update({1000003}, {{'#', 0, 1}})
-s:update({1000004}, {{'#', 0, 1}})
-s:update({1000005}, {{'#', 0, 1}})
+s:update({1000001}, {{'#', 1, 1}})
+s:update({1000002}, {{'#', 1, 1}})
+s:update({1000003}, {{'#', 1, 1}})
+s:update({1000004}, {{'#', 1, 1}})
+s:update({1000005}, {{'#', 1, 1}})
+s:truncate()
+
+-- test arithmetic
+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}})
 s:truncate()
 
 -- test delete multiple fields
 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}, {{'#', 3, 'abirvalg'}})
-s:update({0}, {{'#', 1, 1}, {'#', 3, 2}, {'#', 5, 1}})
-s:update({0}, {{'#', 3, 3}})
-s:update({0}, {{'#', 4, 123456}})
-s:update({0}, {{'#', 2, 4294967295}})
-s:update({0}, {{'#', 1, 0}})
+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}})
 s:truncate()
 
 -- test insert field
 s:insert{1, 3, 6, 9}
-s:update({1}, {{'!', 1, 2}})
-s:update({1}, {{'!', 3, 4}, {'!', 3, 5}, {'!', 4, 7}, {'!', 4, 8}})
-s:update({1}, {{'!', 9, 10}, {'!', 9, 11}, {'!', 9, 12}})
+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}})
 s:truncate()
 s:insert{1, 'tuple'}
-s:update({1}, {{'#', 1, 1}, {'!', 1, 'inserted tuple'}, {'=', 2, 'set tuple'}})
+s:update({1}, {{'#', 2, 1}, {'!', 2, 'inserted tuple'}, {'=', 3, 'set tuple'}})
 s:truncate()
 s:insert{1, 'tuple'}
-s:update({1}, {{'=', 1, 'set tuple'}, {'!', 1, 'inserted tuple'}, {'#', 2, 1}})
-s:update({1}, {{'!', 0, 3}, {'!', 0, 2}})
+s:update({1}, {{'=', 2, 'set tuple'}, {'!', 2, 'inserted tuple'}, {'#', 3, 1}})
+s:update({1}, {{'!', 1, 3}, {'!', 1, 2}})
 s:truncate()
 
 -- test update's assign opearations
 s:replace{1, 'field string value'}
-s:update({1}, {{'=', 1, 'new field string value'}, {'=', 2, 42}, {'=', 3, 0xdeadbeef}})
+s:update({1}, {{'=', 2, 'new field string value'}, {'=', 3, 42}, {'=', 4, 0xdeadbeef}})
 
 -- test multiple update opearations on the same field
-s:update({1}, {{'+', 2, 16}, {'&', 3, 0xffff0000}, {'|', 3, 0x0000a0a0}, {'^', 3, 0xffff00aa}})
+s:update({1}, {{'+', 3, 16}, {'&', 4, 0xffff0000}, {'|', 4, 0x0000a0a0}, {'^', 4, 0xffff00aa}})
 
 -- test update splice operation
-s:update({1}, {{':', 1, 0, 3, 'the newest'}})
+s:update({1}, {{':', 2, 0, 3, 'the newest'}})
 
 s:replace{1953719668, 'something to splice'}
-s:update(1953719668, {{':', 1, 0, 4, 'no'}})
-s:update(1953719668, {{':', 1, 0, 2, 'every'}})
+s:update(1953719668, {{':', 2, 0, 4, 'no'}})
+s:update(1953719668, {{':', 2, 0, 2, 'every'}})
 -- check an incorrect offset
-s:update(1953719668, {{':', 1, 100, 2, 'every'}})
-s:update(1953719668, {{':', 1, -100, 2, 'every'}})
+s:update(1953719668, {{':', 2, 100, 2, 'every'}})
+s:update(1953719668, {{':', 2, -100, 2, 'every'}})
 s:truncate()
 s:insert{1953719668, 'hello', 'october', '20th'}:unpack()
 s:truncate()
 s:insert{1953719668, 'hello world'}
-s:update(1953719668, {{'=', 1, 'bye, world'}})
+s:update(1953719668, {{'=', 2, 'bye, world'}})
 s:delete{1953719668}
 
 -- test update delete operations
-s:update({1}, {{'#', 3, 1}, {'#', 2, 1}})
+s:update({1}, {{'#', 4, 1}, {'#', 3, 1}})
 
 -- test update insert operations
-s:update({1}, {{'!', 1, 1}, {'!', 1, 2}, {'!', 1, 3}, {'!', 1, 4}})
+s:update({1}, {{'!', 2, 1}, {'!', 2, 2}, {'!', 2, 3}, {'!', 2, 4}})
+
+s:truncate()
 
+-- s:update: zero field
+s:insert{48}
+s:update(48, {{'=', 0, 'hello'}})
 s:truncate()
 
 -- s:update: push/pop fields
 s:insert{1684234849}
-s:update({1684234849}, {{'#', 1, 1}})
+s:update({1684234849}, {{'#', 2, 1}})
 s:update({1684234849}, {{'=', -1, 'push1'}})
 s:update({1684234849}, {{'=', -1, 'push2'}})
 s:update({1684234849}, {{'=', -1, 'push3'}})
-s:update({1684234849}, {{'#', 1, 1}, {'=', -1, 'swap1'}})
-s:update({1684234849}, {{'#', 1, 1}, {'=', -1, 'swap2'}})
-s:update({1684234849}, {{'#', 1, 1}, {'=', -1, 'swap3'}})
+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'}})