diff --git a/src/box/blackhole.c b/src/box/blackhole.c
index 88dab3fbebec4d95d50bdbb1ff0f48d02a645961..0412ffe89fbdaeb16a2e411d58a9867fde71957a 100644
--- a/src/box/blackhole.c
+++ b/src/box/blackhole.c
@@ -118,6 +118,7 @@ static const struct space_vtab blackhole_space_vtab = {
 	/* .execute_upsert = */ blackhole_space_execute_upsert,
 	/* .ephemeral_replace = */ generic_space_ephemeral_replace,
 	/* .ephemeral_delete = */ generic_space_ephemeral_delete,
+	/* .ephemeral_rowid_next = */ generic_space_ephemeral_rowid_next,
 	/* .init_system_space = */ generic_init_system_space,
 	/* .init_ephemeral_space = */ generic_init_ephemeral_space,
 	/* .check_index_def = */ generic_space_check_index_def,
diff --git a/src/box/memtx_space.c b/src/box/memtx_space.c
index 1a248b701fcf55663ff3d74809b82e0534c04745..eb790a6fe2e36816449cd55b5101fb6169ead2ea 100644
--- a/src/box/memtx_space.c
+++ b/src/box/memtx_space.c
@@ -601,6 +601,15 @@ memtx_space_ephemeral_delete(struct space *space, const char *key)
 	return 0;
 }
 
+static int
+memtx_space_ephemeral_rowid_next(struct space *space, uint64_t *rowid)
+{
+	assert(rowid != NULL);
+	struct memtx_space *memtx_space = (struct memtx_space *)space;
+	*rowid = memtx_space->rowid++;
+	return 0;
+}
+
 /* }}} DML */
 
 /* {{{ DDL */
@@ -941,6 +950,7 @@ static const struct space_vtab memtx_space_vtab = {
 	/* .execute_upsert = */ memtx_space_execute_upsert,
 	/* .ephemeral_replace = */ memtx_space_ephemeral_replace,
 	/* .ephemeral_delete = */ memtx_space_ephemeral_delete,
+	/* .ephemeral_rowid_next = */ memtx_space_ephemeral_rowid_next,
 	/* .init_system_space = */ memtx_init_system_space,
 	/* .init_ephemeral_space = */ memtx_init_ephemeral_space,
 	/* .check_index_def = */ memtx_space_check_index_def,
@@ -1002,6 +1012,7 @@ memtx_space_new(struct memtx_engine *memtx,
 	tuple_format_unref(format);
 
 	memtx_space->bsize = 0;
+	memtx_space->rowid = 0;
 	memtx_space->replace = memtx_space_replace_no_keys;
 	return (struct space *)memtx_space;
 }
diff --git a/src/box/memtx_space.h b/src/box/memtx_space.h
index 7dc341079e6d5a7d1b559dbd1784748d4333becd..532538312ff214693395778589c0c46ff57f0d74 100644
--- a/src/box/memtx_space.h
+++ b/src/box/memtx_space.h
@@ -42,6 +42,13 @@ struct memtx_space {
 	struct space base;
 	/* Number of bytes used in memory by tuples in the space. */
 	size_t bsize;
+	/**
+	 * This counter is used to generate unique ids for
+	 * ephemeral spaces. Mostly used by SQL: values of this
+	 * var are stored as separate field to hold non-unique
+	 * tuples within one unique primary key.
+	 */
+	uint64_t rowid;
 	/**
 	 * A pointer to replace function, set to different values
 	 * at different stages of recovery.
diff --git a/src/box/space.c b/src/box/space.c
index 548f6678775ea57f156d9d3636124aa1ae0c8b97..4d174f7ecda069a4f4a30c3c0ffb46c479b335d3 100644
--- a/src/box/space.c
+++ b/src/box/space.c
@@ -568,6 +568,15 @@ generic_space_ephemeral_delete(struct space *space, const char *key)
 	return -1;
 }
 
+int
+generic_space_ephemeral_rowid_next(struct space *space, uint64_t *rowid)
+{
+	(void)space;
+	(void)rowid;
+	unreachable();
+	return 0;
+}
+
 void
 generic_init_system_space(struct space *space)
 {
diff --git a/src/box/space.h b/src/box/space.h
index f3e9e1e21cb166c34fc0516dc71b4b7af1f5022e..7eb7ae2924069826da936117ab5e982fb06730aa 100644
--- a/src/box/space.h
+++ b/src/box/space.h
@@ -71,6 +71,8 @@ struct space_vtab {
 
 	int (*ephemeral_delete)(struct space *, const char *);
 
+	int (*ephemeral_rowid_next)(struct space *, uint64_t *);
+
 	void (*init_system_space)(struct space *);
 	/**
 	 * Initialize an ephemeral space instance.
@@ -457,6 +459,7 @@ size_t generic_space_bsize(struct space *);
 int generic_space_apply_initial_join_row(struct space *, struct request *);
 int generic_space_ephemeral_replace(struct space *, const char *, const char *);
 int generic_space_ephemeral_delete(struct space *, const char *);
+int generic_space_ephemeral_rowid_next(struct space *, uint64_t *);
 void generic_init_system_space(struct space *);
 void generic_init_ephemeral_space(struct space *);
 int generic_space_check_index_def(struct space *, struct index_def *);
diff --git a/src/box/sysview.c b/src/box/sysview.c
index ed5bca38efc65037169afd6afc08ea63187a59f3..29de43093790079602528aa3aead012bd2556ae9 100644
--- a/src/box/sysview.c
+++ b/src/box/sysview.c
@@ -484,6 +484,7 @@ static const struct space_vtab sysview_space_vtab = {
 	/* .execute_upsert = */ sysview_space_execute_upsert,
 	/* .ephemeral_replace = */ generic_space_ephemeral_replace,
 	/* .ephemeral_delete = */ generic_space_ephemeral_delete,
+	/* .ephemeral_rowid_next = */ generic_space_ephemeral_rowid_next,
 	/* .init_system_space = */ generic_init_system_space,
 	/* .init_ephemeral_space = */ generic_init_ephemeral_space,
 	/* .check_index_def = */ generic_space_check_index_def,
diff --git a/src/box/vinyl.c b/src/box/vinyl.c
index b09b6ad85c2db85de967e7673484ec13b5d8d2ae..ce81c6ad474dfd4f268bd0eb3520a7b5b0472641 100644
--- a/src/box/vinyl.c
+++ b/src/box/vinyl.c
@@ -4453,6 +4453,7 @@ static const struct space_vtab vinyl_space_vtab = {
 	/* .execute_upsert = */ vinyl_space_execute_upsert,
 	/* .ephemeral_replace = */ generic_space_ephemeral_replace,
 	/* .ephemeral_delete = */ generic_space_ephemeral_delete,
+	/* .ephemeral_rowid_next = */ generic_space_ephemeral_rowid_next,
 	/* .init_system_space = */ generic_init_system_space,
 	/* .init_ephemeral_space = */ generic_init_ephemeral_space,
 	/* .check_index_def = */ vinyl_space_check_index_def,