Tarantool development patches archive
 help / color / mirror / Atom feed
* [tarantool-patches] [PATCH v3 0/7] Expose ephemeral spaces into Lua
@ 2018-07-24 11:58 imeevma
  2018-07-24 11:58 ` [tarantool-patches] [PATCH v3 1/7] box: add space address to index_replace imeevma
                   ` (6 more replies)
  0 siblings, 7 replies; 8+ messages in thread
From: imeevma @ 2018-07-24 11:58 UTC (permalink / raw)
  To: tarantool-patches

These patches allow to use ephemeral spaces in Lua. Ephemeral spaces are special
spaces that in most cases work in the same way as usual spaces, but they do not
touch transactions and are local to one Lua session. The are deleted by GC when
there is no references to them. They can be created and deleted but cannot be
altered. Till now they were used to process some sql queries.

Branch: https://github.com/tarantool/tarantool/compare/imeevma/gh-3375-lua-expose-ephemeral-spaces
Issue: https://github.com/tarantool/tarantool/issues/3375

Mergen Imeev (7):
  box: add space address to index_replace
  box: move checks for key findability from space_vtab
  box: create new methods for ephemeral spaces
  box: move some decode functions from alter.cc
  box: ephemeral space creation and deletion in Lua
  box: primary index for ephemeral spaces
  box: methods for ephemeral space and its index

 src/box/alter.cc                  |  256 +--------
 src/box/box.cc                    |  301 ++++++++---
 src/box/box.h                     |  115 ++++
 src/box/index.cc                  |  174 +++++-
 src/box/index.h                   |  167 +++++-
 src/box/index_def.c               |  117 +++++
 src/box/index_def.h               |   26 +
 src/box/lua/index.c               |  422 ++++++++++++++-
 src/box/lua/misc.cc               |   29 +-
 src/box/lua/misc.h                |   21 +
 src/box/lua/schema.lua            |  406 ++++++++++++--
 src/box/lua/space.cc              |  281 ++++++++--
 src/box/memtx_bitset.c            |    7 +-
 src/box/memtx_engine.c            |    6 +-
 src/box/memtx_hash.c              |   13 +-
 src/box/memtx_rtree.c             |    7 +-
 src/box/memtx_space.c             |  412 ++++++++++-----
 src/box/memtx_tree.c              |   17 +-
 src/box/space.h                   |   17 -
 src/box/space_def.c               |  155 ++++++
 src/box/space_def.h               |   18 +
 src/box/sql.c                     |   33 +-
 src/box/sysview.c                 |    2 -
 src/box/vinyl.c                   |   42 --
 test/box/ephemeral_space.result   | 1047 +++++++++++++++++++++++++++++++++++++
 test/box/ephemeral_space.test.lua |  331 ++++++++++++
 test/engine/iterator.result       |    2 +-
 27 files changed, 3800 insertions(+), 624 deletions(-)
 create mode 100644 test/box/ephemeral_space.result
 create mode 100644 test/box/ephemeral_space.test.lua

-- 
2.7.4

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [tarantool-patches] [PATCH v3 1/7] box: add space address to index_replace
  2018-07-24 11:58 [tarantool-patches] [PATCH v3 0/7] Expose ephemeral spaces into Lua imeevma
@ 2018-07-24 11:58 ` imeevma
  2018-07-24 11:58 ` [tarantool-patches] [PATCH v3 2/7] box: move checks for key findability from space_vtab imeevma
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 8+ messages in thread
From: imeevma @ 2018-07-24 11:58 UTC (permalink / raw)
  To: tarantool-patches

On error in some cases index_replace
looking for space using space_id that
is saved in index. It is wrong as
function that calls index_replace
already have that space. Also in case
of ephemeral spaces space_cache_find
doesn't work right as all ephemeral
space have id == 0.

Part of #3375.
---
 src/box/index.cc       | 16 +++++++++-------
 src/box/index.h        | 30 +++++++++++++++++-------------
 src/box/memtx_bitset.c |  7 ++++---
 src/box/memtx_engine.c |  6 +++---
 src/box/memtx_hash.c   | 13 +++++--------
 src/box/memtx_rtree.c  |  7 ++++---
 src/box/memtx_space.c  | 13 +++++++------
 src/box/memtx_tree.c   | 17 ++++++++---------
 8 files changed, 57 insertions(+), 52 deletions(-)

diff --git a/src/box/index.cc b/src/box/index.cc
index cf81eca..372e0f3 100644
--- a/src/box/index.cc
+++ b/src/box/index.cc
@@ -510,7 +510,7 @@ index_delete(struct index *index)
 }
 
 int
-index_build(struct index *index, struct index *pk)
+index_build(struct index *index, struct space *space, struct index *pk)
 {
 	ssize_t n_tuples = index_size(pk);
 	if (n_tuples < 0)
@@ -539,7 +539,7 @@ index_build(struct index *index, struct index *pk)
 			break;
 		if (tuple == NULL)
 			break;
-		rc = index_build_next(index, tuple);
+		rc = index_build_next(index, space, tuple);
 		if (rc != 0)
 			break;
 	}
@@ -671,10 +671,11 @@ generic_index_get(struct index *index, const char *key,
 }
 
 int
-generic_index_replace(struct index *index, struct tuple *old_tuple,
-		      struct tuple *new_tuple, enum dup_replace_mode mode,
-		      struct tuple **result)
+generic_index_replace(struct index *index, struct space *space,
+		      struct tuple *old_tuple, struct tuple *new_tuple,
+		      enum dup_replace_mode mode, struct tuple **result)
 {
+	(void)space;
 	(void)old_tuple;
 	(void)new_tuple;
 	(void)mode;
@@ -722,10 +723,11 @@ generic_index_reserve(struct index *, uint32_t)
 }
 
 int
-generic_index_build_next(struct index *index, struct tuple *tuple)
+generic_index_build_next(struct index *index, struct space *space,
+			 struct tuple *tuple)
 {
 	struct tuple *unused;
-	return index_replace(index, NULL, tuple, DUP_INSERT, &unused);
+	return index_replace(index, space, NULL, tuple, DUP_INSERT, &unused);
 }
 
 void
diff --git a/src/box/index.h b/src/box/index.h
index 0a1ac61..73ee000 100644
--- a/src/box/index.h
+++ b/src/box/index.h
@@ -41,6 +41,7 @@ extern "C" {
 
 struct tuple;
 struct engine;
+struct space;
 struct index;
 struct index_def;
 struct key_def;
@@ -411,9 +412,9 @@ struct index_vtab {
 			 const char *key, uint32_t part_count);
 	int (*get)(struct index *index, const char *key,
 		   uint32_t part_count, struct tuple **result);
-	int (*replace)(struct index *index, struct tuple *old_tuple,
-		       struct tuple *new_tuple, enum dup_replace_mode mode,
-		       struct tuple **result);
+	int (*replace)(struct index *index, struct space *space,
+		       struct tuple *old_tuple, struct tuple *new_tuple,
+		       enum dup_replace_mode mode, struct tuple **result);
 	/** Create an index iterator. */
 	struct iterator *(*create_iterator)(struct index *index,
 			enum iterator_type type,
@@ -443,7 +444,8 @@ struct index_vtab {
 	 * begin_build().
 	 */
 	int (*reserve)(struct index *index, uint32_t size_hint);
-	int (*build_next)(struct index *index, struct tuple *tuple);
+	int (*build_next)(struct index *index, struct space *space,
+			  struct tuple *tuple);
 	void (*end_build)(struct index *index);
 };
 
@@ -504,7 +506,7 @@ index_delete(struct index *index);
 
 /** Build this index based on the contents of another index. */
 int
-index_build(struct index *index, struct index *pk);
+index_build(struct index *index, struct space *space, struct index *pk);
 
 static inline void
 index_commit_create(struct index *index, int64_t signature)
@@ -596,11 +598,12 @@ index_get(struct index *index, const char *key,
 }
 
 static inline int
-index_replace(struct index *index, struct tuple *old_tuple,
-	      struct tuple *new_tuple, enum dup_replace_mode mode,
-	      struct tuple **result)
+index_replace(struct index *index, struct space *space,
+	      struct tuple *old_tuple, struct tuple *new_tuple,
+	      enum dup_replace_mode mode, struct tuple **result)
 {
-	return index->vtab->replace(index, old_tuple, new_tuple, mode, result);
+	return index->vtab->replace(index, space, old_tuple, new_tuple, mode,
+				    result);
 }
 
 static inline struct iterator *
@@ -647,9 +650,9 @@ index_reserve(struct index *index, uint32_t size_hint)
 }
 
 static inline int
-index_build_next(struct index *index, struct tuple *tuple)
+index_build_next(struct index *index, struct space *space, struct tuple *tuple)
 {
-	return index->vtab->build_next(index, tuple);
+	return index->vtab->build_next(index, space, tuple);
 }
 
 static inline void
@@ -677,7 +680,8 @@ int generic_index_random(struct index *, uint32_t, struct tuple **);
 ssize_t generic_index_count(struct index *, enum iterator_type,
 			    const char *, uint32_t);
 int generic_index_get(struct index *, const char *, uint32_t, struct tuple **);
-int generic_index_replace(struct index *, struct tuple *, struct tuple *,
+int generic_index_replace(struct index *, struct space *,
+			  struct tuple *, struct tuple *,
 			  enum dup_replace_mode, struct tuple **);
 struct snapshot_iterator *generic_index_create_snapshot_iterator(struct index *);
 void generic_index_stat(struct index *, struct info_handler *);
@@ -685,7 +689,7 @@ void generic_index_compact(struct index *);
 void generic_index_reset_stat(struct index *);
 void generic_index_begin_build(struct index *);
 int generic_index_reserve(struct index *, uint32_t);
-int generic_index_build_next(struct index *, struct tuple *);
+int generic_index_build_next(struct index *, struct space *, struct tuple *);
 void generic_index_end_build(struct index *);
 
 #if defined(__cplusplus)
diff --git a/src/box/memtx_bitset.c b/src/box/memtx_bitset.c
index a665f1a..ada6fa8 100644
--- a/src/box/memtx_bitset.c
+++ b/src/box/memtx_bitset.c
@@ -252,10 +252,11 @@ make_key(const char *field, uint32_t *key_len)
 }
 
 static int
-memtx_bitset_index_replace(struct index *base, struct tuple *old_tuple,
-			   struct tuple *new_tuple, enum dup_replace_mode mode,
-			   struct tuple **result)
+memtx_bitset_index_replace(struct index *base, struct space *space,
+			   struct tuple *old_tuple, struct tuple *new_tuple,
+			   enum dup_replace_mode mode, struct tuple **result)
 {
+	(void)space;
 	struct memtx_bitset_index *index = (struct memtx_bitset_index *)base;
 
 	assert(!base->def->opts.is_unique);
diff --git a/src/box/memtx_engine.c b/src/box/memtx_engine.c
index f5ace92..e31261e 100644
--- a/src/box/memtx_engine.c
+++ b/src/box/memtx_engine.c
@@ -160,7 +160,7 @@ memtx_build_secondary_keys(struct space *space, void *param)
 		}
 
 		for (uint32_t j = 1; j < space->index_count; j++) {
-			if (index_build(space->index[j], pk) < 0)
+			if (index_build(space->index[j], space, pk) < 0)
 				return -1;
 		}
 
@@ -449,8 +449,8 @@ memtx_engine_rollback_statement(struct engine *engine, struct txn *txn,
 		struct tuple *unused;
 		struct index *index = space->index[i];
 		/* Rollback must not fail. */
-		if (index_replace(index, stmt->new_tuple, stmt->old_tuple,
-				  DUP_INSERT, &unused) != 0) {
+		if (index_replace(index, space, stmt->new_tuple,
+				  stmt->old_tuple, DUP_INSERT, &unused) != 0) {
 			diag_log();
 			unreachable();
 			panic("failed to rollback change");
diff --git a/src/box/memtx_hash.c b/src/box/memtx_hash.c
index eae07e8..0f70229 100644
--- a/src/box/memtx_hash.c
+++ b/src/box/memtx_hash.c
@@ -35,7 +35,6 @@
 #include "tuple_hash.h"
 #include "memtx_engine.h"
 #include "space.h"
-#include "schema.h" /* space_cache_find() */
 #include "errinj.h"
 
 #include <small/mempool.h>
@@ -244,9 +243,9 @@ memtx_hash_index_get(struct index *base, const char *key,
 }
 
 static int
-memtx_hash_index_replace(struct index *base, struct tuple *old_tuple,
-			 struct tuple *new_tuple, enum dup_replace_mode mode,
-			 struct tuple **result)
+memtx_hash_index_replace(struct index *base, struct space *space,
+			 struct tuple *old_tuple, struct tuple *new_tuple,
+			 enum dup_replace_mode mode, struct tuple **result)
 {
 	struct memtx_hash_index *index = (struct memtx_hash_index *)base;
 	struct light_index_core *hash_table = &index->hash_table;
@@ -281,10 +280,8 @@ memtx_hash_index_replace(struct index *base, struct tuple *old_tuple,
 					      "recover of int hash_table");
 				}
 			}
-			struct space *sp = space_cache_find(base->def->space_id);
-			if (sp != NULL)
-				diag_set(ClientError, errcode, base->def->name,
-					 space_name(sp));
+			diag_set(ClientError, errcode, base->def->name,
+				 space_name(space));
 			return -1;
 		}
 
diff --git a/src/box/memtx_rtree.c b/src/box/memtx_rtree.c
index 0b12cda..333d191 100644
--- a/src/box/memtx_rtree.c
+++ b/src/box/memtx_rtree.c
@@ -210,10 +210,11 @@ memtx_rtree_index_get(struct index *base, const char *key,
 }
 
 static int
-memtx_rtree_index_replace(struct index *base, struct tuple *old_tuple,
-			  struct tuple *new_tuple, enum dup_replace_mode mode,
-			  struct tuple **result)
+memtx_rtree_index_replace(struct index *base, struct space *space,
+			  struct tuple *old_tuple, struct tuple *new_tuple,
+			  enum dup_replace_mode mode, struct tuple **result)
 {
+	(void)space;
 	(void)mode;
 	struct memtx_rtree_index *index = (struct memtx_rtree_index *)base;
 	struct rtree_rect rect;
diff --git a/src/box/memtx_space.c b/src/box/memtx_space.c
index 8cf2e6b..1571a0e 100644
--- a/src/box/memtx_space.c
+++ b/src/box/memtx_space.c
@@ -124,7 +124,7 @@ memtx_space_replace_build_next(struct space *space, struct tuple *old_tuple,
 		panic("Failed to commit transaction when loading "
 		      "from snapshot");
 	}
-	if (index_build_next(space->index[0], new_tuple) != 0)
+	if (index_build_next(space->index[0], space, new_tuple) != 0)
 		return -1;
 	memtx_space_update_bsize(space, NULL, new_tuple);
 	tuple_ref(new_tuple);
@@ -141,7 +141,7 @@ memtx_space_replace_primary_key(struct space *space, struct tuple *old_tuple,
 				enum dup_replace_mode mode,
 				struct tuple **result)
 {
-	if (index_replace(space->index[0], old_tuple,
+	if (index_replace(space->index[0], space, old_tuple,
 			  new_tuple, mode, &old_tuple) != 0)
 		return -1;
 	memtx_space_update_bsize(space, old_tuple, new_tuple);
@@ -262,7 +262,8 @@ memtx_space_replace_all_keys(struct space *space, struct tuple *old_tuple,
 	 * If old_tuple is not NULL, the index has to
 	 * find and delete it, or return an error.
 	 */
-	if (index_replace(pk, old_tuple, new_tuple, mode, &old_tuple) != 0)
+	if (index_replace(pk, space, old_tuple,
+			  new_tuple, mode, &old_tuple) != 0)
 		return -1;
 	assert(old_tuple || new_tuple);
 
@@ -270,7 +271,7 @@ memtx_space_replace_all_keys(struct space *space, struct tuple *old_tuple,
 	for (i++; i < space->index_count; i++) {
 		struct tuple *unused;
 		struct index *index = space->index[i];
-		if (index_replace(index, old_tuple, new_tuple,
+		if (index_replace(index, space, old_tuple, new_tuple,
 				  DUP_INSERT, &unused) != 0)
 			goto rollback;
 	}
@@ -286,7 +287,7 @@ rollback:
 		struct tuple *unused;
 		struct index *index = space->index[i - 1];
 		/* Rollback must not fail. */
-		if (index_replace(index, new_tuple, old_tuple,
+		if (index_replace(index, space, new_tuple, old_tuple,
 				  DUP_INSERT, &unused) != 0) {
 			diag_log();
 			unreachable();
@@ -894,7 +895,7 @@ memtx_space_build_index(struct space *src_space, struct index *new_index,
 		 * @todo: better message if there is a duplicate.
 		 */
 		struct tuple *old_tuple;
-		rc = index_replace(new_index, NULL, tuple,
+		rc = index_replace(new_index, src_space, NULL, tuple,
 				   DUP_INSERT, &old_tuple);
 		if (rc != 0)
 			break;
diff --git a/src/box/memtx_tree.c b/src/box/memtx_tree.c
index f851fb8..cf5d14f 100644
--- a/src/box/memtx_tree.c
+++ b/src/box/memtx_tree.c
@@ -31,7 +31,6 @@
 #include "memtx_tree.h"
 #include "memtx_engine.h"
 #include "space.h"
-#include "schema.h" /* space_cache_find() */
 #include "errinj.h"
 #include "memory.h"
 #include "fiber.h"
@@ -438,9 +437,9 @@ memtx_tree_index_get(struct index *base, const char *key,
 }
 
 static int
-memtx_tree_index_replace(struct index *base, struct tuple *old_tuple,
-			 struct tuple *new_tuple, enum dup_replace_mode mode,
-			 struct tuple **result)
+memtx_tree_index_replace(struct index *base, struct space *space,
+			 struct tuple *old_tuple, struct tuple *new_tuple,
+			 enum dup_replace_mode mode, struct tuple **result)
 {
 	struct memtx_tree_index *index = (struct memtx_tree_index *)base;
 	if (new_tuple) {
@@ -461,10 +460,8 @@ memtx_tree_index_replace(struct index *base, struct tuple *old_tuple,
 			memtx_tree_delete(&index->tree, new_tuple);
 			if (dup_tuple)
 				memtx_tree_insert(&index->tree, dup_tuple, 0);
-			struct space *sp = space_cache_find(base->def->space_id);
-			if (sp != NULL)
-				diag_set(ClientError, errcode, base->def->name,
-					 space_name(sp));
+			diag_set(ClientError, errcode, base->def->name,
+				 space_name(space));
 			return -1;
 		}
 		if (dup_tuple) {
@@ -549,8 +546,10 @@ memtx_tree_index_reserve(struct index *base, uint32_t size_hint)
 }
 
 static int
-memtx_tree_index_build_next(struct index *base, struct tuple *tuple)
+memtx_tree_index_build_next(struct index *base, struct space *space,
+			    struct tuple *tuple)
 {
+	(void)space;
 	struct memtx_tree_index *index = (struct memtx_tree_index *)base;
 	if (index->build_array == NULL) {
 		index->build_array = (struct tuple **)malloc(MEMTX_EXTENT_SIZE);
-- 
2.7.4

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [tarantool-patches] [PATCH v3 2/7] box: move checks for key findability from space_vtab
  2018-07-24 11:58 [tarantool-patches] [PATCH v3 0/7] Expose ephemeral spaces into Lua imeevma
  2018-07-24 11:58 ` [tarantool-patches] [PATCH v3 1/7] box: add space address to index_replace imeevma
@ 2018-07-24 11:58 ` imeevma
  2018-07-24 11:58 ` [tarantool-patches] [PATCH v3 3/7] box: create new methods for ephemeral spaces imeevma
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 8+ messages in thread
From: imeevma @ 2018-07-24 11:58 UTC (permalink / raw)
  To: tarantool-patches

In this patch checks exact_key_validate
for memtx and vy_unique_key_validate
for vinyl were moved from space_vtab
to box.cc. This is needed for creation
_impl versions of some space functions.

Part of #3375.
---
 src/box/box.cc        | 74 ++++++++++++++++++++++++++++++++++++++++++++-------
 src/box/memtx_space.c |  4 ---
 src/box/vinyl.c       | 40 ----------------------------
 3 files changed, 64 insertions(+), 54 deletions(-)

diff --git a/src/box/box.cc b/src/box/box.cc
index 3ed2a4a..71f3a51 100644
--- a/src/box/box.cc
+++ b/src/box/box.cc
@@ -993,16 +993,52 @@ box_index_id_by_name(uint32_t space_id, const char *name, uint32_t len)
 }
 /** \endcond public */
 
+/**
+ * Check space for writeability.
+ *
+ * @param space space for this check.
+ *
+ * @retval 0 space is writeable.
+ * @retval -1 error.
+ */
+static inline int
+space_check_writable(struct space *space)
+{
+	if (!space_is_temporary(space) &&
+	    space_group_id(space) != GROUP_LOCAL &&
+	    box_check_writable() != 0)
+		return -1;
+	return 0;
+}
+
+/**
+ * Check if key is good enough to be used to find tuple.
+ *
+ * @param space space for this check.
+ * @param index_id id of index for this check.
+ * @param key key to check.
+ *
+ * @retval 0 key is findable.
+ * @retval -1 error.
+ */
+static inline int
+key_check_findable(struct space *space, uint32_t index_id, const char *key)
+{
+	struct index *pk = index_find_unique(space, index_id);
+	if (pk == NULL)
+		return -1;
+	uint32_t part_count = mp_decode_array(&key);
+	if (exact_key_validate(pk->def->key_def, key, part_count) != 0)
+		return -1;
+	return 0;
+}
+
 int
 box_process1(struct request *request, box_tuple_t **result)
 {
 	/* Allow to write to temporary spaces in read-only mode. */
 	struct space *space = space_cache_find(request->space_id);
-	if (space == NULL)
-		return -1;
-	if (!space_is_temporary(space) &&
-	    space_group_id(space) != GROUP_LOCAL &&
-	    box_check_writable() != 0)
+	if (space == NULL || space_check_writable(space) != 0)
 		return -1;
 	return box_process_rw(request, space, result);
 }
@@ -1087,13 +1123,16 @@ box_insert(uint32_t space_id, const char *tuple, const char *tuple_end,
 	   box_tuple_t **result)
 {
 	mp_tuple_assert(tuple, tuple_end);
+	struct space *space = space_cache_find(space_id);
+	if (space == NULL || space_check_writable(space) != 0)
+		return -1;
 	struct request request;
 	memset(&request, 0, sizeof(request));
 	request.type = IPROTO_INSERT;
 	request.space_id = space_id;
 	request.tuple = tuple;
 	request.tuple_end = tuple_end;
-	return box_process1(&request, result);
+	return box_process_rw(&request, space, result);
 }
 
 int
@@ -1101,13 +1140,16 @@ box_replace(uint32_t space_id, const char *tuple, const char *tuple_end,
 	    box_tuple_t **result)
 {
 	mp_tuple_assert(tuple, tuple_end);
+	struct space *space = space_cache_find(space_id);
+	if (space == NULL || space_check_writable(space) != 0)
+		return -1;
 	struct request request;
 	memset(&request, 0, sizeof(request));
 	request.type = IPROTO_REPLACE;
 	request.space_id = space_id;
 	request.tuple = tuple;
 	request.tuple_end = tuple_end;
-	return box_process1(&request, result);
+	return box_process_rw(&request, space, result);
 }
 
 int
@@ -1115,6 +1157,10 @@ box_delete(uint32_t space_id, uint32_t index_id, const char *key,
 	   const char *key_end, box_tuple_t **result)
 {
 	mp_tuple_assert(key, key_end);
+	struct space *space = space_cache_find(space_id);
+	if (space == NULL || space_check_writable(space) != 0 ||
+	    key_check_findable(space, index_id, key) != 0)
+		return -1;
 	struct request request;
 	memset(&request, 0, sizeof(request));
 	request.type = IPROTO_DELETE;
@@ -1122,7 +1168,7 @@ box_delete(uint32_t space_id, uint32_t index_id, const char *key,
 	request.index_id = index_id;
 	request.key = key;
 	request.key_end = key_end;
-	return box_process1(&request, result);
+	return box_process_rw(&request, space, result);
 }
 
 int
@@ -1132,6 +1178,10 @@ box_update(uint32_t space_id, uint32_t index_id, const char *key,
 {
 	mp_tuple_assert(key, key_end);
 	mp_tuple_assert(ops, ops_end);
+	struct space *space = space_cache_find(space_id);
+	if (space == NULL || space_check_writable(space) != 0 ||
+	    key_check_findable(space, index_id, key) != 0)
+		return -1;
 	struct request request;
 	memset(&request, 0, sizeof(request));
 	request.type = IPROTO_UPDATE;
@@ -1143,7 +1193,8 @@ box_update(uint32_t space_id, uint32_t index_id, const char *key,
 	/** Legacy: in case of update, ops are passed in in request tuple */
 	request.tuple = ops;
 	request.tuple_end = ops_end;
-	return box_process1(&request, result);
+
+	return box_process_rw(&request, space, result);
 }
 
 int
@@ -1153,6 +1204,9 @@ box_upsert(uint32_t space_id, uint32_t index_id, const char *tuple,
 {
 	mp_tuple_assert(ops, ops_end);
 	mp_tuple_assert(tuple, tuple_end);
+	struct space *space = space_cache_find(space_id);
+	if (space == NULL || space_check_writable(space) != 0)
+		return -1;
 	struct request request;
 	memset(&request, 0, sizeof(request));
 	request.type = IPROTO_UPSERT;
@@ -1163,7 +1217,7 @@ box_upsert(uint32_t space_id, uint32_t index_id, const char *tuple,
 	request.tuple = tuple;
 	request.tuple_end = tuple_end;
 	request.index_base = index_base;
-	return box_process1(&request, result);
+	return box_process_rw(&request, space, result);
 }
 
 /**
diff --git a/src/box/memtx_space.c b/src/box/memtx_space.c
index 1571a0e..0d00b90 100644
--- a/src/box/memtx_space.c
+++ b/src/box/memtx_space.c
@@ -365,8 +365,6 @@ memtx_space_execute_delete(struct space *space, struct txn *txn,
 		return -1;
 	const char *key = request->key;
 	uint32_t part_count = mp_decode_array(&key);
-	if (exact_key_validate(pk->def->key_def, key, part_count) != 0)
-		return -1;
 	struct tuple *old_tuple;
 	if (index_get(pk, key, part_count, &old_tuple) != 0)
 		return -1;
@@ -391,8 +389,6 @@ memtx_space_execute_update(struct space *space, struct txn *txn,
 		return -1;
 	const char *key = request->key;
 	uint32_t part_count = mp_decode_array(&key);
-	if (exact_key_validate(pk->def->key_def, key, part_count) != 0)
-		return -1;
 	struct tuple *old_tuple;
 	if (index_get(pk, key, part_count, &old_tuple) != 0)
 		return -1;
diff --git a/src/box/vinyl.c b/src/box/vinyl.c
index 6e24071..05321cd 100644
--- a/src/box/vinyl.c
+++ b/src/box/vinyl.c
@@ -1589,42 +1589,6 @@ error:
 }
 
 /**
- * Check that the key can be used for search in a unique index
- * LSM tree.
- * @param  lsm        LSM tree for checking.
- * @param  key        MessagePack'ed data, the array without a
- *                    header.
- * @param  part_count Part count of the key.
- *
- * @retval  0 The key is valid.
- * @retval -1 The key is not valid, the appropriate error is set
- *            in the diagnostics area.
- */
-static inline int
-vy_unique_key_validate(struct vy_lsm *lsm, const char *key,
-		       uint32_t part_count)
-{
-	assert(lsm->opts.is_unique);
-	assert(key != NULL || part_count == 0);
-	/*
-	 * The LSM tree contains tuples with concatenation of
-	 * secondary and primary key fields, while the key
-	 * supplied by the user only contains the secondary key
-	 * fields. Use the correct key def to validate the key.
-	 * The key can be used to look up in the LSM tree since
-	 * the supplied key parts uniquely identify the tuple,
-	 * as long as the index is unique.
-	 */
-	uint32_t original_part_count = lsm->key_def->part_count;
-	if (original_part_count != part_count) {
-		diag_set(ClientError, ER_EXACT_MATCH,
-			 original_part_count, part_count);
-		return -1;
-	}
-	return key_validate_parts(lsm->cmp_def, key, part_count, false);
-}
-
-/**
  * Find a tuple in the primary index LSM tree by the key of the
  * specified LSM tree.
  * @param lsm         LSM tree for which the key is specified.
@@ -1738,8 +1702,6 @@ vy_delete(struct vy_env *env, struct vy_tx *tx, struct txn_stmt *stmt,
 	bool has_secondary = space->index_count > 1;
 	const char *key = request->key;
 	uint32_t part_count = mp_decode_array(&key);
-	if (vy_unique_key_validate(lsm, key, part_count))
-		return -1;
 	/*
 	 * There are two cases when need to get the full tuple
 	 * before deletion.
@@ -1830,8 +1792,6 @@ vy_update(struct vy_env *env, struct vy_tx *tx, struct txn_stmt *stmt,
 		return -1;
 	const char *key = request->key;
 	uint32_t part_count = mp_decode_array(&key);
-	if (vy_unique_key_validate(lsm, key, part_count))
-		return -1;
 
 	if (vy_lsm_full_by_key(lsm, tx, vy_tx_read_view(tx),
 			       key, part_count, &stmt->old_tuple) != 0)
-- 
2.7.4

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [tarantool-patches] [PATCH v3 3/7] box: create new methods for ephemeral spaces
  2018-07-24 11:58 [tarantool-patches] [PATCH v3 0/7] Expose ephemeral spaces into Lua imeevma
  2018-07-24 11:58 ` [tarantool-patches] [PATCH v3 1/7] box: add space address to index_replace imeevma
  2018-07-24 11:58 ` [tarantool-patches] [PATCH v3 2/7] box: move checks for key findability from space_vtab imeevma
@ 2018-07-24 11:58 ` imeevma
  2018-07-24 11:58 ` [tarantool-patches] [PATCH v3 4/7] box: move some decode functions from alter.cc imeevma
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 8+ messages in thread
From: imeevma @ 2018-07-24 11:58 UTC (permalink / raw)
  To: tarantool-patches

Up to this patch any space had two additional methods
that were methods of ephemeral spaces. In this patch
these methods deleted from vtab and from now on
ephemeral spaces are spaces with special vtab.

Part of #3375.
---
 src/box/box.cc        | 165 ++++++++++++++++-----
 src/box/box.h         |  94 ++++++++++++
 src/box/memtx_space.c | 395 ++++++++++++++++++++++++++++++++++++--------------
 src/box/space.h       |  17 ---
 src/box/sql.c         |  33 ++++-
 src/box/sysview.c     |   2 -
 src/box/vinyl.c       |   2 -
 7 files changed, 538 insertions(+), 170 deletions(-)

diff --git a/src/box/box.cc b/src/box/box.cc
index 71f3a51..e811496 100644
--- a/src/box/box.cc
+++ b/src/box/box.cc
@@ -872,6 +872,62 @@ box_set_net_msg_max(void)
 /* }}} configuration bindings */
 
 /**
+ * Fill up request according to given operation type.
+ *
+ * @param request[out] request to fill up.
+ * @param type type of operation.
+ * @param space_id id of space for this operation.
+ * @param index_id id of index for this operation.
+ * @param key key field for UPDATE/DELETE operations and ops field
+ * for UPSERT operation.
+ * @param key_end key_end field for UPDATE/DELETE operations and
+ * ops_end field for UPSERT operation.
+ * @param tuple tuple field for REPLACE, UPDATE and UPSERT
+ * operations.
+ * @param tuple_end tuple_end field for REPLACE, UPDATE and UPSERT
+ * operations.
+ * @param index_base index_base field for UPDATE and UPSERT
+ * operations.
+ */
+static inline void
+request_fill(struct request *request, uint32_t type, uint32_t space_id,
+	     uint32_t index_id, const char *key, const char *key_end,
+	     const char *tuple, const char *tuple_end, int index_base)
+{
+	memset(request, 0, sizeof(*request));
+	request->type = type;
+	request->space_id = space_id;
+	request->index_id = index_id;
+	switch (type) {
+	case IPROTO_INSERT:
+	case IPROTO_REPLACE:
+		request->tuple = tuple;
+		request->tuple_end = tuple_end;
+		break;
+	case IPROTO_DELETE:
+		request->key = key;
+		request->key_end = key_end;
+		break;
+	case IPROTO_UPDATE:
+		request->key = key;
+		request->key_end = key_end;
+		request->tuple = tuple;
+		request->tuple_end = tuple_end;
+		request->index_base = index_base;
+		break;
+	case IPROTO_UPSERT:
+		request->ops = key;
+		request->ops_end = key_end;
+		request->tuple = tuple;
+		request->tuple_end = tuple_end;
+		request->index_base = index_base;
+		break;
+	default:
+		unreachable();
+	}
+}
+
+/**
  * Execute a request against a given space id with
  * a variable-argument tuple described in format.
  *
@@ -1127,11 +1183,8 @@ box_insert(uint32_t space_id, const char *tuple, const char *tuple_end,
 	if (space == NULL || space_check_writable(space) != 0)
 		return -1;
 	struct request request;
-	memset(&request, 0, sizeof(request));
-	request.type = IPROTO_INSERT;
-	request.space_id = space_id;
-	request.tuple = tuple;
-	request.tuple_end = tuple_end;
+	request_fill(&request, IPROTO_INSERT, space_id, 0, NULL, NULL, tuple,
+		     tuple_end, 0);
 	return box_process_rw(&request, space, result);
 }
 
@@ -1144,11 +1197,8 @@ box_replace(uint32_t space_id, const char *tuple, const char *tuple_end,
 	if (space == NULL || space_check_writable(space) != 0)
 		return -1;
 	struct request request;
-	memset(&request, 0, sizeof(request));
-	request.type = IPROTO_REPLACE;
-	request.space_id = space_id;
-	request.tuple = tuple;
-	request.tuple_end = tuple_end;
+	request_fill(&request, IPROTO_REPLACE, space_id, 0, NULL, NULL, tuple,
+		     tuple_end, 0);
 	return box_process_rw(&request, space, result);
 }
 
@@ -1162,12 +1212,8 @@ box_delete(uint32_t space_id, uint32_t index_id, const char *key,
 	    key_check_findable(space, index_id, key) != 0)
 		return -1;
 	struct request request;
-	memset(&request, 0, sizeof(request));
-	request.type = IPROTO_DELETE;
-	request.space_id = space_id;
-	request.index_id = index_id;
-	request.key = key;
-	request.key_end = key_end;
+	request_fill(&request, IPROTO_DELETE, space_id, index_id, key, key_end,
+		     NULL, NULL, 0);
 	return box_process_rw(&request, space, result);
 }
 
@@ -1183,17 +1229,8 @@ box_update(uint32_t space_id, uint32_t index_id, const char *key,
 	    key_check_findable(space, index_id, key) != 0)
 		return -1;
 	struct request request;
-	memset(&request, 0, sizeof(request));
-	request.type = IPROTO_UPDATE;
-	request.space_id = space_id;
-	request.index_id = index_id;
-	request.key = key;
-	request.key_end = key_end;
-	request.index_base = index_base;
-	/** Legacy: in case of update, ops are passed in in request tuple */
-	request.tuple = ops;
-	request.tuple_end = ops_end;
-
+	request_fill(&request, IPROTO_UPDATE, space_id, index_id, key, key_end,
+		     ops, ops_end, index_base);
 	return box_process_rw(&request, space, result);
 }
 
@@ -1208,18 +1245,74 @@ box_upsert(uint32_t space_id, uint32_t index_id, const char *tuple,
 	if (space == NULL || space_check_writable(space) != 0)
 		return -1;
 	struct request request;
-	memset(&request, 0, sizeof(request));
-	request.type = IPROTO_UPSERT;
-	request.space_id = space_id;
-	request.index_id = index_id;
-	request.ops = ops;
-	request.ops_end = ops_end;
-	request.tuple = tuple;
-	request.tuple_end = tuple_end;
-	request.index_base = index_base;
+	request_fill(&request, IPROTO_UPSERT, space_id, index_id, ops, ops_end,
+		     tuple, tuple_end, index_base);
 	return box_process_rw(&request, space, result);
 }
 
+int
+box_ephemeral_insert(struct space *space, const char *tuple,
+		     const char *tuple_end, box_tuple_t **result)
+{
+	mp_tuple_assert(tuple, tuple_end);
+	struct request request;
+	request_fill(&request, IPROTO_INSERT, 0, 0, NULL, NULL, tuple,
+		     tuple_end, 0);
+	return space_execute_dml(space, NULL, &request, result);
+}
+
+int
+box_ephemeral_replace(struct space *space, const char *tuple,
+		      const char *tuple_end, box_tuple_t **result)
+{
+	mp_tuple_assert(tuple, tuple_end);
+	struct request request;
+	request_fill(&request, IPROTO_REPLACE, 0, 0, NULL, NULL, tuple,
+		     tuple_end, 0);
+	return space_execute_dml(space, NULL, &request, result);
+}
+
+int
+box_ephemeral_delete(struct space *space, uint32_t index_id, const char *key,
+		     const char *key_end, box_tuple_t **result)
+{
+	mp_tuple_assert(key, key_end);
+	if (key_check_findable(space, index_id, key) != 0)
+		return -1;
+	struct request request;
+	request_fill(&request, IPROTO_DELETE, 0, index_id, key, key_end,
+		     NULL, NULL, 0);
+	return space_execute_dml(space, NULL, &request, result);
+}
+
+int
+box_ephemeral_update(struct space *space, uint32_t index_id, const char *key,
+		     const char *key_end, const char *ops, const char *ops_end,
+		     int index_base, box_tuple_t **result)
+{
+	mp_tuple_assert(key, key_end);
+	mp_tuple_assert(ops, ops_end);
+	if (key_check_findable(space, index_id, key) != 0)
+		return -1;
+	struct request request;
+	request_fill(&request, IPROTO_UPDATE, 0, index_id, key, key_end,
+		     ops, ops_end, index_base);
+	return space_execute_dml(space, NULL, &request, result);
+}
+
+int
+box_ephemeral_upsert(struct space *space, uint32_t index_id, const char *tuple,
+		     const char *tuple_end, const char *ops,
+		     const char *ops_end, int index_base, box_tuple_t **result)
+{
+	mp_tuple_assert(ops, ops_end);
+	mp_tuple_assert(tuple, tuple_end);
+	struct request request;
+	request_fill(&request, IPROTO_UPSERT, 0, index_id, ops, ops_end,
+		     tuple, tuple_end, index_base);
+	return space_execute_dml(space, NULL, &request, result);
+}
+
 /**
  * Trigger space truncation by bumping a counter
  * in _truncate space.
diff --git a/src/box/box.h b/src/box/box.h
index 6ba2bdf..e582fbc 100644
--- a/src/box/box.h
+++ b/src/box/box.h
@@ -404,6 +404,100 @@ int
 box_process1(struct request *request, box_tuple_t **result);
 
 /**
+ * Execute an INSERT request for ephemeral spaces.
+ *
+ * \param space ephemeral space.
+ * \param tuple encoded tuple in
+ * MsgPack Array format.
+ * \param tuple_end end of @a tuple.
+ * \param[out] result a new tuple.
+ * \retval -1 on error.
+ * \retval 0 on success.
+ */
+int
+box_ephemeral_insert(struct space *space, const char *tuple,
+		     const char *tuple_end, box_tuple_t **result);
+
+/**
+ * Execute an REPLACE request for ephemeral spaces.
+ *
+ * \param space ephemeral space.
+ * \param tuple encoded tuple in
+ * MsgPack Array format.
+ * \param tuple_end end of @a tuple.
+ * \param[out] result a new tuple.
+ * \retval -1 on error.
+ * \retval 0 on success.
+ */
+int
+box_ephemeral_replace(struct space *space, const char *tuple,
+		      const char *tuple_end, box_tuple_t **result);
+
+/**
+ * Execute an DELETE request for ephemeral spaces.
+ *
+ * \param space ephemeral space.
+ * \param index_id index identifier
+ * \param key encoded key in
+ * MsgPack Array format.
+ * \param key_end the end of encoded \a key.
+ * \param[out] result an old tuple.
+ * \retval -1 on error.
+ * \retval 0 on success.
+ */
+int
+box_ephemeral_delete(struct space *space, uint32_t index_id, const char *key,
+		     const char *key_end, box_tuple_t **result);
+
+/**
+ * Execute an UPDATE request for ephemeral spaces.
+ *
+ * \param space ephemeral space.
+ * \param index_id index identifier
+ * \param key encoded key in
+ * MsgPack Array format.
+ * \param key_end the end of encoded \a key.
+ * \param ops encoded operations in
+ * MsgPack Arrat format.
+ * \param ops_end the end of encoded \a ops.
+ * \param index_base 0 if fieldnos in
+ * update operations are zero-based
+ * indexed (like C) or 1 if for one-based
+ * indexed field ids (like Lua).
+ * \param[out] result a new tuple.
+ * \retval -1 on error.
+ * \retval 0 on success.
+ */
+int
+box_ephemeral_update(struct space *space, uint32_t index_id, const char *key,
+		     const char *key_end, const char *ops, const char *ops_end,
+		     int index_base, box_tuple_t **result);
+
+/**
+ * Execute an UPSERT request for ephemeral spaces.
+ *
+ * \param space ephemeral space.
+ * \param index_id index identifier
+ * \param ops encoded operations in
+ * MsgPack Arrat format.
+ * \param ops_end the end of encoded \a ops.
+ * \param tuple encoded tuple in
+ * MsgPack Array format.
+ * \param tuple_end end of @a tuple.
+ * \param index_base 0 if fieldnos in update
+ * operations are zero-based
+ * indexed (like C) or 1 if for one-based
+ * indexed field ids (like Lua).
+ * \param[out] result a new tuple.
+ * \retval -1 on error.
+ * \retval 0 on success.
+ */
+int
+box_ephemeral_upsert(struct space *space, uint32_t index_id, const char *tuple,
+		     const char *tuple_end, const char *ops,
+		     const char *ops_end, int index_base, box_tuple_t **result);
+
+/**
  * Execute request on given space.
  *
  * \param request Request to be executed
diff --git a/src/box/memtx_space.c b/src/box/memtx_space.c
index 0d00b90..a5d3594 100644
--- a/src/box/memtx_space.c
+++ b/src/box/memtx_space.c
@@ -332,75 +332,105 @@ rollback:
 	return -1;
 }
 
-static int
-memtx_space_execute_replace(struct space *space, struct txn *txn,
-			    struct request *request, struct tuple **result)
+/**
+ * Executes INSERT and REPLACE operations for given space.
+ *
+ * @param space space for which operation is executed.
+ * @param new_tuple address of caller's new_tuple.
+ * @param old_tuple address of caller's old_tuple.
+ * @param request request which defines operation.
+ * @param[out] result result tuple.
+ *
+ * @returns 0 Success.
+ * @returns -1 Error.
+ */
+static inline int
+memtx_space_replace_impl(struct space *space, struct request *request,
+			 struct tuple **new_tuple, struct tuple **old_tuple,
+			 struct tuple **result)
 {
 	struct memtx_space *memtx_space = (struct memtx_space *)space;
-	struct txn_stmt *stmt = txn_current_stmt(txn);
 	enum dup_replace_mode mode = dup_replace_mode(request->type);
-	stmt->new_tuple = memtx_tuple_new(space->format, request->tuple,
-					  request->tuple_end);
-	if (stmt->new_tuple == NULL)
+	*new_tuple = memtx_tuple_new(space->format, request->tuple,
+				     request->tuple_end);
+	if (*new_tuple == NULL)
 		return -1;
-	tuple_ref(stmt->new_tuple);
-	if (memtx_space->replace(space, NULL, stmt->new_tuple,
-				 mode, &stmt->old_tuple) != 0)
+	if (memtx_space->replace(space, NULL, *new_tuple, mode, old_tuple) != 0)
 		return -1;
-	stmt->engine_savepoint = stmt;
 	/** The new tuple is referenced by the primary key. */
-	*result = stmt->new_tuple;
+	*result = *new_tuple;
 	return 0;
 }
 
-static int
-memtx_space_execute_delete(struct space *space, struct txn *txn,
-			   struct request *request, struct tuple **result)
+/**
+ * Executes DELETE operation for given space.
+ *
+ * @param space space for which operation is executed.
+ * @param old_tuple address of caller's old_tuple.
+ * @param request request which defines operation.
+ * @param[out] result result tuple.
+ *
+ * @returns 0 Success.
+ * @returns -1 Error.
+ */
+static inline int
+memtx_space_delete_impl(struct space *space, struct request *request,
+			struct tuple **old_tuple, struct tuple **result)
 {
 	struct memtx_space *memtx_space = (struct memtx_space *)space;
-	struct txn_stmt *stmt = txn_current_stmt(txn);
 	/* Try to find the tuple by unique key. */
 	struct index *pk = index_find_unique(space, request->index_id);
 	if (pk == NULL)
 		return -1;
 	const char *key = request->key;
 	uint32_t part_count = mp_decode_array(&key);
-	struct tuple *old_tuple;
-	if (index_get(pk, key, part_count, &old_tuple) != 0)
+	struct tuple *tmp_tuple;
+	if (index_get(pk, key, part_count, &tmp_tuple) != 0)
 		return -1;
-	if (old_tuple != NULL &&
-	    memtx_space->replace(space, old_tuple, NULL,
-				 DUP_REPLACE_OR_INSERT, &stmt->old_tuple) != 0)
+	if (tmp_tuple != NULL &&
+	    memtx_space->replace(space, tmp_tuple, NULL,
+				 DUP_REPLACE_OR_INSERT, old_tuple) != 0)
 		return -1;
-	stmt->engine_savepoint = stmt;
-	*result = stmt->old_tuple;
+	*result = *old_tuple;
 	return 0;
 }
 
-static int
-memtx_space_execute_update(struct space *space, struct txn *txn,
-			   struct request *request, struct tuple **result)
+/**
+ * Executes UPDATE operation for given space.
+ *
+ * @param space space for which operation is executed.
+ * @param new_tuple address of caller's new_tuple.
+ * @param old_tuple address of caller's old_tuple.
+ * @param request request which defines operation.
+ * @param[out] result result tuple.
+ *
+ * @returns 0 Success.
+ * @returns -1 Error.
+ */
+static inline int
+memtx_space_update_impl(struct space *space, struct request *request,
+			struct tuple **new_tuple, struct tuple **old_tuple,
+			struct tuple **result)
 {
 	struct memtx_space *memtx_space = (struct memtx_space *)space;
-	struct txn_stmt *stmt = txn_current_stmt(txn);
 	/* Try to find the tuple by unique key. */
 	struct index *pk = index_find_unique(space, request->index_id);
 	if (pk == NULL)
 		return -1;
 	const char *key = request->key;
 	uint32_t part_count = mp_decode_array(&key);
-	struct tuple *old_tuple;
-	if (index_get(pk, key, part_count, &old_tuple) != 0)
+	struct tuple *tmp_tuple;
+	if (index_get(pk, key, part_count, &tmp_tuple) != 0)
 		return -1;
 
-	if (old_tuple == NULL) {
+	if (tmp_tuple == NULL) {
 		*result = NULL;
 		return 0;
 	}
 
 	/* Update the tuple; legacy, request ops are in request->tuple */
 	uint32_t new_size = 0, bsize;
-	const char *old_data = tuple_data_range(old_tuple, &bsize);
+	const char *old_data = tuple_data_range(tmp_tuple, &bsize);
 	const char *new_data =
 		tuple_update_execute(region_aligned_alloc_cb, &fiber()->gc,
 				     request->tuple, request->tuple_end,
@@ -409,25 +439,33 @@ memtx_space_execute_update(struct space *space, struct txn *txn,
 	if (new_data == NULL)
 		return -1;
 
-	stmt->new_tuple = memtx_tuple_new(space->format, new_data,
-					  new_data + new_size);
-	if (stmt->new_tuple == NULL)
+	*new_tuple = memtx_tuple_new(space->format, new_data,
+				     new_data + new_size);
+	if (*new_tuple == NULL)
 		return -1;
-	tuple_ref(stmt->new_tuple);
-	if (memtx_space->replace(space, old_tuple, stmt->new_tuple,
-				 DUP_REPLACE, &stmt->old_tuple) != 0)
+	if (memtx_space->replace(space, tmp_tuple, *new_tuple,
+				 DUP_REPLACE, old_tuple) != 0)
 		return -1;
-	stmt->engine_savepoint = stmt;
-	*result = stmt->new_tuple;
+	*result = *new_tuple;
 	return 0;
 }
 
-static int
-memtx_space_execute_upsert(struct space *space, struct txn *txn,
-			   struct request *request)
+/**
+ * Executes INSERT operation for given space.
+ *
+ * @param space space for which operation is executed.
+ * @param new_tuple address of caller's new_tuple.
+ * @param old_tuple address of caller's old_tuple.
+ * @param request request which defines operation.
+ *
+ * @returns 0 Success.
+ * @returns -1 Error.
+ */
+static inline int
+memtx_space_upsert_impl(struct space *space, struct request *request,
+			struct tuple **new_tuple, struct tuple **old_tuple)
 {
 	struct memtx_space *memtx_space = (struct memtx_space *)space;
-	struct txn_stmt *stmt = txn_current_stmt(txn);
 	/*
 	 * Check all tuple fields: we should produce an error on
 	 * malformed tuple even if upsert turns into an update.
@@ -450,11 +488,11 @@ memtx_space_execute_upsert(struct space *space, struct txn *txn,
 	mp_decode_array(&key);
 
 	/* Try to find the tuple by primary key. */
-	struct tuple *old_tuple;
-	if (index_get(index, key, part_count, &old_tuple) != 0)
+	struct tuple *tmp_tuple;
+	if (index_get(index, key, part_count, &tmp_tuple) != 0)
 		return -1;
 
-	if (old_tuple == NULL) {
+	if (tmp_tuple == NULL) {
 		/**
 		 * Old tuple was not found. A write optimized
 		 * engine may only know this after commit, so
@@ -471,20 +509,20 @@ memtx_space_execute_upsert(struct space *space, struct txn *txn,
 		 *   we get it here, it's also OK to throw it
 		 * @sa https://github.com/tarantool/tarantool/issues/1156
 		 */
-		if (tuple_update_check_ops(region_aligned_alloc_cb, &fiber()->gc,
-				       request->ops, request->ops_end,
-				       request->index_base)) {
+		if (tuple_update_check_ops(region_aligned_alloc_cb,
+					   &fiber()->gc, request->ops,
+					   request->ops_end,
+					   request->index_base)) {
 			return -1;
 		}
-		stmt->new_tuple = memtx_tuple_new(space->format,
+		*new_tuple = memtx_tuple_new(space->format,
 						  request->tuple,
 						  request->tuple_end);
-		if (stmt->new_tuple == NULL)
+		if (*new_tuple == NULL)
 			return -1;
-		tuple_ref(stmt->new_tuple);
 	} else {
 		uint32_t new_size = 0, bsize;
-		const char *old_data = tuple_data_range(old_tuple, &bsize);
+		const char *old_data = tuple_data_range(tmp_tuple, &bsize);
 		/*
 		 * Update the tuple.
 		 * tuple_upsert_execute() fails on totally wrong
@@ -502,23 +540,22 @@ memtx_space_execute_upsert(struct space *space, struct txn *txn,
 		if (new_data == NULL)
 			return -1;
 
-		stmt->new_tuple = memtx_tuple_new(space->format, new_data,
-						  new_data + new_size);
-		if (stmt->new_tuple == NULL)
+		*new_tuple = memtx_tuple_new(space->format, new_data,
+					     new_data + new_size);
+		if (*new_tuple == NULL)
 			return -1;
-		tuple_ref(stmt->new_tuple);
 
 		struct index *pk = space->index[0];
 		if (!key_update_can_be_skipped(pk->def->key_def->column_mask,
 					       column_mask) &&
-		    tuple_compare(old_tuple, stmt->new_tuple,
+		    tuple_compare(tmp_tuple, *new_tuple,
 				  pk->def->key_def) != 0) {
 			/* Primary key is changed: log error and do nothing. */
 			diag_set(ClientError, ER_CANT_UPDATE_PRIMARY_KEY,
 				 pk->def->name, space_name(space));
 			diag_log();
-			tuple_unref(stmt->new_tuple);
-			stmt->new_tuple = NULL;
+			memtx_tuple_delete(space->format, *new_tuple);
+			*new_tuple = NULL;
 		}
 	}
 	/*
@@ -527,74 +564,184 @@ memtx_space_execute_upsert(struct space *space, struct txn *txn,
 	 * we checked this case explicitly and skipped the upsert
 	 * above.
 	 */
-	if (stmt->new_tuple != NULL &&
-	    memtx_space->replace(space, old_tuple, stmt->new_tuple,
-				 DUP_REPLACE_OR_INSERT, &stmt->old_tuple) != 0)
+	if (*new_tuple != NULL &&
+	    memtx_space->replace(space, tmp_tuple, *new_tuple,
+				 DUP_REPLACE_OR_INSERT, old_tuple) != 0)
 		return -1;
-	stmt->engine_savepoint = stmt;
 	/* Return nothing: UPSERT does not return data. */
 	return 0;
 }
 
+static int
+memtx_space_execute_replace(struct space *space, struct txn *txn,
+			    struct request *request, struct tuple **result)
+{
+	struct txn_stmt *stmt = txn_current_stmt(txn);
+	int rc = memtx_space_replace_impl(space, request, &stmt->new_tuple,
+					  &stmt->old_tuple, result);
+	if (stmt->new_tuple != NULL)
+		tuple_ref(stmt->new_tuple);
+	if (rc != 0)
+		return -1;
+	stmt->engine_savepoint = stmt;
+	return 0;
+}
+
+static int
+memtx_space_execute_delete(struct space *space, struct txn *txn,
+			   struct request *request, struct tuple **result)
+{
+	struct txn_stmt *stmt = txn_current_stmt(txn);
+	if (memtx_space_delete_impl(space, request, &stmt->old_tuple,
+				    result) != 0)
+		return -1;
+	stmt->engine_savepoint = stmt;
+	return 0;
+}
+
+static int
+memtx_space_execute_update(struct space *space, struct txn *txn,
+			   struct request *request, struct tuple **result)
+{
+	struct txn_stmt *stmt = txn_current_stmt(txn);
+	int rc = memtx_space_update_impl(space, request, &stmt->new_tuple,
+					 &stmt->old_tuple, result);
+	if (stmt->new_tuple != NULL)
+		tuple_ref(stmt->new_tuple);
+	if (rc != 0)
+		return -1;
+	stmt->engine_savepoint = stmt;
+	return 0;
+}
+
+static int
+memtx_space_execute_upsert(struct space *space, struct txn *txn,
+			   struct request *request)
+{
+	struct txn_stmt *stmt = txn_current_stmt(txn);
+	int rc = memtx_space_upsert_impl(space, request, &stmt->new_tuple,
+					 &stmt->old_tuple);
+	if (stmt->new_tuple != NULL)
+		tuple_ref(stmt->new_tuple);
+	if (rc != 0)
+		return -1;
+	stmt->engine_savepoint = stmt;
+	return 0;
+}
+
 /**
- * This function simply creates new memtx tuple, refs it and calls space's
- * replace function. In constrast to original memtx_space_execute_replace(), it
- * doesn't handle any transaction routine.
- * Ephemeral spaces shouldn't be involved in transaction routine, since
- * they are used only for internal purposes. Moreover, ephemeral spaces
- * can be created and destroyed within one transaction and rollback of already
- * destroyed space may lead to undefined behaviour. For this reason it
- * doesn't take txn as an argument.
+ * Executes INSERT and REPLACE operations for ephemeral spaces.
+ * This function isn't involved in any transaction routine.
+ *
+ * @param space space for which operation is executed.
+ * @param txn txn is NULL for ephemeral spaces.
+ * @param request request which defines operation.
+ * @param[out] result result tuple.
+ *
+ * @returns 0 Success.
+ * @returns -1 Error.
  */
 static int
-memtx_space_ephemeral_replace(struct space *space, const char *tuple,
-				      const char *tuple_end)
+memtx_space_ephemeral_replace(struct space *space, struct txn *txn,
+			      struct request *request, struct tuple **result)
 {
-	struct memtx_space *memtx_space = (struct memtx_space *)space;
-	struct tuple *new_tuple = memtx_tuple_new(space->format, tuple,
-						  tuple_end);
-	if (new_tuple == NULL)
-		return -1;
-	struct tuple *old_tuple;
-	if (memtx_space->replace(space, NULL, new_tuple,
-				 DUP_REPLACE_OR_INSERT, &old_tuple) != 0) {
+	assert(txn == NULL);
+	(void)txn;
+	struct tuple *new_tuple = NULL;
+	struct tuple *old_tuple = NULL;
+	int rc = memtx_space_replace_impl(space, request, &new_tuple,
+					  &old_tuple, result);
+	if (rc != 0 && new_tuple != NULL)
 		memtx_tuple_delete(space->format, new_tuple);
+	if (rc != 0)
 		return -1;
-	}
 	if (old_tuple != NULL)
 		tuple_unref(old_tuple);
 	return 0;
 }
 
 /**
- * Delete tuple with given key from primary index. Tuple checking is omitted
- * due to the ability of ephemeral spaces to hold nulls in primary key.
- * Generally speaking, it is not correct behaviour owing to ambiguity when
- * fetching/deleting tuple from space with several tuples containing
- * nulls in PK. On the other hand, ephemeral spaces are used only for internal
- * needs, so if it is guaranteed that no such situation occur
- * (when several tuples with nulls in PK exist), it is OK to allow
- * insertion nulls in PK.
- *
- * Similarly to ephemeral replace function,
- * it isn't involved in any transaction routine.
+ * Executes DELETE operation for ephemeral space.
+ * This function isn't involved in any transaction routine.
+ *
+ * @param space space for which operation is executed.
+ * @param txn txn is NULL for ephemeral spaces.
+ * @param request request which defines operation.
+ * @param[out] result result tuple.
+ *
+ * @returns 0 Success.
+ * @returns -1 Error.
  */
 static int
-memtx_space_ephemeral_delete(struct space *space, const char *key)
+memtx_space_ephemeral_delete(struct space *space, struct txn *txn,
+			     struct request *request, struct tuple **result)
 {
-	struct memtx_space *memtx_space = (struct memtx_space *)space;
-	struct index *primary_index = space_index(space, 0 /* primary index*/);
-	if (primary_index == NULL)
+	assert(txn == NULL);
+	(void)txn;
+	struct tuple *old_tuple = NULL;
+	if (memtx_space_delete_impl(space, request, &old_tuple, result) != 0)
 		return -1;
-	uint32_t part_count = mp_decode_array(&key);
-	struct tuple *old_tuple;
-	if (index_get(primary_index, key, part_count, &old_tuple) != 0)
+	return 0;
+}
+
+/**
+ * Executes UPDATE operation for ephemeral space.
+ * This function isn't involved in any transaction routine.
+ *
+ * @param space space for which operation is executed.
+ * @param txn txn is NULL for ephemeral spaces.
+ * @param request request which defines operation.
+ * @param[out] result result tuple.
+ *
+ * @returns 0 Success.
+ * @returns -1 Error.
+ */
+static int
+memtx_space_ephemeral_update(struct space *space, struct txn *txn,
+			     struct request *request, struct tuple **result)
+{
+	assert(txn == NULL);
+	(void)txn;
+	struct tuple *new_tuple = NULL;
+	struct tuple *old_tuple = NULL;
+	int rc = memtx_space_update_impl(space, request, &new_tuple,
+					 &old_tuple, result);
+	if (rc != 0 && new_tuple != NULL)
+		memtx_tuple_delete(space->format, new_tuple);
+	if (rc != 0)
 		return -1;
-	if (old_tuple != NULL &&
-	    memtx_space->replace(space, old_tuple, NULL,
-				 DUP_REPLACE, &old_tuple) != 0)
+	if (old_tuple != NULL)
+		tuple_unref(old_tuple);
+	return 0;
+}
+
+/**
+ * Executes UPSERT operation  for ephemral space.
+ * This function isn't involved in any transaction routine.
+ *
+ * @param space space for which operation is executed.
+ * @param txn txn is NULL for ephemeral spaces.
+ * @param request request which defines operation.
+ *
+ * @returns 0 Success.
+ * @returns -1 Error.
+ */
+static int
+memtx_space_ephemeral_upsert(struct space *space, struct txn *txn,
+			     struct request *request)
+{
+	assert(txn == NULL);
+	(void)txn;
+	struct tuple *old_tuple = NULL;
+	struct tuple *new_tuple = NULL;
+	int rc = memtx_space_upsert_impl(space, request, &new_tuple,
+					 &old_tuple);
+	if (rc != 0 && new_tuple != NULL)
+		memtx_tuple_delete(space->format, new_tuple);
+	if (rc != 0)
 		return -1;
-	tuple_unref(old_tuple);
+	if (old_tuple != NULL)
+		tuple_unref(old_tuple);
 	return 0;
 }
 
@@ -835,9 +982,14 @@ memtx_init_system_space(struct space *space)
 }
 
 static void
-memtx_init_ephemeral_space(struct space *space)
+memtx_init_ephemeral_space(struct space *space);
+
+static void
+memtx_init_unsupported_space(struct space *space)
 {
-	memtx_space_add_primary_key(space);
+	(void)space;
+	diag_set(ClientError, ER_UNSUPPORTED, space_name(space),
+		 "init_ephemeral_space");
 }
 
 static int
@@ -936,8 +1088,6 @@ static const struct space_vtab memtx_space_vtab = {
 	/* .execute_delete = */ memtx_space_execute_delete,
 	/* .execute_update = */ memtx_space_execute_update,
 	/* .execute_upsert = */ memtx_space_execute_upsert,
-	/* .ephemeral_replace = */ memtx_space_ephemeral_replace,
-	/* .ephemeral_delete = */ memtx_space_ephemeral_delete,
 	/* .init_system_space = */ memtx_init_system_space,
 	/* .init_ephemeral_space = */ memtx_init_ephemeral_space,
 	/* .check_index_def = */ memtx_space_check_index_def,
@@ -950,6 +1100,33 @@ static const struct space_vtab memtx_space_vtab = {
 	/* .prepare_alter = */ memtx_space_prepare_alter,
 };
 
+static const struct space_vtab memtx_space_ephemeral_vtab = {
+	/* .destroy = */ memtx_space_destroy,
+	/* .bsize = */ memtx_space_bsize,
+	/* .apply_initial_join_row = */ memtx_space_apply_initial_join_row,
+	/* .execute_replace = */ memtx_space_ephemeral_replace,
+	/* .execute_delete = */ memtx_space_ephemeral_delete,
+	/* .execute_update = */ memtx_space_ephemeral_update,
+	/* .execute_upsert = */ memtx_space_ephemeral_upsert,
+	/* .init_system_space = */ memtx_init_system_space,
+	/* .init_ephemeral_space = */ memtx_init_unsupported_space,
+	/* .check_index_def = */ memtx_space_check_index_def,
+	/* .create_index = */ memtx_space_create_index,
+	/* .add_primary_key = */ memtx_space_add_primary_key,
+	/* .drop_primary_key = */ memtx_space_drop_primary_key,
+	/* .check_format  = */ memtx_space_check_format,
+	/* .build_index = */ memtx_space_build_index,
+	/* .swap_index = */ generic_space_swap_index,
+	/* .prepare_alter = */ memtx_space_prepare_alter,
+};
+
+static void
+memtx_init_ephemeral_space(struct space *space)
+{
+	space->vtab = &memtx_space_ephemeral_vtab;
+	memtx_space_add_primary_key(space);
+}
+
 struct space *
 memtx_space_new(struct memtx_engine *memtx,
 		struct space_def *def, struct rlist *key_list)
diff --git a/src/box/space.h b/src/box/space.h
index 01a4af7..da5f7c6 100644
--- a/src/box/space.h
+++ b/src/box/space.h
@@ -67,10 +67,6 @@ struct space_vtab {
 			      struct request *, struct tuple **result);
 	int (*execute_upsert)(struct space *, struct txn *, struct request *);
 
-	int (*ephemeral_replace)(struct space *, const char *, const char *);
-
-	int (*ephemeral_delete)(struct space *, const char *);
-
 	void (*init_system_space)(struct space *);
 	/**
 	 * Initialize an ephemeral space instance.
@@ -310,19 +306,6 @@ int
 space_execute_dml(struct space *space, struct txn *txn,
 		  struct request *request, struct tuple **result);
 
-static inline int
-space_ephemeral_replace(struct space *space, const char *tuple,
-			const char *tuple_end)
-{
-	return space->vtab->ephemeral_replace(space, tuple, tuple_end);
-}
-
-static inline int
-space_ephemeral_delete(struct space *space, const char *key)
-{
-	return space->vtab->ephemeral_delete(space, key);
-}
-
 /**
  * Generic implementation of space_vtab::swap_index
  * that simply swaps the two indexes in index maps.
diff --git a/src/box/sql.c b/src/box/sql.c
index d48c3cf..4b38f98 100644
--- a/src/box/sql.c
+++ b/src/box/sql.c
@@ -451,7 +451,13 @@ int tarantoolSqlite3EphemeralInsert(struct space *space, const char *tuple,
 {
 	assert(space != NULL);
 	mp_tuple_assert(tuple, tuple_end);
-	if (space_ephemeral_replace(space, tuple, tuple_end) != 0)
+	struct request request;
+	memset(&request, 0, sizeof(request));
+	request.type = IPROTO_REPLACE;
+	request.tuple = tuple;
+	request.tuple_end = tuple_end;
+	struct tuple *result;
+	if (space_execute_dml(space, NULL, &request, &result) != 0)
 		return SQL_TARANTOOL_INSERT_FAIL;
 	return SQLITE_OK;
 }
@@ -516,11 +522,20 @@ int tarantoolSqlite3EphemeralDelete(BtCursor *pCur)
 	if (key == NULL)
 		return SQL_TARANTOOL_DELETE_FAIL;
 
-	int rc = space_ephemeral_delete(pCur->space, key);
-	if (rc != 0) {
+	mp_tuple_assert(key, key + key_size);
+	struct request request;
+	struct tuple *result;
+	memset(&request, 0, sizeof(request));
+	request.type = IPROTO_DELETE;
+	request.key = key;
+	request.key_end = key + key_size;
+
+	if (space_execute_dml(pCur->space, NULL, &request, &result) != 0) {
 		diag_log();
 		return SQL_TARANTOOL_DELETE_FAIL;
 	}
+	if (result != NULL)
+		box_tuple_unref(result);
 	return SQLITE_OK;
 }
 
@@ -596,14 +611,24 @@ int tarantoolSqlite3EphemeralClearTable(BtCursor *pCur)
 	struct tuple *tuple;
 	char *key;
 	uint32_t  key_size;
+	struct request request;
+	struct tuple *result;
+	memset(&request, 0, sizeof(request));
+	request.type = IPROTO_DELETE;
 
 	while (iterator_next(it, &tuple) == 0 && tuple != NULL) {
 		key = tuple_extract_key(tuple, it->index->def->key_def,
 					&key_size);
-		if (space_ephemeral_delete(pCur->space, key) != 0) {
+		mp_tuple_assert(key, key + key_size);
+		request.key = key;
+		request.key_end = key + key_size;
+		if (space_execute_dml(pCur->space, NULL, &request,
+				      &result) != 0) {
 			iterator_delete(it);
 			return SQL_TARANTOOL_DELETE_FAIL;
 		}
+		if (result != NULL)
+			box_tuple_unref(result);
 	}
 	iterator_delete(it);
 
diff --git a/src/box/sysview.c b/src/box/sysview.c
index a215e9e..ce2b47e 100644
--- a/src/box/sysview.c
+++ b/src/box/sysview.c
@@ -482,8 +482,6 @@ static const struct space_vtab sysview_space_vtab = {
 	/* .execute_delete = */ sysview_space_execute_delete,
 	/* .execute_update = */ sysview_space_execute_update,
 	/* .execute_upsert = */ sysview_space_execute_upsert,
-	/* .ephemeral_replace = */ generic_space_ephemeral_replace,
-	/* .ephemeral_delete = */ generic_space_ephemeral_delete,
 	/* .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 05321cd..3655378 100644
--- a/src/box/vinyl.c
+++ b/src/box/vinyl.c
@@ -4419,8 +4419,6 @@ static const struct space_vtab vinyl_space_vtab = {
 	/* .execute_delete = */ vinyl_space_execute_delete,
 	/* .execute_update = */ vinyl_space_execute_update,
 	/* .execute_upsert = */ vinyl_space_execute_upsert,
-	/* .ephemeral_replace = */ generic_space_ephemeral_replace,
-	/* .ephemeral_delete = */ generic_space_ephemeral_delete,
 	/* .init_system_space = */ generic_init_system_space,
 	/* .init_ephemeral_space = */ generic_init_ephemeral_space,
 	/* .check_index_def = */ vinyl_space_check_index_def,
-- 
2.7.4

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [tarantool-patches] [PATCH v3 4/7] box: move some decode functions from alter.cc
  2018-07-24 11:58 [tarantool-patches] [PATCH v3 0/7] Expose ephemeral spaces into Lua imeevma
                   ` (2 preceding siblings ...)
  2018-07-24 11:58 ` [tarantool-patches] [PATCH v3 3/7] box: create new methods for ephemeral spaces imeevma
@ 2018-07-24 11:58 ` imeevma
  2018-07-24 11:58 ` [tarantool-patches] [PATCH v3 5/7] box: ephemeral space creation and deletion in Lua imeevma
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 8+ messages in thread
From: imeevma @ 2018-07-24 11:58 UTC (permalink / raw)
  To: tarantool-patches

Move functions to decode space format and
functions to decode index options from
alter.cc to format_def.c and index_def.c.

Part of #3375.
---
 src/box/alter.cc    | 256 +++-------------------------------------------------
 src/box/index_def.c | 117 ++++++++++++++++++++++++
 src/box/index_def.h |  26 ++++++
 src/box/space_def.c | 155 +++++++++++++++++++++++++++++++
 src/box/space_def.h |  18 ++++
 5 files changed, 327 insertions(+), 245 deletions(-)

diff --git a/src/box/alter.cc b/src/box/alter.cc
index 7b6bd1a..4fba40d 100644
--- a/src/box/alter.cc
+++ b/src/box/alter.cc
@@ -30,27 +30,18 @@
  */
 #include "alter.h"
 #include "schema.h"
-#include "user.h"
-#include "space.h"
-#include "index.h"
 #include "func.h"
 #include "coll_id_cache.h"
 #include "coll_id_def.h"
 #include "txn.h"
 #include "tuple.h"
-#include "fiber.h" /* for gc_pool */
 #include "scoped_guard.h"
 #include "third_party/base64.h"
-#include <new> /* for placement new */
-#include <stdio.h> /* snprintf() */
-#include <ctype.h>
 #include "replication.h" /* for replica_set_id() */
 #include "session.h" /* to fetch the current user. */
-#include "vclock.h" /* VCLOCK_MAX */
 #include "xrow.h"
 #include "iproto_constants.h"
 #include "identifier.h"
-#include "version.h"
 #include "sequence.h"
 #include "sql.h"
 
@@ -167,61 +158,6 @@ err:
 }
 
 /**
- * Fill index_opts structure from opts field in tuple of space _index
- * Throw an error is unrecognized option.
- */
-static void
-index_opts_decode(struct index_opts *opts, const char *map,
-		  struct region *region)
-{
-	index_opts_create(opts);
-	if (opts_decode(opts, index_opts_reg, &map, ER_WRONG_INDEX_OPTIONS,
-			BOX_INDEX_FIELD_OPTS, region) != 0)
-		diag_raise();
-	if (opts->distance == rtree_index_distance_type_MAX) {
-		tnt_raise(ClientError, ER_WRONG_INDEX_OPTIONS,
-			  BOX_INDEX_FIELD_OPTS, "distance must be either "\
-			  "'euclid' or 'manhattan'");
-	}
-	if (opts->sql != NULL) {
-		char *sql = strdup(opts->sql);
-		if (sql == NULL) {
-			opts->sql = NULL;
-			tnt_raise(OutOfMemory, strlen(opts->sql) + 1, "strdup",
-				  "sql");
-		}
-		opts->sql = sql;
-	}
-	if (opts->range_size <= 0) {
-		tnt_raise(ClientError, ER_WRONG_INDEX_OPTIONS,
-			  BOX_INDEX_FIELD_OPTS,
-			  "range_size must be greater than 0");
-	}
-	if (opts->page_size <= 0 || opts->page_size > opts->range_size) {
-		tnt_raise(ClientError, ER_WRONG_INDEX_OPTIONS,
-			  BOX_INDEX_FIELD_OPTS,
-			  "page_size must be greater than 0 and "
-			  "less than or equal to range_size");
-	}
-	if (opts->run_count_per_level <= 0) {
-		tnt_raise(ClientError, ER_WRONG_INDEX_OPTIONS,
-			  BOX_INDEX_FIELD_OPTS,
-			  "run_count_per_level must be greater than 0");
-	}
-	if (opts->run_size_ratio <= 1) {
-		tnt_raise(ClientError, ER_WRONG_INDEX_OPTIONS,
-			  BOX_INDEX_FIELD_OPTS,
-			  "run_size_ratio must be greater than 1");
-	}
-	if (opts->bloom_fpr <= 0 || opts->bloom_fpr > 1) {
-		tnt_raise(ClientError, ER_WRONG_INDEX_OPTIONS,
-			  BOX_INDEX_FIELD_OPTS,
-			  "bloom_fpr must be greater than 0 and "
-			  "less than or equal to 1");
-	}
-}
-
-/**
  * Create a index_def object from a record in _index
  * system space.
  *
@@ -237,50 +173,22 @@ static struct index_def *
 index_def_new_from_tuple(struct tuple *tuple, struct space *space)
 {
 	index_def_check_tuple(tuple);
-
-	struct index_opts opts;
 	uint32_t id = tuple_field_u32_xc(tuple, BOX_INDEX_FIELD_SPACE_ID);
 	uint32_t index_id = tuple_field_u32_xc(tuple, BOX_INDEX_FIELD_ID);
-	enum index_type type =
-		STR2ENUM(index_type, tuple_field_cstr_xc(tuple,
-							 BOX_INDEX_FIELD_TYPE));
 	uint32_t name_len;
 	const char *name = tuple_field_str_xc(tuple, BOX_INDEX_FIELD_NAME,
 					      &name_len);
+	const char *type_field = tuple_field_cstr_xc(tuple,
+						     BOX_INDEX_FIELD_TYPE);
 	const char *opts_field =
-		tuple_field_with_type_xc(tuple, BOX_INDEX_FIELD_OPTS,
-					 MP_MAP);
-	index_opts_decode(&opts, opts_field, &fiber()->gc);
+		tuple_field_with_type_xc(tuple, BOX_INDEX_FIELD_OPTS, MP_MAP);
 	const char *parts = tuple_field(tuple, BOX_INDEX_FIELD_PARTS);
-	uint32_t part_count = mp_decode_array(&parts);
-	if (name_len > BOX_NAME_MAX) {
-		tnt_raise(ClientError, ER_MODIFY_INDEX,
-			  tt_cstr(name, BOX_INVALID_NAME_MAX),
-			  space_name(space), "index name is too long");
-	}
-	identifier_check_xc(name, name_len);
-	struct key_def *key_def = NULL;
-	struct key_part_def *part_def = (struct key_part_def *)
-			malloc(sizeof(*part_def) * part_count);
-	if (part_def == NULL) {
-		tnt_raise(OutOfMemory, sizeof(*part_def) * part_count,
-			  "malloc", "key_part_def");
-	}
-	auto key_def_guard = make_scoped_guard([&] {
-		free(part_def);
-		if (key_def != NULL)
-			key_def_delete(key_def);
-	});
-	if (key_def_decode_parts(part_def, part_count, &parts,
-				 space->def->fields,
-				 space->def->field_count) != 0)
-		diag_raise();
-	key_def = key_def_new_with_parts(part_def, part_count);
-	if (key_def == NULL)
-		diag_raise();
 	struct index_def *index_def =
-		index_def_new(id, index_id, name, name_len, type,
-			      &opts, key_def, space_index_key_def(space, 0));
+		index_def_new_decode(id, index_id, space->def->fields,
+				     space->def->field_count, name, name_len,
+				     type_field, opts_field, parts,
+				     space_name(space),
+				     space_index_key_def(space, 0));
 	if (index_def == NULL)
 		diag_raise();
 	auto index_def_guard = make_scoped_guard([=] { index_def_delete(index_def); });
@@ -316,149 +224,6 @@ space_opts_decode(struct space_opts *opts, const char *map,
 }
 
 /**
- * Decode field definition from MessagePack map. Format:
- * {name: <string>, type: <string>}. Type is optional.
- * @param[out] field Field to decode to.
- * @param data MessagePack map to decode.
- * @param space_name Name of a space, from which the field is got.
- *        Used in error messages.
- * @param name_len Length of @a space_name.
- * @param errcode Error code to use for client errors. Either
- *        create or modify space errors.
- * @param fieldno Field number to decode. Used in error messages.
- * @param region Region to allocate field name.
- */
-static void
-field_def_decode(struct field_def *field, const char **data,
-		 const char *space_name, uint32_t name_len,
-		 uint32_t errcode, uint32_t fieldno, struct region *region)
-{
-	if (mp_typeof(**data) != MP_MAP) {
-		tnt_raise(ClientError, errcode, tt_cstr(space_name, name_len),
-			  tt_sprintf("field %d is not map",
-				     fieldno + TUPLE_INDEX_BASE));
-	}
-	int count = mp_decode_map(data);
-	*field = field_def_default;
-	bool is_action_missing = true;
-	uint32_t action_literal_len = strlen("nullable_action");
-	for (int i = 0; i < count; ++i) {
-		if (mp_typeof(**data) != MP_STR) {
-			tnt_raise(ClientError, errcode,
-				  tt_cstr(space_name, name_len),
-				  tt_sprintf("field %d format is not map"\
-					     " with string keys",
-					     fieldno + TUPLE_INDEX_BASE));
-		}
-		uint32_t key_len;
-		const char *key = mp_decode_str(data, &key_len);
-		if (opts_parse_key(field, field_def_reg, key, key_len, data,
-				   ER_WRONG_SPACE_FORMAT,
-				   fieldno + TUPLE_INDEX_BASE, region,
-				   true) != 0)
-			diag_raise();
-		if (is_action_missing &&
-		    key_len == action_literal_len &&
-		    memcmp(key, "nullable_action", action_literal_len) == 0)
-			is_action_missing = false;
-	}
-	if (is_action_missing) {
-		field->nullable_action = field->is_nullable ?
-			ON_CONFLICT_ACTION_NONE
-			: ON_CONFLICT_ACTION_DEFAULT;
-	}
-	if (field->name == NULL) {
-		tnt_raise(ClientError, errcode, tt_cstr(space_name, name_len),
-			  tt_sprintf("field %d name is not specified",
-				     fieldno + TUPLE_INDEX_BASE));
-	}
-	size_t field_name_len = strlen(field->name);
-	if (field_name_len > BOX_NAME_MAX) {
-		tnt_raise(ClientError, errcode, tt_cstr(space_name, name_len),
-			  tt_sprintf("field %d name is too long",
-				     fieldno + TUPLE_INDEX_BASE));
-	}
-	identifier_check_xc(field->name, field_name_len);
-	if (field->type == field_type_MAX) {
-		tnt_raise(ClientError, errcode, tt_cstr(space_name, name_len),
-			  tt_sprintf("field %d has unknown field type",
-				     fieldno + TUPLE_INDEX_BASE));
-	}
-	if (field->nullable_action == on_conflict_action_MAX) {
-		tnt_raise(ClientError, errcode, tt_cstr(space_name, name_len),
-			  tt_sprintf("field %d has unknown field on conflict "
-				     "nullable action",
-				     fieldno + TUPLE_INDEX_BASE));
-	}
-	if (!((field->is_nullable && field->nullable_action ==
-	       ON_CONFLICT_ACTION_NONE)
-	      || (!field->is_nullable
-		  && field->nullable_action != ON_CONFLICT_ACTION_NONE))) {
-		tnt_raise(ClientError, errcode, tt_cstr(space_name, name_len),
-			  tt_sprintf("field %d has conflicting nullability and "
-				     "nullable action properties", fieldno +
-				     TUPLE_INDEX_BASE));
-	}
-	if (field->coll_id != COLL_NONE &&
-	    field->type != FIELD_TYPE_STRING &&
-	    field->type != FIELD_TYPE_SCALAR &&
-	    field->type != FIELD_TYPE_ANY) {
-		tnt_raise(ClientError, errcode, tt_cstr(space_name, name_len),
-			  tt_sprintf("collation is reasonable only for "
-				     "string, scalar and any fields"));
-	}
-
-	const char *dv = field->default_value;
-	if (dv != NULL) {
-		field->default_value_expr = sql_expr_compile(sql_get(), dv,
-							     strlen(dv));
-		if (field->default_value_expr == NULL)
-			diag_raise();
-	}
-}
-
-/**
- * Decode MessagePack array of fields.
- * @param data MessagePack array of fields.
- * @param[out] out_count Length of a result array.
- * @param space_name Space name to use in error messages.
- * @param errcode Errcode for client errors.
- * @param region Region to allocate result array.
- *
- * @retval Array of fields.
- */
-static struct field_def *
-space_format_decode(const char *data, uint32_t *out_count,
-		    const char *space_name, uint32_t name_len,
-		    uint32_t errcode, struct region *region)
-{
-	/* Type is checked by _space format. */
-	assert(mp_typeof(*data) == MP_ARRAY);
-	uint32_t count = mp_decode_array(&data);
-	*out_count = count;
-	if (count == 0)
-		return NULL;
-	size_t size = count * sizeof(struct field_def);
-	struct field_def *region_defs =
-		(struct field_def *) region_alloc_xc(region, size);
-	/*
-	 * Nullify to prevent a case when decoding will fail in
-	 * the middle and space_def_destroy_fields() below will
-	 * work with garbage pointers.
-	 */
-	memset(region_defs, 0, size);
-	auto fields_guard = make_scoped_guard([=] {
-		space_def_destroy_fields(region_defs, count);
-	});
-	for (uint32_t i = 0; i < count; ++i) {
-		field_def_decode(&region_defs[i], &data, space_name, name_len,
-				 errcode, i, region);
-	}
-	fields_guard.is_active = false;
-	return region_defs;
-}
-
-/**
  * Fill space_def structure from struct tuple.
  */
 static struct space_def *
@@ -508,8 +273,9 @@ space_def_new_from_tuple(struct tuple *tuple, uint32_t errcode,
 	const char *format =
 		tuple_field_with_type_xc(tuple, BOX_SPACE_FIELD_FORMAT,
 					 MP_ARRAY);
-	fields = space_format_decode(format, &field_count, name,
-				     name_len, errcode, region);
+	if (space_format_decode(&fields, format, &field_count, name, name_len,
+				errcode, region) != 0)
+		diag_raise();
 	auto fields_guard = make_scoped_guard([=] {
 		space_def_destroy_fields(fields, field_count);
 	});
diff --git a/src/box/index_def.c b/src/box/index_def.c
index 45c74d9..6b4f776 100644
--- a/src/box/index_def.c
+++ b/src/box/index_def.c
@@ -31,6 +31,7 @@
 #include "index_def.h"
 #include "schema_def.h"
 #include "identifier.h"
+#include "fiber.h"
 
 const char *index_type_strs[] = { "HASH", "TREE", "BITSET", "RTREE" };
 
@@ -309,3 +310,119 @@ index_def_is_valid(struct index_def *index_def, const char *space_name)
 	}
 	return true;
 }
+
+/**
+ * Fill index_opts structure from opts field in tuple of space _index
+ * Throw an error is unrecognized option.
+ *
+ * @param opts[out] result options.
+ * @param map options to decode.
+ * @param region region to use for memory alloc.
+ *
+ * @retval not NULL Success.
+ * @retval NULL Error.
+ */
+static inline int
+index_opts_decode(struct index_opts *opts, const char *map,
+		  struct region *region)
+{
+	index_opts_create(opts);
+	if (opts_decode(opts, index_opts_reg, &map, ER_WRONG_INDEX_OPTIONS,
+			BOX_INDEX_FIELD_OPTS, region) != 0) {
+		return -1;
+	}
+	if (opts->distance == rtree_index_distance_type_MAX) {
+		diag_set(ClientError, ER_WRONG_INDEX_OPTIONS,
+			  BOX_INDEX_FIELD_OPTS, "distance must be either "\
+			  "'euclid' or 'manhattan'");
+		return -1;
+	}
+	if (opts->sql != NULL) {
+		char *sql = strdup(opts->sql);
+		if (sql == NULL) {
+			opts->sql = NULL;
+			diag_set(OutOfMemory, strlen(opts->sql) + 1, "strdup",
+				 "sql");
+			return -1;
+		}
+		opts->sql = sql;
+	}
+	if (opts->range_size <= 0) {
+		diag_set(ClientError, ER_WRONG_INDEX_OPTIONS,
+			 BOX_INDEX_FIELD_OPTS,
+			 "range_size must be greater than 0");
+		return -1;
+	}
+	if (opts->page_size <= 0 || opts->page_size > opts->range_size) {
+		diag_set(ClientError, ER_WRONG_INDEX_OPTIONS,
+			 BOX_INDEX_FIELD_OPTS,
+			 "page_size must be greater than 0 and "
+			 "less than or equal to range_size");
+		return -1;
+	}
+	if (opts->run_count_per_level <= 0) {
+		diag_set(ClientError, ER_WRONG_INDEX_OPTIONS,
+			 BOX_INDEX_FIELD_OPTS,
+			 "run_count_per_level must be greater than 0");
+		return -1;
+	}
+	if (opts->run_size_ratio <= 1) {
+		diag_set(ClientError, ER_WRONG_INDEX_OPTIONS,
+			 BOX_INDEX_FIELD_OPTS,
+			 "run_size_ratio must be greater than 1");
+		return -1;
+	}
+	if (opts->bloom_fpr <= 0 || opts->bloom_fpr > 1) {
+		diag_set(ClientError, ER_WRONG_INDEX_OPTIONS,
+			 BOX_INDEX_FIELD_OPTS,
+			 "bloom_fpr must be greater than 0 and "
+			 "less than or equal to 1");
+		return -1;
+	}
+	return 0;
+}
+
+struct index_def *
+index_def_new_decode(uint32_t space_id, uint32_t index_id,
+		     struct field_def *fields,
+		     uint32_t field_count, const char *name,
+		     uint32_t name_len, const char *type_field,
+		     const char *opts_field, const char *parts,
+		     const char *space_name, struct key_def *pk_def)
+{
+	struct index_opts opts;
+	enum index_type type = STR2ENUM(index_type, type_field);
+	if (index_opts_decode(&opts, opts_field, &fiber()->gc) != 0)
+		return NULL;
+	if (name_len > BOX_NAME_MAX) {
+		diag_set(ClientError, ER_MODIFY_INDEX,
+			 tt_cstr(name, BOX_INVALID_NAME_MAX),
+			 space_name, "index name is too long");
+		return NULL;
+	}
+	if (identifier_check(name, name_len) != 0)
+		return NULL;
+	struct key_def *key_def = NULL;
+	uint32_t part_count = mp_decode_array(&parts);
+	struct key_part_def *part_def =
+		(struct key_part_def *) malloc(sizeof(*part_def) * part_count);
+	if (part_def == NULL) {
+		diag_set(OutOfMemory, sizeof(*part_def) * part_count,
+			 "malloc", "key_part_def");
+		return NULL;
+	}
+	if (key_def_decode_parts(part_def, part_count, &parts,
+				 fields, field_count) != 0) {
+		free(part_def);
+		return NULL;
+	}
+	key_def = key_def_new_with_parts(part_def, part_count);
+	free(part_def);
+	if (key_def == NULL)
+		return NULL;
+	struct index_def *index_def =
+		index_def_new(space_id, index_id, name, name_len, type, &opts,
+			      key_def, pk_def);
+	key_def_delete(key_def);
+	return index_def;
+}
diff --git a/src/box/index_def.h b/src/box/index_def.h
index 48a7820..2db6517 100644
--- a/src/box/index_def.h
+++ b/src/box/index_def.h
@@ -358,6 +358,32 @@ index_def_cmp(const struct index_def *key1, const struct index_def *key2);
 bool
 index_def_is_valid(struct index_def *index_def, const char *space_name);
 
+/**
+ * Create a index_def object from given arguments.
+ *
+ * @param space_id Id of space index created for.
+ * @param index_id Id of new index.
+ * @param fields Fields definition.
+ * @param field_count Number of fields.
+ * @param name Index name.
+ * @param name_len Length of index name.
+ * @param type_field Indx type as C-string.
+ * @param opts_field Options packed with MsgPack.
+ * @param parts Parts packed with MsgPack.
+ * @param space_name Name of space index created for.
+ * @param pk_def Primary key definition.
+ *
+ * @retval not NULL Success.
+ * @retval NULL Error.
+ */
+struct index_def *
+index_def_new_decode(uint32_t space_id, uint32_t index_id,
+		     struct field_def *fields,
+		     uint32_t field_count, const char *name,
+		     uint32_t name_len, const char *type_field,
+		     const char *opts_field, const char *parts,
+		     const char *space_name, struct key_def *pk_def);
+
 #if defined(__cplusplus)
 } /* extern "C" */
 
diff --git a/src/box/space_def.c b/src/box/space_def.c
index f5ca0b5..8780eec 100644
--- a/src/box/space_def.c
+++ b/src/box/space_def.c
@@ -34,6 +34,10 @@
 #include "error.h"
 #include "sql.h"
 #include "msgpuck.h"
+#include "tuple_format.h"
+#include "schema_def.h"
+#include "identifier.h"
+#include "small/region.h"
 
 /**
  * Make checks from msgpack.
@@ -351,3 +355,154 @@ error:
 	sql_expr_list_delete(db, checks);
 	return  -1;
 }
+
+/**
+ * Decode field definition from MessagePack map. Format:
+ * {name: <string>, type: <string>}. Type is optional.
+ * @param[out] field Field to decode to.
+ * @param data MessagePack map to decode.
+ * @param space_name Name of a space, from which the field is got.
+ *        Used in error messages.
+ * @param name_len Length of @a space_name.
+ * @param errcode Error code to use for client errors. Either
+ *        create or modify space errors.
+ * @param fieldno Field number to decode. Used in error messages.
+ * @param region Region to allocate field name.
+ *
+ * @retval 0 Success.
+ * @retval -1 Error.
+ */
+static inline int
+field_def_decode(struct field_def *field, const char **data,
+		 const char *space_name, uint32_t name_len,
+		 uint32_t errcode, uint32_t fieldno, struct region *region)
+{
+	if (mp_typeof(**data) != MP_MAP) {
+		diag_set(ClientError, errcode, tt_cstr(space_name, name_len),
+			  tt_sprintf("field %d is not map",
+				     fieldno + TUPLE_INDEX_BASE));
+		return -1;
+	}
+	int count = mp_decode_map(data);
+	*field = field_def_default;
+	bool is_action_missing = true;
+	uint32_t action_literal_len = strlen("nullable_action");
+	for (int i = 0; i < count; ++i) {
+		if (mp_typeof(**data) != MP_STR) {
+			diag_set(ClientError, errcode,
+				  tt_cstr(space_name, name_len),
+				  tt_sprintf("field %d format is not map"\
+					     " with string keys",
+					     fieldno + TUPLE_INDEX_BASE));
+			return -1;
+		}
+		uint32_t key_len;
+		const char *key = mp_decode_str(data, &key_len);
+		if (opts_parse_key(field, field_def_reg, key, key_len, data,
+				   ER_WRONG_SPACE_FORMAT,
+				   fieldno + TUPLE_INDEX_BASE, region,
+				   true) != 0)
+			return -1;
+		if (is_action_missing &&
+		    key_len == action_literal_len &&
+		    memcmp(key, "nullable_action", action_literal_len) == 0)
+			is_action_missing = false;
+	}
+	if (is_action_missing) {
+		field->nullable_action = field->is_nullable ?
+			ON_CONFLICT_ACTION_NONE
+			: ON_CONFLICT_ACTION_DEFAULT;
+	}
+	if (field->name == NULL) {
+		diag_set(ClientError, errcode, tt_cstr(space_name, name_len),
+			  tt_sprintf("field %d name is not specified",
+				     fieldno + TUPLE_INDEX_BASE));
+		return -1;
+	}
+	size_t field_name_len = strlen(field->name);
+	if (field_name_len > BOX_NAME_MAX) {
+		diag_set(ClientError, errcode, tt_cstr(space_name, name_len),
+			  tt_sprintf("field %d name is too long",
+				     fieldno + TUPLE_INDEX_BASE));
+		return -1;
+	}
+	if (identifier_check(field->name, field_name_len))
+		return -1;
+	if (field->type == field_type_MAX) {
+		diag_set(ClientError, errcode, tt_cstr(space_name, name_len),
+			  tt_sprintf("field %d has unknown field type",
+				     fieldno + TUPLE_INDEX_BASE));
+		return -1;
+	}
+	if (field->nullable_action == on_conflict_action_MAX) {
+		diag_set(ClientError, errcode, tt_cstr(space_name, name_len),
+			  tt_sprintf("field %d has unknown field on conflict "
+				     "nullable action",
+				     fieldno + TUPLE_INDEX_BASE));
+		return -1;
+	}
+	if (!((field->is_nullable && field->nullable_action ==
+	       ON_CONFLICT_ACTION_NONE)
+	      || (!field->is_nullable
+		  && field->nullable_action != ON_CONFLICT_ACTION_NONE))) {
+		diag_set(ClientError, errcode, tt_cstr(space_name, name_len),
+			  tt_sprintf("field %d has conflicting nullability and "
+				     "nullable action properties", fieldno +
+				     TUPLE_INDEX_BASE));
+		return -1;
+	}
+	if (field->coll_id != COLL_NONE &&
+	    field->type != FIELD_TYPE_STRING &&
+	    field->type != FIELD_TYPE_SCALAR &&
+	    field->type != FIELD_TYPE_ANY) {
+		diag_set(ClientError, errcode, tt_cstr(space_name, name_len),
+			  tt_sprintf("collation is reasonable only for "
+				     "string, scalar and any fields"));
+		return -1;
+	}
+
+	const char *dv = field->default_value;
+	if (dv != NULL) {
+		field->default_value_expr = sql_expr_compile(sql_get(), dv,
+							     strlen(dv));
+		if (field->default_value_expr == NULL)
+			return -1;
+	}
+	return 0;
+}
+
+int
+space_format_decode(struct field_def **field, const char *data,
+		    uint32_t *out_count, const char *space_name,
+		    uint32_t name_len, uint32_t errcode, struct region *region)
+{
+	/* Type is checked by _space format. */
+	assert(mp_typeof(*data) == MP_ARRAY);
+	*field = NULL;
+	uint32_t count = mp_decode_array(&data);
+	*out_count = count;
+	if (count == 0)
+		return 0;
+	size_t size = count * sizeof(struct field_def);
+	struct field_def *region_defs =
+		(struct field_def *) region_alloc(region, size);
+	if (region_defs == NULL) {
+		diag_set(OutOfMemory, size, "region_alloc", "region_defs");
+		return -1;
+	}
+	/*
+	 * Nullify to prevent a case when decoding will fail in
+	 * the middle and space_def_destroy_fields() below will
+	 * work with garbage pointers.
+	 */
+	memset(region_defs, 0, size);
+	for (uint32_t i = 0; i < count; ++i) {
+		if (field_def_decode(&region_defs[i], &data, space_name,
+				     name_len, errcode, i, region) != 0) {
+			space_def_destroy_fields(region_defs, count);
+			return -1;
+		}
+	}
+	*field = region_defs;
+	return 0;
+}
diff --git a/src/box/space_def.h b/src/box/space_def.h
index 0d1e902..7a01b92 100644
--- a/src/box/space_def.h
+++ b/src/box/space_def.h
@@ -182,6 +182,24 @@ space_def_sizeof(uint32_t name_len, const struct field_def *fields,
 		 uint32_t field_count, uint32_t *names_offset,
 		 uint32_t *fields_offset, uint32_t *def_expr_offset);
 
+/**
+ * Decode MessagePack array of fields.
+ * @param[out] field Field to decode to.
+ * @param data MessagePack array of fields.
+ * @param[out] out_count Length of a result array.
+ * @param space_name Space name to use in error messages.
+ * @param name_len Length of @a space_name.
+ * @param errcode Errcode for client errors.
+ * @param region Region to allocate result array.
+ *
+ * @retval 0 Success.
+ * @retval -1 Error.
+ */
+int
+space_format_decode(struct field_def **field, const char *data,
+		    uint32_t *out_count, const char *space_name,
+		    uint32_t name_len, uint32_t errcode, struct region *region);
+
 #if defined(__cplusplus)
 } /* extern "C" */
 
-- 
2.7.4

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [tarantool-patches] [PATCH v3 5/7] box: ephemeral space creation and deletion in Lua
  2018-07-24 11:58 [tarantool-patches] [PATCH v3 0/7] Expose ephemeral spaces into Lua imeevma
                   ` (3 preceding siblings ...)
  2018-07-24 11:58 ` [tarantool-patches] [PATCH v3 4/7] box: move some decode functions from alter.cc imeevma
@ 2018-07-24 11:58 ` imeevma
  2018-07-24 11:58 ` [tarantool-patches] [PATCH v3 6/7] box: primary index for ephemeral spaces imeevma
  2018-07-24 11:58 ` [tarantool-patches] [PATCH v3 7/7] box: methods for ephemeral space and its index imeevma
  6 siblings, 0 replies; 8+ messages in thread
From: imeevma @ 2018-07-24 11:58 UTC (permalink / raw)
  To: tarantool-patches

Import functions to create ephemeral space in Lua and some
its methods that do not require index.

Part of #3375.
---
 src/box/lua/misc.cc               |  21 ++++
 src/box/lua/misc.h                |  12 +++
 src/box/lua/schema.lua            |  87 +++++++++++++++
 src/box/lua/space.cc              | 216 ++++++++++++++++++++++++++++++++------
 test/box/ephemeral_space.result   | 165 +++++++++++++++++++++++++++++
 test/box/ephemeral_space.test.lua |  58 ++++++++++
 test/engine/iterator.result       |   2 +-
 7 files changed, 526 insertions(+), 35 deletions(-)
 create mode 100644 test/box/ephemeral_space.result
 create mode 100644 test/box/ephemeral_space.test.lua

diff --git a/src/box/lua/misc.cc b/src/box/lua/misc.cc
index bc76065..13ca18c 100644
--- a/src/box/lua/misc.cc
+++ b/src/box/lua/misc.cc
@@ -39,6 +39,8 @@
 #include "box/port.h"
 #include "box/lua/tuple.h"
 
+static uint32_t CTID_STRUCT_SPACE_POINTER = 0;
+
 /** {{{ Miscellaneous utils **/
 
 char *
@@ -55,6 +57,19 @@ lbox_encode_tuple_on_gc(lua_State *L, int idx, size_t *p_len)
 	return (char *) region_join_xc(gc, *p_len);
 }
 
+struct space *
+lua_checkephemeralspace(struct lua_State *L, int idx)
+{
+	uint32_t ctypeid = 0;
+	void *data = luaL_checkcdata(L, idx, &ctypeid);
+	if (ctypeid != CTID_STRUCT_SPACE_POINTER) {
+		luaL_error(L, "Invalid argument #%d (space expected, got %s)",
+			   idx, lua_typename(L, ctypeid));
+		return NULL;
+	}
+	return *(struct space **) data;
+}
+
 /* }}} */
 
 /** {{{ Lua/C implementation of index:select(): used only by Vinyl **/
@@ -115,6 +130,12 @@ lbox_select(lua_State *L)
 void
 box_lua_misc_init(struct lua_State *L)
 {
+	int rc = luaL_cdef(L, "struct space;");
+	assert(rc == 0);
+	(void) rc;
+	CTID_STRUCT_SPACE_POINTER = luaL_ctypeid(L, "struct space *");
+	assert(CTID_STRUCT_SPACE_POINTER != 0);
+
 	static const struct luaL_Reg boxlib_internal[] = {
 		{"select", lbox_select},
 		{NULL, NULL}
diff --git a/src/box/lua/misc.h b/src/box/lua/misc.h
index dfedfe3..6162baa 100644
--- a/src/box/lua/misc.h
+++ b/src/box/lua/misc.h
@@ -42,6 +42,18 @@ struct lua_State;
 char *
 lbox_encode_tuple_on_gc(struct lua_State *L, int idx, size_t *p_len);
 
+/**
+ * Get space from Lua stack.
+ *
+ * @param L Lua stack to get space from.
+ * @param idx Index by which to get the space from @a L.
+ *
+ * @retval struct space *space - success.
+ * @retval NULL - error.
+ */
+struct space *
+lua_checkephemeralspace(struct lua_State *L, int idx);
+
 void
 box_lua_misc_init(struct lua_State *L);
 
diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua
index d14dd74..5962ce2 100644
--- a/src/box/lua/schema.lua
+++ b/src/box/lua/schema.lua
@@ -477,6 +477,63 @@ box.schema.space.create = function(name, options)
     return box.space[id], "created"
 end
 
+local space_new_ephemeral = box.internal.space.space_new_ephemeral
+box.internal.space.space_new_ephemeral = nil
+local space_delete_ephemeral = box.internal.space.space_delete_ephemeral
+box.internal.space.space_delete_ephemeral = nil
+local space_ephemeral_methods = box.internal.space_ephemeral_methods
+box.internal.space_ephemeral_methods = nil
+local space_ephemeral_mt = {}
+
+box.schema.space.create_ephemeral = function(options)
+    local options_template = {
+        engine = 'string',
+        field_count = 'number',
+        format = 'table',
+    }
+    local options_defaults = {
+        engine = 'memtx',
+        field_count = 0,
+    }
+    check_param_table(options, options_template)
+    options = update_param_table(options, options_defaults)
+    local format = options.format and options.format or {}
+    check_param(format, 'format', 'table')
+    format = update_format(format)
+
+    local space = {}
+    space.space = space_new_ephemeral(options.engine,
+                                      options.field_count, format)
+    space.space_format = format
+    space.engine = options.engine
+    space.field_count = options.field_count
+    space.index = {}
+    setmetatable(space, space_ephemeral_mt)
+    -- Set GC for result
+    space.proxy = newproxy(true)
+    getmetatable(space.proxy).__gc = function(self)
+        space:drop()
+    end
+    return space
+end
+
+box.schema.space.drop_ephemeral = function(space)
+    check_param(space.space, 'space', 'cdata')
+    space_delete_ephemeral(space.space)
+    getmetatable(space.proxy).__gc = nil
+    for k,_ in pairs(space) do
+        space[k] = nil
+    end
+    local dropped_mt = {
+        __index = function()
+            error('The space is dropped and can not be used')
+        end
+    }
+    setmetatable(space, dropped_mt)
+end
+
+box.schema.create_ephemeral_space = box.schema.space.create_ephemeral
+
 -- space format - the metadata about space fields
 function box.schema.space.format(id, format)
     local _space = box.space._space
@@ -1080,6 +1137,12 @@ local function check_space_arg(space, method)
         error(string.format(fmt, method, method))
     end
 end
+local function check_ephemeral_space_arg(space, method)
+    if type(space) ~= 'table' or param_type(space.space) ~= 'cdata' then
+        local fmt = 'Use space:%s(...) instead of space.%s(...)'
+        error(string.format(fmt, method, method))
+    end
+end
 box.internal.check_space_arg = check_space_arg -- for net.box
 
 -- Helper function for nicer error messages
@@ -1505,6 +1568,30 @@ end
 space_mt.frommap = box.internal.space.frommap
 space_mt.__index = space_mt
 
+-- Metatable for ephemeral space
+space_ephemeral_mt.format = function(space)
+    check_ephemeral_space_arg(space, 'format')
+    return space.space_format
+end
+space_ephemeral_mt.run_triggers = function(space, yesno)
+    check_ephemeral_space_arg(space, 'run_triggers')
+    builtin.space_run_triggers(space.space, yesno)
+end
+space_ephemeral_mt.frommap = function(space, map, options)
+    check_ephemeral_space_arg(space, 'frommap')
+    if type(map) ~= 'table' then
+        error('Usage: space:frommap(map, opts)')
+    end
+    options = options or {}
+    return space_ephemeral_methods.frommap(space.space, map, options)
+end
+space_ephemeral_mt.bsize = function(space)
+    check_ephemeral_space_arg(space, 'bsize')
+    return builtin.space_bsize(space.space)
+end
+space_ephemeral_mt.drop = box.schema.space.drop_ephemeral
+space_ephemeral_mt.__index = space_ephemeral_mt
+
 box.schema.index_mt = base_index_mt
 box.schema.memtx_index_mt = memtx_index_mt
 box.schema.vinyl_index_mt = vinyl_index_mt
diff --git a/src/box/lua/space.cc b/src/box/lua/space.cc
index ca3fefc..6f21907 100644
--- a/src/box/lua/space.cc
+++ b/src/box/lua/space.cc
@@ -49,6 +49,7 @@ extern "C" {
 #include "box/sequence.h"
 #include "box/coll_id_cache.h"
 #include "box/replication.h" /* GROUP_LOCAL */
+#include "box/lua/misc.h"
 
 /**
  * Trigger function for all spaces
@@ -441,19 +442,154 @@ static struct trigger on_alter_space_in_lua = {
 };
 
 /**
+ * Create an ephemeral space and push in on Lua stack.
+ *
+ * @param L Lua stack in which ephemeral space will be pushed to.
+ * @param space_def Space definition of ephemeral space.
+ * @param index_def Index definition of new index for ephemeral
+ * space.
+ *
+ * @retval not nil ephemeral space created.
+ * @retval nil error, A reason is returned in
+ * the second value.
+ */
+static inline int
+box_space_new_ephemeral(struct lua_State *L, struct space_def *space_def,
+			struct index_def *index_def)
+{
+	struct rlist key_list;
+	rlist_create(&key_list);
+	if (index_def != NULL)
+		rlist_add_entry(&key_list, index_def, link);
+	struct space *space = space_new_ephemeral(space_def, &key_list);
+	space_def_delete(space_def);
+	if (index_def != NULL)
+		index_def_delete(index_def);
+	if (space == NULL)
+		return luaT_error(L);
+	uint32_t ctypeid = luaL_ctypeid(L, "struct space *");
+	struct space **ptr =
+		(struct space **) luaL_pushcdata(L, ctypeid);
+	*ptr = space;
+	return 1;
+}
+
+/**
+ * Create an ephemeral space.
+ *
+ * @param L Lua stack to next get arguments from:
+ * const char *engine_name, uint32_t field_count,
+ * tuple format
+ *
+ * @retval not nil ephemeral space created.
+ * @retval nil error, A reason is returned in
+ * the second value.
+ */
+static int
+lbox_space_new_ephemeral(struct lua_State *L)
+{
+	if (lua_gettop(L) != 3 || !lua_istable(L, 3))
+		return luaL_error(L, "Error with creating ephemeral space");
+	const char *engine_name = luaL_checkstring (L, 1);
+	uint32_t exact_field_count = luaL_checknumber (L, 2);
+	size_t format_len;
+	const char *format = lbox_encode_tuple_on_gc(L, 3, &format_len);
+
+	struct region *region = &fiber()->gc;
+	uint32_t field_count;
+	struct field_def *fields;
+	const char *name = "ephemeral";
+	uint32_t name_len = strlen(name);
+
+	if (space_format_decode(&fields, format, &field_count, name, name_len,
+				ER_CREATE_SPACE, region) != 0)
+		return luaT_error(L);
+	if (exact_field_count != 0 && exact_field_count < field_count) {
+		return luaL_error(L, "exact_field_count must be either 0 or"\
+				  ">= formatted field count");
+	}
+	struct space_def *space_def =
+		space_def_new(0, 0, exact_field_count, name, name_len,
+			      engine_name, strlen(engine_name),
+			      &space_opts_default, fields, field_count);
+	space_def_destroy_fields(fields, field_count);
+	if (space_def == NULL)
+		return luaT_error(L);
+	return box_space_new_ephemeral(L, space_def, NULL);
+}
+
+/**
+ * Delete an ephemeral space.
+ *
+ * @param L Lua stack to get space from.
+ */
+static int
+lbox_space_delete_ephemeral(struct lua_State *L)
+{
+	if (lua_gettop(L) != 1)
+		return luaL_error(L, "Usage: ephemeral_space:drop()");
+	struct space *space = lua_checkephemeralspace(L, 1);
+	space_delete(space);
+	return 0;
+}
+
+/**
  * Make a tuple or a table Lua object by map.
- * @param Lua space object.
- * @param Lua map table object.
- * @param Lua opts table object (optional).
+ *
+ * @param L Lua stack to next get table map from.
+ * Map should be on second position in this stack.
+ * @param space space to get format from.
+ * @param table flag to decide what to return.
+ *
+ * @retval not nil A tuple or a table conforming to a space
+ * format.
+ * @retval nil, err Can not built a tuple. A reason is returned
+ * in the second value.
+ */
+static inline int
+box_space_frommap(struct lua_State *L, struct space *space, bool table)
+{
+	assert(space->format != NULL);
+	struct tuple_dictionary *dict = space->format->dict;
+	lua_createtable(L, space->def->field_count, 0);
+
+	lua_pushnil(L);
+	while (lua_next(L, 2) != 0) {
+		uint32_t fieldno;
+		size_t key_len;
+		const char *key = lua_tolstring(L, -2, &key_len);
+		uint32_t key_hash = lua_hashstring(L, -2);
+		if (tuple_fieldno_by_name(dict, key, key_len, key_hash,
+					  &fieldno)) {
+			lua_pushnil(L);
+			lua_pushstring(L, tt_sprintf("Unknown field '%s'",
+						     key));
+			return 2;
+		}
+		lua_rawseti(L, -3, fieldno+1);
+	}
+	if (table)
+		return 1;
+
+	lua_replace(L, 1);
+	lua_settop(L, 1);
+	return lbox_tuple_new(L);
+}
+
+/**
+ * Make a tuple or a table Lua object by map for usual
+ * spaces.
+ *
+ * @param L Lua stack to next get arguments from:
+ * uint32_t space_id, table map, table options (optional)
  * @retval not nil A tuple or a table conforming to a space
- *         format.
- * @retval nil, err Can not built a tuple. A reason is returned in
- *         the second value.
+ * format.
+ * @retval nil, err Can not built a tuple. A reason is returned
+ * in the second value.
  */
 static int
 lbox_space_frommap(struct lua_State *L)
 {
-	struct tuple_dictionary *dict = NULL;
 	uint32_t id = 0;
 	struct space *space = NULL;
 	int argc = lua_gettop(L);
@@ -478,36 +614,35 @@ lbox_space_frommap(struct lua_State *L)
 					     "doesn't exist", id));
 		return 2;
 	}
-	assert(space->format != NULL);
-
-	dict = space->format->dict;
-	lua_createtable(L, space->def->field_count, 0);
-
-	lua_pushnil(L);
-	while (lua_next(L, 2) != 0) {
-		uint32_t fieldno;
-		size_t key_len;
-		const char *key = lua_tolstring(L, -2, &key_len);
-		uint32_t key_hash = lua_hashstring(L, -2);
-		if (tuple_fieldno_by_name(dict, key, key_len, key_hash,
-					  &fieldno)) {
-			lua_pushnil(L);
-			lua_pushstring(L, tt_sprintf("Unknown field '%s'",
-						     key));
-			return 2;
-		}
-		lua_rawseti(L, -3, fieldno+1);
-	}
-	if (table)
-		return 1;
-
-	lua_replace(L, 1);
-	lua_settop(L, 1);
-	return lbox_tuple_new(L);
+	return box_space_frommap(L, space, table);
 usage_error:
 	return luaL_error(L, "Usage: space:frommap(map, opts)");
 }
 
+/**
+ * Make a tuple or a table Lua object by map for ephemeral
+ * spaces.
+ *
+ * @param L Lua stack to next get arguments from:
+ * struct space *space, table map, table options
+ * @retval not nil A tuple or a table conforming to a space
+ * format.
+ * @retval nil, err Can not built a tuple. A reason is returned
+ * in the second value.
+ */
+static int
+lbox_space_frommap_ephemeral(struct lua_State *L)
+{
+	if (lua_gettop(L) != 3 || !lua_istable(L, 2) || !lua_istable(L, 3))
+		return luaL_error(L, "Usage: space:frommap(map, opts)");
+	struct space *space = lua_checkephemeralspace(L, 1);
+	lua_getfield(L, 3, "table");
+	if (!lua_isboolean(L, -1) && !lua_isnil(L, -1))
+		return luaL_error(L, "Usage: space:frommap(map, opts)");
+	bool table = lua_toboolean(L, -1);
+	return box_space_frommap(L, space, table);
+}
+
 void
 box_lua_space_init(struct lua_State *L)
 {
@@ -595,12 +730,25 @@ box_lua_space_init(struct lua_State *L)
 	lua_setfield(L, -2, "REPLICA_MAX");
 	lua_pushnumber(L, SQL_BIND_PARAMETER_MAX);
 	lua_setfield(L, -2, "SQL_BIND_PARAMETER_MAX");
-	lua_pop(L, 2); /* box, schema */
+	lua_pop(L, 1); /* schema */
+	lua_getfield(L, -1, "internal");
+	lua_newtable(L);
+	lua_setfield(L, -2, "space_ephemeral_methods");
+	lua_pop(L, 2); /* box, internal */
 
 	static const struct luaL_Reg space_internal_lib[] = {
 		{"frommap", lbox_space_frommap},
+		{"space_new_ephemeral", lbox_space_new_ephemeral},
+		{"space_delete_ephemeral", lbox_space_delete_ephemeral},
 		{NULL, NULL}
 	};
 	luaL_register(L, "box.internal.space", space_internal_lib);
 	lua_pop(L, 1);
+	static const struct luaL_Reg space_ephemeral_lib[] = {
+		{"frommap", lbox_space_frommap_ephemeral},
+		{NULL, NULL}
+	};
+	luaL_register(L, "box.internal.space_ephemeral_methods",
+		      space_ephemeral_lib);
+	lua_pop(L, 1);
 }
diff --git a/test/box/ephemeral_space.result b/test/box/ephemeral_space.result
new file mode 100644
index 0000000..b0e91bb
--- /dev/null
+++ b/test/box/ephemeral_space.result
@@ -0,0 +1,165 @@
+-- Ephemeral space: create and drop
+s = box.schema.space.create_ephemeral()
+---
+...
+s.index
+---
+- []
+...
+s.engine
+---
+- memtx
+...
+s.field_count
+---
+- 0
+...
+s:drop()
+---
+...
+format = {{name='field1', type='unsigned'}, {name='field2', type='string'}}
+---
+...
+options = {engine = 'memtx', field_count = 7, format = format}
+---
+...
+s = box.schema.space.create_ephemeral(options)
+---
+...
+s.index
+---
+- []
+...
+s.engine
+---
+- memtx
+...
+s.field_count
+---
+- 7
+...
+s:drop()
+---
+...
+s = box.schema.space.create_ephemeral({engine = 'other'})
+---
+- error: Space engine 'other' does not exist
+...
+s = box.schema.space.create_ephemeral({field_count = 'asd'})
+---
+- error: Illegal parameters, options parameter 'field_count' should be of type number
+...
+s = box.schema.space.create_ephemeral({format = 'a'})
+---
+- error: Illegal parameters, options parameter 'format' should be of type table
+...
+-- Multiple creation and drop
+for j = 1,10 do for i=1,10 do s = box.schema.space.create_ephemeral(); s:drop(); end; collectgarbage('collect'); end
+---
+...
+-- Multiple drop
+s = box.schema.space.create_ephemeral()
+---
+...
+s:drop()
+---
+...
+s:drop()
+---
+- error: 'builtin/box/schema.lua:529: The space is dropped and can not be used'
+...
+-- Drop using function from box.schema
+s = box.schema.space.create_ephemeral()
+---
+...
+box.schema.space.drop_ephemeral(s)
+---
+...
+s
+---
+- []
+...
+-- Ephemeral space: methods
+format = {{name='field1', type='unsigned'}, {name='field2', type='string'}}
+---
+...
+options = {engine = 'memtx', field_count = 7, format = format}
+---
+...
+s = box.schema.space.create_ephemeral(options)
+---
+...
+s:format()
+---
+- - name: field1
+    type: unsigned
+  - name: field2
+    type: string
+...
+s:run_triggers(true)
+---
+...
+s:drop()
+---
+...
+format = {}
+---
+...
+format[1] = {name = 'aaa', type = 'unsigned'}
+---
+...
+format[2] = {name = 'bbb', type = 'unsigned'}
+---
+...
+format[3] = {name = 'ccc', type = 'unsigned'}
+---
+...
+format[4] = {name = 'ddd', type = 'unsigned'}
+---
+...
+s = box.schema.space.create_ephemeral({format = format})
+---
+...
+s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4})
+---
+- [2, 4, 3, 1]
+...
+s:frommap({ddd = 1, aaa = 2, bbb = 3})
+---
+- [2, 3, null, 1]
+...
+s:frommap({ddd = 1, aaa = 2, ccc = 3, eee = 4})
+---
+- null
+- Unknown field 'eee'
+...
+s:frommap()
+---
+- error: 'builtin/box/schema.lua:1583: Usage: space:frommap(map, opts)'
+...
+s:frommap({})
+---
+- []
+...
+s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {table = true})
+---
+- - 2
+  - 4
+  - 3
+  - 1
+...
+s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {table = false})
+---
+- [2, 4, 3, 1]
+...
+s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = box.NULL})
+---
+- [2, null, 3, 1]
+...
+s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {dummy = true})
+---
+- [2, 4, 3, 1]
+...
+s:drop()
+---
+...
diff --git a/test/box/ephemeral_space.test.lua b/test/box/ephemeral_space.test.lua
new file mode 100644
index 0000000..a7e3404
--- /dev/null
+++ b/test/box/ephemeral_space.test.lua
@@ -0,0 +1,58 @@
+-- Ephemeral space: create and drop
+
+s = box.schema.space.create_ephemeral()
+s.index
+s.engine
+s.field_count
+s:drop()
+
+format = {{name='field1', type='unsigned'}, {name='field2', type='string'}}
+options = {engine = 'memtx', field_count = 7, format = format}
+s = box.schema.space.create_ephemeral(options)
+s.index
+s.engine
+s.field_count
+s:drop()
+
+s = box.schema.space.create_ephemeral({engine = 'other'})
+s = box.schema.space.create_ephemeral({field_count = 'asd'})
+s = box.schema.space.create_ephemeral({format = 'a'})
+
+-- Multiple creation and drop
+for j = 1,10 do for i=1,10 do s = box.schema.space.create_ephemeral(); s:drop(); end; collectgarbage('collect'); end
+
+-- Multiple drop
+s = box.schema.space.create_ephemeral()
+s:drop()
+s:drop()
+
+-- Drop using function from box.schema
+s = box.schema.space.create_ephemeral()
+box.schema.space.drop_ephemeral(s)
+s
+
+
+-- Ephemeral space: methods
+format = {{name='field1', type='unsigned'}, {name='field2', type='string'}}
+options = {engine = 'memtx', field_count = 7, format = format}
+s = box.schema.space.create_ephemeral(options)
+s:format()
+s:run_triggers(true)
+s:drop()
+
+format = {}
+format[1] = {name = 'aaa', type = 'unsigned'}
+format[2] = {name = 'bbb', type = 'unsigned'}
+format[3] = {name = 'ccc', type = 'unsigned'}
+format[4] = {name = 'ddd', type = 'unsigned'}
+s = box.schema.space.create_ephemeral({format = format})
+s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4})
+s:frommap({ddd = 1, aaa = 2, bbb = 3})
+s:frommap({ddd = 1, aaa = 2, ccc = 3, eee = 4})
+s:frommap()
+s:frommap({})
+s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {table = true})
+s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {table = false})
+s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = box.NULL})
+s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {dummy = true})
+s:drop()
diff --git a/test/engine/iterator.result b/test/engine/iterator.result
index 98b0b3e..2552ddd 100644
--- a/test/engine/iterator.result
+++ b/test/engine/iterator.result
@@ -4213,7 +4213,7 @@ s:replace{35}
 ...
 state, value = gen(param,state)
 ---
-- error: 'builtin/box/schema.lua:1051: usage: next(param, state)'
+- error: 'builtin/box/schema.lua:1108: usage: next(param, state)'
 ...
 value
 ---
-- 
2.7.4

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [tarantool-patches] [PATCH v3 6/7] box: primary index for ephemeral spaces
  2018-07-24 11:58 [tarantool-patches] [PATCH v3 0/7] Expose ephemeral spaces into Lua imeevma
                   ` (4 preceding siblings ...)
  2018-07-24 11:58 ` [tarantool-patches] [PATCH v3 5/7] box: ephemeral space creation and deletion in Lua imeevma
@ 2018-07-24 11:58 ` imeevma
  2018-07-24 11:58 ` [tarantool-patches] [PATCH v3 7/7] box: methods for ephemeral space and its index imeevma
  6 siblings, 0 replies; 8+ messages in thread
From: imeevma @ 2018-07-24 11:58 UTC (permalink / raw)
  To: tarantool-patches

Functions for creation and deletion primary index
of ephemeral space added.

Part of #3375.
---
 src/box/lua/schema.lua            | 163 ++++++++++++++++++++++++++++----------
 src/box/lua/space.cc              |  65 +++++++++++++++
 test/box/ephemeral_space.result   |  84 +++++++++++++++++++-
 test/box/ephemeral_space.test.lua |  33 ++++++++
 test/engine/iterator.result       |   2 +-
 5 files changed, 302 insertions(+), 45 deletions(-)

diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua
index 5962ce2..cc8c66b 100644
--- a/src/box/lua/schema.lua
+++ b/src/box/lua/schema.lua
@@ -765,23 +765,7 @@ for k, v in pairs(index_options) do
     alter_index_template[k] = v
 end
 
---
--- check_param_table() template for create_index(), includes
--- all index options and if_not_exists specifier
---
-local create_index_template = table.deepcopy(alter_index_template)
-create_index_template.if_not_exists = "boolean"
-
-box.schema.index.create = function(space_id, name, options)
-    check_param(space_id, 'space_id', 'number')
-    check_param(name, 'name', 'string')
-    check_param_table(options, create_index_template)
-    local space = box.space[space_id]
-    if not space then
-        box.error(box.error.NO_SUCH_SPACE, '#'..tostring(space_id))
-    end
-    local format = space:format()
-
+local create_index_options = function(options, format, engine)
     local options_defaults = {
         type = 'tree',
     }
@@ -803,7 +787,7 @@ box.schema.index.create = function(space_id, name, options)
         end
     end
     options = update_param_table(options, options_defaults)
-    if space.engine == 'vinyl' then
+    if engine == 'vinyl' then
         options_defaults = {
             page_size = box.cfg.vinyl_page_size,
             range_size = box.cfg.vinyl_range_size,
@@ -815,6 +799,57 @@ box.schema.index.create = function(space_id, name, options)
         options_defaults = {}
     end
     options = update_param_table(options, options_defaults)
+    return options
+end
+
+local update_index_options = function(options, parts)
+    -- create_index() options contains type, parts, etc,
+    -- stored separately. Remove these members from index_opts
+    local index_opts = {
+            dimension = options.dimension,
+            unique = options.unique,
+            distance = options.distance,
+            page_size = options.page_size,
+            range_size = options.range_size,
+            run_count_per_level = options.run_count_per_level,
+            run_size_ratio = options.run_size_ratio,
+            bloom_fpr = options.bloom_fpr,
+    }
+    local field_type_aliases = {
+        num = 'unsigned'; -- Deprecated since 1.7.2
+        uint = 'unsigned';
+        str = 'string';
+        int = 'integer';
+        ['*'] = 'any';
+    };
+    for _, part in pairs(parts) do
+        local field_type = part.type:lower()
+        part.type = field_type_aliases[field_type] or field_type
+        if field_type == 'num' then
+            log.warn("field type '%s' is deprecated since Tarantool 1.7, "..
+                     "please use '%s' instead", field_type, part.type)
+        end
+    end
+    return index_opts
+end
+
+--
+-- check_param_table() template for create_index(), includes
+-- all index options and if_not_exists specifier
+--
+local create_index_template = table.deepcopy(alter_index_template)
+create_index_template.if_not_exists = "boolean"
+
+box.schema.index.create = function(space_id, name, options)
+    check_param(space_id, 'space_id', 'number')
+    check_param(name, 'name', 'string')
+    check_param_table(options, create_index_template)
+    local space = box.space[space_id]
+    if not space then
+        box.error(box.error.NO_SUCH_SPACE, '#'..tostring(space_id))
+    end
+    local format = space:format()
+    options = create_index_options(options, format, space.engine)
 
     local _index = box.space[box.schema.INDEX_ID]
     local _vindex = box.space[box.schema.VINDEX_ID]
@@ -844,31 +879,7 @@ box.schema.index.create = function(space_id, name, options)
         update_index_parts(format, options.parts)
     -- create_index() options contains type, parts, etc,
     -- stored separately. Remove these members from index_opts
-    local index_opts = {
-            dimension = options.dimension,
-            unique = options.unique,
-            distance = options.distance,
-            page_size = options.page_size,
-            range_size = options.range_size,
-            run_count_per_level = options.run_count_per_level,
-            run_size_ratio = options.run_size_ratio,
-            bloom_fpr = options.bloom_fpr,
-    }
-    local field_type_aliases = {
-        num = 'unsigned'; -- Deprecated since 1.7.2
-        uint = 'unsigned';
-        str = 'string';
-        int = 'integer';
-        ['*'] = 'any';
-    };
-    for _, part in pairs(parts) do
-        local field_type = part.type:lower()
-        part.type = field_type_aliases[field_type] or field_type
-        if field_type == 'num' then
-            log.warn("field type '%s' is deprecated since Tarantool 1.7, "..
-                     "please use '%s' instead", field_type, part.type)
-        end
-    end
+    local index_opts = update_index_options(options, parts)
     local _space_sequence = box.space[box.schema.SPACE_SEQUENCE_ID]
     local sequence_is_generated = false
     local sequence = options.sequence or nil -- ignore sequence = false
@@ -1083,6 +1094,67 @@ ffi.metatype(iterator_t, {
     end;
 })
 
+local index_new_ephemeral = box.internal.space.index_new_ephemeral
+box.internal.space.index_new_ephemeral = nil
+local index_delete_ephemeral = box.internal.space.index_delete_ephemeral
+box.internal.space.index_delete_ephemeral = nil
+local index_ephemeral_mt = {}
+
+local create_ephemeral_index = function(space, name, options)
+    if type(space) ~= 'table' then
+        error("Usage: space:create_index(name, opts)")
+    end
+    check_param(name, 'name', 'string')
+    check_param_table(options, create_index_template)
+    local format = space:format()
+    options = create_index_options(options, format, space.engine)
+    local iid = options.id or 0
+    if space.index[iid] then
+        if options.if_not_exists then
+            return space.index[iid], "not created"
+        else
+            box.error(box.error.INDEX_EXISTS, name)
+        end
+    end
+    local parts, parts_can_be_simplified =
+        update_index_parts(format, options.parts)
+    local index_opts = update_index_options(options, parts)
+    if parts_can_be_simplified then
+        parts = simplify_index_parts(parts)
+    end
+    space.space = index_new_ephemeral(space.space, iid, name, options.type,
+                                      msgpack.encode(index_opts),
+                                      msgpack.encode(parts))
+    space.index[iid] = {}
+    space.index[iid].unique = index_opts.unique or true
+    space.index[iid].parts = parts
+    space.index[iid].id = iid
+    space.index[iid].name = name
+    space.index[iid].type = type
+    space.index[iid].options = options
+    space.index[iid].space = space
+    space.index[name] = space.index[iid]
+    setmetatable(space.index[iid], index_ephemeral_mt)
+    return space.index[name]
+end
+
+local drop_ephemeral_index = function(index)
+    if type(index) ~= 'table' then
+        error("Usage: index:drop()")
+    end
+    index.space.space = index_delete_ephemeral(index.space.space)
+    index.space.index[index.name] = nil
+    index.space.index[index.id] = nil
+    for k,_ in pairs(index) do
+        index[k] = nil
+    end
+    local dropped_mt = {
+        __index = function()
+            error('The index is dropped and can not be used')
+        end
+    }
+end
+
 local iterator_gen = function(param, state)
     --[[
         index:pairs() mostly conforms to the Lua for-in loop conventions and
@@ -1568,6 +1640,10 @@ end
 space_mt.frommap = box.internal.space.frommap
 space_mt.__index = space_mt
 
+-- Metatable for primary index of ephemeral space
+index_ephemeral_mt.drop = drop_ephemeral_index
+index_ephemeral_mt.__index = index_ephemeral_mt
+
 -- Metatable for ephemeral space
 space_ephemeral_mt.format = function(space)
     check_ephemeral_space_arg(space, 'format')
@@ -1589,6 +1665,7 @@ space_ephemeral_mt.bsize = function(space)
     check_ephemeral_space_arg(space, 'bsize')
     return builtin.space_bsize(space.space)
 end
+space_ephemeral_mt.create_index = create_ephemeral_index
 space_ephemeral_mt.drop = box.schema.space.drop_ephemeral
 space_ephemeral_mt.__index = space_ephemeral_mt
 
diff --git a/src/box/lua/space.cc b/src/box/lua/space.cc
index 6f21907..db746ab 100644
--- a/src/box/lua/space.cc
+++ b/src/box/lua/space.cc
@@ -534,6 +534,69 @@ lbox_space_delete_ephemeral(struct lua_State *L)
 }
 
 /**
+ * Create an index for ephemeral space.
+ *
+ * @param L Lua stack to next get arguments from:
+ * struct space *space, uint32_t iid, const char *name,
+ * const char *type_field, tuple index_opts, tuple parts
+ *
+ * @retval not nil index for ephemeral space created.
+ * @retval nil error
+ */
+static int
+lbox_index_new_ephemeral(struct lua_State *L)
+{
+	uint32_t argc = lua_gettop(L);
+	if (argc != 6)
+		return luaL_error(L, "Using: ephemeral:create_index(opts");
+	struct space *space = lua_checkephemeralspace(L, 1);
+	uint32_t index_id = luaL_checknumber (L, 2);
+	const char *name = luaL_checkstring (L, 3);
+	const char *type_field = luaL_checkstring(L, 4);
+	const char *opts_field = luaL_checkstring(L, 5);
+	const char *parts = luaL_checkstring(L, 6);
+	if (index_id != 0) {
+		diag_set(ClientError, ER_UNSUPPORTED, "Ephemeral space",
+			 "non-primary index");
+		return luaT_error(L);
+	}
+
+	struct space_def *space_def = space_def_dup(space->def);
+	if (space_def == NULL)
+		return luaT_error(L);
+	struct index_def *index_def =
+		index_def_new_decode(0, index_id, space->def->fields,
+				     space->def->field_count, name,
+				     strlen(name), type_field, opts_field,
+				     parts, space_name(space), NULL);
+	if (index_def == NULL)
+		return luaT_error(L);
+	if (!index_def_is_valid(index_def, space_name(space)) ||
+	    space_check_index_def(space, index_def) != 0) {
+		index_def_delete(index_def);
+		return luaT_error(L);
+	}
+	space_delete(space);
+	return box_space_new_ephemeral(L, space_def, index_def);
+}
+
+/**
+ * Drop primary index of ephemeral space.
+ *
+ * @param L Lua stack to get space from.
+ */
+static int
+lbox_index_drop_ephemeral(struct lua_State *L)
+{
+	struct space *space = lua_checkephemeralspace(L, 1);
+	struct space_def *space_def = space_def_dup(space->def);
+	if (space_def == NULL)
+		return luaT_error(L);
+	space_delete(space);
+	return box_space_new_ephemeral(L, space_def, NULL);
+}
+
+/**
  * Make a tuple or a table Lua object by map.
  *
  * @param L Lua stack to next get table map from.
@@ -740,6 +803,8 @@ box_lua_space_init(struct lua_State *L)
 		{"frommap", lbox_space_frommap},
 		{"space_new_ephemeral", lbox_space_new_ephemeral},
 		{"space_delete_ephemeral", lbox_space_delete_ephemeral},
+		{"index_new_ephemeral", lbox_index_new_ephemeral},
+		{"index_delete_ephemeral", lbox_index_drop_ephemeral},
 		{NULL, NULL}
 	};
 	luaL_register(L, "box.internal.space", space_internal_lib);
diff --git a/test/box/ephemeral_space.result b/test/box/ephemeral_space.result
index b0e91bb..4ef395f 100644
--- a/test/box/ephemeral_space.result
+++ b/test/box/ephemeral_space.result
@@ -135,7 +135,7 @@ s:frommap({ddd = 1, aaa = 2, ccc = 3, eee = 4})
 ...
 s:frommap()
 ---
-- error: 'builtin/box/schema.lua:1583: Usage: space:frommap(map, opts)'
+- error: 'builtin/box/schema.lua:1659: Usage: space:frommap(map, opts)'
 ...
 s:frommap({})
 ---
@@ -163,3 +163,85 @@ s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {dummy = true})
 s:drop()
 ---
 ...
+-- Ephemeral space: index create and drop.
+s = box.schema.space.create_ephemeral()
+---
+...
+i = s:create_index('a')
+---
+...
+i.unique
+---
+- true
+...
+i.parts
+---
+- - - 0
+    - unsigned
+...
+i.id
+---
+- 0
+...
+i.name
+---
+- a
+...
+i:drop()
+---
+...
+i = s:create_index('a', {parts={{5,'string', collation='Unicode'}}})
+---
+...
+i.parts
+---
+- - field: 4
+    collation: 1
+    type: string
+...
+i:drop()
+---
+...
+i = s:create_index('a', {parts={2, 'unsigned', 3, 'unsigned'}})
+---
+...
+i.parts
+---
+- - - 1
+    - unsigned
+  - - 2
+    - unsigned
+...
+i:drop()
+---
+...
+-- Double creation of index for ephemeral space.
+i = s:create_index('a')
+---
+...
+i = s:create_index('a')
+---
+- error: Index 'a' already exists
+...
+i:drop()
+---
+...
+i = s:create_index('a')
+---
+...
+i = s:create_index('a', {if_not_exists=true})
+---
+...
+i:drop()
+---
+...
+-- Ephemeral space can have only primary index with id == 0.
+i = s:create_index('a', {id = 10})
+---
+- error: Ephemeral space does not support non-primary index
+...
+i = s:create_index('a', {type = 'bitset', parts = {1, 'unsigned', 2, 'unsigned'}})
+---
+- error: 'Can''t create or modify index ''a'' in space ''ephemeral'': primary key
+    must be unique'
+...
diff --git a/test/box/ephemeral_space.test.lua b/test/box/ephemeral_space.test.lua
index a7e3404..93211c6 100644
--- a/test/box/ephemeral_space.test.lua
+++ b/test/box/ephemeral_space.test.lua
@@ -56,3 +56,36 @@ s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {table = false})
 s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = box.NULL})
 s:frommap({ddd = 1, aaa = 2, ccc = 3, bbb = 4}, {dummy = true})
 s:drop()
+
+
+-- Ephemeral space: index create and drop.
+s = box.schema.space.create_ephemeral()
+
+i = s:create_index('a')
+i.unique
+i.parts
+i.id
+i.name
+i:drop()
+
+i = s:create_index('a', {parts={{5,'string', collation='Unicode'}}})
+i.parts
+i:drop()
+
+i = s:create_index('a', {parts={2, 'unsigned', 3, 'unsigned'}})
+i.parts
+i:drop()
+
+-- Double creation of index for ephemeral space.
+i = s:create_index('a')
+i = s:create_index('a')
+i:drop()
+
+i = s:create_index('a')
+i = s:create_index('a', {if_not_exists=true})
+i:drop()
+
+-- Ephemeral space can have only primary index with id == 0.
+i = s:create_index('a', {id = 10})
+
+i = s:create_index('a', {type = 'bitset', parts = {1, 'unsigned', 2, 'unsigned'}})
diff --git a/test/engine/iterator.result b/test/engine/iterator.result
index 2552ddd..f39a15a 100644
--- a/test/engine/iterator.result
+++ b/test/engine/iterator.result
@@ -4213,7 +4213,7 @@ s:replace{35}
 ...
 state, value = gen(param,state)
 ---
-- error: 'builtin/box/schema.lua:1108: usage: next(param, state)'
+- error: 'builtin/box/schema.lua:1180: usage: next(param, state)'
 ...
 value
 ---
-- 
2.7.4

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [tarantool-patches] [PATCH v3 7/7] box: methods for ephemeral space and its index
  2018-07-24 11:58 [tarantool-patches] [PATCH v3 0/7] Expose ephemeral spaces into Lua imeevma
                   ` (5 preceding siblings ...)
  2018-07-24 11:58 ` [tarantool-patches] [PATCH v3 6/7] box: primary index for ephemeral spaces imeevma
@ 2018-07-24 11:58 ` imeevma
  6 siblings, 0 replies; 8+ messages in thread
From: imeevma @ 2018-07-24 11:58 UTC (permalink / raw)
  To: tarantool-patches

This patch defines most methods for index of
ephemeral space and ephemeral space.

Closes #3375.
---
 src/box/box.cc                    |  64 ++-
 src/box/box.h                     |  21 +
 src/box/index.cc                  | 158 ++++++++
 src/box/index.h                   | 137 +++++++
 src/box/lua/index.c               | 422 +++++++++++++++++++-
 src/box/lua/misc.cc               |  10 +-
 src/box/lua/misc.h                |   9 +
 src/box/lua/schema.lua            | 156 ++++++++
 test/box/ephemeral_space.result   | 802 +++++++++++++++++++++++++++++++++++++-
 test/box/ephemeral_space.test.lua | 240 ++++++++++++
 test/engine/iterator.result       |   2 +-
 11 files changed, 1992 insertions(+), 29 deletions(-)

diff --git a/src/box/box.cc b/src/box/box.cc
index e811496..40d0f24 100644
--- a/src/box/box.cc
+++ b/src/box/box.cc
@@ -1099,16 +1099,11 @@ box_process1(struct request *request, box_tuple_t **result)
 	return box_process_rw(request, space, result);
 }
 
-int
-box_select(uint32_t space_id, uint32_t index_id,
-	   int iterator, uint32_t offset, uint32_t limit,
-	   const char *key, const char *key_end,
-	   struct port *port)
+static inline int
+box_select_impl(struct space *space, uint32_t index_id,
+		int iterator, uint32_t offset, uint32_t limit,
+		const char *key, struct port *port)
 {
-	(void)key_end;
-
-	rmean_collect(rmean_box, IPROTO_SELECT, 1);
-
 	if (iterator < 0 || iterator >= iterator_type_MAX) {
 		diag_set(ClientError, ER_ILLEGAL_PARAMS,
 			 "Invalid iterator type");
@@ -1116,11 +1111,6 @@ box_select(uint32_t space_id, uint32_t index_id,
 		return -1;
 	}
 
-	struct space *space = space_cache_find(space_id);
-	if (space == NULL)
-		return -1;
-	if (access_check_space(space, PRIV_R) != 0)
-		return -1;
 	struct index *index = index_find(space, index_id);
 	if (index == NULL)
 		return -1;
@@ -1135,16 +1125,10 @@ box_select(uint32_t space_id, uint32_t index_id,
 		return -1;
 	});
 
-	struct txn *txn;
-	if (txn_begin_ro_stmt(space, &txn) != 0)
-		return -1;
-
 	struct iterator *it = index_create_iterator(index, type,
 						    key, part_count);
-	if (it == NULL) {
-		txn_rollback_stmt();
+	if (it == NULL)
 		return -1;
-	}
 
 	int rc = 0;
 	uint32_t found = 0;
@@ -1167,9 +1151,36 @@ box_select(uint32_t space_id, uint32_t index_id,
 
 	if (rc != 0) {
 		port_destroy(port);
+		return -1;
+	}
+	return 0;
+}
+
+int
+box_select(uint32_t space_id, uint32_t index_id,
+	   int iterator, uint32_t offset, uint32_t limit,
+	   const char *key, const char *key_end,
+	   struct port *port)
+{
+	(void)key_end;
+
+	rmean_collect(rmean_box, IPROTO_SELECT, 1);
+
+	struct space *space = space_cache_find(space_id);
+	if (space == NULL)
+		return -1;
+	if (access_check_space(space, PRIV_R) != 0)
+		return -1;
+	struct txn *txn;
+	if (txn_begin_ro_stmt(space, &txn) != 0)
+		return -1;
+
+	if (box_select_impl(space, index_id, iterator, offset, limit, key,
+			    port) != 0) {
 		txn_rollback_stmt();
 		return -1;
 	}
+
 	txn_commit_ro_stmt(txn);
 	return 0;
 }
@@ -1251,6 +1262,17 @@ box_upsert(uint32_t space_id, uint32_t index_id, const char *tuple,
 }
 
 int
+box_ephemeral_select(struct space *space, uint32_t index_id,
+		     int iterator, uint32_t offset, uint32_t limit,
+		     const char *key, const char *key_end,
+		     struct port *port)
+{
+	(void)key_end;
+	return box_select_impl(space, index_id, iterator, offset, limit, key,
+			       port);
+}
+
+int
 box_ephemeral_insert(struct space *space, const char *tuple,
 		     const char *tuple_end, box_tuple_t **result)
 {
diff --git a/src/box/box.h b/src/box/box.h
index e582fbc..40fc31b 100644
--- a/src/box/box.h
+++ b/src/box/box.h
@@ -404,6 +404,27 @@ int
 box_process1(struct request *request, box_tuple_t **result);
 
 /**
+ * Select the number of tuple matched conditions.
+ *
+ * \param space ephemeral space.
+ * \param index_id id of index for select.
+ * \param iterator iterator to use for select.
+ * \param offset offset for select.
+ * \param limit limit for select.
+ * \param key key for select.
+ * \param key_end end of \a key.
+ * \param port port to save result of select to.
+ *
+ * \retval -1 on error.
+ * \retval 0 on success.
+ */
+int
+box_ephemeral_select(struct space *space, uint32_t index_id,
+		     int iterator, uint32_t offset, uint32_t limit,
+		     const char *key, const char *key_end,
+		     struct port *port);
+
+/**
  * Execute an INSERT request for ephemeral spaces.
  *
  * \param space ephemeral space.
diff --git a/src/box/index.cc b/src/box/index.cc
index 372e0f3..e2cb4f0 100644
--- a/src/box/index.cc
+++ b/src/box/index.cc
@@ -198,6 +198,15 @@ box_index_len(uint32_t space_id, uint32_t index_id)
 }
 
 ssize_t
+box_index_len_ephemeral(struct space *space, uint32_t index_id)
+{
+	struct index *index = index_find(space, index_id);
+	if (index == NULL)
+		return -1;
+	return index_size(index);
+}
+
+ssize_t
 box_index_bsize(uint32_t space_id, uint32_t index_id)
 {
 	struct space *space;
@@ -208,6 +217,15 @@ box_index_bsize(uint32_t space_id, uint32_t index_id)
 	return index_bsize(index);
 }
 
+ssize_t
+box_index_bsize_ephemeral(struct space *space, uint32_t index_id)
+{
+	struct index *index = index_find(space, index_id);
+	if (index == NULL)
+		return 0;
+	return index_bsize(index);
+}
+
 int
 box_index_random(uint32_t space_id, uint32_t index_id, uint32_t rnd,
 		box_tuple_t **result)
@@ -226,6 +244,19 @@ box_index_random(uint32_t space_id, uint32_t index_id, uint32_t rnd,
 }
 
 int
+box_index_random_ephemeral(struct space *space, uint32_t index_id,
+			   uint32_t rnd, box_tuple_t **result)
+{
+	assert(result != NULL);
+	struct index *index = index_find(space, index_id);
+	if (index == NULL || index_random(index, rnd, result) != 0)
+		return -1;
+	if (*result != NULL)
+		tuple_bless(*result);
+	return 0;
+}
+
+int
 box_index_get(uint32_t space_id, uint32_t index_id, const char *key,
 	      const char *key_end, box_tuple_t **result)
 {
@@ -259,6 +290,29 @@ box_index_get(uint32_t space_id, uint32_t index_id, const char *key,
 }
 
 int
+box_index_get_ephemeral(struct space *space, uint32_t index_id, const char *key,
+			const char *key_end, box_tuple_t **result)
+{
+	assert(key != NULL && key_end != NULL && result != NULL);
+	mp_tuple_assert(key, key_end);
+	struct index *index = index_find(space, index_id);
+	if (index == NULL)
+		return -1;
+	if (!index->def->opts.is_unique) {
+		diag_set(ClientError, ER_MORE_THAN_ONE_TUPLE);
+		return -1;
+	}
+	uint32_t part_count = mp_decode_array(&key);
+	if (exact_key_validate(index->def->key_def, key, part_count))
+		return -1;
+	if (index_get(index, key, part_count, result) != 0)
+		return -1;
+	if (*result != NULL)
+		tuple_bless(*result);
+	return 0;
+}
+
+int
 box_index_min(uint32_t space_id, uint32_t index_id, const char *key,
 	      const char *key_end, box_tuple_t **result)
 {
@@ -291,6 +345,29 @@ box_index_min(uint32_t space_id, uint32_t index_id, const char *key,
 }
 
 int
+box_index_min_ephemeral(struct space *space, uint32_t index_id, const char *key,
+			const char *key_end, box_tuple_t **result)
+{
+	assert(key != NULL && key_end != NULL && result != NULL);
+	mp_tuple_assert(key, key_end);
+	struct index *index = index_find(space, index_id);
+	if (index == NULL)
+		return -1;
+	if (index->def->type != TREE) {
+		diag_set(UnsupportedIndexFeature, index->def, "min()");
+		return -1;
+	}
+	uint32_t part_count = mp_decode_array(&key);
+	if (key_validate(index->def, ITER_GE, key, part_count))
+		return -1;
+	if (index_min(index, key, part_count, result) != 0)
+		return -1;
+	if (*result != NULL)
+		tuple_bless(*result);
+	return 0;
+}
+
+int
 box_index_max(uint32_t space_id, uint32_t index_id, const char *key,
 	      const char *key_end, box_tuple_t **result)
 {
@@ -322,6 +399,29 @@ box_index_max(uint32_t space_id, uint32_t index_id, const char *key,
 	return 0;
 }
 
+int
+box_index_max_ephemeral(struct space *space, uint32_t index_id, const char *key,
+			const char *key_end, box_tuple_t **result)
+{
+	mp_tuple_assert(key, key_end);
+	assert(result != NULL);
+	struct index *index = index_find(space, index_id);
+	if (index == NULL)
+		return -1;
+	if (index->def->type != TREE) {
+		diag_set(UnsupportedIndexFeature, index->def, "max()");
+		return -1;
+	}
+	uint32_t part_count = mp_decode_array(&key);
+	if (key_validate(index->def, ITER_LE, key, part_count))
+		return -1;
+	if (index_max(index, key, part_count, result) != 0)
+		return -1;
+	if (*result != NULL)
+		tuple_bless(*result);
+	return 0;
+}
+
 ssize_t
 box_index_count(uint32_t space_id, uint32_t index_id, int type,
 		const char *key, const char *key_end)
@@ -354,6 +454,30 @@ box_index_count(uint32_t space_id, uint32_t index_id, int type,
 	return count;
 }
 
+ssize_t
+box_index_count_ephemeral(struct space *space, uint32_t index_id, int type,
+			  const char *key, const char *key_end)
+{
+	assert(key != NULL && key_end != NULL);
+	mp_tuple_assert(key, key_end);
+	if (type < 0 || type >= iterator_type_MAX) {
+		diag_set(ClientError, ER_ILLEGAL_PARAMS,
+			 "Invalid iterator type");
+		return -1;
+	}
+	enum iterator_type itype = (enum iterator_type) type;
+	struct index *index = index_find(space, index_id);
+	if (index == NULL)
+		return -1;
+	uint32_t part_count = mp_decode_array(&key);
+	if (key_validate(index->def, itype, key, part_count))
+		return -1;
+	ssize_t count = index_count(index, itype, key, part_count);
+	if (count < 0)
+		return -1;
+	return count;
+}
+
 /* }}} */
 
 /* {{{ Iterators ************************************************/
@@ -391,6 +515,30 @@ box_index_iterator(uint32_t space_id, uint32_t index_id, int type,
 	return it;
 }
 
+box_iterator_t *
+box_index_iterator_ephemeral(struct space *space, uint32_t index_id, int type,
+			     const char *key, const char *key_end)
+{
+	assert(key != NULL && key_end != NULL);
+	mp_tuple_assert(key, key_end);
+	if (type < 0 || type >= iterator_type_MAX) {
+		diag_set(ClientError, ER_ILLEGAL_PARAMS,
+			 "Invalid iterator type");
+		return NULL;
+	}
+	struct index *index = index_find(space, index_id);
+	if (index == NULL)
+		return NULL;
+	enum iterator_type itype = (enum iterator_type) type;
+	assert(mp_typeof(*key) == MP_ARRAY); /* checked by Lua */
+	uint32_t part_count = mp_decode_array(&key);
+	if (key_validate(index->def, itype, key, part_count))
+		return NULL;
+	struct iterator *it = index_create_iterator(index, itype,
+						    key, part_count);
+	return it;
+}
+
 int
 box_iterator_next(box_iterator_t *itr, box_tuple_t **result)
 {
@@ -435,6 +583,16 @@ box_index_compact(uint32_t space_id, uint32_t index_id)
 	return 0;
 }
 
+int
+box_index_compact_ephemeral(struct space *space, uint32_t index_id)
+{
+	struct index *index = index_find(space, index_id);
+	if (index == NULL)
+		return -1;
+	index_compact(index);
+	return 0;
+}
+
 /* }}} */
 
 /* {{{ Internal API */
diff --git a/src/box/index.h b/src/box/index.h
index 73ee000..83e30bf 100644
--- a/src/box/index.h
+++ b/src/box/index.h
@@ -236,6 +236,143 @@ box_index_stat(uint32_t space_id, uint32_t index_id,
 int
 box_index_compact(uint32_t space_id, uint32_t index_id);
 
+/**
+ * Return the number of element in the index.
+ *
+ * \param space - ephemeral space.
+ * \param index_id - index identifier.
+ *
+ * \retval -1 on error.
+ * \retval >= 0 on success.
+ */
+ssize_t
+box_index_len_ephemeral(struct space *space, uint32_t index_id);
+
+/**
+ * Return the number of bytes used in memory by the index.
+ * If space have no index it returns 0.
+ *
+ * \param space ephemeral space.
+ * \param index_id index identifier.
+ *
+ * \retval -1 on error.
+ * \retval >= 0 on success.
+ */
+ssize_t
+box_index_bsize_ephemeral(struct space *space, uint32_t index_id);
+
+/**
+ * Return a random tuple from the index.
+ *
+ * \param space ephemeral space.
+ * \param index_id index identifier.
+ * \param rnd random seed.
+ * \param[out] result a tuple or NULL if index is empty.
+ *
+ * \retval -1 on error.
+ * \retval 0 on success.
+ */
+int
+box_index_random_ephemeral(struct space *space, uint32_t index_id,
+			   uint32_t rnd, box_tuple_t **result);
+
+/**
+ * Get a tuple from index by the key.
+ *
+ * \param space ephemeral space.
+ * \param index_id index identifier.
+ * \param key encoded key in MsgPack Array format.
+ * \param key_end the end of encoded \a key.
+ * \param[out] result a tuple or NULL if index is empty.
+ *
+ * \retval -1 on error.
+ * \retval 0 on success.
+ * \pre key != NULL
+ */
+int
+box_index_get_ephemeral(struct space *space, uint32_t index_id, const char *key,
+			const char *key_end, box_tuple_t **result);
+
+/**
+ * Return first (minimal) tuple matched the provided key.
+ *
+ * \param space ephemeral space.
+ * \param index_id index identifier.
+ * \param key encoded key in MsgPack Array format.
+ * \param key_end the end of encoded \a key.
+ * \param[out] result a tuple or NULL if index is empty.
+ *
+ * \retval -1 on error.
+ * \retval 0 on success.
+ */
+int
+box_index_min_ephemeral(struct space *space, uint32_t index_id, const char *key,
+			const char *key_end, box_tuple_t **result);
+
+/**
+ * Return last (maximal) tuple matched the provided key.
+ *
+ * \param space ephemeral space.
+ * \param index_id index identifier.
+ * \param key encoded key in MsgPack Array format.
+ * \param key_end the end of encoded \a key.
+ * \param[out] result a tuple or NULL if index is empty.
+ *
+ * \retval -1 on error.
+ * \retval 0 on success.
+ */
+int
+box_index_max_ephemeral(struct space *space, uint32_t index_id, const char *key,
+			const char *key_end, box_tuple_t **result);
+
+/**
+ * Count the number of tuple matched the provided key.
+ *
+ * \param space ephemeral space.
+ * \param index_id index identifier.
+ * \param key encoded key in MsgPack Array format.
+ * \param key_end the end of encoded \a key.
+ * \param[out] result a tuple or NULL if index is empty.
+ *
+ * \retval -1 on error.
+ * \retval 0 on success.
+ */
+ssize_t
+box_index_count_ephemeral(struct space *space, uint32_t index_id, int type,
+			  const char *key, const char *key_end);
+
+/**
+ * Allocate and initialize iterator for ephemeral space
+ *
+ * A returned iterator must be destroyed by box_iterator_free().
+ *
+ * \param space ephemeral space.
+ * \param index_id index identifier.
+ * \param type \link iterator_type iterator type \endlink
+ * \param key encoded key in MsgPack Array format ([part1, part2, ...]).
+ * \param key_end the end of encoded \a key
+ *
+ * \retval NULL on error (check box_error_last())
+ * \retval iterator otherwise
+ * \sa box_iterator_next()
+ * \sa box_iterator_free()
+ */
+box_iterator_t *
+box_index_iterator_ephemeral(struct space *space, uint32_t index_id, int type,
+			     const char *key, const char *key_end);
+
+/**
+ * Trigger index compaction.
+ *
+ * \param space ephemeral space.
+ * \param index_id index identifier.
+ *
+ * \retval -1 on error.
+ * \retval 0 on success.
+ */
+int
+box_index_compact_ephemeral(struct space *space, uint32_t index_id);
+
 struct iterator {
 	/**
 	 * Iterate to the next tuple.
diff --git a/src/box/lua/index.c b/src/box/lua/index.c
index ef89c39..79427e8 100644
--- a/src/box/lua/index.c
+++ b/src/box/lua/index.c
@@ -35,7 +35,9 @@
 #include "box/info.h"
 #include "box/lua/info.h"
 #include "box/lua/tuple.h"
-#include "box/lua/misc.h" /* lbox_encode_tuple_on_gc() */
+#include "box/lua/misc.h"
+#include "box/port.h"
+#include "box/box.h"
 
 /** {{{ box.index Lua library: access to spaces and indexes
  */
@@ -58,6 +60,30 @@ lbox_insert(lua_State *L)
 	return luaT_pushtupleornil(L, result);
 }
 
+/**
+ * Insert tuple into ephemeral space.
+ *
+ * @param L Lua stack to get space and tuple from.
+ *
+ * @retval not nil tuple that was inserted.
+ * @retval nil error.
+ */
+static int
+lbox_insert_ephemeral(lua_State *L)
+{
+	if (lua_gettop(L) != 2)
+		return luaL_error(L, "Usage space:insert(tuple)");
+
+	struct space *space = lua_checkephemeralspace(L, 1);
+	size_t tuple_len;
+	const char *tuple = lbox_encode_tuple_on_gc(L, 2, &tuple_len);
+
+	struct tuple *result;
+	if (box_ephemeral_insert(space, tuple, tuple + tuple_len, &result) != 0)
+		return luaT_error(L);
+	return luaT_pushtupleornil(L, result);
+}
+
 static int
 lbox_replace(lua_State *L)
 {
@@ -74,6 +100,31 @@ lbox_replace(lua_State *L)
 	return luaT_pushtupleornil(L, result);
 }
 
+/**
+ * Replace tuple from ephemeral space.
+ *
+ * @param L Lua stack to get space and tuple from.
+ *
+ * @retval not nil tuple that was inserted.
+ * @retval nil error.
+ */
+static int
+lbox_replace_ephemeral(lua_State *L)
+{
+	if (lua_gettop(L) != 2)
+		return luaL_error(L, "Usage space:replace(tuple)");
+
+	struct space *space = lua_checkephemeralspace(L, 1);
+	size_t tuple_len;
+	const char *tuple = lbox_encode_tuple_on_gc(L, 2, &tuple_len);
+
+	struct tuple *result;
+	if (box_ephemeral_replace(space, tuple, tuple + tuple_len,
+				  &result) != 0)
+		return luaT_error(L);
+	return luaT_pushtupleornil(L, result);
+}
+
 static int
 lbox_index_update(lua_State *L)
 {
@@ -96,6 +147,36 @@ lbox_index_update(lua_State *L)
 	return luaT_pushtupleornil(L, result);
 }
 
+/**
+ * Update tuple matched provided key.
+ *
+ * @param L Lua stack to get space, index_id, key and ops from.
+ *
+ * @retval not nil tuple - result of update.
+ * @retval nil error.
+ */
+static int
+lbox_index_update_ephemeral(lua_State *L)
+{
+	if (lua_gettop(L) != 4 || !lua_isnumber(L, 2) ||
+	    (!lua_istable(L, 3) && luaT_istuple(L, 3) == NULL) ||
+	    (!lua_istable(L, 4) && luaT_istuple(L, 4) == NULL))
+		return luaL_error(L, "Usage index:update(key, ops)");
+
+	struct space *space = lua_checkephemeralspace(L, 1);
+	uint32_t index_id = lua_tonumber(L, 2);
+	size_t key_len;
+	const char *key = lbox_encode_tuple_on_gc(L, 3, &key_len);
+	size_t ops_len;
+	const char *ops = lbox_encode_tuple_on_gc(L, 4, &ops_len);
+
+	struct tuple *result;
+	if (box_ephemeral_update(space, index_id, key, key + key_len,
+				 ops, ops + ops_len, 1, &result) != 0)
+		return luaT_error(L);
+	return luaT_pushtupleornil(L, result);
+}
+
 static int
 lbox_upsert(lua_State *L)
 {
@@ -117,6 +198,35 @@ lbox_upsert(lua_State *L)
 	return luaT_pushtupleornil(L, result);
 }
 
+/**
+ * Insert or update tuple from ephemeral space.
+ *
+ * @param L Lua stack to get space, tuple and ops from.
+ *
+ * @retval not nil tuple - result of upsert.
+ * @retval nil error.
+ */
+static int
+lbox_upsert_ephemeral(lua_State *L)
+{
+	if (lua_gettop(L) != 3 ||
+	    (!lua_istable(L, 2) && luaT_istuple(L, 2) == NULL) ||
+	    (!lua_istable(L, 3) && luaT_istuple(L, 3) == NULL))
+		return luaL_error(L, "Usage index:upsert(key, ops)");
+
+	struct space *space = lua_checkephemeralspace(L, 1);
+	size_t tuple_len;
+	const char *tuple = lbox_encode_tuple_on_gc(L, 2, &tuple_len);
+	size_t ops_len;
+	const char *ops = lbox_encode_tuple_on_gc(L, 3, &ops_len);
+
+	struct tuple *result;
+	if (box_ephemeral_upsert(space, 0, tuple, tuple + tuple_len,
+				 ops, ops + ops_len, 1, &result) != 0)
+		return luaT_error(L);
+	return luaT_pushtupleornil(L, result);
+}
+
 static int
 lbox_index_delete(lua_State *L)
 {
@@ -135,6 +245,73 @@ lbox_index_delete(lua_State *L)
 	return luaT_pushtupleornil(L, result);
 }
 
+/**
+ * Delete tuple matched the provided key.
+ *
+ * @param L Lua stack to get space, index_id and key from.
+ *
+ * @retval not nil tuple that was deleted.
+ * @retval nil error.
+ */
+static int
+lbox_index_delete_ephemeral(lua_State *L)
+{
+	if (lua_gettop(L) != 3 || !lua_isnumber(L, 2) ||
+	    (!lua_istable(L, 3) && luaT_istuple(L, 3) == NULL))
+		return luaL_error(L, "Usage index:delete(key)");
+
+	struct space *space = lua_checkephemeralspace(L, 1);
+	uint32_t index_id = lua_tonumber(L, 2);
+	size_t key_len;
+	const char *key = lbox_encode_tuple_on_gc(L, 3, &key_len);
+
+	struct tuple *result;
+	if (box_ephemeral_delete(space, index_id, key, key + key_len,
+				 &result) != 0)
+		return luaT_error(L);
+	return luaT_pushtupleornil(L, result);
+}
+
+/**
+ * Push number of element in the index on stack.
+ *
+ * @param L Lua stack to get space from.
+ *
+ * @retval number number number of return values.
+ */
+static int
+lbox_index_len_ephemeral(struct lua_State *L)
+{
+	if (lua_gettop(L) != 2 || !lua_isnumber(L, 2))
+		return luaL_error(L, "Usage: index:len()");
+
+	struct space *space = lua_checkephemeralspace(L, 1);
+	uint32_t index_id = lua_tonumber(L, 2);
+
+	lua_pushnumber(L, box_index_len_ephemeral(space, index_id));
+	return 1;
+}
+
+/**
+ * Push number of bytes used in memory by index on stack.
+ * 
+ * @param L Lua stack to get space from.
+ *
+ * @retval number number of return values.
+ */
+static int
+lbox_index_bsize_ephemeral(struct lua_State *L)
+{
+	if (lua_gettop(L) != 2 || !lua_isnumber(L, 2))
+		return luaL_error(L, "Usage: index:bsize()");
+
+	struct space *space = lua_checkephemeralspace(L, 1);
+	uint32_t index_id = lua_tonumber(L, 2);
+
+	lua_pushnumber(L, box_index_bsize_ephemeral(space, index_id));
+	return 1;
+}
+
 static int
 lbox_index_random(lua_State *L)
 {
@@ -152,6 +329,30 @@ lbox_index_random(lua_State *L)
 	return luaT_pushtupleornil(L, tuple);
 }
 
+/**
+ * Return a random tuple from the index.
+ *
+ * @param L Lua stack to get space and seed from.
+ *
+ * @retval not nil random tuple.
+ * @retval nil error.
+ */
+static int
+lbox_index_random_ephemeral(lua_State *L)
+{
+	if (lua_gettop(L) != 3 || !lua_isnumber(L, 2) || !lua_isnumber(L, 3))
+		return luaL_error(L, "Usage index:random(seed)");
+
+	struct space *space = lua_checkephemeralspace(L, 1);
+	uint32_t index_id = lua_tonumber(L, 2);
+	uint32_t rnd = lua_tonumber(L, 3);
+
+	struct tuple *tuple;
+	if (box_index_random_ephemeral(space, index_id, rnd, &tuple) != 0)
+		return luaT_error(L);
+	return luaT_pushtupleornil(L, tuple);
+}
+
 static int
 lbox_index_get(lua_State *L)
 {
@@ -169,6 +370,32 @@ lbox_index_get(lua_State *L)
 	return luaT_pushtupleornil(L, tuple);
 }
 
+/**
+ * Return a tuple from the index by given key.
+ *
+ * @param L Lua stack to get space and key from.
+ *
+ * @retval not nil matched tuple.
+ * @retval nil error.
+ */
+static int
+lbox_index_get_ephemeral(lua_State *L)
+{
+	if (lua_gettop(L) != 3 || !lua_isnumber(L, 2))
+		return luaL_error(L, "Usage index:get(key)");
+
+	struct space *space = lua_checkephemeralspace(L, 1);
+	uint32_t index_id = lua_tonumber(L, 2);
+	size_t key_len;
+	const char *key = lbox_encode_tuple_on_gc(L, 3, &key_len);
+
+	struct tuple *tuple;
+	if (box_index_get_ephemeral(space, index_id, key, key + key_len,
+				    &tuple) != 0)
+		return luaT_error(L);
+	return luaT_pushtupleornil(L, tuple);
+}
+
 static int
 lbox_index_min(lua_State *L)
 {
@@ -186,6 +413,32 @@ lbox_index_min(lua_State *L)
 	return luaT_pushtupleornil(L, tuple);
 }
 
+/**
+ * Return first (minimal) tuple from the index matched provided
+ * key.
+ *
+ * @param L Lua stack to get space and key from.
+ *
+ * @retval not nil matched tuple.
+ * @retval nil error.
+ */
+static int
+lbox_index_min_ephemeral(lua_State *L)
+{
+	if (lua_gettop(L) != 3 || !lua_isnumber(L, 2))
+		return luaL_error(L, "Usage index:min(key)");
+	struct space *space = lua_checkephemeralspace(L, 1);
+	uint32_t index_id = lua_tonumber(L, 2);
+	size_t key_len;
+	const char *key = lbox_encode_tuple_on_gc(L, 3, &key_len);
+
+	struct tuple *tuple;
+	if (box_index_min_ephemeral(space, index_id, key, key + key_len,
+				    &tuple) != 0)
+		return luaT_error(L);
+	return luaT_pushtupleornil(L, tuple);
+}
+
 static int
 lbox_index_max(lua_State *L)
 {
@@ -203,6 +456,33 @@ lbox_index_max(lua_State *L)
 	return luaT_pushtupleornil(L, tuple);
 }
 
+/**
+ * Return last (maximal) tuple from the index matched provided
+ * key.
+ *
+ * @param L Lua stack to get space and key from.
+ *
+ * @retval not nil matched tuple.
+ * @retval nil error.
+ */
+static int
+lbox_index_max_ephemeral(lua_State *L)
+{
+	if (lua_gettop(L) != 3 || !lua_isnumber(L, 2))
+		return luaL_error(L, "Usage index:max(key)");
+
+	struct space *space = lua_checkephemeralspace(L, 1);
+	uint32_t index_id = lua_tonumber(L, 2);
+	size_t key_len;
+	const char *key = lbox_encode_tuple_on_gc(L, 3, &key_len);
+
+	struct tuple *tuple;
+	if (box_index_max_ephemeral(space, index_id, key, key + key_len,
+				    &tuple) != 0)
+		return luaT_error(L);
+	return luaT_pushtupleornil(L, tuple);
+}
+
 static int
 lbox_index_count(lua_State *L)
 {
@@ -226,6 +506,65 @@ lbox_index_count(lua_State *L)
 	return 1;
 }
 
+/**
+ * Count tuples matched provided key and push this number on
+ * stack.
+ *
+ * @param L Lua stack to get space and key from.
+ *
+ * @retval number number of return values.
+ */
+static int
+lbox_index_count_ephemeral(lua_State *L)
+{
+	if (lua_gettop(L) != 4 || !lua_isnumber(L, 2) || !lua_isnumber(L, 3))
+		return luaL_error(L, "Usage index:count(type, key)");
+
+	struct space *space = lua_checkephemeralspace(L, 1);
+	uint32_t index_id = lua_tonumber(L, 2);
+	uint32_t iterator = lua_tonumber(L, 3);
+	size_t key_len;
+	const char *key = lbox_encode_tuple_on_gc(L, 4, &key_len);
+
+	ssize_t count = box_index_count_ephemeral(space, index_id, iterator,
+						  key, key + key_len);
+	if (count == -1)
+		return luaT_error(L);
+	lua_pushinteger(L, count);
+	return 1;
+}
+
+/**
+ * Select tuples matched conditions and push them on stack as
+ * table.
+ *
+ * @param L Lua stack to get space, iterator, offset, limit and
+ * key from.
+ *
+ * @retval number number of return values.
+ */
+static int
+lbox_index_select_ephemeral(lua_State *L)
+{
+	if (lua_gettop(L) != 5) {
+		return luaL_error(L, "Usage index:select(iterator, offset, "\
+				  "limit, key)");
+	}
+	struct space *space = lua_checkephemeralspace(L, 1);
+	int iterator = luaL_checknumber(L, 2);
+	uint32_t offset = luaL_checknumber(L, 3);
+	uint32_t limit = luaL_checknumber(L, 4);
+	size_t key_len;
+	const char *key = lbox_encode_tuple_on_gc(L, 5, &key_len);
+	struct port port;
+	if (box_ephemeral_select(space, 0, iterator, offset, limit,
+				 key, key + key_len, &port) != 0)
+		return luaT_error(L);
+	lbox_port_to_table(L, &port);
+	port_destroy(&port);
+	return 1; /* lua table with tuples */
+}
+
 static void
 box_index_init_iterator_types(struct lua_State *L, int idx)
 {
@@ -264,6 +603,40 @@ lbox_index_iterator(lua_State *L)
 	return 1;
 }
 
+/**
+ * Creates iterator for ephemeral space according to given type
+ * and pushes it on stack.
+ *
+ * @param L Lua stack to get space, index_id, iterator type and
+ * encoded with msgpack key from.
+ *
+ * @retval number number of return values.
+ */
+static int
+lbox_index_iterator_ephemeral(lua_State *L)
+{
+	if (lua_gettop(L) != 4 || !lua_isnumber(L, 2) || !lua_isnumber(L, 3))
+		return luaL_error(L, "Usage index:iterator(type, key)");
+
+	struct space *space = lua_checkephemeralspace(L, 1);
+	uint32_t index_id = lua_tonumber(L, 2);
+	uint32_t iterator = lua_tonumber(L, 3);
+	size_t mpkey_len;
+	/* Key encoded by Lua */
+	const char *mpkey = lua_tolstring(L, 4, &mpkey_len);
+	struct iterator *it =
+		box_index_iterator_ephemeral(space, index_id, iterator, mpkey,
+					     mpkey + mpkey_len);
+	if (it == NULL)
+		return luaT_error(L);
+
+	assert(CTID_STRUCT_ITERATOR_REF != 0);
+	struct iterator **ptr =
+		(struct iterator **)luaL_pushcdata(L, CTID_STRUCT_ITERATOR_REF);
+	*ptr = it; /* NULL handled by Lua, gc also set by Lua */
+	return 1;
+}
+
 static int
 lbox_iterator_next(lua_State *L)
 {
@@ -328,6 +701,24 @@ lbox_index_compact(lua_State *L)
 	return 0;
 }
 
+/**
+ * Run index compaction.
+ *
+ * @param L Lua stack to get space and key from.
+ */
+static int
+lbox_index_compact_ephemeral(lua_State *L)
+{
+	if (lua_gettop(L) != 2 || !lua_isnumber(L, 2))
+		return luaL_error(L, "Usage index:compact()");
+	struct space *space = lua_checkephemeralspace(L, 1);
+	uint32_t index_id = lua_tonumber(L, 2);
+
+	if (box_index_compact_ephemeral(space, index_id) != 0)
+		return luaT_error(L);
+	return 0;
+}
+
 /* }}} */
 
 void
@@ -340,6 +731,12 @@ box_lua_index_init(struct lua_State *L)
 	CTID_STRUCT_ITERATOR_REF = luaL_ctypeid(L, "struct iterator&");
 	assert(CTID_STRUCT_ITERATOR_REF != 0);
 
+	lua_getfield(L, LUA_GLOBALSINDEX, "box");
+	lua_getfield(L, -1, "internal");
+	lua_newtable(L);
+	lua_setfield(L, -2, "index_ephemeral_methods");
+	lua_pop(L, 2); /* box, internal */
+
 	static const struct luaL_Reg indexlib [] = {
 		{NULL, NULL}
 	};
@@ -370,4 +767,27 @@ box_lua_index_init(struct lua_State *L)
 
 	luaL_register(L, "box.internal", boxlib_internal);
 	lua_pop(L, 1);
+
+	static const struct luaL_Reg ephemeral_index_lib[] = {
+		{"insert", lbox_insert_ephemeral},
+		{"replace",  lbox_replace_ephemeral},
+		{"update", lbox_index_update_ephemeral},
+		{"upsert",  lbox_upsert_ephemeral},
+		{"delete",  lbox_index_delete_ephemeral},
+		{"len",  lbox_index_len_ephemeral},
+		{"bsize", lbox_index_bsize_ephemeral},
+		{"random", lbox_index_random_ephemeral},
+		{"select",  lbox_index_select_ephemeral},
+		{"get",  lbox_index_get_ephemeral},
+		{"min", lbox_index_min_ephemeral},
+		{"max", lbox_index_max_ephemeral},
+		{"count", lbox_index_count_ephemeral},
+		{"iterator", lbox_index_iterator_ephemeral},
+		{"compact", lbox_index_compact_ephemeral},
+		{NULL, NULL}
+	};
+
+	luaL_register(L, "box.internal.index_ephemeral_methods",
+		      ephemeral_index_lib);
+	lua_pop(L, 1);
 }
diff --git a/src/box/lua/misc.cc b/src/box/lua/misc.cc
index 13ca18c..32ffb20 100644
--- a/src/box/lua/misc.cc
+++ b/src/box/lua/misc.cc
@@ -70,11 +70,7 @@ lua_checkephemeralspace(struct lua_State *L, int idx)
 	return *(struct space **) data;
 }
 
-/* }}} */
-
-/** {{{ Lua/C implementation of index:select(): used only by Vinyl **/
-
-static inline void
+void
 lbox_port_to_table(lua_State *L, struct port *port_base)
 {
 	struct port_tuple *port = port_tuple(port_base);
@@ -87,6 +83,10 @@ lbox_port_to_table(lua_State *L, struct port *port_base)
 	}
 }
 
+/* }}} */
+
+/** {{{ Lua/C implementation of index:select(): used only by Vinyl **/
+
 static int
 lbox_select(lua_State *L)
 {
diff --git a/src/box/lua/misc.h b/src/box/lua/misc.h
index 6162baa..6859142 100644
--- a/src/box/lua/misc.h
+++ b/src/box/lua/misc.h
@@ -54,6 +54,15 @@ lbox_encode_tuple_on_gc(struct lua_State *L, int idx, size_t *p_len);
 struct space *
 lua_checkephemeralspace(struct lua_State *L, int idx);
 
+/**
+ * Transform given port to Lua table.
+ *
+ * @param L Lua stack to push table to.
+ * @param port_base port to transform.
+ */
+void
+lbox_port_to_table(lua_State *L, struct port *port_base);
+
 void
 box_lua_misc_init(struct lua_State *L);
 
diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua
index cc8c66b..3b40401 100644
--- a/src/box/lua/schema.lua
+++ b/src/box/lua/schema.lua
@@ -1098,6 +1098,8 @@ local index_new_ephemeral = box.internal.space.index_new_ephemeral
 box.internal.space.index_new_ephemeral = nil
 local index_delete_ephemeral = box.internal.space.index_delete_ephemeral
 box.internal.space.index_delete_ephemeral = nil
+local index_ephemeral_methods = box.internal.index_ephemeral_methods
+box.internal.index_ephemeral_methods = nil
 local index_ephemeral_mt = {}
 
 local create_ephemeral_index = function(space, name, options)
@@ -1234,6 +1236,13 @@ local function check_index_arg(index, method)
         error(string.format(fmt, method, method))
     end
 end
+local function check_index_arg_ephemeral(index, method)
+    if type(index) ~= 'table' or type(index.space) ~= 'table' or
+       param_type(index.space.space) ~= 'cdata' then
+        local fmt = 'Use index:%s(...) instead of index.%s(...)'
+        error(string.format(fmt, method, method))
+    end
+end
 box.internal.check_index_arg = check_index_arg -- for net.box
 
 -- Helper function to check that space have primary key and return it
@@ -1642,6 +1651,81 @@ space_mt.__index = space_mt
 
 -- Metatable for primary index of ephemeral space
 index_ephemeral_mt.drop = drop_ephemeral_index
+index_ephemeral_mt.update = function(index, key, ops)
+    check_index_arg_ephemeral(index, 'update')
+    key = keify(key)
+    return index_ephemeral_methods.update(index.space.space, index.id, key, ops)
+end
+index_ephemeral_mt.delete = function(index, key)
+    check_index_arg_ephemeral(index, 'delete')
+    key = keify(key)
+    return index_ephemeral_methods.delete(index.space.space, index.id, key)
+end
+index_ephemeral_mt.len = function(index)
+    check_index_arg_ephemeral(index, 'len')
+    local ret = index_ephemeral_methods.len(index.space.space, index.id)
+    if ret == -1 then
+        box.error()
+    end
+    return tonumber(ret)
+end
+index_ephemeral_mt.__len = index_ephemeral_mt.len
+index_ephemeral_mt.bsize = function(index)
+    check_index_arg_ephemeral(index, 'bsize')
+    local ret = index_ephemeral_methods.bsize(index.space.space, index.id)
+    if ret == -1 then
+        box.error()
+    end
+    return tonumber(ret)
+end
+index_ephemeral_mt.random = function(index, rnd)
+    check_index_arg_ephemeral(index, 'random')
+    rnd = rnd or math.random()
+    return index_ephemeral_methods.random(index.space.space, index.id, rnd);
+end
+index_ephemeral_mt.get = function(index, key)
+    check_index_arg_ephemeral(index, 'get')
+    key = keify(key)
+    return index_ephemeral_methods.get(index.space.space, index.id, key)
+end
+index_ephemeral_mt.select = function(index, key, opts)
+    check_index_arg_ephemeral(index, 'select')
+    local key = keify(key)
+    local iterator, offset, limit = check_select_opts(opts, #key == 0)
+    return index_ephemeral_methods.select(index.space.space, iterator, offset, limit, key)
+end
+index_ephemeral_mt.min = function(index, key)
+    check_index_arg_ephemeral(index, 'min')
+    key = keify(key)
+    return index_ephemeral_methods.min(index.space.space, index.id, key);
+end
+index_ephemeral_mt.max = function(index, key)
+    check_index_arg_ephemeral(index, 'max')
+    key = keify(key)
+    return index_ephemeral_methods.max(index.space.space, index.id, key);
+end
+index_ephemeral_mt.count = function(index, key, opts)
+    check_index_arg_ephemeral(index, 'count')
+    key = keify(key)
+    local itype = check_iterator_type(opts, #key == 0);
+    return index_ephemeral_methods.count(index.space.space, index.id, itype,
+                                         key);
+end
+index_ephemeral_mt.pairs = function(index, key, opts)
+    check_index_arg_ephemeral(index, 'pairs')
+    key = keify(key)
+    local itype = check_iterator_type(opts, #key == 0);
+    local keymp = msgpack.encode(key)
+    local keybuf = ffi.string(keymp, #keymp)
+    local cdata = index_ephemeral_methods.iterator(index.space.space, index.id,
+                                                   itype, keymp);
+    return fun.wrap(iterator_gen_luac, keybuf,
+        ffi.gc(cdata, builtin.box_iterator_free))
+end
+index_ephemeral_mt.compact = function(index)
+    check_index_arg_ephemeral(index, 'compact')
+    return index_ephemeral_methods.compact(index.space.space, index.id)
+end
 index_ephemeral_mt.__index = index_ephemeral_mt
 
 -- Metatable for ephemeral space
@@ -1665,9 +1749,81 @@ space_ephemeral_mt.bsize = function(space)
     check_ephemeral_space_arg(space, 'bsize')
     return builtin.space_bsize(space.space)
 end
+space_ephemeral_mt.auto_increment = function(space, tuple)
+    check_ephemeral_space_arg(space, 'auto_increment')
+    local max_tuple = check_primary_index(space):max()
+    local max = 0
+    if max_tuple ~= nil then
+        max = max_tuple[1]
+    end
+    table.insert(tuple, 1, max + 1)
+    return space:insert(tuple)
+end
+space_ephemeral_mt.insert = function(space, tuple)
+    check_ephemeral_space_arg(space, 'insert')
+    return index_ephemeral_methods.insert(space.space, tuple);
+end
+space_ephemeral_mt.replace = function(space, tuple)
+    check_ephemeral_space_arg(space, 'replace')
+    return index_ephemeral_methods.replace(space.space, tuple);
+end
+space_ephemeral_mt.upsert = function(space, tuple_key, ops, deprecated)
+    check_ephemeral_space_arg(space, 'upsert')
+    if deprecated ~= nil then
+        local msg = "Error: extra argument in upsert call: "
+        msg = msg .. tostring(deprecated)
+        msg = msg .. ". Usage :upsert(tuple, operations)"
+        box.error(box.error.PROC_LUA, msg)
+    end
+    return index_ephemeral_methods.upsert(space.space, tuple_key, ops);
+end
+space_ephemeral_mt.pairs = function(space, key, opts)
+    check_ephemeral_space_arg(space, 'pairs')
+    local pk = space.index[0]
+    if pk == nil then
+        -- empty space without indexes, return empty iterator
+        return fun.iter({})
+    end
+    return pk:pairs(key, opts)
+end
+space_ephemeral_mt.__pairs = space_ephemeral_mt.pairs -- Lua 5.2 compatibility
+space_ephemeral_mt.__ipairs = space_ephemeral_mt.pairs -- Lua 5.2 compatibility
+space_ephemeral_mt.truncate = function(space)
+    check_ephemeral_space_arg(space, 'truncate')
+    local index = space.index[0]
+    local name = index.name
+    local options = index.options
+    index:drop()
+    space:create_index(name, options)
+end
 space_ephemeral_mt.create_index = create_ephemeral_index
 space_ephemeral_mt.drop = box.schema.space.drop_ephemeral
 space_ephemeral_mt.__index = space_ephemeral_mt
+space_ephemeral_mt.put = space_ephemeral_methods.replace
+space_ephemeral_mt.update = function(space, key, ops)
+    check_ephemeral_space_arg(space, 'update')
+    return check_primary_index(space):update(key, ops)
+end
+space_ephemeral_mt.delete = function(space, key)
+    check_ephemeral_space_arg(space, 'delete')
+    return check_primary_index(space):delete(key)
+end
+space_ephemeral_mt.get = function(space, key)
+    check_ephemeral_space_arg(space, 'get')
+    return check_primary_index(space):get(key)
+end
+space_ephemeral_mt.select = function(space, key, opts)
+    check_ephemeral_space_arg(space, 'select')
+    return check_primary_index(space):select(key, opts)
+end
+space_ephemeral_mt.count = function(space, key, opts)
+    check_ephemeral_space_arg(space, 'count')
+    return check_primary_index(space):count(key, opts)
+end
+space_ephemeral_mt.len = function(space)
+    check_ephemeral_space_arg(space, 'len')
+    return check_primary_index(space):len()
+end
 
 box.schema.index_mt = base_index_mt
 box.schema.memtx_index_mt = memtx_index_mt
diff --git a/test/box/ephemeral_space.result b/test/box/ephemeral_space.result
index 4ef395f..3137f63 100644
--- a/test/box/ephemeral_space.result
+++ b/test/box/ephemeral_space.result
@@ -135,7 +135,7 @@ s:frommap({ddd = 1, aaa = 2, ccc = 3, eee = 4})
 ...
 s:frommap()
 ---
-- error: 'builtin/box/schema.lua:1659: Usage: space:frommap(map, opts)'
+- error: 'builtin/box/schema.lua:1743: Usage: space:frommap(map, opts)'
 ...
 s:frommap({})
 ---
@@ -245,3 +245,803 @@ i = s:create_index('a', {type = 'bitset', parts = {1, 'unsigned', 2, 'unsigned'}
 - error: 'Can''t create or modify index ''a'' in space ''ephemeral'': primary key
     must be unique'
 ...
+-- Ephemeral space: methods
+s = box.schema.space.create_ephemeral({field_count = 3})
+---
+...
+i = s:create_index('a')
+---
+...
+s:insert{1}
+---
+- error: Tuple field count 1 does not match space field count 3
+...
+s:insert{2,2,2}
+---
+- [2, 2, 2]
+...
+s:drop()
+---
+...
+s = box.schema.space.create_ephemeral()
+---
+...
+i = s:create_index('a', { type = 'tree', parts = {1, 'string'} })
+---
+...
+s:insert{'1'}
+---
+- ['1']
+...
+s:get{'1'}
+---
+- ['1']
+...
+s:insert{'1'}
+---
+- error: Duplicate key exists in unique index 'a' in space 'ephemeral'
+...
+s:insert{1}
+---
+- error: 'Tuple field 1 type does not match one required by operation: expected string'
+...
+i:drop()
+---
+...
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} })
+---
+...
+s:insert{1}
+---
+- [1]
+...
+s:get{1}
+---
+- [1]
+...
+s:insert{1}
+---
+- error: Duplicate key exists in unique index 'a' in space 'ephemeral'
+...
+s:insert{'1'}
+---
+- error: 'Tuple field 1 type does not match one required by operation: expected unsigned'
+...
+i:drop()
+---
+...
+i = s:create_index('a', { type = 'tree', parts = {1, 'string'} })
+---
+...
+s:replace{'1'}
+---
+- ['1']
+...
+s:get{'1'}
+---
+- ['1']
+...
+s:replace{'1'}
+---
+- ['1']
+...
+s:replace{1}
+---
+- error: 'Tuple field 1 type does not match one required by operation: expected string'
+...
+i:drop()
+---
+...
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} })
+---
+...
+s:replace{1}
+---
+- [1]
+...
+s:get{1}
+---
+- [1]
+...
+s:replace{1}
+---
+- [1]
+...
+s:replace{'1'}
+---
+- error: 'Tuple field 1 type does not match one required by operation: expected unsigned'
+...
+i:drop()
+---
+...
+i = s:create_index('a', { type = 'tree', parts = {1, 'string'} })
+---
+...
+s:replace{'1'}
+---
+- ['1']
+...
+s:get{'1'}
+---
+- ['1']
+...
+s:replace{'1'}
+---
+- ['1']
+...
+s:replace{1}
+---
+- error: 'Tuple field 1 type does not match one required by operation: expected string'
+...
+i:drop()
+---
+...
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} })
+---
+...
+s:upsert({1, 0}, {{'+', 2, 1}})
+---
+...
+s:get{1}
+---
+- [1, 0]
+...
+s:upsert({1, 0}, {{'+', 2, 1}})
+---
+...
+s:get{1}
+---
+- [1, 1]
+...
+s:upsert({1, 0}, {{'+', 1, 1}})
+---
+...
+s:get{1}
+---
+- [1, 1]
+...
+s:get{2}
+---
+...
+s:upsert({'1'}, {{'!', 2, 100}})
+---
+- error: 'Tuple field 1 type does not match one required by operation: expected unsigned'
+...
+s:upsert({1}, {{'a', 2, 100}})
+---
+- error: Unknown UPDATE operation
+...
+i:drop()
+---
+...
+i = s:create_index('a')
+---
+...
+s:insert{1, 2, 3, 4, 5}
+---
+- [1, 2, 3, 4, 5]
+...
+s:update({1}, {{'#', 1, 1}})
+---
+- error: Attempt to modify a tuple field which is part of index 'a' in space 'ephemeral'
+...
+s:update({1}, {{'#', 1, "only one record please"}})
+---
+- error: 'Argument type in operation ''#'' on field 1 does not match field type: expected
+    a number of fields to delete'
+...
+i:drop()
+---
+...
+i = s:create_index('a')
+---
+...
+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]
+...
+i:drop()
+---
+...
+i = s:create_index('a')
+---
+...
+s:insert{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
+---
+- [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
+...
+s:update({0}, {{'#', 4, 'abirvalg'}})
+---
+- error: 'Argument type in operation ''#'' on field 4 does not match field type: expected
+    a number of fields to delete'
+...
+s:update({0}, {{'#', 2, 1}, {'#', 4, 2}, {'#', 6, 1}})
+---
+- [0, 2, 3, 6, 7, 9, 10, 11, 12, 13, 14, 15]
+...
+s:update({0}, {{'#', 4, 3}})
+---
+- [0, 2, 3, 10, 11, 12, 13, 14, 15]
+...
+s:update({0}, {{'#', 5, 123456}})
+---
+- [0, 2, 3, 10]
+...
+s:update({0}, {{'#', 3, 4294967295}})
+---
+- [0, 2]
+...
+s:update({0}, {{'#', 2, 0}})
+---
+- error: 'Field 2 UPDATE error: cannot delete 0 fields'
+...
+i:drop()
+---
+...
+i = s:create_index('a', { type = 'tree', parts = {1, 'string'} })
+---
+...
+s:insert{'1'}
+---
+- ['1']
+...
+s:insert{'5'}
+---
+- ['5']
+...
+s:insert{'6'}
+---
+- ['6']
+...
+s:insert{'11'}
+---
+- ['11']
+...
+t = {} for state, v in i:pairs({}, {iterator = 'ALL'}) do table.insert(t, v) end
+---
+...
+t
+---
+- - ['1']
+  - ['11']
+  - ['5']
+  - ['6']
+...
+t = {} for state, v in i:pairs({}, {iterator = 'GE'}) do table.insert(t, v) end
+---
+...
+t
+---
+- - ['1']
+  - ['11']
+  - ['5']
+  - ['6']
+...
+t = {} for state, v in i:pairs('5', {iterator = 'GE'}) do table.insert(t, v) end
+---
+...
+t
+---
+- - ['5']
+  - ['6']
+...
+t = {} for state, v in i:pairs('5', {iterator = 'GT'}) do table.insert(t, v) end
+---
+...
+t
+---
+- - ['6']
+...
+t = {} for state, v in i:pairs({}, {iterator = 'LE'}) do table.insert(t, v) end
+---
+...
+t
+---
+- - ['6']
+  - ['5']
+  - ['11']
+  - ['1']
+...
+t = {} for state, v in i:pairs('5', {iterator = 'LE'}) do table.insert(t, v) end
+---
+...
+t
+---
+- - ['5']
+  - ['11']
+  - ['1']
+...
+t = {} for state, v in i:pairs({}, {iterator = 'LT'}) do table.insert(t, v) end
+---
+...
+t
+---
+- - ['6']
+  - ['5']
+  - ['11']
+  - ['1']
+...
+t = {} for state, v in i:pairs('5', {iterator = 'LT'}) do table.insert(t, v) end
+---
+...
+t
+---
+- - ['11']
+  - ['1']
+...
+i:drop()
+---
+...
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} })
+---
+...
+s:insert{1}
+---
+- [1]
+...
+s:insert{5}
+---
+- [5]
+...
+s:insert{11}
+---
+- [11]
+...
+t = {} for state, v in i:pairs({}, {iterator = 'ALL'}) do table.insert(t, v) end
+---
+...
+t
+---
+- - [1]
+  - [5]
+  - [11]
+...
+t = {} for state, v in i:pairs({}, {iterator = 'GE'}) do table.insert(t, v) end
+---
+...
+t
+---
+- - [1]
+  - [5]
+  - [11]
+...
+t = {} for state, v in i:pairs(5, {iterator = 'GE'}) do table.insert(t, v) end
+---
+...
+t
+---
+- - [5]
+  - [11]
+...
+t = {} for state, v in i:pairs(5, {iterator = 'GT'}) do table.insert(t, v) end
+---
+...
+t
+---
+- - [11]
+...
+t = {} for state, v in i:pairs({}, {iterator = 'LE'}) do table.insert(t, v) end
+---
+...
+t
+---
+- - [11]
+  - [5]
+  - [1]
+...
+t = {} for state, v in i:pairs(5, {iterator = 'LE'}) do table.insert(t, v) end
+---
+...
+t
+---
+- - [5]
+  - [1]
+...
+t = {} for state, v in i:pairs({}, {iterator = 'LT'}) do table.insert(t, v) end
+---
+...
+t
+---
+- - [11]
+  - [5]
+  - [1]
+...
+t = {} for state, v in i:pairs(5, {iterator = 'LT'}) do table.insert(t, v) end
+---
+...
+t
+---
+- - [1]
+...
+i:drop()
+---
+...
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned', 2, 'unsigned'} })
+---
+...
+s:insert{1, 1}
+---
+- [1, 1]
+...
+s:insert{5, 5}
+---
+- [5, 5]
+...
+s:insert{11, 11}
+---
+- [11, 11]
+...
+t = {} for state, v in i:pairs({}, {iterator = 'ALL'}) do table.insert(t, v) end
+---
+...
+t
+---
+- - [1, 1]
+  - [5, 5]
+  - [11, 11]
+...
+t = {} for state, v in i:pairs({}, {iterator = 'GE'}) do table.insert(t, v) end
+---
+...
+t
+---
+- - [1, 1]
+  - [5, 5]
+  - [11, 11]
+...
+t = {} for state, v in i:pairs({5, 5}, {iterator = 'GE'}) do table.insert(t, v) end
+---
+...
+t
+---
+- - [5, 5]
+  - [11, 11]
+...
+t = {} for state, v in i:pairs({5, 5}, {iterator = 'GT'}) do table.insert(t, v) end
+---
+...
+t
+---
+- - [11, 11]
+...
+t = {} for state, v in i:pairs({}, {iterator = 'LE'}) do table.insert(t, v) end
+---
+...
+t
+---
+- - [11, 11]
+  - [5, 5]
+  - [1, 1]
+...
+t = {} for state, v in i:pairs({5, 5}, {iterator = 'LE'}) do table.insert(t, v) end
+---
+...
+t
+---
+- - [5, 5]
+  - [1, 1]
+...
+t = {} for state, v in i:pairs({}, {iterator = 'LT'}) do table.insert(t, v) end
+---
+...
+t
+---
+- - [11, 11]
+  - [5, 5]
+  - [1, 1]
+...
+t = {} for state, v in i:pairs({5, 5}, {iterator = 'LT'}) do table.insert(t, v) end
+---
+...
+t
+---
+- - [1, 1]
+...
+i:drop()
+---
+...
+i = s:create_index('a')
+---
+...
+s:auto_increment{1}
+---
+- [1, 1]
+...
+s:auto_increment{2}
+---
+- [2, 2]
+...
+s:auto_increment{3}
+---
+- [3, 3]
+...
+s:pairs(2, 'GE'):totable()
+---
+- - [2, 2]
+  - [3, 3]
+...
+i:count({2}, 'GT')
+---
+- 1
+...
+i:drop()
+---
+...
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} })
+---
+...
+s:replace{1}
+---
+- [1]
+...
+s:replace{2}
+---
+- [2]
+...
+s:delete{1}
+---
+- [1]
+...
+s:delete{2}
+---
+- [2]
+...
+s:select()
+---
+- []
+...
+i:drop()
+---
+...
+s:drop()
+---
+...
+test_run = require('test_run').new()
+---
+...
+utils = dofile('utils.lua')
+---
+...
+s = box.schema.space.create_ephemeral()
+---
+...
+idx = s:create_index('a')
+---
+...
+for i = 1, 13 do s:insert{ i, string.rep('x', i) } end
+---
+...
+s:len()
+---
+- 13
+...
+s:bsize()
+---
+- 130
+...
+utils.space_bsize(s)
+---
+- 130
+...
+for i = 1, 13, 2 do s:delete{ i } end
+---
+...
+s:len()
+---
+- 6
+...
+s:bsize()
+---
+- 60
+...
+utils.space_bsize(s)
+---
+- 60
+...
+for i = 2, 13, 2 do s:update( { i }, {{ ":", 2, i, 0, string.rep('y', i) }} ) end
+---
+...
+s:len()
+---
+- 6
+...
+s:bsize()
+---
+- 102
+...
+utils.space_bsize(s)
+---
+- 102
+...
+idx:drop()
+---
+...
+i = s:create_index('a', { type = 'tree', parts = {1, 'string'} })
+---
+...
+s:insert({'1', "AAAA"})
+---
+- ['1', 'AAAA']
+...
+s:insert({'2', "AAAA"})
+---
+- ['2', 'AAAA']
+...
+s:insert({'3', "AAAA"})
+---
+- ['3', 'AAAA']
+...
+s:insert({'4', "AAAA"})
+---
+- ['4', 'AAAA']
+...
+i:select()
+---
+- - ['1', 'AAAA']
+  - ['2', 'AAAA']
+  - ['3', 'AAAA']
+  - ['4', 'AAAA']
+...
+i:max('2')
+---
+- ['2', 'AAAA']
+...
+i:min('2')
+---
+- ['2', 'AAAA']
+...
+i:count('2')
+---
+- 1
+...
+i:max()
+---
+- ['4', 'AAAA']
+...
+i:min()
+---
+- ['1', 'AAAA']
+...
+i:count()
+---
+- 4
+...
+s:insert({'20', "AAAA"})
+---
+- ['20', 'AAAA']
+...
+s:insert({'30', "AAAA"})
+---
+- ['30', 'AAAA']
+...
+s:insert({'40', "AAAA"})
+---
+- ['40', 'AAAA']
+...
+s:select()
+---
+- - ['1', 'AAAA']
+  - ['2', 'AAAA']
+  - ['20', 'AAAA']
+  - ['3', 'AAAA']
+  - ['30', 'AAAA']
+  - ['4', 'AAAA']
+  - ['40', 'AAAA']
+...
+i:max('15')
+---
+...
+i:min('15')
+---
+...
+s:count('15')
+---
+- 0
+...
+i:max()
+---
+- ['40', 'AAAA']
+...
+i:min()
+---
+- ['1', 'AAAA']
+...
+s:count()
+---
+- 7
+...
+s:insert({'-2', "AAAA"})
+---
+- ['-2', 'AAAA']
+...
+s:insert({'-3', "AAAA"})
+---
+- ['-3', 'AAAA']
+...
+s:insert({'-4', "AAAA"})
+---
+- ['-4', 'AAAA']
+...
+i:select()
+---
+- - ['-2', 'AAAA']
+  - ['-3', 'AAAA']
+  - ['-4', 'AAAA']
+  - ['1', 'AAAA']
+  - ['2', 'AAAA']
+  - ['20', 'AAAA']
+  - ['3', 'AAAA']
+  - ['30', 'AAAA']
+  - ['4', 'AAAA']
+  - ['40', 'AAAA']
+...
+i:max('0')
+---
+...
+i:min('0')
+---
+...
+i:count('0')
+---
+- 0
+...
+i:max()
+---
+- ['40', 'AAAA']
+...
+i:min()
+---
+- ['-2', 'AAAA']
+...
+i:count()
+---
+- 10
+...
+s:drop()
+---
+...
+test_run:cmd("restart server default")
diff --git a/test/box/ephemeral_space.test.lua b/test/box/ephemeral_space.test.lua
index 93211c6..4c2bf22 100644
--- a/test/box/ephemeral_space.test.lua
+++ b/test/box/ephemeral_space.test.lua
@@ -89,3 +89,243 @@ i:drop()
 i = s:create_index('a', {id = 10})
 
 i = s:create_index('a', {type = 'bitset', parts = {1, 'unsigned', 2, 'unsigned'}})
+
+-- Ephemeral space: methods
+
+s = box.schema.space.create_ephemeral({field_count = 3})
+i = s:create_index('a')
+
+s:insert{1}
+s:insert{2,2,2}
+
+s:drop()
+
+s = box.schema.space.create_ephemeral()
+i = s:create_index('a', { type = 'tree', parts = {1, 'string'} })
+s:insert{'1'}
+s:get{'1'}
+s:insert{'1'}
+s:insert{1}
+i:drop()
+
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} })
+s:insert{1}
+s:get{1}
+s:insert{1}
+s:insert{'1'}
+i:drop()
+
+i = s:create_index('a', { type = 'tree', parts = {1, 'string'} })
+s:replace{'1'}
+s:get{'1'}
+s:replace{'1'}
+s:replace{1}
+i:drop()
+
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} })
+s:replace{1}
+s:get{1}
+s:replace{1}
+s:replace{'1'}
+i:drop()
+
+i = s:create_index('a', { type = 'tree', parts = {1, 'string'} })
+s:replace{'1'}
+s:get{'1'}
+s:replace{'1'}
+s:replace{1}
+i:drop()
+
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} })
+s:upsert({1, 0}, {{'+', 2, 1}})
+s:get{1}
+s:upsert({1, 0}, {{'+', 2, 1}})
+s:get{1}
+s:upsert({1, 0}, {{'+', 1, 1}})
+s:get{1}
+s:get{2}
+
+s:upsert({'1'}, {{'!', 2, 100}})
+s:upsert({1}, {{'a', 2, 100}})
+
+i:drop()
+
+i = s:create_index('a')
+s:insert{1, 2, 3, 4, 5}
+s:update({1}, {{'#', 1, 1}})
+s:update({1}, {{'#', 1, "only one record please"}})
+i:drop()
+
+i = s:create_index('a')
+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}})
+i:drop()
+
+i = s:create_index('a')
+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}, {{'#', 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}})
+i:drop()
+
+
+
+i = s:create_index('a', { type = 'tree', parts = {1, 'string'} })
+s:insert{'1'}
+s:insert{'5'}
+s:insert{'6'}
+s:insert{'11'}
+t = {} for state, v in i:pairs({}, {iterator = 'ALL'}) do table.insert(t, v) end
+t
+t = {} for state, v in i:pairs({}, {iterator = 'GE'}) do table.insert(t, v) end
+t
+t = {} for state, v in i:pairs('5', {iterator = 'GE'}) do table.insert(t, v) end
+t
+t = {} for state, v in i:pairs('5', {iterator = 'GT'}) do table.insert(t, v) end
+t
+t = {} for state, v in i:pairs({}, {iterator = 'LE'}) do table.insert(t, v) end
+t
+t = {} for state, v in i:pairs('5', {iterator = 'LE'}) do table.insert(t, v) end
+t
+t = {} for state, v in i:pairs({}, {iterator = 'LT'}) do table.insert(t, v) end
+t
+t = {} for state, v in i:pairs('5', {iterator = 'LT'}) do table.insert(t, v) end
+t
+i:drop()
+
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} })
+s:insert{1}
+s:insert{5}
+s:insert{11}
+t = {} for state, v in i:pairs({}, {iterator = 'ALL'}) do table.insert(t, v) end
+t
+t = {} for state, v in i:pairs({}, {iterator = 'GE'}) do table.insert(t, v) end
+t
+t = {} for state, v in i:pairs(5, {iterator = 'GE'}) do table.insert(t, v) end
+t
+t = {} for state, v in i:pairs(5, {iterator = 'GT'}) do table.insert(t, v) end
+t
+t = {} for state, v in i:pairs({}, {iterator = 'LE'}) do table.insert(t, v) end
+t
+t = {} for state, v in i:pairs(5, {iterator = 'LE'}) do table.insert(t, v) end
+t
+t = {} for state, v in i:pairs({}, {iterator = 'LT'}) do table.insert(t, v) end
+t
+t = {} for state, v in i:pairs(5, {iterator = 'LT'}) do table.insert(t, v) end
+t
+i:drop()
+
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned', 2, 'unsigned'} })
+s:insert{1, 1}
+s:insert{5, 5}
+s:insert{11, 11}
+t = {} for state, v in i:pairs({}, {iterator = 'ALL'}) do table.insert(t, v) end
+t
+t = {} for state, v in i:pairs({}, {iterator = 'GE'}) do table.insert(t, v) end
+t
+t = {} for state, v in i:pairs({5, 5}, {iterator = 'GE'}) do table.insert(t, v) end
+t
+t = {} for state, v in i:pairs({5, 5}, {iterator = 'GT'}) do table.insert(t, v) end
+t
+t = {} for state, v in i:pairs({}, {iterator = 'LE'}) do table.insert(t, v) end
+t
+t = {} for state, v in i:pairs({5, 5}, {iterator = 'LE'}) do table.insert(t, v) end
+t
+t = {} for state, v in i:pairs({}, {iterator = 'LT'}) do table.insert(t, v) end
+t
+t = {} for state, v in i:pairs({5, 5}, {iterator = 'LT'}) do table.insert(t, v) end
+t
+i:drop()
+
+i = s:create_index('a')
+s:auto_increment{1}
+s:auto_increment{2}
+s:auto_increment{3}
+s:pairs(2, 'GE'):totable()
+i:count({2}, 'GT')
+i:drop()
+
+i = s:create_index('a', { type = 'tree', parts = {1, 'unsigned'} })
+s:replace{1}
+s:replace{2}
+s:delete{1}
+s:delete{2}
+s:select()
+i:drop()
+s:drop()
+
+test_run = require('test_run').new()
+utils = dofile('utils.lua')
+
+s = box.schema.space.create_ephemeral()
+idx = s:create_index('a')
+for i = 1, 13 do s:insert{ i, string.rep('x', i) } end
+s:len()
+s:bsize()
+utils.space_bsize(s)
+
+for i = 1, 13, 2 do s:delete{ i } end
+s:len()
+s:bsize()
+utils.space_bsize(s)
+
+for i = 2, 13, 2 do s:update( { i }, {{ ":", 2, i, 0, string.rep('y', i) }} ) end
+s:len()
+s:bsize()
+utils.space_bsize(s)
+idx:drop()
+
+i = s:create_index('a', { type = 'tree', parts = {1, 'string'} })
+s:insert({'1', "AAAA"})
+s:insert({'2', "AAAA"})
+s:insert({'3', "AAAA"})
+s:insert({'4', "AAAA"})
+
+i:select()
+i:max('2')
+i:min('2')
+i:count('2')
+i:max()
+i:min()
+i:count()
+
+s:insert({'20', "AAAA"})
+s:insert({'30', "AAAA"})
+s:insert({'40', "AAAA"})
+
+s:select()
+i:max('15')
+i:min('15')
+s:count('15')
+i:max()
+i:min()
+s:count()
+
+s:insert({'-2', "AAAA"})
+s:insert({'-3', "AAAA"})
+s:insert({'-4', "AAAA"})
+
+i:select()
+i:max('0')
+i:min('0')
+i:count('0')
+i:max()
+i:min()
+i:count()
+
+s:drop()
+
+test_run:cmd("restart server default")
diff --git a/test/engine/iterator.result b/test/engine/iterator.result
index f39a15a..9e39a32 100644
--- a/test/engine/iterator.result
+++ b/test/engine/iterator.result
@@ -4213,7 +4213,7 @@ s:replace{35}
 ...
 state, value = gen(param,state)
 ---
-- error: 'builtin/box/schema.lua:1180: usage: next(param, state)'
+- error: 'builtin/box/schema.lua:1182: usage: next(param, state)'
 ...
 value
 ---
-- 
2.7.4

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2018-07-24 11:58 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-07-24 11:58 [tarantool-patches] [PATCH v3 0/7] Expose ephemeral spaces into Lua imeevma
2018-07-24 11:58 ` [tarantool-patches] [PATCH v3 1/7] box: add space address to index_replace imeevma
2018-07-24 11:58 ` [tarantool-patches] [PATCH v3 2/7] box: move checks for key findability from space_vtab imeevma
2018-07-24 11:58 ` [tarantool-patches] [PATCH v3 3/7] box: create new methods for ephemeral spaces imeevma
2018-07-24 11:58 ` [tarantool-patches] [PATCH v3 4/7] box: move some decode functions from alter.cc imeevma
2018-07-24 11:58 ` [tarantool-patches] [PATCH v3 5/7] box: ephemeral space creation and deletion in Lua imeevma
2018-07-24 11:58 ` [tarantool-patches] [PATCH v3 6/7] box: primary index for ephemeral spaces imeevma
2018-07-24 11:58 ` [tarantool-patches] [PATCH v3 7/7] box: methods for ephemeral space and its index imeevma

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox