diff --git a/include/errcode.h b/include/errcode.h
index 2675b113e67590323243c9fda77cbdea93d5cc4f..1128e8e25e8829443ee9bbcc1295df1128211ea9 100644
--- a/include/errcode.h
+++ b/include/errcode.h
@@ -77,7 +77,7 @@ enum { TNT_ERRMSG_MAX = 512 };
 	/* 23 */_(ER_RESERVED23,		0, "Reserved23") \
 		/* end of silverproxy error codes */ \
 	/* 24 */_(ER_UNUSED24,			2, "Unused24") \
-	/* 25 */_(ER_TUPLE_IS_EMPTY,		2, "UPDATE error: the new tuple has no fields") \
+	/* 25 */_(ER_UNUSED25,			2, "Unused25") \
 	/* 26 */_(ER_FIBER_STACK,		2, "Can not create a new fiber: recursion limit reached") \
 	/* 27 */_(ER_UNUSED27,			2, "Unused27") \
 	/* 28 */_(ER_UNUSED28,			2, "Unused28") \
diff --git a/include/pickle.h b/include/pickle.h
index 9fc087c1861fbbd3b8463840b373794c3a0b19e0..ac3fea07539ac7bd64308aa6142a861e06ddd71e 100644
--- a/include/pickle.h
+++ b/include/pickle.h
@@ -228,4 +228,17 @@ pack_lstr(char *buf, const void *str, uint32_t len)
 	return (char *) memcpy(pack_varint32(buf, len), str, len) + len;
 }
 
+#define pack_u(bits)						\
+static inline char *						\
+pack_u##bits(char *buf, uint##bits##_t val)			\
+{								\
+	*(uint##bits##_t *) buf = val;				\
+	return buf + sizeof(uint##bits##_t);			\
+}
+
+pack_u(8)
+pack_u(16)
+pack_u(32)
+pack_u(64)
+
 #endif /* TARANTOOL_PICKLE_H_INCLUDED */
diff --git a/src/box/bitset_index.cc b/src/box/bitset_index.cc
index 1b9070e3a5af62bba724b9e0a412e55d5d36ba62..49d9ac00aed18f6e08a3f722796d361e77918132 100644
--- a/src/box/bitset_index.cc
+++ b/src/box/bitset_index.cc
@@ -93,7 +93,7 @@ bitset_index_iterator_next(struct iterator *iterator)
 BitsetIndex::BitsetIndex(struct key_def *key_def, struct space *space)
 	: Index(key_def, space)
 {
-	assert (!key_def->is_unique);
+	assert(!key_def->is_unique);
 
 	if (bitset_index_create(&index, realloc) != 0)
 		panic_syserror("bitset_index_create");
@@ -126,7 +126,7 @@ BitsetIndex::endBuild()
 void
 BitsetIndex::build(Index *pk)
 {
-	assert (!key_def->is_unique);
+	assert(!key_def->is_unique);
 
 	struct iterator *it = pk->position();
 	struct tuple *tuple;
@@ -213,7 +213,7 @@ BitsetIndex::replace(struct tuple *old_tuple, struct tuple *new_tuple,
 		if (bitset_index_contains_value(&index, value)) {
 			ret = old_tuple;
 
-			assert (old_tuple != new_tuple);
+			assert(old_tuple != new_tuple);
 			bitset_index_remove_value(&index, value);
 		}
 	}
diff --git a/src/box/box_lua.cc b/src/box/box_lua.cc
index 808b1d015cf5c9d41fb0252afba37e90d5f20927..ce6e1711411dd148e463b252dafefe7302ad54e7 100644
--- a/src/box/box_lua.cc
+++ b/src/box/box_lua.cc
@@ -32,6 +32,7 @@
 #include "box/box.h"
 #include "request.h"
 #include "txn.h"
+#include "tuple_update.h"
 
 extern "C" {
 #include <lua.h>
@@ -182,63 +183,6 @@ lbox_tuple_slice(struct lua_State *L)
 	return end - start;
 }
 
-
-/**
- * Given a tuple, range of fields to remove (start and end field
- * numbers), and a list of fields to paste, calculate the size of
- * the resulting tuple.
- *
- * @param L      lua stack, contains a list of arguments to paste
- * @param start  offset in the lua stack where paste arguments start
- * @param tuple  old tuple
- * @param offset cut field offset
- * @param len    how many fields to cut
- * @param[out]   sizes of the left and right part
- *
- * @return size of the new tuple
-*/
-static size_t
-transform_calculate(struct lua_State *L, struct tuple *tuple,
-		    int start, int argc, int offset, int len,
-		    size_t lr[2])
-{
-	/* calculate size of the new tuple */
-	const char *tuple_end = tuple->data + tuple->bsize;
-	const char *tuple_field = tuple->data;
-
-	lr[0] = tuple_range_size(&tuple_field, tuple_end, offset);
-
-	/* calculate sizes of supplied fields */
-	size_t mid = 0;
-	for (int i = start ; i <= argc ; i++) {
-		switch (lua_type(L, i)) {
-		case LUA_TNUMBER:
-			mid += varint32_sizeof(sizeof(u32)) + sizeof(u32);
-			break;
-		case LUA_TCDATA:
-			mid += varint32_sizeof(sizeof(u64)) + sizeof(u64);
-			break;
-		case LUA_TSTRING: {
-			size_t field_size = lua_objlen(L, i);
-			mid += varint32_sizeof(field_size) + field_size;
-			break;
-		}
-		default:
-			luaL_error(L, "tuple.transform(): unsupported field type '%s'",
-				   lua_typename(L, lua_type(L, i)));
-			break;
-		}
-	}
-
-	/* calculate size of the removed fields */
-	tuple_range_size(&tuple_field, tuple_end, len);
-
-	/* calculate last part of the tuple fields */
-	lr[1] = tuple_end - tuple_field;
-
-	return lr[0] + mid + lr[1];
-}
-
 /**
  * Tuple transforming function.
  *
@@ -256,8 +200,8 @@ lbox_tuple_transform(struct lua_State *L)
 	int argc = lua_gettop(L);
 	if (argc < 3)
 		luaL_error(L, "tuple.transform(): bad arguments");
-	int offset = lua_tointeger(L, 2);
-	int len = lua_tointeger(L, 3);
+	lua_Integer offset = lua_tointeger(L, 2);  /* Can be negative and can be > INT_MAX */
+	lua_Integer len = lua_tointeger(L, 3);
 
 	/* validate offset and len */
 	if (offset < 0) {
@@ -272,45 +216,100 @@ lbox_tuple_transform(struct lua_State *L)
 	if (len > tuple->field_count - offset)
 		len = tuple->field_count - offset;
 
-	/* calculate size of the new tuple */
-	size_t lr[2]; /* left and right part sizes */
-	size_t size = transform_calculate(L, tuple, 4, argc, offset, len, lr);
+	assert(offset + len <= tuple->field_count);
 
-	/* allocate new tuple */
-	struct tuple *dest = tuple_alloc(size);
-	dest->field_count = (tuple->field_count - len) + (argc - 3);
+	/*
+	 * Calculate the number of operations and length of UPDATE expression
+	 */
+	uint32_t op_cnt = 0;
+	size_t expr_len = 0;
+	expr_len += sizeof(uint32_t); /* op_count */
+	if (offset < tuple->field_count) {
+		/* Add an UPDATE operation for each removed field. */
+		op_cnt += len;
+		expr_len += len * sizeof(uint32_t);	/* Field */
+		expr_len += len * sizeof(uint8_t);	/* UPDATE_OP_DELETE */
+		expr_len += len * varint32_sizeof(0);	/* Unused */
+	}
 
-	/* construct tuple */
-	memcpy(dest->data, tuple->data, lr[0]);
-	char *ptr = dest->data + lr[0];
 	for (int i = 4; i <= argc; i++) {
+		uint32_t field_len = 0;
 		switch (lua_type(L, i)) {
-		case LUA_TNUMBER: {
-			u32 v = lua_tonumber(L, i);
-			ptr = pack_lstr(ptr, &v, sizeof(v));
+		case LUA_TNUMBER:
+			field_len = sizeof(uint32_t);
 			break;
-		}
-		case LUA_TCDATA: {
-			u64 v = tarantool_lua_tointeger64(L, i);
-			ptr = pack_lstr(ptr, &v, sizeof(v));
+		case LUA_TCDATA:
+			field_len = sizeof(uint64_t);
 			break;
-		}
-		case LUA_TSTRING: {
-			size_t field_size = 0;
-			const char *v = luaL_checklstring(L, i, &field_size);
-			ptr = pack_lstr(ptr, v, field_size);
+		case LUA_TSTRING:
+			field_len = lua_objlen(L, i);
+			break;
+		default:
+			luaL_error(L, "tuple.transform(): unsupported field type '%s'",
+				   lua_typename(L, lua_type(L, i)));
 			break;
 		}
+
+		/* Insert one field */
+		op_cnt++;
+		expr_len += sizeof(uint32_t);		/* Field Number */
+		expr_len += sizeof(uint8_t);		/* UPDATE_OP_SET */
+		expr_len += varint32_sizeof(field_len) + field_len; /* Field */
+	}
+	if (op_cnt == 0) {
+		/* tuple_upate() does not accept an empty operation list. */
+		lbox_pushtuple(L, tuple);
+		return 1;
+	}
+
+	/*
+	 * Prepare UPDATE expression
+	 */
+	char *expr = (char *) palloc(fiber->gc_pool, expr_len);
+	char *pos = expr;
+	pos = pack_u32(pos, op_cnt);
+	for (uint32_t i = 0; i < (uint32_t) len; i++) {
+		pos = pack_u32(pos, offset);
+		pos = pack_u8(pos, UPDATE_OP_DELETE);
+		pos = pack_varint32(pos, 0);
+	}
+
+	for (int i = argc ; i >= 4; i--) {
+		uint32_t field_u32;
+		uint64_t field_u64;
+		const char *field = NULL;
+		size_t field_len = 0;
+		switch (lua_type(L, i)) {
+		case LUA_TNUMBER:
+			field_u32 = lua_tonumber(L, i);
+			field = (const char *) &field_u32;
+			field_len = sizeof(uint32_t);
+			break;
+		case LUA_TCDATA:
+			field_u64 = tarantool_lua_tointeger64(L, i);
+			field = (const char *) &field_u64;
+			field_len = sizeof(uint64_t);
+			break;
+		case LUA_TSTRING:
+			field = luaL_checklstring(L, i, &field_len);
+			break;
 		default:
-			/* default type check is done in transform_calculate()
-			 * function */
+			assert(false);
 			break;
 		}
+
+		assert(field_len <= UINT32_MAX);
+		/* Insert the field */
+		pos = pack_u32(pos, offset);		/* Field Number */
+		pos = pack_u8(pos, UPDATE_OP_INSERT);	/* Operation */
+		pos = pack_lstr(pos, field, field_len);	/* Field Value */
 	}
 
-	memcpy(ptr, tuple_field_old(tuple, offset + len), lr[1]);
+	assert(pos == expr + expr_len);
 
-	lbox_pushtuple(L, dest);
+	/* Execute tuple_update */
+	struct tuple *new_tuple = tuple_update(tuple, expr, expr + expr_len);
+	lbox_pushtuple(L, new_tuple);
 	return 1;
 }
 
diff --git a/src/box/space.cc b/src/box/space.cc
index 52f6d24de887cec9c24e3f18a662295a48be3078..dc9eb92017f1d9a74ecb3514e291fd0f9e264f9d 100644
--- a/src/box/space.cc
+++ b/src/box/space.cc
@@ -383,7 +383,7 @@ space_config()
 			enum index_type type = STR2ENUM(index_type, cfg_index->type);
 			struct key_def *key_def = &space->key_defs[j];
 			Index *index = Index::factory(type, key_def, space);
-			assert (index != NULL);
+			assert(index != NULL);
 			space->index[j] = index;
 		}
 
diff --git a/src/box/tree_index.cc b/src/box/tree_index.cc
index 7ba2fabd215c63ec58366f5b17feacf3a1432a3f..a09c4052694f349a8f66d40b109a43820931d780 100644
--- a/src/box/tree_index.cc
+++ b/src/box/tree_index.cc
@@ -304,7 +304,7 @@ key_is_linear(struct key_def *key_def)
 static void
 fold_with_sparse_parts(struct key_def *key_def, struct tuple *tuple, union sparse_part* parts)
 {
-	assert (tuple->field_count >= key_def->max_fieldno);
+	assert(tuple->field_count >= key_def->max_fieldno);
 
 	const char *part_data = tuple->data;
 
diff --git a/src/box/tuple.cc b/src/box/tuple.cc
index f2a95912127d1719e84ae5a106bb4f360960875d..8ddcbefa86ec3a91d8dd163d585045fca78f1875 100644
--- a/src/box/tuple.cc
+++ b/src/box/tuple.cc
@@ -188,7 +188,7 @@ tuple_print(struct tbuf *buf, const struct tuple *tuple)
 		if (likely(++field_no < tuple->field_count))
 			tbuf_printf(buf, ", ");
 	}
-	assert (field_no == tuple->field_count);
+	assert(field_no == tuple->field_count);
 
 	tbuf_printf(buf, "}");
 }
diff --git a/src/box/tuple_update.cc b/src/box/tuple_update.cc
index 2e5d7f46402922fe99238e2d5dcd07f04ef1498c..837b5339c413b82aef3401e2a0b3b9e18b2cdbd6 100644
--- a/src/box/tuple_update.cc
+++ b/src/box/tuple_update.cc
@@ -35,7 +35,7 @@
 #include <exception.h>
 #include <pickle.h>
 
-/** {{{ UPDATE request implementation.
+/** UPDATE request implementation.
  * UPDATE request is represented by a sequence of operations, each
  * working with a single field. There also are operations which
  * add or remove fields. More than one operation on the same field
@@ -528,9 +528,6 @@ update_calc_new_tuple_length(struct tuple_update *update)
 	if (update->new_tuple_size > UINT32_MAX)
 		tnt_raise(ClientError, ER_TUPLE_IS_TOO_LONG,
 			  update->new_tuple_size);
-
-	if (update->new_tuple_size == 0)
-		tnt_raise(ClientError, ER_TUPLE_IS_EMPTY);
 }
 
 static void
@@ -582,7 +579,7 @@ do_update_ops(struct tuple_update *update, char *new_data)
 				new_field = (char *) update->alloc(
 					update->alloc_ctx, op->new_field_len);
 			}
-			assert (op->meta != NULL);
+			assert(op->meta != NULL);
 			op->meta->do_op(&op->arg, old_field, new_field);
 			/* Next op uses previous op output as its input. */
 			old_field = new_field;
@@ -603,7 +600,7 @@ do_update_ops(struct tuple_update *update, char *new_data)
 		total_field_count += field_count;
 	}
 
-	assert (update->new_tuple_fcount == total_field_count);
+	assert(update->new_tuple_fcount == total_field_count);
 }
 
 static void
@@ -651,7 +648,7 @@ tuple_update_prepare(region_alloc_func alloc, void *alloc_ctx,
 
 	struct tuple_update *update = (struct tuple_update *)
 			alloc(alloc_ctx, sizeof(*update));
-	assert (update != NULL);
+	assert(update != NULL);
 	memset(update, 0, sizeof(*update));
 	update->alloc = alloc;
 	update->alloc_ctx = alloc_ctx;
diff --git a/src/lib/bitset/expr.c b/src/lib/bitset/expr.c
index 8c004c56073fe6ec894ecad2e0923424deb3727d..dee7d6c6c4f3fff8ddbd4973fba87f92600b62a6 100644
--- a/src/lib/bitset/expr.c
+++ b/src/lib/bitset/expr.c
@@ -160,7 +160,7 @@ int
 bitset_expr_add_param(struct bitset_expr *expr, size_t bitset_id,
 		      bool pre_not)
 {
-	assert (expr->size > 0);
+	assert(expr->size > 0);
 	struct bitset_expr_conj *conj = &expr->conjs[expr->size - 1];
 
 	if (bitset_expr_conj_reserve(expr, conj, conj->size + 1) != 0)
diff --git a/src/lib/bitset/index.c b/src/lib/bitset/index.c
index 7250dcb5208a72295a3227d26b42a07f6ebc65e8..541ac65a92ac9b5bdd4f60a30565bd11309fc286 100644
--- a/src/lib/bitset/index.c
+++ b/src/lib/bitset/index.c
@@ -43,7 +43,7 @@ int
 bitset_index_create(struct bitset_index *index,
 		    void *(*realloc)(void *ptr, size_t size))
 {
-	assert (index != NULL);
+	assert(index != NULL);
 	memset(index, 0, sizeof(*index));
 	index->realloc = realloc;
 	if (bitset_index_reserve(index, 1) != 0)
@@ -55,8 +55,8 @@ bitset_index_create(struct bitset_index *index,
 void
 bitset_index_destroy(struct bitset_index *index)
 {
-	assert (index != NULL);
-	assert (index->capacity > 0);
+	assert(index != NULL);
+	assert(index->capacity > 0);
 
 	for (size_t b = 0; b < index->capacity; b++) {
 		if (index->bitsets[b] == NULL)
@@ -138,9 +138,9 @@ int
 bitset_index_insert(struct bitset_index *index, const void *key,
 		    size_t key_size, size_t value)
 {
-	assert (index != NULL);
-	assert (key != NULL);
-	assert (index->capacity > 0);
+	assert(index != NULL);
+	assert(key != NULL);
+	assert(index->capacity > 0);
 
 	/*
 	 * Step 0: allocate enough number of bitsets
@@ -367,8 +367,8 @@ int
 bitset_index_init_iterator(struct bitset_index *index,
 			   struct bitset_iterator *it, struct bitset_expr *expr)
 {
-	assert (index != NULL);
-	assert (it != NULL);
+	assert(index != NULL);
+	assert(it != NULL);
 
 	/* Check that we have all required bitsets */
 	size_t max = 0;
diff --git a/src/lib/bitset/page.h b/src/lib/bitset/page.h
index 4f519b30c8183db31a7c2f156f885b92f7400c6e..2f77925e239d72f87fdeed0666b8f0abe0f4ec13 100644
--- a/src/lib/bitset/page.h
+++ b/src/lib/bitset/page.h
@@ -151,7 +151,7 @@ bitset_page_and(struct bitset_page *dst, struct bitset_page *src)
 	bitset_word_t *d = (bitset_word_t *) bitset_page_data(dst);
 	bitset_word_t *s = (bitset_word_t *) bitset_page_data(src);
 
-	assert (BITSET_PAGE_DATA_SIZE % sizeof(bitset_word_t) == 0);
+	assert(BITSET_PAGE_DATA_SIZE % sizeof(bitset_word_t) == 0);
 	int cnt = BITSET_PAGE_DATA_SIZE / sizeof(bitset_word_t);
 	for (int i = 0; i < cnt; i++) {
 		*d++ &= *s++;
@@ -164,7 +164,7 @@ bitset_page_nand(struct bitset_page *dst, struct bitset_page *src)
 	bitset_word_t *d = (bitset_word_t *) bitset_page_data(dst);
 	bitset_word_t *s = (bitset_word_t *) bitset_page_data(src);
 
-	assert (BITSET_PAGE_DATA_SIZE % sizeof(bitset_word_t) == 0);
+	assert(BITSET_PAGE_DATA_SIZE % sizeof(bitset_word_t) == 0);
 	int cnt = BITSET_PAGE_DATA_SIZE / sizeof(bitset_word_t);
 	for (int i = 0; i < cnt; i++) {
 		*d++ &= ~*s++;
@@ -177,7 +177,7 @@ bitset_page_or(struct bitset_page *dst, struct bitset_page *src)
 	bitset_word_t *d = (bitset_word_t *) bitset_page_data(dst);
 	bitset_word_t *s = (bitset_word_t *) bitset_page_data(src);
 
-	assert (BITSET_PAGE_DATA_SIZE % sizeof(bitset_word_t) == 0);
+	assert(BITSET_PAGE_DATA_SIZE % sizeof(bitset_word_t) == 0);
 	int cnt = BITSET_PAGE_DATA_SIZE / sizeof(bitset_word_t);
 	for (int i = 0; i < cnt; i++) {
 		*d++ |= *s++;
diff --git a/src/memcached.cc b/src/memcached.cc
index a4b0567db196764ad5c7d9479efaeb0966478d14..1fa36b8f37fcc2d8b3afb9fe0db944d4c48c0abe 100644
--- a/src/memcached.cc
+++ b/src/memcached.cc
@@ -182,7 +182,7 @@ memcached_meta(struct tuple *tuple)
 {
 	uint32_t len;
 	const char *field = tuple_field(tuple, 1, &len);
-	assert (sizeof(struct meta) <= len);
+	assert(sizeof(struct meta) <= len);
 	return (struct meta *) field;
 }
 
diff --git a/test/box/lua.result b/test/box/lua.result
index f5d95e967cc70c33fbd8ab96171a5b290241946a..bcaca2c74ff89aea4f052734a0fbb7ca58666b3d 100644
--- a/test/box/lua.result
+++ b/test/box/lua.result
@@ -1186,7 +1186,7 @@ Found 1 tuple:
 [896755060]
 lua box.update(0, 'tes5', '#p', 0, '')
 ---
-error: 'UPDATE error: the new tuple has no fields'
+error: 'Illegal parameters, tuple must have all indexed fields'
 ...
 lua box.space[0]:truncate()
 ---
diff --git a/test/box/lua_misc.result b/test/box/lua_misc.result
index 31495fe9f81dff39e6722736d0b62c40769479e5..d931d1f6be428a68e8d13331a967c9874d199d82 100644
--- a/test/box/lua_misc.result
+++ b/test/box/lua_misc.result
@@ -106,25 +106,24 @@ box.error.ER_PROC_RET: 12290
 box.error.ER_TUPLE_IS_TOO_LONG: 11010
 box.error.ER_EXACT_MATCH: 11522
 box.error.ER_SECONDARY: 770
-box.error.ER_SPACE_DISABLED: 13314
 box.error.ER_OK: 0
-box.error.ER_TUPLE_FOUND: 14082
+box.error.ER_NO_SUCH_INDEX: 13570
 box.error.ER_TUPLE_NOT_FOUND: 12546
 box.error.ER_FIBER_STACK: 6658
-box.error.ER_SPLICE: 10754
-box.error.ER_NO_SUCH_FIELD: 13826
+box.error.ER_UNKNOWN_UPDATE_OP: 11266
+box.error.ER_TUPLE_FOUND: 14082
 box.error.ER_UNSUPPORTED: 2562
 box.error.ER_INJECTION: 2306
-box.error.ER_NO_SUCH_INDEX: 13570
+box.error.ER_NO_SUCH_FIELD: 13826
 box.error.ER_TUPLE_IS_RO: 1025
 box.error.ER_ARG_TYPE: 10498
 box.error.ER_NO_SUCH_SPACE: 14594
-box.error.ER_TUPLE_IS_EMPTY: 6402
+box.error.ER_SPACE_DISABLED: 13314
 box.error.ER_PROC_LUA: 13058
 box.error.ER_NO_SUCH_PROC: 12802
-box.error.ER_UNKNOWN_UPDATE_OP: 11266
+box.error.ER_SPLICE: 10754
 box.error.ER_KEY_PART_COUNT: 12034
-box.error.ER_FIELD_TYPE: 10242
 box.error.ER_WAL_IO: 9986
+box.error.ER_FIELD_TYPE: 10242
 box.error.ER_MEMORY_ISSUE: 1793
 ...
diff --git a/test/lib/sql_ast.py b/test/lib/sql_ast.py
index 1d828a732079bb7d6a40e848cc20b4d788a92016..6bf6e9f59938b4976efa0c2bca1ac35df8afc305 100644
--- a/test/lib/sql_ast.py
+++ b/test/lib/sql_ast.py
@@ -50,7 +50,7 @@ ER = {
    22: "ER_RESERVED22"          ,
    23: "ER_RESERVED23"          ,
    24: "ER_UNUSED24"            ,
-   25: "ER_TUPLE_IS_EMPTY"      ,
+   25: "ER_UNUSED25"            ,
    26: "ER_FIBER_STACK"         ,
    27: "ER_UNUSED27"            ,
    28: "ER_UNUSED28"            ,