[tarantool-patches] [PATCH 4/5] Primary index for ephemeral spaces

imeevma at tarantool.org imeevma at tarantool.org
Thu Jul 12 14:16:15 MSK 2018


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

Part of #3375.
---
 src/box/index_def.c               |  42 +++++++
 src/box/index_def.h               |  18 +++
 src/box/lua/schema.lua            | 130 ++++++++++++++++++++++
 src/box/lua/space.cc              |  92 ++++++++++++++++
 test/box/ephemeral_space.result   | 225 ++++++++++++++++++++++++++++++++++++--
 test/box/ephemeral_space.test.lua |  88 +++++++++++++--
 test/engine/iterator.result       |   2 +-
 7 files changed, 582 insertions(+), 15 deletions(-)

diff --git a/src/box/index_def.c b/src/box/index_def.c
index e0d54c0..9622dce 100644
--- a/src/box/index_def.c
+++ b/src/box/index_def.c
@@ -31,6 +31,8 @@
 #include "index_def.h"
 #include "schema_def.h"
 #include "identifier.h"
+#include "fiber.h"
+#include "space.h"
 
 const char *index_type_strs[] = { "HASH", "TREE", "BITSET", "RTREE" };
 
@@ -369,3 +371,43 @@ index_opts_decode(struct index_opts *opts, const char *map,
 	}
 	return 0;
 }
+
+struct index_def *
+ephemeral_index_def(struct space *space, uint32_t index_id,
+		    const char *name, const char *type_field,
+		    const char *opts_field, const char *parts)
+{
+	const struct field_def *fields = space->def->fields;
+	uint32_t field_count = space->def->field_count;
+	struct index_opts opts;
+	struct key_def *key_def = NULL;
+	uint32_t part_count = mp_decode_array(&parts);
+	enum index_type type = STR2ENUM(index_type, type_field);
+	if(index_opts_decode(&opts, opts_field, &fiber()->gc) != 0)
+		return NULL;
+	if(identifier_check(name, strlen(name)) != 0)
+		return NULL;
+	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(0, index_id, name, strlen(name),
+			      type, &opts, key_def, NULL);
+	key_def_delete(key_def);
+	if (index_def == NULL)
+		return NULL;
+	return index_def;
+}
diff --git a/src/box/index_def.h b/src/box/index_def.h
index 4c3625b..9725a8e 100644
--- a/src/box/index_def.h
+++ b/src/box/index_def.h
@@ -39,6 +39,8 @@
 extern "C" {
 #endif /* defined(__cplusplus) */
 
+struct space;
+
 enum index_type {
 	HASH = 0, /* HASH Index */
 	TREE,     /* TREE Index */
@@ -366,6 +368,22 @@ int
 index_opts_decode(struct index_opts *opts, const char *map,
 		  struct region *region);
 
+/**
+ * Create index definition for primary index
+ * of ephemeral space in Lua
+ *
+ * @param space - ephemeral space.
+ * @param index_id - index id.
+ * @param name - name of index.
+ * @param type_field - type of field as string.
+ * @param opts_field - options in msgpack format
+ * @param parts - parts in msgpack format
+ */
+struct index_def *
+ephemeral_index_def(struct space *space, uint32_t index_id,
+		    const char *name, const char *type_field,
+		    const char *opts_field, const char *parts);
+
 #if defined(__cplusplus)
 } /* extern "C" */
 
diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua
index 81f0fa0..0fe109a 100644
--- a/src/box/lua/schema.lua
+++ b/src/box/lua/schema.lua
@@ -527,6 +527,9 @@ box.schema.space.drop_ephemeral = function(ephemeral_space)
         return
     end
     check_param(ephemeral_space.space, 'space', 'cdata')
+    if ephemeral_space.index ~= nil and ephemeral_space.index[0] ~= nil then
+        ephemeral_space.index[0]:drop()
+    end
     space_delete_ephemeral(ephemeral_space)
     for k,_ in pairs(ephemeral_space) do
         ephemeral_space[k] = nil
@@ -1084,6 +1087,128 @@ 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_methods = box.schema.index_ephemeral_methods
+box.schema.index_ephemeral_mt = nil
+local index_ephemeral_mt = {}
+
+local index_ephemeral_create = function(ephemeral_space, name, options)
+    if type(ephemeral_space) ~= 'table' then
+        error("Usage: ephemeral_space:create_index(name, opts)")
+    end
+    check_param(name, 'name', 'string')
+    check_param_table(options, create_index_template)
+    local space = ephemeral_space
+    local format = space:format()
+
+    local options_defaults = {
+        type = 'tree',
+    }
+    options = update_param_table(options, options_defaults)
+    local type_dependent_defaults = {
+        rtree = {parts = { 2, 'array' }, unique = false},
+        bitset = {parts = { 2, 'unsigned' }, unique = false},
+        other = {parts = { 1, 'unsigned' }, unique = true},
+    }
+    options_defaults = type_dependent_defaults[options.type]
+            or type_dependent_defaults.other
+    if not options.parts then
+        local fieldno = options_defaults.parts[1]
+        if #format >= fieldno then
+            local t = format[fieldno].type
+            if t ~= 'any' then
+                options.parts = {{fieldno, format[fieldno].type}}
+            end
+        end
+    end
+    options = update_param_table(options, options_defaults)
+    if space.engine == 'vinyl' then
+        options_defaults = {
+            page_size = box.cfg.vinyl_page_size,
+            range_size = box.cfg.vinyl_range_size,
+            run_count_per_level = box.cfg.vinyl_run_count_per_level,
+            run_size_ratio = box.cfg.vinyl_run_size_ratio,
+            bloom_fpr = box.cfg.vinyl_bloom_fpr
+        }
+    else
+        options_defaults = {}
+    end
+    options = update_param_table(options, options_defaults)
+
+    if ephemeral_space.index ~= nil and ephemeral_space.index[0] then
+        if options.if_not_exists then
+            return space.index[0], "not created"
+        else
+            error("Primary index for this ephemeral index already exists")
+        end
+    end
+    local iid = 0
+    if options.id and options.id ~= 0 then
+        error("Ephemeral space can have only primary index.")
+    end
+    local parts, parts_can_be_simplified =
+        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
+    if parts_can_be_simplified then
+        parts = simplify_index_parts(parts)
+    end
+    space.space = index_new_ephemeral(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
+    space.index[iid].ephemeral_space = space
+    space.index[name] = space.index[iid]
+    setmetatable(space.index[iid], index_ephemeral_mt)
+    return space.index[name]
+end
+
+local index_ephemeral_drop = function(ephemeral_index)
+    if type(ephemeral_index) ~= 'table' then
+        error("Usage: ephemeral_space.index[0]:drop()")
+    end
+    ephemeral_index.ephemeral_space.space = index_delete_ephemeral(ephemeral_index)
+    ephemeral_index.ephemeral_space.index = {}
+    for k,_ in pairs(ephemeral_index) do
+        ephemeral_index[k] = nil
+    end
+end
+
 local iterator_gen = function(param, state)
     --[[
         index:pairs() mostly conforms to the Lua for-in loop conventions and
@@ -1563,6 +1688,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 = index_ephemeral_drop
+index_ephemeral_mt.__index = index_ephemeral_mt
+
 -- Metatable for ephemeral space
 space_ephemeral_mt.format = function(ephemeral_space)
     return ephemeral_space.space_format
@@ -1571,6 +1700,7 @@ 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.create_index = index_ephemeral_create
 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 955c465..4374db9 100644
--- a/src/box/lua/space.cc
+++ b/src/box/lua/space.cc
@@ -34,6 +34,7 @@
 #include "lua/utils.h"
 #include "lua/trigger.h"
 #include "box/box.h"
+#include "box/lua/misc.h" /* lbox_encode_tuple_on_gc() */
 
 extern "C" {
 	#include <lua.h>
@@ -616,6 +617,93 @@ lbox_space_delete_ephemeral(struct lua_State *L)
 }
 
 /**
+ * Create an index for ephemeral space.
+ * @param Lua table - ephemeral space.
+ * @param Lua uint32_t id - id of index, for
+ * ephemeral spaces it is 0 as they can have only
+ * primary index.
+ * @param Lua const char *name - name of index.
+ * @param Lua const char *type_field - type of field.
+ * @param Lua const char *opts_field - options in
+ * msgpack format.
+ * @param Lua const char *parts - parts in msgpack
+ * format.
+ * @retval not nil - ephemeral space created.
+ * @retval nil - error, A reason is returned in
+ *         the second value.
+ */
+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 = (struct 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);
+	assert(index_id == 0);
+	struct space_def *ephemeral_space_def = space_def_dup(space->def);
+	if (ephemeral_space_def == NULL)
+		return luaL_error(L, "Error with creating space_def");
+	struct index_def *index_def_ephemeral =
+		ephemeral_index_def(space, index_id, name, type_field,
+				    opts_field, parts);
+	if(index_def_ephemeral == NULL)
+		return luaT_error(L);
+	if(!index_def_is_valid(index_def_ephemeral, space_name(space)) ||
+	   space_check_index_def(space, index_def_ephemeral) != 0) {
+		index_def_delete(index_def_ephemeral);
+		return luaT_error(L);
+	}
+	struct rlist key_list;
+	rlist_create(&key_list);
+	rlist_add_entry(&key_list, index_def_ephemeral, link);
+	struct space *new_space = space_new_ephemeral(ephemeral_space_def,
+						      &key_list);
+	index_def_delete(index_def_ephemeral);
+	space_def_delete(ephemeral_space_def);
+	if(new_space == NULL) {
+		return luaL_error(L, "Error with creating index for "\
+				     "ephemeral space");
+	}
+	space_delete(space);
+	struct space **ptr =
+		(struct space **) luaL_pushcdata(L, CTID_STRUCT_SPACE_POINTER);
+	*ptr = new_space;
+	return 1;
+}
+
+/**
+ * Drop primary index of ephemeral space.
+ * @param Lua ephemeral space.
+ */
+static int
+lbox_index_drop_ephemeral(struct lua_State *L)
+{
+	struct space *space = (struct space *)lua_checkephemeralspace(L, 1);
+	struct space_def *ephemeral_space_def = space_def_dup(space->def);
+	if (ephemeral_space_def == NULL)
+		return luaL_error(L, "Error with creating space_def");
+	struct rlist key_list;
+	rlist_create(&key_list);
+	struct space *new_space = space_new_ephemeral(ephemeral_space_def,
+						      &key_list);
+	space_def_delete(ephemeral_space_def);
+	if(new_space == NULL) {
+		return luaL_error(L, "Error with creating index for "\
+				     "ephemeral space");
+	}
+	space_delete(space);
+	struct space **ptr =
+		(struct space **) luaL_pushcdata(L, CTID_STRUCT_SPACE_POINTER);
+	*ptr = new_space;
+	return 1;
+}
+
+/**
  * Make a tuple or a table Lua object by map.
  * @param Lua space object.
  * @param Lua map table object.
@@ -769,12 +857,16 @@ box_lua_space_init(struct lua_State *L)
 	lua_setfield(L, -2, "SQL_BIND_PARAMETER_MAX");
 	lua_newtable(L);
 	lua_setfield(L, -2, "space_ephemeral_methods");
+	lua_newtable(L);
+	lua_setfield(L, -2, "index_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},
+		{"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 135df3c..d958ffe 100644
--- a/test/box/ephemeral_space.result
+++ b/test/box/ephemeral_space.result
@@ -1,5 +1,5 @@
--- Ephemeral space: creation and dropping
--- Simple creation
+-- Ephemeral space: creation and dropping.
+-- Simple creation.
 s = box.schema.space.create_ephemeral()
 ---
 ...
@@ -22,7 +22,7 @@ s.engine
 s:drop()
 ---
 ...
--- Creation with defined engine (only memtx for now)
+-- Creation with defined engine (only memtx for now).
 s = box.schema.space.create_ephemeral({engine = 'memtx'})
 ---
 ...
@@ -49,7 +49,7 @@ s = box.schema.space.create_ephemeral({engine = 'other'})
 ---
 - error: 'builtin/box/schema.lua:507: Error with creating space'
 ...
--- Creation with defined field_count
+-- Creation with defined field_count.
 s = box.schema.space.create_ephemeral({field_count = 10})
 ---
 ...
@@ -76,7 +76,7 @@ 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
+-- Creation with defined format.
 format = {{name='field1', type='unsigned'}, {name='field2', type='string'}}
 ---
 ...
@@ -106,7 +106,7 @@ s = box.schema.space.create_ephemeral({format = 'a'})
 ---
 - error: Illegal parameters, options parameter 'format' should be of type table
 ...
--- All options defined
+-- All options defined.
 format = {{name='field1', type='unsigned'}, {name='field2', type='string'}}
 ---
 ...
@@ -185,7 +185,218 @@ s
 ---
 - []
 ...
--- Ephemeral space: methods
+-- Ephemeral space: index creation and dropping.
+s = box.schema.space.create_ephemeral()
+---
+...
+-- Simple creation
+i = s:create_index('a')
+---
+...
+i.unique
+---
+- true
+...
+i.parts
+---
+- - - 0
+    - unsigned
+...
+i.id
+---
+- 0
+...
+i.name
+---
+- a
+...
+i:drop()
+---
+...
+-- Double creation of index for ephemeral space.
+i = s:create_index('a')
+---
+...
+i = s:create_index('a')
+---
+- error: 'builtin/box/schema.lua:1145: Primary index for this ephemeral index already
+    exists'
+...
+i:drop()
+---
+...
+-- Double creation of index for ephemeral space
+-- but with if_not_exists=true.
+i = s:create_index('a')
+---
+...
+i = s:create_index('a', {if_not_exists=true})
+---
+...
+i:drop()
+---
+...
+-- Ephemeral space can have only primary.
+-- index so its id == 0
+i = s:create_index('a', {id = 10})
+---
+- error: 'builtin/box/schema.lua:1150: Ephemeral space can have only primary index.'
+...
+-- Set parameters: parts.
+i = s:create_index('a', {parts={1}})
+---
+...
+i.unique
+---
+- true
+...
+i.parts
+---
+- - - 0
+    - scalar
+...
+i.id
+---
+- 0
+...
+i.name
+---
+- a
+...
+i:drop()
+---
+...
+i = s:create_index('a', {parts={1,"integer"}})
+---
+...
+i.unique
+---
+- true
+...
+i.parts
+---
+- - - 0
+    - integer
+...
+i.id
+---
+- 0
+...
+i.name
+---
+- a
+...
+i:drop()
+---
+...
+i = s:create_index('a', {parts={1,'string',collation='Unicode'}})
+---
+...
+i.unique
+---
+- true
+...
+i.parts
+---
+- - - 0
+    - string
+...
+i.id
+---
+- 0
+...
+i.name
+---
+- a
+...
+i:drop()
+---
+...
+i = s:create_index('a', {parts={1,'uint',2,'int',3,'str'}})
+---
+...
+i.unique
+---
+- true
+...
+i.parts
+---
+- - - 0
+    - unsigned
+  - - 1
+    - integer
+  - - 2
+    - string
+...
+i.id
+---
+- 0
+...
+i.name
+---
+- a
+...
+i:drop()
+---
+...
+i = s:create_index('a', {parts={{5,'string',collation='Unicode'}}})
+---
+...
+i.unique
+---
+- true
+...
+i.parts
+---
+- - field: 4
+    collation: 1
+    type: string
+...
+i.id
+---
+- 0
+...
+i.name
+---
+- a
+...
+i:drop()
+---
+...
+i = s:create_index('a', {parts={3,"scalar"}})
+---
+...
+i.unique
+---
+- true
+...
+i.parts
+---
+- - - 2
+    - scalar
+...
+i.id
+---
+- 0
+...
+i.name
+---
+- a
+...
+i:drop()
+---
+...
+i = s:create_index('a', {parts={{5,'uint',collation='Unicode'}}})
+---
+- error: 'Wrong index options (field 1): collation is reasonable only for string and
+    scalar parts'
+...
+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.
 format = {{name='field1', type='unsigned'}, {name='field2', type='string'}}
 ---
 ...
diff --git a/test/box/ephemeral_space.test.lua b/test/box/ephemeral_space.test.lua
index 3cc936f..211014c 100644
--- a/test/box/ephemeral_space.test.lua
+++ b/test/box/ephemeral_space.test.lua
@@ -1,6 +1,6 @@
--- Ephemeral space: creation and dropping
+-- Ephemeral space: creation and dropping.
 
--- Simple creation
+-- Simple creation.
 s = box.schema.space.create_ephemeral()
 s.index
 s.temporary
@@ -8,7 +8,7 @@ s.field_count
 s.engine
 s:drop()
 
--- Creation with defined engine (only memtx for now)
+-- Creation with defined engine (only memtx for now).
 s = box.schema.space.create_ephemeral({engine = 'memtx'})
 s.index
 s.temporary
@@ -18,7 +18,7 @@ s:drop()
 
 s = box.schema.space.create_ephemeral({engine = 'other'})
 
--- Creation with defined field_count
+-- Creation with defined field_count.
 s = box.schema.space.create_ephemeral({field_count = 10})
 s.index
 s.temporary
@@ -28,7 +28,7 @@ s:drop()
 
 s = box.schema.space.create_ephemeral({field_count = 'asd'})
 
--- Creation with defined format
+-- Creation with defined format.
 format = {{name='field1', type='unsigned'}, {name='field2', type='string'}}
 s = box.schema.space.create_ephemeral({format = format})
 s.index
@@ -39,7 +39,7 @@ s:drop()
 
 s = box.schema.space.create_ephemeral({format = 'a'})
 
--- All options defined
+-- 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)
@@ -70,7 +70,81 @@ box.schema.space.drop_ephemeral(s)
 s
 
 
--- Ephemeral space: methods
+-- Ephemeral space: index creation and dropping.
+s = box.schema.space.create_ephemeral()
+
+-- Simple creation
+i = s:create_index('a')
+i.unique
+i.parts
+i.id
+i.name
+i:drop()
+
+-- Double creation of index for ephemeral space.
+i = s:create_index('a')
+i = s:create_index('a')
+i:drop()
+
+-- Double creation of index for ephemeral space
+-- but with if_not_exists=true.
+i = s:create_index('a')
+i = s:create_index('a', {if_not_exists=true})
+i:drop()
+
+-- Ephemeral space can have only primary.
+-- index so its id == 0
+i = s:create_index('a', {id = 10})
+
+-- Set parameters: parts.
+i = s:create_index('a', {parts={1}})
+i.unique
+i.parts
+i.id
+i.name
+i:drop()
+
+i = s:create_index('a', {parts={1,"integer"}})
+i.unique
+i.parts
+i.id
+i.name
+i:drop()
+
+i = s:create_index('a', {parts={1,'string',collation='Unicode'}})
+i.unique
+i.parts
+i.id
+i.name
+i:drop()
+
+i = s:create_index('a', {parts={1,'uint',2,'int',3,'str'}})
+i.unique
+i.parts
+i.id
+i.name
+i:drop()
+
+i = s:create_index('a', {parts={{5,'string',collation='Unicode'}}})
+i.unique
+i.parts
+i.id
+i.name
+i:drop()
+
+i = s:create_index('a', {parts={3,"scalar"}})
+i.unique
+i.parts
+i.id
+i.name
+i:drop()
+
+i = s:create_index('a', {parts={{5,'uint',collation='Unicode'}}})
+
+i = s:create_index('a', {type='bitset',parts={1,'unsigned',2,'unsigned'}})
+
+
+-- 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)
diff --git a/test/engine/iterator.result b/test/engine/iterator.result
index f9da661..e1bc7ca 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:1109: usage: next(param, state)'
+- error: 'builtin/box/schema.lua:1234: usage: next(param, state)'
 ...
 value
 ---
-- 
2.7.4





More information about the Tarantool-patches mailing list