Tarantool development patches archive
 help / color / mirror / Atom feed
From: imeevma@tarantool.org
To: tarantool-patches@freelists.org
Subject: [tarantool-patches] [PATCH 3/5] Ephemeral space creation and deletion in Lua
Date: Thu, 12 Jul 2018 14:16:13 +0300	[thread overview]
Message-ID: <877bbdb510d00753bc61ebbd17bb501a5948292f.1531393821.git.imeevma@gmail.com> (raw)
In-Reply-To: <cover.1531393821.git.imeevma@gmail.com>

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

Part of #3375.
---
 src/box/lua/schema.lua            |  69 ++++++++++
 src/box/lua/space.cc              | 183 +++++++++++++++++++++++++
 test/box/ephemeral_space.result   | 271 ++++++++++++++++++++++++++++++++++++++
 test/box/ephemeral_space.test.lua |  96 ++++++++++++++
 test/engine/iterator.result       |   2 +-
 5 files changed, 620 insertions(+), 1 deletion(-)
 create mode 100644 test/box/ephemeral_space.result
 create mode 100644 test/box/ephemeral_space.test.lua

diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua
index e4c1b1d..81f0fa0 100644
--- a/src/box/lua/schema.lua
+++ b/src/box/lua/schema.lua
@@ -477,6 +477,64 @@ 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.schema.space_ephemeral_methods
+box.schema.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 packed_format = msgpack.encode(format)
+
+    local ephemeral_space = {}
+    ephemeral_space.space = space_new_ephemeral(options.engine,
+                                                options.field_count,
+                                                packed_format)
+    ephemeral_space.space_format = format
+    ephemeral_space.engine = options.engine
+    ephemeral_space.field_count = options.field_count
+    ephemeral_space.temporary = true
+    ephemeral_space.index = {}
+    -- Set GC for result
+    setmetatable(ephemeral_space, space_ephemeral_mt)
+    ephemeral_space.proxy = newproxy(true)
+    getmetatable(ephemeral_space.proxy).__gc = function(self)
+        box.schema.space.drop_ephemeral(ephemeral_space)
+    end
+    -- Return result
+    return ephemeral_space
+end
+
+box.schema.space.drop_ephemeral = function(ephemeral_space)
+    if ephemeral_space == nil or ephemeral_space.space == nil then
+        return
+    end
+    check_param(ephemeral_space.space, 'space', 'cdata')
+    space_delete_ephemeral(ephemeral_space)
+    for k,_ in pairs(ephemeral_space) do
+        ephemeral_space[k] = nil
+    end
+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
@@ -1505,6 +1563,17 @@ end
 space_mt.frommap = box.internal.space.frommap
 space_mt.__index = space_mt
 
+-- Metatable for ephemeral space
+space_ephemeral_mt.format = function(ephemeral_space)
+    return ephemeral_space.space_format
+end
+space_ephemeral_mt.run_triggers = function(ephemeral_space, yesno)
+    builtin.space_run_triggers(ephemeral_space.space, yesno)
+end
+space_ephemeral_mt.frommap = space_ephemeral_methods.frommap
+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..955c465 100644
--- a/src/box/lua/space.cc
+++ b/src/box/lua/space.cc
@@ -33,6 +33,7 @@
 #include "box/sql/sqliteLimit.h"
 #include "lua/utils.h"
 #include "lua/trigger.h"
+#include "box/box.h"
 
 extern "C" {
 	#include <lua.h>
@@ -50,6 +51,8 @@ extern "C" {
 #include "box/coll_id_cache.h"
 #include "box/replication.h" /* GROUP_LOCAL */
 
+static uint32_t CTID_STRUCT_SPACE_POINTER = 0;
+
 /**
  * Trigger function for all spaces
  */
@@ -508,6 +511,169 @@ usage_error:
 	return luaL_error(L, "Usage: space:frommap(map, opts)");
 }
 
+/**
+ * Check if given argument have an appropriate type.
+ * @param Lua struct space *space - ephemeral space.
+ * @retval struct space *space - success.
+ * @retval NULL - error.
+ */
+static inline struct space *
+luaT_isspace(struct lua_State *L, int narg)
+{
+	uint32_t ctypeid = 0;
+	void *data;
+
+	if (lua_type(L, narg) != LUA_TCDATA)
+		return NULL;
+
+	data = luaL_checkcdata(L, narg, &ctypeid);
+	if (ctypeid != CTID_STRUCT_SPACE_POINTER)
+		return NULL;
+
+	return *(struct space **) data;
+}
+
+/**
+ * Puts space field of given argument in stack.
+ * onto Lua state stack and calls luaT_isspace
+ * @param Lua table with field space.
+ * @retval struct space *space - success.
+ * @retval NULL - error.
+ */
+static inline struct space *
+lua_checkephemeralspace(struct lua_State *L, int narg)
+{
+	lua_getfield(L, 1, "space");
+	struct space *space = luaT_isspace(L, -1);
+	lua_pop(L, 1);
+	if (space == NULL) {
+		luaL_error(L, "Invalid argument #%d (ephemeral space expected,"\
+			   "got %s)", narg, lua_typename(L, lua_type(L, narg)));
+	}
+	return space;
+}
+
+/**
+ * Create an ephemeral space.
+ * @param Lua const char *engine_name - name of engine.
+ * @param Lua uint32_t field_count - number of fields.
+ * @param Lua const char *format - format in msgpack.
+ * @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)
+{
+	uint32_t argc = lua_gettop(L);
+	if (argc != 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);
+	const char *format = luaL_checkstring (L, 3);
+	struct region *region = &fiber()->gc;
+	uint32_t field_count;
+	struct field_def *fields = space_format_decode(format, &field_count,
+		"ephemeral", strlen("ephemeral"), ER_CREATE_SPACE, region);
+	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 *ephemeral_space_def =
+		space_def_new(0, 0, exact_field_count, "ephemeral",
+			      strlen("ephemeral"), engine_name,
+			      strlen(engine_name), &space_opts_default, fields,
+			      field_count);
+	if (ephemeral_space_def == NULL)
+		return luaL_error(L, "Error with creating space_def");
+	struct rlist key_list;
+	rlist_create(&key_list);
+	struct space *space = space_new_ephemeral(ephemeral_space_def,
+						  &key_list);
+	space_def_delete(ephemeral_space_def);
+	if (space == NULL)
+		return luaL_error(L, "Error with creating space");
+	struct space **ptr =
+		(struct space **) luaL_pushcdata(L, CTID_STRUCT_SPACE_POINTER);
+	*ptr = space;
+	return 1;
+}
+
+/**
+ * Delete an ephemeral space.
+ * @param Lua table with field space - ephemeral space.
+ */
+static int
+lbox_space_delete_ephemeral(struct lua_State *L)
+{
+	uint32_t argc = lua_gettop(L);
+	if (argc != 1 || !lua_istable(L, 1))
+		return luaL_error(L, "Usage: ephemeral_space:drop()");
+	struct space *space = (struct 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).
+ * @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)
+{
+	struct tuple_dictionary *dict = NULL;
+	int argc = lua_gettop(L);
+	bool table = false;
+	if (argc < 2 || argc > 3 || !lua_istable(L, 1) || !lua_istable(L, 2))
+		return luaL_error(L, "Usage: ephemeral_space:"\
+				     "frommap(map, opts)");
+	if (argc == 3) {
+		if (!lua_istable(L, 3))
+			return luaL_error(L, "Usage: ephemeral_space:"\
+					     "frommap(map, opts)");
+		lua_getfield(L, 3, "table");
+		if (!lua_isboolean(L, -1) && !lua_isnil(L, -1))
+			return luaL_error(L, "Usage: ephemeral_space:"\
+					     "frommap(map, opts)");
+		table = lua_toboolean(L, -1);
+	}
+
+	struct space *space = (struct space *)lua_checkephemeralspace(L, 1);
+	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);
+}
+
 void
 box_lua_space_init(struct lua_State *L)
 {
@@ -515,6 +681,12 @@ box_lua_space_init(struct lua_State *L)
 	on_alter_space_in_lua.data = L;
 	trigger_add(&on_alter_space, &on_alter_space_in_lua);
 
+	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);
+
 	lua_getfield(L, LUA_GLOBALSINDEX, "box");
 	lua_newtable(L);
 	lua_setfield(L, -2, "schema");
@@ -595,12 +767,23 @@ 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_newtable(L);
+	lua_setfield(L, -2, "space_ephemeral_methods");
 	lua_pop(L, 2); /* box, schema */
 
 	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.schema.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..135df3c
--- /dev/null
+++ b/test/box/ephemeral_space.result
@@ -0,0 +1,271 @@
+-- Ephemeral space: creation and dropping
+-- Simple creation
+s = box.schema.space.create_ephemeral()
+---
+...
+s.index
+---
+- []
+...
+s.temporary
+---
+- true
+...
+s.field_count
+---
+- 0
+...
+s.engine
+---
+- memtx
+...
+s:drop()
+---
+...
+-- Creation with defined engine (only memtx for now)
+s = box.schema.space.create_ephemeral({engine = 'memtx'})
+---
+...
+s.index
+---
+- []
+...
+s.temporary
+---
+- true
+...
+s.field_count
+---
+- 0
+...
+s.engine
+---
+- memtx
+...
+s:drop()
+---
+...
+s = box.schema.space.create_ephemeral({engine = 'other'})
+---
+- error: 'builtin/box/schema.lua:507: Error with creating space'
+...
+-- Creation with defined field_count
+s = box.schema.space.create_ephemeral({field_count = 10})
+---
+...
+s.index
+---
+- []
+...
+s.temporary
+---
+- true
+...
+s.field_count
+---
+- 10
+...
+s.engine
+---
+- memtx
+...
+s:drop()
+---
+...
+s = box.schema.space.create_ephemeral({field_count = 'asd'})
+---
+- error: Illegal parameters, options parameter 'field_count' should be of type number
+...
+-- Creation with defined format
+format = {{name='field1', type='unsigned'}, {name='field2', type='string'}}
+---
+...
+s = box.schema.space.create_ephemeral({format = format})
+---
+...
+s.index
+---
+- []
+...
+s.temporary
+---
+- true
+...
+s.field_count
+---
+- 0
+...
+s.engine
+---
+- memtx
+...
+s:drop()
+---
+...
+s = box.schema.space.create_ephemeral({format = 'a'})
+---
+- error: Illegal parameters, options parameter 'format' should be of type table
+...
+-- All options defined
+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.temporary
+---
+- true
+...
+s.field_count
+---
+- 7
+...
+s.engine
+---
+- memtx
+...
+s:drop()
+---
+...
+-- 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()
+---
+...
+s:drop()
+---
+...
+s:drop()
+---
+...
+s:drop()
+---
+...
+-- Drop using function from box.schema
+s = box.schema.space.create_ephemeral()
+---
+...
+s.index
+---
+- []
+...
+s.temporary
+---
+- true
+...
+s.field_count
+---
+- 0
+...
+s.engine
+---
+- memtx
+...
+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: 'Usage: ephemeral_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..3cc936f
--- /dev/null
+++ b/test/box/ephemeral_space.test.lua
@@ -0,0 +1,96 @@
+-- Ephemeral space: creation and dropping
+
+-- Simple creation
+s = box.schema.space.create_ephemeral()
+s.index
+s.temporary
+s.field_count
+s.engine
+s:drop()
+
+-- Creation with defined engine (only memtx for now)
+s = box.schema.space.create_ephemeral({engine = 'memtx'})
+s.index
+s.temporary
+s.field_count
+s.engine
+s:drop()
+
+s = box.schema.space.create_ephemeral({engine = 'other'})
+
+-- Creation with defined field_count
+s = box.schema.space.create_ephemeral({field_count = 10})
+s.index
+s.temporary
+s.field_count
+s.engine
+s:drop()
+
+s = box.schema.space.create_ephemeral({field_count = 'asd'})
+
+-- Creation with defined format
+format = {{name='field1', type='unsigned'}, {name='field2', type='string'}}
+s = box.schema.space.create_ephemeral({format = format})
+s.index
+s.temporary
+s.field_count
+s.engine
+s:drop()
+
+s = box.schema.space.create_ephemeral({format = 'a'})
+
+-- All options defined
+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.temporary
+s.field_count
+s.engine
+s:drop()
+
+-- 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()
+s:drop()
+s:drop()
+s:drop()
+
+-- Drop using function from box.schema
+s = box.schema.space.create_ephemeral()
+s.index
+s.temporary
+s.field_count
+s.engine
+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..f9da661 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:1109: usage: next(param, state)'
 ...
 value
 ---
-- 
2.7.4

  parent reply	other threads:[~2018-07-12 11:16 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-07-12 11:16 [tarantool-patches] [PATCH 0/5] Expose ephemeral spaces into Lua imeevma
2018-07-12 11:16 ` [tarantool-patches] [PATCH 1/5] Create new methods for ephemeral spaces imeevma
2018-07-13 16:32   ` [tarantool-patches] " Vladislav Shpilevoy
2018-07-12 11:16 ` [tarantool-patches] [PATCH 2/5] Move some decode functions from alter.cc imeevma
2018-07-13 16:32   ` [tarantool-patches] " Vladislav Shpilevoy
2018-07-12 11:16 ` imeevma [this message]
2018-07-13 16:32   ` [tarantool-patches] Re: [PATCH 3/5] Ephemeral space creation and deletion in Lua Vladislav Shpilevoy
2018-07-12 11:16 ` [tarantool-patches] [PATCH 4/5] Primary index for ephemeral spaces imeevma
2018-07-13 16:32   ` [tarantool-patches] " Vladislav Shpilevoy
2018-07-12 11:16 ` [tarantool-patches] [PATCH 5/5] Methods for ephemeral space and its index imeevma
2018-07-13 16:32   ` [tarantool-patches] " Vladislav Shpilevoy
2018-07-12 11:30 ` [tarantool-patches] Re: [PATCH 0/5] Expose ephemeral spaces into Lua Imeev Mergen

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=877bbdb510d00753bc61ebbd17bb501a5948292f.1531393821.git.imeevma@gmail.com \
    --to=imeevma@tarantool.org \
    --cc=tarantool-patches@freelists.org \
    --subject='Re: [tarantool-patches] [PATCH 3/5] Ephemeral space creation and deletion in Lua' \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

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