diff --git a/src/box/iproto_constants.c b/src/box/iproto_constants.c
index 329331a92a6b4fcdb4f3ec19d4cd8f7e563209da..8cdf759ea2d07d42bea886657e42c7859590f834 100644
--- a/src/box/iproto_constants.c
+++ b/src/box/iproto_constants.c
@@ -83,6 +83,7 @@ const unsigned char iproto_key_type[IPROTO_KEY_MAX] =
 	/* 0x25 */	MP_STR, /* IPROTO_CLUSTER_UUID */
 	/* 0x26 */	MP_MAP, /* IPROTO_VCLOCK */
 	/* 0x27 */	MP_STR, /* IPROTO_EXPR */
+	/* 0x28 */	MP_ARRAY, /* IPROTO_OPS */
 	/* }}} */
 };
 
@@ -97,11 +98,11 @@ const char *iproto_type_strs[] =
 	"CALL",
 	"AUTH",
 	"EVAL",
-	"UPSERT"
+	"UPSERT",
 };
 
 #define bit(c) (1ULL<<IPROTO_##c)
-const uint64_t iproto_body_key_map[IPROTO_EVAL + 1] = {
+const uint64_t iproto_body_key_map[IPROTO_UPSERT + 1] = {
 	0,                                                     /* unused */
 	bit(SPACE_ID) | bit(LIMIT) | bit(KEY),                 /* SELECT */
 	bit(SPACE_ID) | bit(TUPLE),                            /* INSERT */
@@ -109,8 +110,9 @@ const uint64_t iproto_body_key_map[IPROTO_EVAL + 1] = {
 	bit(SPACE_ID) | bit(KEY) | bit(TUPLE),                 /* UPDATE */
 	bit(SPACE_ID) | bit(KEY),                              /* DELETE */
 	bit(FUNCTION_NAME) | bit(TUPLE),                       /* CALL */
-	bit(USER_NAME) | bit(TUPLE),                           /* AUTH */
-	bit(EXPR)      | bit(TUPLE),                           /* EVAL */
+	bit(USER_NAME)| bit(TUPLE),                            /* AUTH */
+	bit(EXPR)     | bit(TUPLE),                            /* EVAL */
+	bit(SPACE_ID) | bit(KEY) | bit(OPS) | bit(TUPLE),      /* UPSERT */
 };
 #undef bit
 
@@ -155,5 +157,6 @@ const char *iproto_key_strs[IPROTO_KEY_MAX] = {
 	"cluster UUID",     /* 0x25 */
 	"vector clock",     /* 0x26 */
 	"expression",       /* 0x27 */
+	"operations",       /* 0x28 */
 };
 
diff --git a/src/box/iproto_constants.h b/src/box/iproto_constants.h
index a4ea276a19608e748db2245524cc0a31635090b6..f87752ec660d39eec741e2dbc5b5b352c6d02781 100644
--- a/src/box/iproto_constants.h
+++ b/src/box/iproto_constants.h
@@ -69,7 +69,7 @@ enum iproto_key {
 	IPROTO_CLUSTER_UUID = 0x25,
 	IPROTO_VCLOCK = 0x26,
 	IPROTO_EXPR = 0x27, /* EVAL */
-	IPROTO_DEF_TUPLE = 0x28,
+	IPROTO_OPS = 0x28, /* UPSERT but not UPDATE ops, because of legacy */
 	/* Leave a gap between request keys and response keys */
 	IPROTO_DATA = 0x30,
 	IPROTO_ERROR = 0x31,
@@ -83,7 +83,7 @@ enum iproto_key {
 #define IPROTO_BODY_BMAP (bit(SPACE_ID) | bit(INDEX_ID) | bit(LIMIT) |\
 			  bit(OFFSET) | bit(ITERATOR) | bit(INDEX_BASE) |\
 			  bit(KEY) | bit(TUPLE) | bit(FUNCTION_NAME) | \
-			  bit(USER_NAME) | bit(EXPR))
+			  bit(USER_NAME) | bit(EXPR) | bit(OPS))
 
 static inline bool
 xrow_header_has_key(const char *pos, const char *end)
@@ -121,14 +121,11 @@ enum iproto_type {
 	IPROTO_REPLACE = 3,
 	IPROTO_UPDATE = 4,
 	IPROTO_DELETE = 5,
-	IPROTO_TYPE_DML_MAX = IPROTO_DELETE + 1,
 	IPROTO_CALL = 6,
 	IPROTO_AUTH = 7,
 	IPROTO_EVAL = 8,
 	IPROTO_UPSERT = 9,
-	IPROTO_TYPE_EXT_DML_MAX = IPROTO_UPSERT + 1,
 	IPROTO_TYPE_STAT_MAX = IPROTO_UPSERT + 1,
-	/* new range of dml command codes */
 	/* admin command codes */
 	IPROTO_PING = 64,
 	IPROTO_JOIN = 65,
@@ -161,8 +158,8 @@ iproto_type_is_select(uint32_t type)
 static inline bool
 iproto_type_is_dml(uint32_t type)
 {
-	return (type >= IPROTO_SELECT && type < IPROTO_TYPE_DML_MAX) ||
-	       (type == IPROTO_UPSERT && type < IPROTO_TYPE_EXT_DML_MAX);
+	return (type >= IPROTO_SELECT && type <= IPROTO_DELETE) ||
+		type == IPROTO_UPSERT;
 }
 
 static inline bool
diff --git a/src/box/lua/call.cc b/src/box/lua/call.cc
index c43e35b36400baf72eb9c0d7dc60c1a9c2a74f0e..14366f043d677d48a96b7f8c23a91de03f443ca5 100644
--- a/src/box/lua/call.cc
+++ b/src/box/lua/call.cc
@@ -189,7 +189,7 @@ lbox_process(lua_State *L)
 void
 lbox_request_create(struct request *request,
 		    struct lua_State *L, enum iproto_type type,
-		    int key, int tuple, int default_tuple)
+		    int key, int tuple, int ops)
 {
 	request_create(request, type);
 	request->space_id = lua_tointeger(L, 1);
@@ -219,15 +219,14 @@ lbox_request_create(struct request *request,
 		request->tuple = (char *) region_join(gc, tuple_len);
 		request->tuple_end = request->tuple + tuple_len;
 	}
-	if (default_tuple > 0) {
+	if (ops > 0) {
 		size_t used = region_used(gc);
 		mpstream_reset(&stream);
-		luamp_encode_tuple(L, luaL_msgpack_default, &stream,
-				   default_tuple);
+		luamp_encode_tuple(L, luaL_msgpack_default, &stream, ops);
 		mpstream_flush(&stream);
-		size_t tuple_len = region_used(gc) - used;
-		request->default_tuple = (char *) region_join(gc, tuple_len);
-		request->default_tuple_end = request->default_tuple + tuple_len;
+		size_t ops_len = region_used(gc) - used;
+		request->ops = (char *) region_join(gc, ops_len);
+		request->ops_end = request->ops + ops_len;
 	}
 }
 
@@ -313,6 +312,7 @@ lbox_update(lua_State *L)
 
 	struct request request;
 	struct port_lua port;
+	/** Legacy: in case of update, ops are passed in in request tuple */
 	lbox_request_create(&request, L, IPROTO_UPDATE, 3, 4, -1);
 	request.index_base = 1; /* field ids are one-indexed */
 	port_lua_create(&port, L);
@@ -327,11 +327,11 @@ lbox_upsert(lua_State *L)
 	if (lua_gettop(L) != 5 || !lua_isnumber(L, 1) || !lua_isnumber(L, 2) ||
 	    lua_type(L, 3) != LUA_TTABLE || lua_type(L, 4) != LUA_TTABLE ||
 	    lua_type(L, 5) != LUA_TTABLE)
-		return luaL_error(L, "Usage space:upsert(key, ops, def_tuple)");
+		return luaL_error(L, "Usage space:upsert(key, ops, tuple)");
 
 	struct request request;
 	struct port_lua port;
-	lbox_request_create(&request, L, IPROTO_UPSERT, 3, 4, 5);
+	lbox_request_create(&request, L, IPROTO_UPSERT, 3, 5, 4);
 	request.index_base = 1; /* field ids are one-indexed */
 	port_lua_create(&port, L);
 	/* Ignore index_id for now */
diff --git a/src/box/lua/tuple.cc b/src/box/lua/tuple.cc
index 323cc18673d264dcc53b8ff870e2aedccdd29bad..631dd265dfce085368d48b6583d0826dad8f0119 100644
--- a/src/box/lua/tuple.cc
+++ b/src/box/lua/tuple.cc
@@ -367,4 +367,18 @@ boxffi_tuple_update(struct tuple *tuple, const char *expr, const char *expr_end)
 	} catch (ClientError *e) {
 		return NULL;
 	}
+
+}
+struct tuple *
+boxffi_tuple_upsert(struct tuple *tuple, const char *expr, const char *expr_end)
+{
+	RegionGuard region_guard(&fiber()->gc);
+	try {
+		struct tuple *new_tuple = tuple_upsert(tuple_format_ber,
+			region_alloc_cb, &fiber()->gc, tuple, expr, expr_end, 1);
+		tuple_ref(new_tuple); /* must not throw in this case */
+		return new_tuple;
+	} catch (ClientError *e) {
+		return NULL;
+	}
 }
diff --git a/src/box/lua/tuple.lua b/src/box/lua/tuple.lua
index 7a87974f67f41f159bc32bbd2b203d9f0083d482..7ca694feff8fea1c518ff0e05231124680e901e0 100644
--- a/src/box/lua/tuple.lua
+++ b/src/box/lua/tuple.lua
@@ -42,7 +42,12 @@ void
 tuple_to_buf(struct tuple *tuple, char *buf);
 
 struct tuple *
-boxffi_tuple_update(struct tuple *tuple, const char *expr, const char *expr_end);
+boxffi_tuple_update(struct tuple *tuple, const char *expr,
+                    const char *expr_end);
+
+struct tuple *
+boxffi_tuple_upsert(struct tuple *tuple, const char *expr,
+                    const char *expr_end);
 ]])
 
 local builtin = ffi.C
@@ -167,6 +172,18 @@ local function tuple_update(tuple, expr)
     return tuple_bless(tuple)
 end
 
+local function tuple_upsert(tuple, expr)
+    if type(expr) ~= 'table' then
+        error("Usage: tuple:upsert({ { op, field, arg}+ })")
+    end
+    local pexpr, pexpr_end = msgpackffi.encode_tuple(expr)
+    local tuple = builtin.boxffi_tuple_upsert(tuple, pexpr, pexpr_end)
+    if tuple == nil then
+        return box.error()
+    end
+    return tuple_bless(tuple)
+end
+
 -- Set encode hooks for msgpackffi
 local function tuple_to_msgpack(buf, tuple)
     local data = buf:alloc(tuple._bsize)
@@ -189,6 +206,7 @@ local methods = {
     ["unpack"]      = tuple_unpack;
     ["totable"]     = tuple_totable;
     ["update"]      = tuple_update;
+    ["upsert"]      = tuple_upsert;
     ["bsize"]       = function(tuple)
         return tonumber(tuple._bsize)
     end;
diff --git a/src/box/request.cc b/src/box/request.cc
index b127f63185abad237b99d5ecfda8543b16b05ad6..2e7295b2a2641a4a87f9b1993436721f67fad059 100644
--- a/src/box/request.cc
+++ b/src/box/request.cc
@@ -93,7 +93,7 @@ execute_update(struct request *request, struct port *port)
 	}
 	TupleGuard old_guard(old_tuple);
 
-	/* Update the tuple. */
+	/* Update the tuple; legacy, request ops are in request->tuple */
 	struct tuple *new_tuple = tuple_update(space->format,
 					       region_alloc_cb,
 					       &fiber()->gc,
@@ -115,7 +115,7 @@ execute_update(struct request *request, struct port *port)
 }
 
 static void
-execute_upsert(struct request *request, struct port *port)
+execute_upsert(struct request *request, struct port * /* port */)
 {
 	struct space *space = space_cache_find(request->space_id);
 	struct txn *txn = txn_begin_stmt(request, space);
@@ -128,39 +128,33 @@ execute_upsert(struct request *request, struct port *port)
 	primary_key_validate(pk->key_def, key, part_count);
 	struct tuple *old_tuple = pk->findByKey(key, part_count);
 
-	TupleGuardSafe old_guard(old_tuple);
+	if (old_tuple == NULL) {
+		struct tuple *new_tuple = tuple_new(space->format,
+						    request->tuple,
+						    request->tuple_end);
+		TupleGuard guard(new_tuple);
+		space_validate_tuple(space, new_tuple);
+
+		txn_replace(txn, space, NULL, new_tuple, DUP_INSERT);
+	} else {
+		TupleGuard old_guard(old_tuple);
+
+		/* Update the tuple. */
+		struct tuple *new_tuple =
+			tuple_upsert(space->format, region_alloc_cb,
+				     &fiber()->gc, old_tuple,
+				     request->ops, request->ops_end,
+				     request->index_base);
+		TupleGuard guard(new_tuple);
 
-	/* Update the tuple. */
-	struct tuple *new_tuple =
-		tuple_upsert(space->format, region_alloc_cb,
-			     &fiber()->gc, old_tuple,
-			     request->default_tuple, request->default_tuple_end,
-			     request->tuple, request->tuple_end,
-			     request->index_base);
-	TupleGuard guard(new_tuple);
-	try {
 		space_validate_tuple(space, new_tuple);
-		if (old_tuple &&
-		   !engine_auto_check_update(space->handler->engine->flags))
+		if (!engine_auto_check_update(space->handler->engine->flags))
 			space_check_update(space, old_tuple, new_tuple);
-		txn_replace(txn, space, old_tuple, new_tuple,
-			    old_tuple ? DUP_REPLACE : DUP_INSERT);
-	} catch (ClientError *e) {
-		say_error("The following error occured during UPSERT "
-				  "operation:");
-		e->log();
-		txn_rollback_stmt();
-		if (old_tuple)
-			port_add_tuple(port, old_tuple);
-		return;
+		txn_replace(txn, space, old_tuple, new_tuple, DUP_REPLACE);
 	}
+
 	txn_commit_stmt(txn);
-	/*
-	 * Adding result to port must be after possible WAL write.
-	 * The reason is that any yield between port_add_tuple and port_eof
-	 * calls could lead to sending not finished response to iproto socket.
-	 */
-	port_add_tuple(port, new_tuple);
+	/* Return nothing: upsert does not return data. */
 }
 
 static void
@@ -318,9 +312,11 @@ request_decode(struct request *request, const char *data, uint32_t len)
 		case IPROTO_EXPR:
 			request->key = value;
 			request->key_end = data;
-		case IPROTO_DEF_TUPLE:
-			request->default_tuple = value;
-			request->default_tuple_end = data;
+			break;
+		case IPROTO_OPS:
+			request->ops = value;
+			request->ops_end = data;
+			break;
 		default:
 			break;
 		}
@@ -339,9 +335,10 @@ int
 request_encode(struct request *request, struct iovec *iov)
 {
 	int iovcnt = 1;
-	const int HEADER_LEN_MAX = 32;
+	const int MAP_LEN_MAX = 40;
 	uint32_t key_len = request->key_end - request->key;
-	uint32_t len = HEADER_LEN_MAX + key_len;
+	uint32_t ops_len = request->ops_end - request->ops;
+	uint32_t len = MAP_LEN_MAX + key_len;
 	char *begin = (char *) region_alloc(&fiber()->gc, len);
 	char *pos = begin + 1;     /* skip 1 byte for MP_MAP */
 	int map_size = 0;
@@ -355,21 +352,27 @@ request_encode(struct request *request, struct iovec *iov)
 		pos = mp_encode_uint(pos, request->index_id);
 		map_size++;
 	}
+	if (request->index_base) { /* UPDATE/UPSERT */
+		pos = mp_encode_uint(pos, IPROTO_INDEX_BASE);
+		pos = mp_encode_uint(pos, request->index_base);
+		map_size++;
+	}
 	if (request->key) {
 		pos = mp_encode_uint(pos, IPROTO_KEY);
 		memcpy(pos, request->key, key_len);
 		pos += key_len;
 		map_size++;
 	}
-	if (request->index_base) { /* only for UPDATE */
-		pos = mp_encode_uint(pos, IPROTO_INDEX_BASE);
-		pos = mp_encode_uint(pos, request->index_base);
+	if (request->ops) {
+		pos = mp_encode_uint(pos, IPROTO_OPS);
+		memcpy(pos, request->ops, ops_len);
+		pos += ops_len;
 		map_size++;
 	}
 	if (request->tuple) {
 		pos = mp_encode_uint(pos, IPROTO_TUPLE);
-		iov[1].iov_base = (void *) request->tuple;
-		iov[1].iov_len = (request->tuple_end - request->tuple);
+		iov[iovcnt].iov_base = (void *) request->tuple;
+		iov[iovcnt].iov_len = (request->tuple_end - request->tuple);
 		iovcnt++;
 		map_size++;
 	}
diff --git a/src/box/request.h b/src/box/request.h
index 16d6d81d2a84de8c02b178621a35951708414b90..82e3d477dd3ad92e0332882f191a0fd64523f745 100644
--- a/src/box/request.h
+++ b/src/box/request.h
@@ -55,13 +55,13 @@ struct request
 	/** Search key or proc name. */
 	const char *key;
 	const char *key_end;
-	/** Insert/replace tuple or proc argument or update operations. */
+	/** Insert/replace/upsert tuple or proc argument or update operations. */
 	const char *tuple;
 	const char *tuple_end;
-	/** Additional tuple of request, currently used by UPSERT */
-	const char *default_tuple;
-	const char *default_tuple_end;
-	/** Base field offset for UPDATE, e.g. 0 for C and 1 for Lua. */
+	/** Upsert operations. */
+	const char *ops;
+	const char *ops_end;
+	/** Base field offset for UPDATE/UPSERT, e.g. 0 for C and 1 for Lua. */
 	int index_base;
 };
 
diff --git a/src/box/tuple.cc b/src/box/tuple.cc
index 0589714b0fd4e1016aa50eca73e8dc4d36b0f52d..6fee7b7fbb394e22648612abd1179d62f9beaf9b 100644
--- a/src/box/tuple.cc
+++ b/src/box/tuple.cc
@@ -399,18 +399,9 @@ tuple_field_cstr(struct tuple *tuple, uint32_t i)
 }
 
 struct tuple *
-tuple_update(struct tuple_format *format,
-	     tuple_update_alloc_func f, void *alloc_ctx,
-	     const struct tuple *old_tuple, const char *expr,
-	     const char *expr_end, int field_base)
+tuple_bless(struct tuple_format *format,
+	    const char *new_data, size_t new_size)
 {
-	uint32_t new_size = 0;
-	const char *new_data =
-		tuple_update_execute(f, alloc_ctx,
-				     expr, expr_end, old_tuple->data,
-				     old_tuple->data + old_tuple->bsize,
-				     &new_size, field_base);
-
 	/* Allocate a new tuple. */
 	assert(mp_typeof(*new_data) == MP_ARRAY);
 	struct tuple *new_tuple = tuple_new(format, new_data,
@@ -425,39 +416,37 @@ tuple_update(struct tuple_format *format,
 	return new_tuple;
 }
 
+struct tuple *
+tuple_update(struct tuple_format *format,
+	     tuple_update_alloc_func f, void *alloc_ctx,
+	     const struct tuple *old_tuple, const char *expr,
+	     const char *expr_end, int field_base)
+{
+	uint32_t new_size = 0;
+	const char *new_data =
+		tuple_update_execute(f, alloc_ctx,
+				     expr, expr_end, old_tuple->data,
+				     old_tuple->data + old_tuple->bsize,
+				     &new_size, field_base);
+
+	return tuple_bless(format, new_data, new_size);
+}
+
 struct tuple *
 tuple_upsert(struct tuple_format *format,
 	     void *(*region_alloc)(void *, size_t), void *alloc_ctx,
 	     const struct tuple *old_tuple,
-	     const char *plan_b_tuple_data, const char *plan_b_tuple_data_end,
 	     const char *expr, const char *expr_end, int field_base)
 {
 	uint32_t new_size = 0;
-	const char *old_data, *old_data_end;
-	if (old_tuple) {
-		old_data = old_tuple->data;
-		old_data_end = old_tuple->data + old_tuple->bsize;
-	} else {
-		old_data = plan_b_tuple_data;
-		old_data_end = plan_b_tuple_data_end;
-	}
+
 	const char *new_data =
 		tuple_upsert_execute(region_alloc, alloc_ctx, expr, expr_end,
-				     old_data, old_data_end,
+				     old_tuple->data,
+				     old_tuple->data + old_tuple->bsize,
 				     &new_size, field_base);
 
-	/* Allocate a new tuple. */
-	assert(mp_typeof(*new_data) == MP_ARRAY);
-	struct tuple *new_tuple = tuple_new(format, new_data,
-					    new_data + new_size);
-
-	try {
-		tuple_init_field_map(format, new_tuple, (uint32_t *)new_tuple);
-	} catch (Exception *e) {
-		tuple_delete(new_tuple);
-		throw;
-	}
-	return new_tuple;
+	return tuple_bless(format, new_data, new_size);
 }
 
 struct tuple *
diff --git a/src/box/tuple.h b/src/box/tuple.h
index 002eed68c101f1e2ed47261b3398603c18aa6e44..11c0fd6f6c22617500d283cc32ab45fe272bead4 100644
--- a/src/box/tuple.h
+++ b/src/box/tuple.h
@@ -232,29 +232,12 @@ tuple_unref(struct tuple *tuple)
 /** Make tuple references exception-friendly in absence of @finally. */
 struct TupleGuard {
 	struct tuple *tuple;
-	TupleGuard(struct tuple *arg) : tuple(arg) { tuple_ref(tuple); }
+	TupleGuard(struct tuple *arg) :tuple(arg) { tuple_ref(tuple); }
 	~TupleGuard() { tuple_unref(tuple); }
 	TupleGuard(const TupleGuard&) = delete;
 	void operator=(const TupleGuard&) = delete;
 };
 
-/** Same as TupleGuard, but accepts normally NULL pointers */
-struct TupleGuardSafe {
-	struct tuple *tuple;
-	TupleGuardSafe(struct tuple *arg) : tuple(arg)
-	{
-		if (tuple)
-			tuple_ref(tuple);
-	}
-	~TupleGuardSafe()
-	{
-		if (tuple)
-			tuple_unref(tuple);
-	}
-	TupleGuardSafe(const TupleGuardSafe&) = delete;
-	void operator=(const TupleGuardSafe&) = delete;
-};
-
 /**
 * @brief Return a tuple format instance
 * @param tuple tuple
@@ -483,7 +466,6 @@ struct tuple *
 tuple_upsert(struct tuple_format *new_format,
 	     void *(*region_alloc)(void *, size_t), void *alloc_ctx,
 	     const struct tuple *old_tuple,
-	     const char *plan_b_tuple_data, const char *plan_b_tuple_data_end,
 	     const char *expr, const char *expr_end, int field_base);
 
 /**
diff --git a/src/box/tuple_update.cc b/src/box/tuple_update.cc
index 8c633d890d33d35cbcb39186dd15961100042db3..93cdb830a9f0a81d205078a718d4582e852644cd 100644
--- a/src/box/tuple_update.cc
+++ b/src/box/tuple_update.cc
@@ -928,9 +928,8 @@ upsert_do_ops(struct tuple_update *update)
 	for (; op < ops_end; op++) {
 		try {
 			op->meta->do_op(update, op);
-		} catch(ClientError *e) {
-			say_error("The following error occured during UPSERT "
-				  "operation:");
+		} catch (ClientError *e) {
+			say_error("UPSERT operation failed:");
 			e->log();
 		}
 	}
diff --git a/src/box/tuple_update.h b/src/box/tuple_update.h
index e847096275844fe2a0b25e26a4a1ded14d91f151..14deacfdcfed6910cdb335053c544c1b420da640 100644
--- a/src/box/tuple_update.h
+++ b/src/box/tuple_update.h
@@ -49,7 +49,7 @@ tuple_update_execute(tuple_update_alloc_func alloc, void *alloc_ctx,
 
 const char *
 tuple_upsert_execute(tuple_update_alloc_func alloc, void *alloc_ctx,
-		     const char *expr,const char *expr_end,
+		     const char *expr, const char *expr_end,
 		     const char *old_data, const char *old_data_end,
 		     uint32_t *p_new_size, int index_base);
 
diff --git a/test/big/lua.result b/test/big/lua.result
index 6bf25aeb88b3046b5d2eef726faaef23d6c40b21..07b817ce80f7cb9ca16f630bc232fcd92612d24b 100644
--- a/test/big/lua.result
+++ b/test/big/lua.result
@@ -734,7 +734,7 @@ t:find(2, '2')
 ...
 t:find(89, '2')
 ---
-- error: '[string "-- tuple.lua (internal file)..."]:84: error: invalid key to ''next'''
+- error: '[string "-- tuple.lua (internal file)..."]:89: error: invalid key to ''next'''
 ...
 t:findall(4, '3')
 ---
diff --git a/test/box/tuple.result b/test/box/tuple.result
index 2c9e15e1a8bbc5b4874ea79cae635ffaf8a53940..9bb0e2fde2bd31ded68c240e0e20be7471fa122c 100644
--- a/test/box/tuple.result
+++ b/test/box/tuple.result
@@ -305,12 +305,12 @@ t:unpack(2, 1)
 ...
 t:totable(0)
 ---
-- error: '[string "-- tuple.lua (internal file)..."]:111: tuple.totable: invalid second
+- error: '[string "-- tuple.lua (internal file)..."]:116: tuple.totable: invalid second
     argument'
 ...
 t:totable(1, 0)
 ---
-- error: '[string "-- tuple.lua (internal file)..."]:120: tuple.totable: invalid third
+- error: '[string "-- tuple.lua (internal file)..."]:125: tuple.totable: invalid third
     argument'
 ...
 --
@@ -435,15 +435,15 @@ t:next(3)
 ...
 t:next(4)
 ---
-- error: '[string "-- tuple.lua (internal file)..."]:84: error: invalid key to ''next'''
+- error: '[string "-- tuple.lua (internal file)..."]:89: error: invalid key to ''next'''
 ...
 t:next(-1)
 ---
-- error: '[string "-- tuple.lua (internal file)..."]:84: error: invalid key to ''next'''
+- error: '[string "-- tuple.lua (internal file)..."]:89: error: invalid key to ''next'''
 ...
 t:next("fdsaf")
 ---
-- error: '[string "-- tuple.lua (internal file)..."]:67: error: invalid key to ''next'''
+- error: '[string "-- tuple.lua (internal file)..."]:72: error: invalid key to ''next'''
 ...
 box.tuple.new({'x', 'y', 'z'}):next()
 ---
@@ -455,7 +455,7 @@ t=space:insert{1953719668}
 ...
 t:next(1684234849)
 ---
-- error: '[string "-- tuple.lua (internal file)..."]:84: error: invalid key to ''next'''
+- error: '[string "-- tuple.lua (internal file)..."]:89: error: invalid key to ''next'''
 ...
 t:next(1)
 ---
@@ -611,7 +611,7 @@ r = {}
 ...
 for _state, val in t:pairs(10) do table.insert(r, val) end
 ---
-- error: '[string "-- tuple.lua (internal file)..."]:84: error: invalid key to ''next'''
+- error: '[string "-- tuple.lua (internal file)..."]:89: error: invalid key to ''next'''
 ...
 r
 ---
@@ -697,19 +697,19 @@ t:findall(1, 'xxxxx')
 ...
 t:find(100, 'a')
 ---
-- error: '[string "-- tuple.lua (internal file)..."]:84: error: invalid key to ''next'''
+- error: '[string "-- tuple.lua (internal file)..."]:89: error: invalid key to ''next'''
 ...
 t:findall(100, 'a')
 ---
-- error: '[string "-- tuple.lua (internal file)..."]:84: error: invalid key to ''next'''
+- error: '[string "-- tuple.lua (internal file)..."]:89: error: invalid key to ''next'''
 ...
 t:find(100, 'xxxxx')
 ---
-- error: '[string "-- tuple.lua (internal file)..."]:84: error: invalid key to ''next'''
+- error: '[string "-- tuple.lua (internal file)..."]:89: error: invalid key to ''next'''
 ...
 t:findall(100, 'xxxxx')
 ---
-- error: '[string "-- tuple.lua (internal file)..."]:84: error: invalid key to ''next'''
+- error: '[string "-- tuple.lua (internal file)..."]:89: error: invalid key to ''next'''
 ...
 ---
 -- Lua type coercion
@@ -803,12 +803,12 @@ t = box.tuple.new({'a', 'b', 'c', 'd', 'e'})
 ...
 t:update()
 ---
-- error: '[string "-- tuple.lua (internal file)..."]:160: Usage: tuple:update({ {
+- error: '[string "-- tuple.lua (internal file)..."]:165: Usage: tuple:update({ {
     op, field, arg}+ })'
 ...
 t:update(10)
 ---
-- error: '[string "-- tuple.lua (internal file)..."]:160: Usage: tuple:update({ {
+- error: '[string "-- tuple.lua (internal file)..."]:165: Usage: tuple:update({ {
     op, field, arg}+ })'
 ...
 t:update({})
diff --git a/test/box/update.result b/test/box/update.result
index 576f6f57e9e7a09ed603868988a9a6b7d894509f..30be9e9fe3b35ab656a998ca4137f7394cc277a4 100644
--- a/test/box/update.result
+++ b/test/box/update.result
@@ -767,27 +767,40 @@ s:delete{0}
 ...
 s:upsert({0}, {{'+', 2, 2}}) -- wrong usage
 ---
-- error: Usage space:upsert(key, ops, def_tuple)
+- error: Usage space:upsert(key, ops, tuple)
+...
+s:select{0}
+---
+- []
 ...
 s:upsert({0}, {{'+', 2, 2}}, {0, 0})
 ---
-- [0, 2]
+...
+s:select{0}
+---
+- - [0, 0]
 ...
 s:delete{0}
 ---
-- [0, 2]
+- [0, 0]
 ...
 s:upsert({0}, {{'+', 2, 2}}, {0, 0, 0})
 ---
-- [0, 2, 0]
+...
+s:select{0}
+---
+- - [0, 0, 0]
 ...
 s:delete{0}
 ---
-- [0, 2, 0]
+- [0, 0, 0]
 ...
 s:upsert({0}, {{'+', 2, 2}}, {0})
 ---
-- [0]
+...
+s:select{0}
+---
+- - [0]
 ...
 s:replace{0, 1, 2, 4}
 ---
@@ -795,7 +808,10 @@ s:replace{0, 1, 2, 4}
 ...
 s:upsert({0}, {{'+', 2, 2}}, {0, 0, "you will not see it"})
 ---
-- [0, 3, 2, 4]
+...
+s:select{0}
+---
+- - [0, 3, 2, 4]
 ...
 s:replace{0, -0x4000000000000000ll}
 ---
@@ -803,7 +819,10 @@ s:replace{0, -0x4000000000000000ll}
 ...
 s:upsert({0}, {{'+', 2, -0x4000000000000001ll}}, {0})  -- overflow
 ---
-- [0, -4611686018427387904]
+...
+s:select{0}
+---
+- - [0, -4611686018427387904]
 ...
 s:replace{0, "thing"}
 ---
@@ -811,7 +830,10 @@ s:replace{0, "thing"}
 ...
 s:upsert({0}, {{'+', 2, 2}}, {0, "nothing"})
 ---
-- [0, 'thing']
+...
+s:select{0}
+---
+- - [0, 'thing']
 ...
 s:delete{0}
 ---
@@ -819,7 +841,10 @@ s:delete{0}
 ...
 s:upsert({0}, {{'+', 2, 2}}, {0, "thing"})
 ---
-- [0, 'thing']
+...
+s:select{0}
+---
+- - [0, 'thing']
 ...
 s:replace{0, 1, 2}
 ---
@@ -827,15 +852,24 @@ s:replace{0, 1, 2}
 ...
 s:upsert({0}, {{'!', 42, 42}}, {0})
 ---
-- [0, 1, 2]
+...
+s:select{0}
+---
+- - [0, 1, 2]
 ...
 s:upsert({0}, {{'#', 42, 42}}, {0})
 ---
-- [0, 1, 2]
+...
+s:select{0}
+---
+- - [0, 1, 2]
 ...
 s:upsert({0}, {{'=', 42, 42}}, {0})
 ---
-- [0, 1, 2]
+...
+s:select{0}
+---
+- - [0, 1, 2]
 ...
 s:replace{0, 1.5}
 ---
@@ -843,7 +877,11 @@ s:replace{0, 1.5}
 ...
 s:upsert({0}, {{'|', 1, 255}}, {0})
 ---
-- [0, 1.5]
+- error: Attempt to modify a tuple field which is part of index 'pk' in space 'tweedledum'
+...
+s:select{0}
+---
+- - [0, 1.5]
 ...
 s:replace{0, 1.5}
 ---
@@ -855,15 +893,24 @@ s:replace{0, 'something to splice'}
 ...
 s:upsert({0}, {{':', 2, 1, 4, 'no'}}, {0})
 ---
-- [0, 'nothing to splice']
+...
+s:select{0}
+---
+- - [0, 'nothing to splice']
 ...
 s:upsert({0}, {{':', 2, 1, 2, 'every'}}, {0})
 ---
-- [0, 'everything to splice']
+...
+s:select{0}
+---
+- - [0, 'everything to splice']
 ...
 s:upsert({0}, {{':', 2, -100, 2, 'every'}}, {0})
 ---
-- [0, 'everything to splice']
+...
+s:select{0}
+---
+- - [0, 'everything to splice']
 ...
 s:drop()
 ---
diff --git a/test/box/update.test.lua b/test/box/update.test.lua
index e6373bb02c5f6f4229d2f9c082fe54190d95dbf6..f9ec4e781f93bfa3a2bebd1b182a435cbaa2791d 100644
--- a/test/box/update.test.lua
+++ b/test/box/update.test.lua
@@ -236,30 +236,45 @@ s:update({0}, {{'+', 2, -0x4000000000000001ll}})  -- overflow
 --UPSERT https://github.com/tarantool/tarantool/issues/905
 s:delete{0}
 s:upsert({0}, {{'+', 2, 2}}) -- wrong usage
+s:select{0}
 s:upsert({0}, {{'+', 2, 2}}, {0, 0})
+s:select{0}
 s:delete{0}
 s:upsert({0}, {{'+', 2, 2}}, {0, 0, 0})
+s:select{0}
 s:delete{0}
 s:upsert({0}, {{'+', 2, 2}}, {0})
+s:select{0}
 s:replace{0, 1, 2, 4}
 s:upsert({0}, {{'+', 2, 2}}, {0, 0, "you will not see it"})
+s:select{0}
 s:replace{0, -0x4000000000000000ll}
 s:upsert({0}, {{'+', 2, -0x4000000000000001ll}}, {0})  -- overflow
+s:select{0}
 s:replace{0, "thing"}
 s:upsert({0}, {{'+', 2, 2}}, {0, "nothing"})
+s:select{0}
 s:delete{0}
 s:upsert({0}, {{'+', 2, 2}}, {0, "thing"})
+s:select{0}
 s:replace{0, 1, 2}
 s:upsert({0}, {{'!', 42, 42}}, {0})
+s:select{0}
 s:upsert({0}, {{'#', 42, 42}}, {0})
+s:select{0}
 s:upsert({0}, {{'=', 42, 42}}, {0})
+s:select{0}
 s:replace{0, 1.5}
 s:upsert({0}, {{'|', 1, 255}}, {0})
+s:select{0}
 s:replace{0, 1.5}
 s:replace{0, 'something to splice'}
 s:upsert({0}, {{':', 2, 1, 4, 'no'}}, {0})
+s:select{0}
 s:upsert({0}, {{':', 2, 1, 2, 'every'}}, {0})
+s:select{0}
 s:upsert({0}, {{':', 2, -100, 2, 'every'}}, {0})
+s:select{0}
 
 s:drop()
 
diff --git a/test/replication/init_storage.result b/test/replication/init_storage.result
index 852144da15a733000e78ea0860da4c034212b5f4..5660e969ce6421775e85ce701fb979764846fda5 100644
--- a/test/replication/init_storage.result
+++ b/test/replication/init_storage.result
@@ -54,6 +54,9 @@ replica test 2 (must be ok)
 for k = 10, 19 do box.space[42]:insert{k, k*k*k} end
 ---
 ...
+for k = 20, 29 do box.space[42]:upsert({k}, {}, {k}) end
+---
+...
 space = box.space.test
 ---
 ...
diff --git a/test/replication/init_storage.test.py b/test/replication/init_storage.test.py
index d1bb875a012861fced25181fba660e67cbb464f3..ab556986d9597b87e896ccbd9ad229ef934685d8 100644
--- a/test/replication/init_storage.test.py
+++ b/test/replication/init_storage.test.py
@@ -57,6 +57,7 @@ print '-------------------------------------------------------------'
 
 master.restart()
 master.admin('for k = 10, 19 do box.space[42]:insert{k, k*k*k} end')
+master.admin("for k = 20, 29 do box.space[42]:upsert({k}, {}, {k}) end")
 lsn = master.get_lsn(master_id)
 
 replica = TarantoolServer(server.ini)