diff --git a/doc/sphinx/book/box/authentication.rst b/doc/sphinx/book/box/authentication.rst
index 8adeee8b3b080c86c3b1966c04987b8c3fd271ae..fee93d092a418eb8cccdb457dc56d76c751c31a9 100644
--- a/doc/sphinx/book/box/authentication.rst
+++ b/doc/sphinx/book/box/authentication.rst
@@ -193,7 +193,7 @@ The function for granting a privilege is:
 .. cssclass:: highlight
 .. parsed-literal::
 
-    box.schema.user.grant(*grantee*, *operation*, *object-type*, *obejct-name*[, *options*])
+    box.schema.user.grant(*grantee*, *operation*, *object-type*, *object-name*[, *options*])
     -- OR
     box.schema.user.grant(*grantee*, *operation*, 'universe' [, nil, *options*])
 
diff --git a/doc/sphinx/book/box/box_space.rst b/doc/sphinx/book/box/box_space.rst
index 2b6377f24365c95bce98ca6392edb16894e58832..e627f984b1ff6301e911852a51b93e0eccfb07c1 100644
--- a/doc/sphinx/book/box/box_space.rst
+++ b/doc/sphinx/book/box/box_space.rst
@@ -78,6 +78,7 @@ A list of all ``box.space`` functions follows, then comes a list of all
         | :class:`box.space._cluster`                                         | .(Metadata) List of clusters    |
         +---------------------------------------------------------------------+---------------------------------+
 
+
 .. module:: box.space
 
 .. class:: space_object
@@ -548,6 +549,7 @@ A list of all ``box.space`` functions follows, then comes a list of all
         :return: null.
 
         Possible errors: it is illegal to modify a primary-key field.
+        It is illegal to use upsert with a space that has a unique secondary index.
 
         **Complexity factors:** Index size, Index type, number of indexes accessed, WAL
         settings.
diff --git a/doc/sphinx/reference/digest.rst b/doc/sphinx/reference/digest.rst
index 075f34de502a7ca0201ec4aa33b3f1834927af08..5e1bf02c668f5ea4f6df1351164eb9b20c5313d5 100644
--- a/doc/sphinx/reference/digest.rst
+++ b/doc/sphinx/reference/digest.rst
@@ -109,7 +109,7 @@ functions in digest are:
 
 .. _digest-guava:
 
-.. function:: guaava(integer, integer)
+.. function:: guava(integer, integer)
 
     Returns a number made with consistent hash.
 
diff --git a/doc/sphinx/reference/shard.rst b/doc/sphinx/reference/shard.rst
index a33033bc0a83f12c4496e7b484346661ed1fb03d..d24e2e7f79caaf4401b6f86dff47fb35196eac56 100644
--- a/doc/sphinx/reference/shard.rst
+++ b/doc/sphinx/reference/shard.rst
@@ -97,9 +97,9 @@ The shard package will conclude that there is only one shard.
 
     tarantool> cfg = {
              >   servers = {
-             >     { uri = 'loclhost:33131', zone = '1' },
-             >     { uri = 'loclhost:33132', zone = '2' },
-             >     { uri = 'loclhost:33133', zone = '3' },1
+             >     { uri = 'localhost:33131', zone = '1' },
+             >     { uri = 'localhost:33132', zone = '2' },
+             >     { uri = 'localhost:33133', zone = '3' },1
              >   },
              >   login = 'tester',
              >   password = 'pass',
@@ -125,13 +125,13 @@ necessarily an error, because perhaps one of the servers in the list is not aliv
 
     tarantool> cfg = {
              >   servers = {
-             >     { uri = 'loclhost:33131', zone = '1' },
-             >     { uri = 'loclhost:33131', zone = '2' },
-             >     { uri = 'loclhost:33132', zone = '1' },
-             >     { uri = 'loclhost:33133', zone = '2' },
-             >     { uri = 'loclhost:33131', zone = '1' },
-             >     { uri = 'loclhost:33132', zone = '2' },
-             >     { uri = 'loclhost:33133', zone = '1' },
+             >     { uri = 'localhost:33131', zone = '1' },
+             >     { uri = 'localhost:33131', zone = '2' },
+             >     { uri = 'localhost:33132', zone = '1' },
+             >     { uri = 'localhost:33133', zone = '2' },
+             >     { uri = 'localhost:33131', zone = '1' },
+             >     { uri = 'localhost:33132', zone = '2' },
+             >     { uri = 'localhost:33133', zone = '1' },
              >   },
              >   login = 'tester',
              >   password = 'pass',
diff --git a/doc/sphinx/reference/socket.rst b/doc/sphinx/reference/socket.rst
index 4c9a9bf1b31221aabdc74f8ba9d14a85b161084e..db571d1ffd2f96c43d35d866210aee32e7d4d748 100644
--- a/doc/sphinx/reference/socket.rst
+++ b/doc/sphinx/reference/socket.rst
@@ -27,8 +27,8 @@ are ``errno``, ``error``.
     |    Purposes    |    Names                                                      |
     +================+===============================================================+
     |    setup       | :ref:`socket() <socket-socket>`                               |
-    |                +---------------------------------------------------------------+
-    |                | :func:`socket.tcp_connect() <socket.tcp_connect>`             |
+    +----------------+---------------------------------------------------------------+
+    |    ""          | :func:`socket.tcp_connect() <socket.tcp_connect>`             |
     +----------------+---------------------------------------------------------------+
     |    ""          | :func:`socket.tcp_server() <socket.tcp_server>`               |
     +----------------+---------------------------------------------------------------+
diff --git a/doc/sphinx/reference/tarantool.rst b/doc/sphinx/reference/tarantool.rst
index 88bab8c2ff6538098436f79c3544512de8e05d82..770f670795a6fb52ffc7f659a030b4a368271e34 100644
--- a/doc/sphinx/reference/tarantool.rst
+++ b/doc/sphinx/reference/tarantool.rst
@@ -25,7 +25,7 @@ the tarantool package is recommended.
     ---
     - build:
         target: Linux-x86_64-RelWithDebInfo
-        options: cmake . -DCMAKE_INSTALL_PREFIX=/usr -DENABLE_TRACE=ON -DENABLE_BACKTRACE=ON
+        options: cmake . -DCMAKE_INSTALL_PREFIX=/usr -DENABLE_BACKTRACE=ON
         mod_format: so
         flags: ' -fno-common -fno-omit-frame-pointer -fno-stack-protector -fexceptions
           -funwind-tables -fopenmp -msse2 -std=c11 -Wall -Wextra -Wno-sign-compare -Wno-strict-aliasing
diff --git a/src/box/sophia_index.cc b/src/box/sophia_index.cc
index 43c9cf94cc1023728892ccb021aa6efb2f705c19..cb7c3df8700b5f9fd767276a8a225d5ceba32eb2 100644
--- a/src/box/sophia_index.cc
+++ b/src/box/sophia_index.cc
@@ -413,7 +413,7 @@ sophia_upsert_mp(char **tuple, int *tuple_size_key, struct key_def *key_def,
 			mp_keysize += mp_sizeof_uint(load_u64(key[i]));
 		i++;
 	}
-	*tuple_size_key = mp_keysize + mp_sizeof_array(key_def->part_count);
+	*tuple_size_key = mp_keysize;
 
 	/* count fields */
 	int count = key_def->part_count;
@@ -474,14 +474,20 @@ sophia_upsert(char **result,
 		sophia_mempool_free(&alloc);
 		return -1;
 	}
+
+	/* skip array size and key */
+	const char *ptr = up;
+	mp_decode_array(&ptr);
+	ptr += tuple_size_key;
+
 	/* get new value */
-	int size = up_size - tuple_size_key;
+	int size = (int)((up + up_size) -  ptr);
 	*result = (char *)malloc(size);
 	if (! *result) {
 		sophia_mempool_free(&alloc);
 		return -1;
 	}
-	memcpy(*result, up + tuple_size_key, size);
+	memcpy(*result, ptr, size);
 	sophia_mempool_free(&alloc);
 	return size;
 }
diff --git a/src/lua/fio.c b/src/lua/fio.c
index 32b26e7fa0927a02462b256e7fb60b661ad79b35..90281805960ec70bb6c4ffde65489fb790586ac7 100644
--- a/src/lua/fio.c
+++ b/src/lua/fio.c
@@ -486,7 +486,7 @@ lbox_fio_mkdir(struct lua_State *L)
 	if (top >= 2)
 		mode = lua_tointeger(L, 2);
 	else
-		mode = 0;
+		mode = 0777;
 	lua_pushboolean(L, coeio_mkdir(pathname, mode) == 0);
 	return 1;
 }
diff --git a/src/lua/ipc.c b/src/lua/ipc.c
index 244ab2c17213330527f119f39ceafbb53f8dc679..3be7f56816c618cb30e04df6f1850fb2272d0327 100644
--- a/src/lua/ipc.c
+++ b/src/lua/ipc.c
@@ -52,14 +52,16 @@ lbox_ipc_channel(struct lua_State *L)
 {
 	lua_Integer size = 0;
 
-	if (lua_gettop(L) > 0) {
-		if (lua_gettop(L) != 1 || !lua_isnumber(L, 1))
-			luaL_error(L, "fiber.channel(size): bad arguments");
-
+	if (lua_isnoneornil(L, 1)) {
+		size = 0;
+	} else if (lua_isnumber(L, 1)) {
 		size = lua_tointeger(L, -1);
 		if (size < 0)
 			luaL_error(L, "fiber.channel(size): negative size");
+	} else {
+		luaL_error(L, "fiber.channel(size): bad arguments");
 	}
+
 	struct ipc_channel *ch = (struct ipc_channel *)
 		lua_newuserdata(L, ipc_channel_memsize(size));
 	if (ch == NULL)
@@ -73,17 +75,13 @@ lbox_ipc_channel(struct lua_State *L)
 }
 
 static inline struct ipc_channel *
-lbox_check_channel(struct lua_State *L, int narg, const char *source)
+lbox_check_channel(struct lua_State *L, int index, const char *source)
 {
-	void *ch;
-	int top = lua_gettop(L);
-
-	if (narg > top || top + narg < 0 ||
-	    (ch = luaL_checkudata(L, narg, ipc_lib)) == NULL) {
-
+	assert(index > 0);
+	if (index > lua_gettop(L))
 		luaL_error(L, "usage: %s", source);
-	}
-	return (struct ipc_channel *) ch;
+	/* Note: checkudata errs on mismatch, no point in checking res */
+	return (struct ipc_channel *) luaL_checkudata(L, index, ipc_lib);
 }
 
 static int
@@ -99,7 +97,7 @@ lbox_ipc_channel_gc(struct lua_State *L)
 static int
 lbox_ipc_channel_is_full(struct lua_State *L)
 {
-	struct ipc_channel *ch = lbox_check_channel(L, -1, "channel:is_full()");
+	struct ipc_channel *ch = lbox_check_channel(L, 1, "channel:is_full()");
 	lua_pushboolean(L, ipc_channel_is_full(ch));
 	return 1;
 }
@@ -107,7 +105,7 @@ lbox_ipc_channel_is_full(struct lua_State *L)
 static int
 lbox_ipc_channel_is_empty(struct lua_State *L)
 {
-	struct ipc_channel *ch = lbox_check_channel(L, -1,
+	struct ipc_channel *ch = lbox_check_channel(L, 1,
 						    "channel:is_empty()");
 	lua_pushboolean(L, ipc_channel_is_empty(ch));
 	return 1;
@@ -124,26 +122,25 @@ lua_ipc_value_destroy(struct ipc_msg *base)
 static int
 lbox_ipc_channel_put(struct lua_State *L)
 {
+	static const char usage[] = "channel:put(var [, timeout])";
 	int rc = -1;
 	struct ipc_channel *ch =
-		lbox_check_channel(L, 1, "channel:put(var, [, timeout])");
+		lbox_check_channel(L, 1, usage);
 	ev_tstamp timeout;
 
-	switch (lua_gettop(L)) {
-	case 2:
+	/* val */
+	if (lua_gettop(L) < 2)
+		luaL_error(L, "usage: %s", usage);
+
+	/* timeout (optional) */
+	if (lua_isnoneornil(L, 3)) {
 		timeout = TIMEOUT_INFINITY;
-		break;
-	case 3:
-		if (lua_isnumber(L, -1)) {
-			timeout = lua_tonumber(L, -1);
-			if (timeout >= 0)
-				/** Leave var on top of the stack. */
-				lua_pop(L, 1);
-				break;
-		}
-		/** Fall through. */
-	default:
-		luaL_error(L, "usage: channel:put(var [, timeout])");
+	} else if (lua_isnumber(L, 3)) {
+		timeout = lua_tonumber(L, 3);
+		if (timeout < 0)
+			luaL_error(L, "usage: %s", usage);
+	} else {
+		luaL_error(L, "usage: %s", usage);
 	}
 
 	struct ipc_value *value = ipc_value_new();
@@ -151,7 +148,7 @@ lbox_ipc_channel_put(struct lua_State *L)
 		goto end;
 
 	value->base.destroy = lua_ipc_value_destroy;
-	/* The referenced value is on top of the stack. */
+	lua_pushvalue(L, 2);
 	value->i = luaL_ref(L, LUA_REGISTRYINDEX);
 
 	rc = ipc_channel_put_msg_timeout(ch, &value->base, timeout);
@@ -173,23 +170,20 @@ lbox_ipc_channel_put(struct lua_State *L)
 static int
 lbox_ipc_channel_get(struct lua_State *L)
 {
+	static const char usage[] = "channel:get([timeout])";
 	struct ipc_channel *ch =
-		lbox_check_channel(L, 1, "channel:get([timeout])");
+		lbox_check_channel(L, 1, usage);
 	ev_tstamp timeout;
 
-	switch (lua_gettop(L)) {
-	case 1:
+	/* timeout (optional) */
+	if (lua_isnoneornil(L, 2)) {
 		timeout = TIMEOUT_INFINITY;
-		break;
-	case 2:
-		if (lua_isnumber(L, -1)) {
-			timeout = lua_tonumber(L, -1);
-			if (timeout >= 0)
-				break;
-		}
-		/** Fall through. */
-	default:
-		luaL_error(L, "usage: channel:get([timeout])");
+	} else if (lua_isnumber(L, 2)) {
+		timeout = lua_tonumber(L, 2);
+		if (timeout < 0)
+			luaL_error(L, "usage: %s", usage);
+	} else {
+		luaL_error(L, "usage: %s", usage);
 	}
 
 	struct ipc_value *value;
@@ -213,7 +207,7 @@ lbox_ipc_channel_get(struct lua_State *L)
 static int
 lbox_ipc_channel_has_readers(struct lua_State *L)
 {
-	struct ipc_channel *ch = lbox_check_channel(L, -1,
+	struct ipc_channel *ch = lbox_check_channel(L, 1,
 						    "channel:has_readers()");
 	lua_pushboolean(L, ipc_channel_has_readers(ch));
 	return 1;
@@ -222,7 +216,7 @@ lbox_ipc_channel_has_readers(struct lua_State *L)
 static int
 lbox_ipc_channel_has_writers(struct lua_State *L)
 {
-	struct ipc_channel *ch = lbox_check_channel(L, -1,
+	struct ipc_channel *ch = lbox_check_channel(L, 1,
 						    "channel:has_writers()");
 	lua_pushboolean(L, ipc_channel_has_writers(ch));
 	return 1;
@@ -231,7 +225,7 @@ lbox_ipc_channel_has_writers(struct lua_State *L)
 static int
 lbox_ipc_channel_size(struct lua_State *L)
 {
-	struct ipc_channel *ch = lbox_check_channel(L, -1, "channel:size()");
+	struct ipc_channel *ch = lbox_check_channel(L, 1, "channel:size()");
 	lua_pushinteger(L, ipc_channel_size(ch));
 	return 1;
 }
@@ -239,7 +233,7 @@ lbox_ipc_channel_size(struct lua_State *L)
 static int
 lbox_ipc_channel_count(struct lua_State *L)
 {
-	struct ipc_channel *ch = lbox_check_channel(L, -1, "channel:count()");
+	struct ipc_channel *ch = lbox_check_channel(L, 1, "channel:count()");
 	lua_pushinteger(L, ipc_channel_count(ch));
 	return 1;
 }
diff --git a/test/app/fio.result b/test/app/fio.result
index 4e115aa57356746e11ef9b467ee4f4756b342ae6..3378f7f5dd8c66943bdefe36113194b342d19b94 100644
--- a/test/app/fio.result
+++ b/test/app/fio.result
@@ -309,14 +309,32 @@ bit.band(fio.stat(file4).mode, 0x1FF) == 0x1F8
 ---
 - true
 ...
+dir1 = fio.pathjoin(tmpdir, 'dir1')
+---
+...
+dir2 = fio.pathjoin(tmpdir, 'dir2')
+---
+...
 fio.mkdir(nil)
 ---
 - error: Usage fio.mkdir(pathname[, mode])
 ...
-fio.mkdir(fio.pathjoin(tmpdir, "dir"))
+fio.mkdir(dir1) -- standard mode
+---
+- true
+...
+fio.mkdir(dir2, 1) -- custom mode
 ---
 - true
 ...
+string.format('%04o', bit.band(fio.stat(dir1).mode, 0x1FF))
+---
+- '0777'
+...
+string.format('%04o', bit.band(fio.stat(dir2).mode, 0x1FF))
+---
+- '0001'
+...
 -- cleanup directories
 { fh1:close(), fh3:close() }
 ---
@@ -334,7 +352,11 @@ fio.rmdir(nil)
 ---
 - error: 'Usage: fio.rmdir(pathname)'
 ...
-fio.rmdir(fio.pathjoin(tmpdir, "dir"))
+fio.rmdir(dir1)
+---
+- true
+...
+fio.rmdir(dir2)
 ---
 - true
 ...
diff --git a/test/app/fio.test.lua b/test/app/fio.test.lua
index 2883c46ebd2a71f314e918a8a198bc12ec4e1faf..281e70d2203408abc24b4fb895f5890eb03447ae 100644
--- a/test/app/fio.test.lua
+++ b/test/app/fio.test.lua
@@ -103,15 +103,21 @@ bit.band(fh3:stat().mode, 0x1FF) == 0x1F8
 bit.band(fio.stat(file4).mode, 0x1FF) == 0x1F8
 
 
+dir1 = fio.pathjoin(tmpdir, 'dir1')
+dir2 = fio.pathjoin(tmpdir, 'dir2')
 fio.mkdir(nil)
-fio.mkdir(fio.pathjoin(tmpdir, "dir"))
+fio.mkdir(dir1) -- standard mode
+fio.mkdir(dir2, 1) -- custom mode
+string.format('%04o', bit.band(fio.stat(dir1).mode, 0x1FF))
+string.format('%04o', bit.band(fio.stat(dir2).mode, 0x1FF))
 
 -- cleanup directories
 { fh1:close(), fh3:close() }
 { fh1:close(), errno.strerror(), fh3:close(), errno.strerror() }
 
 fio.rmdir(nil)
-fio.rmdir(fio.pathjoin(tmpdir, "dir"))
+fio.rmdir(dir1)
+fio.rmdir(dir2)
 
 { fio.unlink(file1), fio.unlink(file2), fio.unlink(file3), fio.unlink(file4) }
 { fio.unlink(file1), fio.unlink(file2), fio.unlink(file3), fio.unlink(file4) }
diff --git a/test/app/ipc.result b/test/app/ipc.result
index 75683072e08030d10c7a9ebebc015cc836f9c240..fd242923239862c268f3129a35cb79342e6394e2 100644
--- a/test/app/ipc.result
+++ b/test/app/ipc.result
@@ -7,6 +7,10 @@ env = require('test_run')
 test_run = env.new()
 ---
 ...
+-- channel methods ignore extra arguments, as regular Lua functions do
+ignored_args = {'Extra', 'arguments', 'are', 'ignored'}
+---
+...
 ch = fiber.channel(1)
 ---
 ...
@@ -26,10 +30,34 @@ ch:is_empty()
 ---
 - true
 ...
+ch:size(unpack(ignored_args))
+---
+- 1
+...
+ch:count(unpack(ignored_args))
+---
+- 0
+...
+ch:is_full(unpack(ignored_args))
+---
+- false
+...
+ch:is_empty(unpack(ignored_args))
+---
+- true
+...
 ch:get(.1)
 ---
 - null
 ...
+ch:get(.1, nil)
+---
+- null
+...
+ch:get(.1, nil, unpack(ignored_args))
+---
+- null
+...
 ch:put()
 ---
 - error: 'usage: channel:put(var [, timeout])'
@@ -46,6 +74,16 @@ ch:get()
 ---
 - test
 ...
+ch:put('test', nil), ch:get()
+---
+- true
+- test
+...
+ch:put('test', nil, unpack(ignored_args)), ch:get()
+---
+- true
+- test
+...
 ch:get('wrong timeout')
 ---
 - error: 'usage: channel:get([timeout])'
diff --git a/test/app/ipc.test.lua b/test/app/ipc.test.lua
index 1e812d0a01753a188625c60f0185e1257acb9c13..36c467611c3e63a331bfdbe4ef012fda7dc77897 100644
--- a/test/app/ipc.test.lua
+++ b/test/app/ipc.test.lua
@@ -2,16 +2,27 @@ fiber = require('fiber')
 env = require('test_run')
 test_run = env.new()
 
+-- channel methods ignore extra arguments, as regular Lua functions do
+ignored_args = {'Extra', 'arguments', 'are', 'ignored'}
+
 ch = fiber.channel(1)
 ch:size()
 ch:count()
 ch:is_full()
 ch:is_empty()
+ch:size(unpack(ignored_args))
+ch:count(unpack(ignored_args))
+ch:is_full(unpack(ignored_args))
+ch:is_empty(unpack(ignored_args))
 ch:get(.1)
+ch:get(.1, nil)
+ch:get(.1, nil, unpack(ignored_args))
 ch:put()
 ch:count()
 ch:put('test')
 ch:get()
+ch:put('test', nil), ch:get()
+ch:put('test', nil, unpack(ignored_args)), ch:get()
 ch:get('wrong timeout')
 ch:get(-10)
 ch:put(234)
diff --git a/test/sophia/gh.result b/test/sophia/gh.result
index 9d735c79733cd53bfa8163ecc5a2e12f50f46d41..f2f5b005a736aa7c72c739f9396c437971aa306e 100644
--- a/test/sophia/gh.result
+++ b/test/sophia/gh.result
@@ -207,3 +207,80 @@ i:select({'1','2',nil},{iterator='GT'})
 s:drop()
 ---
 ...
+-- gh-1407: upsert generate garbage data
+email_space_id = 'email'
+---
+...
+email_space = box.schema.space.create(email_space_id, { engine = 'sophia', if_not_exists = true })
+---
+...
+i = email_space:create_index('primary', { parts = {1, 'STR'} })
+---
+...
+time = 1234
+---
+...
+email = "test@domain.com"
+---
+...
+email_hash_index = "asdfasdfs"
+---
+...
+box.space.email:upsert({email, email_hash_index, time}, {{'!', -1, email_hash_index}, {'!', -1, time}})
+---
+...
+box.space.email:upsert({email, email_hash_index, time}, {{'!', -1, email_hash_index}, {'!', -1, time}})
+---
+...
+box.space.email:upsert({email, email_hash_index, time}, {{'!', -1, email_hash_index}, {'!', -1, time}})
+---
+...
+box.space.email:upsert({email, email_hash_index, time}, {{'!', -1, email_hash_index}, {'!', -1, time}})
+---
+...
+box.space.email:upsert({email, email_hash_index, time}, {{'!', -1, email_hash_index}, {'!', -1, time}})
+---
+...
+box.space.email:upsert({email, email_hash_index, time}, {{'!', -1, email_hash_index}, {'!', -1, time}})
+---
+...
+box.space.email:upsert({email, email_hash_index, time}, {{'!', -1, email_hash_index}, {'!', -1, time}})
+---
+...
+box.space.email:upsert({email, email_hash_index, time}, {{'!', -1, email_hash_index}, {'!', -1, time}})
+---
+...
+box.space.email:upsert({email, email_hash_index, time}, {{'!', -1, email_hash_index}, {'!', -1, time}})
+---
+...
+box.space.email:upsert({email, email_hash_index, time}, {{'!', -1, email_hash_index}, {'!', -1, time}})
+---
+...
+box.space.email:upsert({email, email_hash_index, time}, {{'!', -1, email_hash_index}, {'!', -1, time}})
+---
+...
+box.space.email:upsert({email, email_hash_index, time}, {{'!', -1, email_hash_index}, {'!', -1, time}})
+---
+...
+box.space.email:upsert({email, email_hash_index, time}, {{'!', -1, email_hash_index}, {'!', -1, time}})
+---
+...
+box.space.email:upsert({email, email_hash_index, time}, {{'!', -1, email_hash_index}, {'!', -1, time}})
+---
+...
+box.space.email:upsert({email, email_hash_index, time}, {{'!', -1, email_hash_index}, {'!', -1, time}})
+---
+...
+box.space.email:upsert({email, email_hash_index, time}, {{'!', -1, email_hash_index}, {'!', -1, time}})
+---
+...
+box.space.email:select{email}
+---
+- - ['test@domain.com', 'asdfasdfs', 1234, 'asdfasdfs', 1234, 'asdfasdfs', 1234, 'asdfasdfs',
+    1234, 'asdfasdfs', 1234, 'asdfasdfs', 1234, 'asdfasdfs', 1234, 'asdfasdfs', 1234,
+    'asdfasdfs', 1234, 'asdfasdfs', 1234, 'asdfasdfs', 1234, 'asdfasdfs', 1234, 'asdfasdfs',
+    1234, 'asdfasdfs', 1234, 'asdfasdfs', 1234, 'asdfasdfs', 1234]
+...
+box.space.email:drop()
+---
+...
diff --git a/test/sophia/gh.test.lua b/test/sophia/gh.test.lua
index 34408742ab57c47d38f1074d848205bdb9f536d0..44506bc6a746ec10b5d17bbceb364e1043ad57f7 100644
--- a/test/sophia/gh.test.lua
+++ b/test/sophia/gh.test.lua
@@ -88,3 +88,31 @@ s:insert{'1','2','3'}
 s:insert{'1','2','0'}
 i:select({'1','2',nil},{iterator='GT'})
 s:drop()
+
+
+-- gh-1407: upsert generate garbage data
+email_space_id = 'email'
+email_space = box.schema.space.create(email_space_id, { engine = 'sophia', if_not_exists = true })
+i = email_space:create_index('primary', { parts = {1, 'STR'} })
+
+time = 1234
+email = "test@domain.com"
+email_hash_index = "asdfasdfs"
+box.space.email:upsert({email, email_hash_index, time}, {{'!', -1, email_hash_index}, {'!', -1, time}})
+box.space.email:upsert({email, email_hash_index, time}, {{'!', -1, email_hash_index}, {'!', -1, time}})
+box.space.email:upsert({email, email_hash_index, time}, {{'!', -1, email_hash_index}, {'!', -1, time}})
+box.space.email:upsert({email, email_hash_index, time}, {{'!', -1, email_hash_index}, {'!', -1, time}})
+box.space.email:upsert({email, email_hash_index, time}, {{'!', -1, email_hash_index}, {'!', -1, time}})
+box.space.email:upsert({email, email_hash_index, time}, {{'!', -1, email_hash_index}, {'!', -1, time}})
+box.space.email:upsert({email, email_hash_index, time}, {{'!', -1, email_hash_index}, {'!', -1, time}})
+box.space.email:upsert({email, email_hash_index, time}, {{'!', -1, email_hash_index}, {'!', -1, time}})
+box.space.email:upsert({email, email_hash_index, time}, {{'!', -1, email_hash_index}, {'!', -1, time}})
+box.space.email:upsert({email, email_hash_index, time}, {{'!', -1, email_hash_index}, {'!', -1, time}})
+box.space.email:upsert({email, email_hash_index, time}, {{'!', -1, email_hash_index}, {'!', -1, time}})
+box.space.email:upsert({email, email_hash_index, time}, {{'!', -1, email_hash_index}, {'!', -1, time}})
+box.space.email:upsert({email, email_hash_index, time}, {{'!', -1, email_hash_index}, {'!', -1, time}})
+box.space.email:upsert({email, email_hash_index, time}, {{'!', -1, email_hash_index}, {'!', -1, time}})
+box.space.email:upsert({email, email_hash_index, time}, {{'!', -1, email_hash_index}, {'!', -1, time}})
+box.space.email:upsert({email, email_hash_index, time}, {{'!', -1, email_hash_index}, {'!', -1, time}})
+box.space.email:select{email}
+box.space.email:drop()