* [PATCH 0/2] schema: expose space_mt and index_mt on table
@ 2018-03-23 13:07 Vladislav Shpilevoy
2018-03-23 13:07 ` [PATCH 1/2] schema: expose space_mt and index_mt on `box.schema` table Vladislav Shpilevoy
` (2 more replies)
0 siblings, 3 replies; 20+ messages in thread
From: Vladislav Shpilevoy @ 2018-03-23 13:07 UTC (permalink / raw)
To: tarantool-patches; +Cc: vdavydov.dev, Vladislav Shpilevoy
Branch: http://github.com/tarantool/tarantool/tree/pr-3204-expose-space-index-mt
Issue: https://github.com/tarantool/tarantool/issues/3204
Some of users want to extend space and index methods with their
own, but do not want to do it for each space, because a metatable
are created individually for a space.
Move these metatables to box.schema, and lookup into them, when
a method was not found in an original metatable.
Vladislav Shpilevoy (1):
schema: review fixes for box.schema.space/index metatables
aleclarson (1):
schema: expose space_mt and index_mt on `box.schema` table
src/box/lua/schema.lua | 631 ++++++++++++++++++++--------------------
test/box-tap/schema_mt.test.lua | 79 +++++
2 files changed, 391 insertions(+), 319 deletions(-)
create mode 100755 test/box-tap/schema_mt.test.lua
--
2.14.3 (Apple Git-98)
^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH 1/2] schema: expose space_mt and index_mt on `box.schema` table
2018-03-23 13:07 [PATCH 0/2] schema: expose space_mt and index_mt on table Vladislav Shpilevoy
@ 2018-03-23 13:07 ` Vladislav Shpilevoy
2018-03-23 13:07 ` [PATCH 2/2] schema: review fixes for box.schema.space/index metatables Vladislav Shpilevoy
2018-03-29 10:54 ` [PATCH 0/2] schema: expose space_mt and index_mt on table Vladimir Davydov
2 siblings, 0 replies; 20+ messages in thread
From: Vladislav Shpilevoy @ 2018-03-23 13:07 UTC (permalink / raw)
To: tarantool-patches; +Cc: vdavydov.dev, aleclarson
From: aleclarson <alec.stanford.larson@gmail.com>
This commit allows userland to extend the space and index metatables
with their own functions or even metamethods. Reducing barriers for
this kind of experimentation is vital for user contribution toward
the improvement of Tarantool's API.
Also, this commit reuses functions between all spaces/indexes.
The metatables are exposed on `box` to make them simple to discover.
The suggested alternatives were `box.internal` and `box.schema`.
In a future commit, we should look into "freezing" the built-in
methods so the user cannot accidentally override them. This is
especially important when updating Tarantool.
Closes #3204
---
src/box/lua/schema.lua | 639 ++++++++++++++++++++--------------------
test/box-tap/schema_mt.test.lua | 74 +++++
2 files changed, 398 insertions(+), 315 deletions(-)
create mode 100755 test/box-tap/schema_mt.test.lua
diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua
index 5a0f71559..3fb626efd 100644
--- a/src/box/lua/schema.lua
+++ b/src/box/lua/schema.lua
@@ -1074,218 +1074,348 @@ end
internal.check_iterator_type = check_iterator_type -- export for net.box
-function box.schema.space.bless(space)
- local index_mt = {}
- -- __len and __index
- index_mt.len = function(index)
- check_index_arg(index, 'len')
- local ret = builtin.box_index_len(index.space_id, index.id)
- if ret == -1 then
- box.error()
- end
- return tonumber(ret)
- end
- -- index.bsize
- index_mt.bsize = function(index)
- check_index_arg(index, 'bsize')
- local ret = builtin.box_index_bsize(index.space_id, index.id)
- if ret == -1 then
- box.error()
- end
- return tonumber(ret)
- end
- index_mt.__len = index_mt.len -- Lua 5.2 compatibility
- index_mt.__newindex = function(table, index)
- return error('Attempt to modify a read-only table') end
- index_mt.__index = index_mt
- -- min and max
- index_mt.min_ffi = function(index, key)
- check_index_arg(index, 'min')
- local pkey, pkey_end = tuple_encode(key)
- if builtin.box_index_min(index.space_id, index.id,
- pkey, pkey_end, ptuple) ~= 0 then
- box.error() -- error
- elseif ptuple[0] ~= nil then
- return tuple_bless(ptuple[0])
- else
- return
- end
- end
- index_mt.min_luac = function(index, key)
- check_index_arg(index, 'min')
- key = keify(key)
- return internal.min(index.space_id, index.id, key);
+local space_mt = {}
+space_mt.__index = space_mt
+box.schema.space_mt = space_mt
+
+space_mt.len = function(space)
+ check_space_arg(space, 'len')
+ local pk = space.index[0]
+ if pk == nil then
+ return 0 -- empty space without indexes, return 0
end
- index_mt.max_ffi = function(index, key)
- check_index_arg(index, 'max')
- local pkey, pkey_end = tuple_encode(key)
- if builtin.box_index_max(index.space_id, index.id,
- pkey, pkey_end, ptuple) ~= 0 then
- box.error() -- error
- elseif ptuple[0] ~= nil then
- return tuple_bless(ptuple[0])
- else
- return
- end
+ return space.index[0]:len()
+end
+space_mt.count = function(space, key, opts)
+ check_space_arg(space, 'count')
+ local pk = space.index[0]
+ if pk == nil then
+ return 0 -- empty space without indexes, return 0
end
- index_mt.max_luac = function(index, key)
- check_index_arg(index, 'max')
- key = keify(key)
- return internal.max(index.space_id, index.id, key);
+ return pk:count(key, opts)
+end
+space_mt.bsize = function(space)
+ check_space_arg(space, 'bsize')
+ local s = builtin.space_by_id(space.id)
+ if s == nil then
+ box.error(box.error.NO_SUCH_SPACE, space.name)
end
- index_mt.random_ffi = function(index, rnd)
- check_index_arg(index, 'random')
- rnd = rnd or math.random()
- if builtin.box_index_random(index.space_id, index.id, rnd,
- ptuple) ~= 0 then
- box.error() -- error
- elseif ptuple[0] ~= nil then
- return tuple_bless(ptuple[0])
- else
- return
- end
+ return builtin.space_bsize(s)
+end
+
+space_mt.get = function(space, key)
+ check_space_arg(space, 'get')
+ return check_primary_index(space):get(key)
+end
+space_mt.select = function(space, key, opts)
+ check_space_arg(space, 'select')
+ return check_primary_index(space):select(key, opts)
+end
+space_mt.insert = function(space, tuple)
+ check_space_arg(space, 'insert')
+ return internal.insert(space.id, tuple);
+end
+space_mt.replace = function(space, tuple)
+ check_space_arg(space, 'replace')
+ return internal.replace(space.id, tuple);
+end
+space_mt.put = space_mt.replace; -- put is an alias for replace
+space_mt.update = function(space, key, ops)
+ check_space_arg(space, 'update')
+ return check_primary_index(space):update(key, ops)
+end
+space_mt.upsert = function(space, tuple_key, ops, deprecated)
+ check_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
- index_mt.random_luac = function(index, rnd)
- check_index_arg(index, 'random')
- rnd = rnd or math.random()
- return internal.random(index.space_id, index.id, rnd);
- end
- -- iteration
- index_mt.pairs_ffi = function(index, key, opts)
- check_index_arg(index, 'pairs')
- local pkey, pkey_end = tuple_encode(key)
- local itype = check_iterator_type(opts, pkey + 1 >= pkey_end);
-
- local keybuf = ffi.string(pkey, pkey_end - pkey)
- local pkeybuf = ffi.cast('const char *', keybuf)
- local cdata = builtin.box_index_iterator(index.space_id, index.id,
- itype, pkeybuf, pkeybuf + #keybuf);
- if cdata == nil then
- box.error()
- end
- return fun.wrap(iterator_gen, keybuf,
- ffi.gc(cdata, builtin.box_iterator_free))
- end
- index_mt.pairs_luac = function(index, key, opts)
- check_index_arg(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 = internal.iterator(index.space_id, index.id, itype, keymp);
- return fun.wrap(iterator_gen_luac, keybuf,
- ffi.gc(cdata, builtin.box_iterator_free))
- end
-
- -- index subtree size
- index_mt.count_ffi = function(index, key, opts)
- check_index_arg(index, 'count')
- local pkey, pkey_end = tuple_encode(key)
- local itype = check_iterator_type(opts, pkey + 1 >= pkey_end);
- local count = builtin.box_index_count(index.space_id, index.id,
- itype, pkey, pkey_end);
- if count == -1 then
- box.error()
- end
- return tonumber(count)
- end
- index_mt.count_luac = function(index, key, opts)
- check_index_arg(index, 'count')
- key = keify(key)
- local itype = check_iterator_type(opts, #key == 0);
- return internal.count(index.space_id, index.id, itype, key);
- end
-
- index_mt.get_ffi = function(index, key)
- check_index_arg(index, 'get')
- local key, key_end = tuple_encode(key)
- if builtin.box_index_get(index.space_id, index.id,
- key, key_end, ptuple) ~= 0 then
- return box.error() -- error
- elseif ptuple[0] ~= nil then
- return tuple_bless(ptuple[0])
- else
- return
- end
+ return internal.upsert(space.id, tuple_key, ops);
+end
+space_mt.delete = function(space, key)
+ check_space_arg(space, 'delete')
+ return check_primary_index(space):delete(key)
+end
+-- Assumes that spaceno has a TREE (NUM) primary key
+-- inserts a tuple after getting the next value of the
+-- primary key and returns it back to the user
+space_mt.auto_increment = function(space, tuple)
+ check_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
- index_mt.get_luac = function(index, key)
- check_index_arg(index, 'get')
- key = keify(key)
- return internal.get(index.space_id, index.id, key)
+ table.insert(tuple, 1, max + 1)
+ return space:insert(tuple)
+end
+
+space_mt.pairs = function(space, key, opts)
+ check_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_mt.__pairs = space_mt.pairs -- Lua 5.2 compatibility
+space_mt.__ipairs = space_mt.pairs -- Lua 5.2 compatibility
+space_mt.truncate = function(space)
+ check_space_arg(space, 'truncate')
+ return internal.truncate(space.id)
+end
+space_mt.format = function(space, format)
+ check_space_arg(space, 'format')
+ return box.schema.space.format(space.id, format)
+end
+space_mt.drop = function(space)
+ check_space_arg(space, 'drop')
+ check_space_exists(space)
+ return box.schema.space.drop(space.id, space.name)
+end
+space_mt.rename = function(space, name)
+ check_space_arg(space, 'rename')
+ check_space_exists(space)
+ return box.schema.space.rename(space.id, name)
+end
+space_mt.create_index = function(space, name, options)
+ check_space_arg(space, 'create_index')
+ check_space_exists(space)
+ return box.schema.index.create(space.id, name, options)
+end
+space_mt.run_triggers = function(space, yesno)
+ check_space_arg(space, 'run_triggers')
+ local s = builtin.space_by_id(space.id)
+ if s == nil then
+ box.error(box.error.NO_SUCH_SPACE, space.name)
end
+ builtin.space_run_triggers(s, yesno)
+end
- local function check_select_opts(opts, key_is_nil)
- local offset = 0
- local limit = 4294967295
- local iterator = check_iterator_type(opts, key_is_nil)
- if opts ~= nil then
- if opts.offset ~= nil then
- offset = opts.offset
- end
- if opts.limit ~= nil then
- limit = opts.limit
- end
- end
- return iterator, offset, limit
+local index_mt = {}
+index_mt.__index = index_mt
+box.schema.index_mt = index_mt
+
+-- __len and __index
+index_mt.len = function(index)
+ check_index_arg(index, 'len')
+ local ret = builtin.box_index_len(index.space_id, index.id)
+ if ret == -1 then
+ box.error()
+ end
+ return tonumber(ret)
+end
+-- index.bsize
+index_mt.bsize = function(index)
+ check_index_arg(index, 'bsize')
+ local ret = builtin.box_index_bsize(index.space_id, index.id)
+ if ret == -1 then
+ box.error()
+ end
+ return tonumber(ret)
+end
+index_mt.__len = index_mt.len -- Lua 5.2 compatibility
+index_mt.__index = index_mt
+-- min and max
+index_mt.min_ffi = function(index, key)
+ check_index_arg(index, 'min')
+ local pkey, pkey_end = tuple_encode(key)
+ if builtin.box_index_min(index.space_id, index.id,
+ pkey, pkey_end, ptuple) ~= 0 then
+ box.error() -- error
+ elseif ptuple[0] ~= nil then
+ return tuple_bless(ptuple[0])
+ else
+ return
+ end
+end
+index_mt.min_luac = function(index, key)
+ check_index_arg(index, 'min')
+ key = keify(key)
+ return internal.min(index.space_id, index.id, key);
+end
+index_mt.max_ffi = function(index, key)
+ check_index_arg(index, 'max')
+ local pkey, pkey_end = tuple_encode(key)
+ if builtin.box_index_max(index.space_id, index.id,
+ pkey, pkey_end, ptuple) ~= 0 then
+ box.error() -- error
+ elseif ptuple[0] ~= nil then
+ return tuple_bless(ptuple[0])
+ else
+ return
end
+end
+index_mt.max_luac = function(index, key)
+ check_index_arg(index, 'max')
+ key = keify(key)
+ return internal.max(index.space_id, index.id, key);
+end
+index_mt.random_ffi = function(index, rnd)
+ check_index_arg(index, 'random')
+ rnd = rnd or math.random()
+ if builtin.box_index_random(index.space_id, index.id, rnd,
+ ptuple) ~= 0 then
+ box.error() -- error
+ elseif ptuple[0] ~= nil then
+ return tuple_bless(ptuple[0])
+ else
+ return
+ end
+end
+index_mt.random_luac = function(index, rnd)
+ check_index_arg(index, 'random')
+ rnd = rnd or math.random()
+ return internal.random(index.space_id, index.id, rnd);
+end
+-- iteration
+index_mt.pairs_ffi = function(index, key, opts)
+ check_index_arg(index, 'pairs')
+ local pkey, pkey_end = tuple_encode(key)
+ local itype = check_iterator_type(opts, pkey + 1 >= pkey_end);
- index_mt.select_ffi = function(index, key, opts)
- check_index_arg(index, 'select')
- local key, key_end = tuple_encode(key)
- local iterator, offset, limit = check_select_opts(opts, key + 1 >= key_end)
+ local keybuf = ffi.string(pkey, pkey_end - pkey)
+ local pkeybuf = ffi.cast('const char *', keybuf)
+ local cdata = builtin.box_index_iterator(index.space_id, index.id,
+ itype, pkeybuf, pkeybuf + #keybuf);
+ if cdata == nil then
+ box.error()
+ end
+ return fun.wrap(iterator_gen, keybuf,
+ ffi.gc(cdata, builtin.box_iterator_free))
+end
+index_mt.pairs_luac = function(index, key, opts)
+ check_index_arg(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 = internal.iterator(index.space_id, index.id, itype, keymp);
+ return fun.wrap(iterator_gen_luac, keybuf,
+ ffi.gc(cdata, builtin.box_iterator_free))
+end
+
+-- index subtree size
+index_mt.count_ffi = function(index, key, opts)
+ check_index_arg(index, 'count')
+ local pkey, pkey_end = tuple_encode(key)
+ local itype = check_iterator_type(opts, pkey + 1 >= pkey_end);
+ local count = builtin.box_index_count(index.space_id, index.id,
+ itype, pkey, pkey_end);
+ if count == -1 then
+ box.error()
+ end
+ return tonumber(count)
+end
+index_mt.count_luac = function(index, key, opts)
+ check_index_arg(index, 'count')
+ key = keify(key)
+ local itype = check_iterator_type(opts, #key == 0);
+ return internal.count(index.space_id, index.id, itype, key);
+end
- local port = ffi.cast('struct port *', port_tuple)
+index_mt.get_ffi = function(index, key)
+ check_index_arg(index, 'get')
+ local key, key_end = tuple_encode(key)
+ if builtin.box_index_get(index.space_id, index.id,
+ key, key_end, ptuple) ~= 0 then
+ return box.error() -- error
+ elseif ptuple[0] ~= nil then
+ return tuple_bless(ptuple[0])
+ else
+ return
+ end
+end
+index_mt.get_luac = function(index, key)
+ check_index_arg(index, 'get')
+ key = keify(key)
+ return internal.get(index.space_id, index.id, key)
+end
- if builtin.box_select(index.space_id, index.id,
- iterator, offset, limit, key, key_end, port) ~= 0 then
- return box.error()
+local function check_select_opts(opts, key_is_nil)
+ local offset = 0
+ local limit = 4294967295
+ local iterator = check_iterator_type(opts, key_is_nil)
+ if opts ~= nil then
+ if opts.offset ~= nil then
+ offset = opts.offset
end
-
- local ret = {}
- local entry = port_tuple.first
- for i=1,tonumber(port_tuple.size),1 do
- ret[i] = tuple_bless(entry.tuple)
- entry = entry.next
+ if opts.limit ~= nil then
+ limit = opts.limit
end
- builtin.port_destroy(port);
- return ret
end
+ return iterator, offset, limit
+end
- index_mt.select_luac = function(index, key, opts)
- check_index_arg(index, 'select')
- local key = keify(key)
- local iterator, offset, limit = check_select_opts(opts, #key == 0)
- return internal.select(index.space_id, index.id, iterator,
- offset, limit, key)
- end
+index_mt.select_ffi = function(index, key, opts)
+ check_index_arg(index, 'select')
+ local key, key_end = tuple_encode(key)
+ local iterator, offset, limit = check_select_opts(opts, key + 1 >= key_end)
- index_mt.update = function(index, key, ops)
- check_index_arg(index, 'update')
- return internal.update(index.space_id, index.id, keify(key), ops);
- end
- index_mt.delete = function(index, key)
- check_index_arg(index, 'delete')
- return internal.delete(index.space_id, index.id, keify(key));
- end
+ local port = ffi.cast('struct port *', port_tuple)
- index_mt.info = function(index)
- return internal.info(index.space_id, index.id);
+ if builtin.box_select(index.space_id, index.id,
+ iterator, offset, limit, key, key_end, port) ~= 0 then
+ return box.error()
end
- index_mt.drop = function(index)
- check_index_arg(index, 'drop')
- return box.schema.index.drop(index.space_id, index.id)
+ local ret = {}
+ local entry = port_tuple.first
+ for i=1,tonumber(port_tuple.size),1 do
+ ret[i] = tuple_bless(entry.tuple)
+ entry = entry.next
end
- index_mt.rename = function(index, name)
- check_index_arg(index, 'rename')
- return box.schema.index.rename(index.space_id, index.id, name)
+ builtin.port_destroy(port);
+ return ret
+end
+
+index_mt.select_luac = function(index, key, opts)
+ check_index_arg(index, 'select')
+ local key = keify(key)
+ local iterator, offset, limit = check_select_opts(opts, #key == 0)
+ return internal.select(index.space_id, index.id, iterator,
+ offset, limit, key)
+end
+
+index_mt.update = function(index, key, ops)
+ check_index_arg(index, 'update')
+ return internal.update(index.space_id, index.id, keify(key), ops);
+end
+index_mt.delete = function(index, key)
+ check_index_arg(index, 'delete')
+ return internal.delete(index.space_id, index.id, keify(key));
+end
+
+index_mt.info = function(index)
+ return internal.info(index.space_id, index.id);
+end
+
+index_mt.drop = function(index)
+ check_index_arg(index, 'drop')
+ return box.schema.index.drop(index.space_id, index.id)
+end
+index_mt.rename = function(index, name)
+ check_index_arg(index, 'rename')
+ return box.schema.index.rename(index.space_id, index.id, name)
+end
+index_mt.alter = function(index, options)
+ check_index_arg(index, 'alter')
+ if index.id == nil or index.space_id == nil then
+ box.error(box.error.PROC_LUA, "Usage: index:alter{opts}")
end
- index_mt.alter = function(index, options)
- check_index_arg(index, 'alter')
- if index.id == nil or index.space_id == nil then
- box.error(box.error.PROC_LUA, "Usage: index:alter{opts}")
- end
- return box.schema.index.alter(index.space_id, index.id, options)
+ return box.schema.index.alter(index.space_id, index.id, options)
+end
+
+index_mt.__pairs = index_mt.pairs -- Lua 5.2 compatibility
+index_mt.__ipairs = index_mt.pairs -- Lua 5.2 compatibility
+
+function box.schema.space.bless(space)
+ local index_mt = {}
+ index_mt.__index = function(index, key)
+ return index_mt[key] or box.schema.index_mt[key]
end
-- true if reading operations may yield
@@ -1294,136 +1424,15 @@ function box.schema.space.bless(space)
for _, op in ipairs(read_ops) do
if read_yields then
-- use Lua/C implmenetation
- index_mt[op] = index_mt[op .. "_luac"]
+ index_mt[op] = box.schema.index_mt[op .. "_luac"]
else
-- use FFI implementation
- index_mt[op] = index_mt[op .. "_ffi"]
+ index_mt[op] = box.schema.index_mt[op .. "_ffi"]
end
end
- index_mt.__pairs = index_mt.pairs -- Lua 5.2 compatibility
- index_mt.__ipairs = index_mt.pairs -- Lua 5.2 compatibility
--
- local space_mt = {}
- space_mt.len = function(space)
- check_space_arg(space, 'len')
- local pk = space.index[0]
- if pk == nil then
- return 0 -- empty space without indexes, return 0
- end
- return space.index[0]:len()
- end
- space_mt.count = function(space, key, opts)
- check_space_arg(space, 'count')
- local pk = space.index[0]
- if pk == nil then
- return 0 -- empty space without indexes, return 0
- end
- return pk:count(key, opts)
- end
- space_mt.bsize = function(space)
- check_space_arg(space, 'bsize')
- local s = builtin.space_by_id(space.id)
- if s == nil then
- box.error(box.error.NO_SUCH_SPACE, space.name)
- end
- return builtin.space_bsize(s)
- end
- space_mt.__newindex = index_mt.__newindex
- space_mt.get = function(space, key)
- check_space_arg(space, 'get')
- return check_primary_index(space):get(key)
- end
- space_mt.select = function(space, key, opts)
- check_space_arg(space, 'select')
- return check_primary_index(space):select(key, opts)
- end
- space_mt.insert = function(space, tuple)
- check_space_arg(space, 'insert')
- return internal.insert(space.id, tuple);
- end
- space_mt.replace = function(space, tuple)
- check_space_arg(space, 'replace')
- return internal.replace(space.id, tuple);
- end
- space_mt.put = space_mt.replace; -- put is an alias for replace
- space_mt.update = function(space, key, ops)
- check_space_arg(space, 'update')
- return check_primary_index(space):update(key, ops)
- end
- space_mt.upsert = function(space, tuple_key, ops, deprecated)
- check_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 internal.upsert(space.id, tuple_key, ops);
- end
- space_mt.delete = function(space, key)
- check_space_arg(space, 'delete')
- return check_primary_index(space):delete(key)
- end
--- Assumes that spaceno has a TREE (NUM) primary key
--- inserts a tuple after getting the next value of the
--- primary key and returns it back to the user
- space_mt.auto_increment = function(space, tuple)
- check_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_mt.pairs = function(space, key, opts)
- check_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_mt.__pairs = space_mt.pairs -- Lua 5.2 compatibility
- space_mt.__ipairs = space_mt.pairs -- Lua 5.2 compatibility
- space_mt.truncate = function(space)
- check_space_arg(space, 'truncate')
- return internal.truncate(space.id)
- end
- space_mt.format = function(space, format)
- check_space_arg(space, 'format')
- return box.schema.space.format(space.id, format)
- end
- space_mt.drop = function(space)
- check_space_arg(space, 'drop')
- check_space_exists(space)
- return box.schema.space.drop(space.id, space.name)
- end
- space_mt.rename = function(space, name)
- check_space_arg(space, 'rename')
- check_space_exists(space)
- return box.schema.space.rename(space.id, name)
- end
- space_mt.create_index = function(space, name, options)
- check_space_arg(space, 'create_index')
- check_space_exists(space)
- return box.schema.index.create(space.id, name, options)
- end
- space_mt.run_triggers = function(space, yesno)
- check_space_arg(space, 'run_triggers')
- local s = builtin.space_by_id(space.id)
- if s == nil then
- box.error(box.error.NO_SUCH_SPACE, space.name)
- end
- builtin.space_run_triggers(s, yesno)
- end
- space_mt.__index = space_mt
-
- setmetatable(space, space_mt)
+ setmetatable(space, box.schema.space_mt)
if type(space.index) == 'table' and space.enabled then
for j, index in pairs(space.index) do
if type(j) == 'number' then
diff --git a/test/box-tap/schema_mt.test.lua b/test/box-tap/schema_mt.test.lua
new file mode 100755
index 000000000..7e531dd01
--- /dev/null
+++ b/test/box-tap/schema_mt.test.lua
@@ -0,0 +1,74 @@
+#!/usr/bin/env tarantool
+--
+-- pr-3204: expose space_mt, index_mt into box.schema.
+--
+
+local tap = require('tap')
+local test = tap.test('schema_mt')
+
+test:plan(7)
+
+box.cfg{
+ log="tarantool.log",
+}
+
+local sp1 = box.schema.space.create('test')
+local sp2 = box.schema.space.create('test2')
+
+test:is(
+ getmetatable(sp1),
+ getmetatable(sp2),
+ 'all spaces use the same metatable'
+)
+
+local idx1 = sp1:create_index('primary')
+local idx2 = sp2:create_index('primary')
+
+test:isnt(
+ getmetatable(idx1),
+ getmetatable(idx2),
+ 'all indexes have their own metatable'
+)
+
+test:is(
+ idx1.get,
+ idx2.get,
+ 'memtx indexes share read methods'
+)
+
+local sp3 = box.schema.space.create('test3', {engine='vinyl'})
+local sp4 = box.schema.space.create('test4', {engine='vinyl'})
+
+local idx3 = sp3:create_index('primary')
+local idx4 = sp4:create_index('primary')
+
+test:is(
+ idx3.get,
+ idx4.get,
+ 'vinyl indexes share read methods'
+)
+
+test:isnt(
+ idx1.get,
+ idx3.get,
+ 'memtx and vinyl indexes have separate read methods'
+)
+
+function box.schema.space_mt.foo() end
+
+test:is(
+ sp1.foo,
+ box.schema.space_mt.foo,
+ 'box.schema.space_mt is mutable'
+)
+
+function box.schema.index_mt.foo() end
+
+test:is(
+ idx1.foo,
+ box.schema.index_mt.foo,
+ 'box.schema.index_mt is mutable'
+)
+
+test:check()
+os.exit(0)
--
2.14.3 (Apple Git-98)
^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH 2/2] schema: review fixes for box.schema.space/index metatables
2018-03-23 13:07 [PATCH 0/2] schema: expose space_mt and index_mt on table Vladislav Shpilevoy
2018-03-23 13:07 ` [PATCH 1/2] schema: expose space_mt and index_mt on `box.schema` table Vladislav Shpilevoy
@ 2018-03-23 13:07 ` Vladislav Shpilevoy
2018-03-29 10:54 ` [PATCH 0/2] schema: expose space_mt and index_mt on table Vladimir Davydov
2 siblings, 0 replies; 20+ messages in thread
From: Vladislav Shpilevoy @ 2018-03-23 13:07 UTC (permalink / raw)
To: tarantool-patches; +Cc: vdavydov.dev, Vladislav Shpilevoy
Follow up #3204
---
src/box/lua/schema.lua | 352 +++++++++++++++++++---------------------
test/box-tap/schema_mt.test.lua | 5 +
2 files changed, 173 insertions(+), 184 deletions(-)
diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua
index 3fb626efd..c3a70db70 100644
--- a/src/box/lua/schema.lua
+++ b/src/box/lua/schema.lua
@@ -1078,83 +1078,81 @@ local space_mt = {}
space_mt.__index = space_mt
box.schema.space_mt = space_mt
-space_mt.len = function(space)
- check_space_arg(space, 'len')
- local pk = space.index[0]
+function space_mt:len()
+ check_space_arg(self, 'len')
+ local pk = self.index[0]
if pk == nil then
return 0 -- empty space without indexes, return 0
end
- return space.index[0]:len()
+ return self.index[0]:len()
end
-space_mt.count = function(space, key, opts)
- check_space_arg(space, 'count')
- local pk = space.index[0]
+function space_mt:count(key, opts)
+ check_space_arg(self, 'count')
+ local pk = self.index[0]
if pk == nil then
return 0 -- empty space without indexes, return 0
end
return pk:count(key, opts)
end
-space_mt.bsize = function(space)
- check_space_arg(space, 'bsize')
- local s = builtin.space_by_id(space.id)
+function space_mt:bsize()
+ check_space_arg(self, 'bsize')
+ local s = builtin.space_by_id(self.id)
if s == nil then
- box.error(box.error.NO_SUCH_SPACE, space.name)
+ box.error(box.error.NO_SUCH_SPACE, self.name)
end
return builtin.space_bsize(s)
end
-
-space_mt.get = function(space, key)
- check_space_arg(space, 'get')
- return check_primary_index(space):get(key)
+function space_mt:get(key)
+ check_space_arg(self, 'get')
+ return check_primary_index(self):get(key)
end
-space_mt.select = function(space, key, opts)
- check_space_arg(space, 'select')
- return check_primary_index(space):select(key, opts)
+function space_mt:select(key, opts)
+ check_space_arg(self, 'select')
+ return check_primary_index(self):select(key, opts)
end
-space_mt.insert = function(space, tuple)
- check_space_arg(space, 'insert')
- return internal.insert(space.id, tuple);
+function space_mt:insert(tuple)
+ check_space_arg(self, 'insert')
+ return internal.insert(self.id, tuple)
end
-space_mt.replace = function(space, tuple)
- check_space_arg(space, 'replace')
- return internal.replace(space.id, tuple);
+function space_mt:replace(tuple)
+ check_space_arg(self, 'replace')
+ return internal.replace(self.id, tuple)
end
-space_mt.put = space_mt.replace; -- put is an alias for replace
-space_mt.update = function(space, key, ops)
- check_space_arg(space, 'update')
- return check_primary_index(space):update(key, ops)
+space_mt.put = space_mt.replace -- put is an alias for replace
+function space_mt:update(key, ops)
+ check_space_arg(self, 'update')
+ return check_primary_index(self):update(key, ops)
end
-space_mt.upsert = function(space, tuple_key, ops, deprecated)
- check_space_arg(space, 'upsert')
+function space_mt:upsert(tuple_key, ops, deprecated)
+ check_space_arg(self, '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 internal.upsert(space.id, tuple_key, ops);
+ return internal.upsert(self.id, tuple_key, ops)
end
-space_mt.delete = function(space, key)
- check_space_arg(space, 'delete')
- return check_primary_index(space):delete(key)
+function space_mt:delete(key)
+ check_space_arg(self, 'delete')
+ return check_primary_index(self):delete(key)
end
-- Assumes that spaceno has a TREE (NUM) primary key
-- inserts a tuple after getting the next value of the
-- primary key and returns it back to the user
-space_mt.auto_increment = function(space, tuple)
- check_space_arg(space, 'auto_increment')
- local max_tuple = check_primary_index(space):max()
+function space_mt:auto_increment(tuple)
+ check_space_arg(self, 'auto_increment')
+ local max_tuple = check_primary_index(self):max()
local max = 0
if max_tuple ~= nil then
max = max_tuple[1]
end
table.insert(tuple, 1, max + 1)
- return space:insert(tuple)
+ return self:insert(tuple)
end
-
-space_mt.pairs = function(space, key, opts)
- check_space_arg(space, 'pairs')
- local pk = space.index[0]
+function space_mt:pairs(key, opts)
+ check_space_arg(self, 'pairs')
+ local pk = self.index[0]
if pk == nil then
-- empty space without indexes, return empty iterator
return fun.iter({})
@@ -1163,34 +1161,34 @@ space_mt.pairs = function(space, key, opts)
end
space_mt.__pairs = space_mt.pairs -- Lua 5.2 compatibility
space_mt.__ipairs = space_mt.pairs -- Lua 5.2 compatibility
-space_mt.truncate = function(space)
- check_space_arg(space, 'truncate')
- return internal.truncate(space.id)
-end
-space_mt.format = function(space, format)
- check_space_arg(space, 'format')
- return box.schema.space.format(space.id, format)
-end
-space_mt.drop = function(space)
- check_space_arg(space, 'drop')
- check_space_exists(space)
- return box.schema.space.drop(space.id, space.name)
-end
-space_mt.rename = function(space, name)
- check_space_arg(space, 'rename')
- check_space_exists(space)
- return box.schema.space.rename(space.id, name)
-end
-space_mt.create_index = function(space, name, options)
- check_space_arg(space, 'create_index')
- check_space_exists(space)
- return box.schema.index.create(space.id, name, options)
-end
-space_mt.run_triggers = function(space, yesno)
- check_space_arg(space, 'run_triggers')
- local s = builtin.space_by_id(space.id)
+function space_mt:truncate()
+ check_space_arg(self, 'truncate')
+ return internal.truncate(self.id)
+end
+function space_mt:format(format)
+ check_space_arg(self, 'format')
+ return box.schema.space.format(self.id, format)
+end
+function space_mt:drop()
+ check_space_arg(self, 'drop')
+ check_space_exists(self)
+ return box.schema.space.drop(self.id, self.name)
+end
+function space_mt:rename(name)
+ check_space_arg(self, 'rename')
+ check_space_exists(self)
+ return box.schema.space.rename(self.id, name)
+end
+function space_mt:create_index(name, options)
+ check_space_arg(self, 'create_index')
+ check_space_exists(self)
+ return box.schema.index.create(self.id, name, options)
+end
+function space_mt:run_triggers(yesno)
+ check_space_arg(self, 'run_triggers')
+ local s = builtin.space_by_id(self.id)
if s == nil then
- box.error(box.error.NO_SUCH_SPACE, space.name)
+ box.error(box.error.NO_SUCH_SPACE, self.name)
end
builtin.space_run_triggers(s, yesno)
end
@@ -1199,19 +1197,17 @@ local index_mt = {}
index_mt.__index = index_mt
box.schema.index_mt = index_mt
--- __len and __index
-index_mt.len = function(index)
- check_index_arg(index, 'len')
- local ret = builtin.box_index_len(index.space_id, index.id)
+function index_mt:len()
+ check_index_arg(self, 'len')
+ local ret = builtin.box_index_len(self.space_id, self.id)
if ret == -1 then
box.error()
end
return tonumber(ret)
end
--- index.bsize
-index_mt.bsize = function(index)
- check_index_arg(index, 'bsize')
- local ret = builtin.box_index_bsize(index.space_id, index.id)
+function index_mt:bsize()
+ check_index_arg(self, 'bsize')
+ local ret = builtin.box_index_bsize(self.space_id, self.id)
if ret == -1 then
box.error()
end
@@ -1219,120 +1215,106 @@ index_mt.bsize = function(index)
end
index_mt.__len = index_mt.len -- Lua 5.2 compatibility
index_mt.__index = index_mt
--- min and max
-index_mt.min_ffi = function(index, key)
- check_index_arg(index, 'min')
+function index_mt:min_ffi(key)
+ check_index_arg(self, 'min')
local pkey, pkey_end = tuple_encode(key)
- if builtin.box_index_min(index.space_id, index.id,
+ if builtin.box_index_min(self.space_id, self.id,
pkey, pkey_end, ptuple) ~= 0 then
- box.error() -- error
+ box.error()
elseif ptuple[0] ~= nil then
return tuple_bless(ptuple[0])
- else
- return
end
end
-index_mt.min_luac = function(index, key)
- check_index_arg(index, 'min')
+function index_mt:min_luac(key)
+ check_index_arg(self, 'min')
key = keify(key)
- return internal.min(index.space_id, index.id, key);
+ return internal.min(self.space_id, self.id, key)
end
-index_mt.max_ffi = function(index, key)
- check_index_arg(index, 'max')
+function index_mt:max_ffi(key)
+ check_index_arg(self, 'max')
local pkey, pkey_end = tuple_encode(key)
- if builtin.box_index_max(index.space_id, index.id,
+ if builtin.box_index_max(self.space_id, self.id,
pkey, pkey_end, ptuple) ~= 0 then
- box.error() -- error
+ box.error()
elseif ptuple[0] ~= nil then
return tuple_bless(ptuple[0])
- else
- return
end
end
-index_mt.max_luac = function(index, key)
- check_index_arg(index, 'max')
+function index_mt:max_luac(key)
+ check_index_arg(self, 'max')
key = keify(key)
- return internal.max(index.space_id, index.id, key);
+ return internal.max(self.space_id, self.id, key)
end
-index_mt.random_ffi = function(index, rnd)
- check_index_arg(index, 'random')
+function index_mt:random_ffi(rnd)
+ check_index_arg(self, 'random')
rnd = rnd or math.random()
- if builtin.box_index_random(index.space_id, index.id, rnd,
- ptuple) ~= 0 then
- box.error() -- error
+ if builtin.box_index_random(self.space_id, self.id, rnd, ptuple) ~= 0 then
+ box.error()
elseif ptuple[0] ~= nil then
return tuple_bless(ptuple[0])
- else
- return
end
end
-index_mt.random_luac = function(index, rnd)
- check_index_arg(index, 'random')
+function index_mt:random_luac(rnd)
+ check_index_arg(self, 'random')
rnd = rnd or math.random()
- return internal.random(index.space_id, index.id, rnd);
+ return internal.random(self.space_id, self.id, rnd)
end
--- iteration
-index_mt.pairs_ffi = function(index, key, opts)
- check_index_arg(index, 'pairs')
+function index_mt:pairs_ffi(key, opts)
+ check_index_arg(self, 'pairs')
local pkey, pkey_end = tuple_encode(key)
- local itype = check_iterator_type(opts, pkey + 1 >= pkey_end);
+ local itype = check_iterator_type(opts, pkey + 1 >= pkey_end)
local keybuf = ffi.string(pkey, pkey_end - pkey)
local pkeybuf = ffi.cast('const char *', keybuf)
- local cdata = builtin.box_index_iterator(index.space_id, index.id,
- itype, pkeybuf, pkeybuf + #keybuf);
+ local cdata = builtin.box_index_iterator(self.space_id, self.id, itype,
+ pkeybuf, pkeybuf + #keybuf)
if cdata == nil then
box.error()
end
return fun.wrap(iterator_gen, keybuf,
- ffi.gc(cdata, builtin.box_iterator_free))
+ ffi.gc(cdata, builtin.box_iterator_free))
end
-index_mt.pairs_luac = function(index, key, opts)
- check_index_arg(index, 'pairs')
+function index_mt:pairs_luac(key, opts)
+ check_index_arg(self, 'pairs')
key = keify(key)
- local itype = check_iterator_type(opts, #key == 0);
+ local itype = check_iterator_type(opts, #key == 0)
local keymp = msgpack.encode(key)
local keybuf = ffi.string(keymp, #keymp)
- local cdata = internal.iterator(index.space_id, index.id, itype, keymp);
+ local cdata = internal.iterator(self.space_id, self.id, itype, keymp)
return fun.wrap(iterator_gen_luac, keybuf,
- ffi.gc(cdata, builtin.box_iterator_free))
+ ffi.gc(cdata, builtin.box_iterator_free))
end
-
--- index subtree size
-index_mt.count_ffi = function(index, key, opts)
- check_index_arg(index, 'count')
+function index_mt:count_ffi(key, opts)
+ check_index_arg(self, 'count')
local pkey, pkey_end = tuple_encode(key)
- local itype = check_iterator_type(opts, pkey + 1 >= pkey_end);
- local count = builtin.box_index_count(index.space_id, index.id,
- itype, pkey, pkey_end);
+ local itype = check_iterator_type(opts, pkey + 1 >= pkey_end)
+ local count = builtin.box_index_count(self.space_id, self.id, itype, pkey,
+ pkey_end)
if count == -1 then
box.error()
end
return tonumber(count)
end
-index_mt.count_luac = function(index, key, opts)
- check_index_arg(index, 'count')
+function index_mt:count_luac(key, opts)
+ check_index_arg(self, 'count')
key = keify(key)
- local itype = check_iterator_type(opts, #key == 0);
- return internal.count(index.space_id, index.id, itype, key);
+ local itype = check_iterator_type(opts, #key == 0)
+ return internal.count(self.space_id, self.id, itype, key)
end
-
-index_mt.get_ffi = function(index, key)
- check_index_arg(index, 'get')
+function index_mt:get_ffi(key)
+ check_index_arg(self, 'get')
local key, key_end = tuple_encode(key)
- if builtin.box_index_get(index.space_id, index.id,
- key, key_end, ptuple) ~= 0 then
- return box.error() -- error
+ if builtin.box_index_get(self.space_id, self.id, key, key_end,
+ ptuple) ~= 0 then
+ return box.error()
elseif ptuple[0] ~= nil then
return tuple_bless(ptuple[0])
- else
- return
end
end
-index_mt.get_luac = function(index, key)
- check_index_arg(index, 'get')
+function index_mt:get_luac(key)
+ check_index_arg(self, 'get')
key = keify(key)
- return internal.get(index.space_id, index.id, key)
+ return internal.get(self.space_id, self.id, key)
end
local function check_select_opts(opts, key_is_nil)
@@ -1350,15 +1332,15 @@ local function check_select_opts(opts, key_is_nil)
return iterator, offset, limit
end
-index_mt.select_ffi = function(index, key, opts)
- check_index_arg(index, 'select')
+function index_mt:select_ffi(key, opts)
+ check_index_arg(self, 'select')
local key, key_end = tuple_encode(key)
local iterator, offset, limit = check_select_opts(opts, key + 1 >= key_end)
local port = ffi.cast('struct port *', port_tuple)
- if builtin.box_select(index.space_id, index.id,
- iterator, offset, limit, key, key_end, port) ~= 0 then
+ if builtin.box_select(self.space_id, self.id, iterator, offset, limit, key,
+ key_end, port) ~= 0 then
return box.error()
end
@@ -1368,75 +1350,77 @@ index_mt.select_ffi = function(index, key, opts)
ret[i] = tuple_bless(entry.tuple)
entry = entry.next
end
- builtin.port_destroy(port);
+ builtin.port_destroy(port)
return ret
end
-
-index_mt.select_luac = function(index, key, opts)
- check_index_arg(index, 'select')
+function index_mt:select_luac(key, opts)
+ check_index_arg(self, 'select')
local key = keify(key)
local iterator, offset, limit = check_select_opts(opts, #key == 0)
- return internal.select(index.space_id, index.id, iterator,
- offset, limit, key)
+ return internal.select(self.space_id, self.id, iterator, offset, limit, key)
end
-
-index_mt.update = function(index, key, ops)
- check_index_arg(index, 'update')
- return internal.update(index.space_id, index.id, keify(key), ops);
+function index_mt:update(key, ops)
+ check_index_arg(self, 'update')
+ return internal.update(self.space_id, self.id, keify(key), ops)
end
-index_mt.delete = function(index, key)
- check_index_arg(index, 'delete')
- return internal.delete(index.space_id, index.id, keify(key));
+function index_mt:delete(key)
+ check_index_arg(self, 'delete')
+ return internal.delete(self.space_id, self.id, keify(key))
end
-
-index_mt.info = function(index)
- return internal.info(index.space_id, index.id);
+function index_mt:info()
+ return internal.info(self.space_id, self.id)
end
-
-index_mt.drop = function(index)
- check_index_arg(index, 'drop')
- return box.schema.index.drop(index.space_id, index.id)
+function index_mt:drop()
+ check_index_arg(self, 'drop')
+ return box.schema.index.drop(self.space_id, self.id)
end
-index_mt.rename = function(index, name)
- check_index_arg(index, 'rename')
- return box.schema.index.rename(index.space_id, index.id, name)
+function index_mt:rename(name)
+ check_index_arg(self, 'rename')
+ return box.schema.index.rename(self.space_id, self.id, name)
end
-index_mt.alter = function(index, options)
- check_index_arg(index, 'alter')
- if index.id == nil or index.space_id == nil then
+function index_mt:alter(options)
+ check_index_arg(self, 'alter')
+ if self.id == nil or self.space_id == nil then
box.error(box.error.PROC_LUA, "Usage: index:alter{opts}")
end
- return box.schema.index.alter(index.space_id, index.id, options)
+ return box.schema.index.alter(self.space_id, self.id, options)
end
-index_mt.__pairs = index_mt.pairs -- Lua 5.2 compatibility
-index_mt.__ipairs = index_mt.pairs -- Lua 5.2 compatibility
-
function box.schema.space.bless(space)
- local index_mt = {}
- index_mt.__index = function(index, key)
- return index_mt[key] or box.schema.index_mt[key]
+ local local_index_mt = {}
+ -- At first, reference all global index functions. At second,
+ -- choose an implementation for read operations. They are not
+ -- in a global index_mt, because they are engine specific.
+ -- All common index_mt functions must be referenced here to
+ -- be able to get them using getmetatable(index_object) - if
+ -- they are only in index_mt, then getmetatable() returns only
+ -- read ops.
+ for k, v in pairs(index_mt) do
+ local_index_mt[k] = v
+ end
+ local_index_mt.__index = function(index, key)
+ return local_index_mt[key] or index_mt[key]
end
-
- -- true if reading operations may yield
local read_yields = space.engine == 'vinyl'
local read_ops = {'select', 'get', 'min', 'max', 'count', 'random', 'pairs'}
for _, op in ipairs(read_ops) do
if read_yields then
-- use Lua/C implmenetation
- index_mt[op] = box.schema.index_mt[op .. "_luac"]
+ local_index_mt[op] = local_index_mt[op .. "_luac"]
else
-- use FFI implementation
- index_mt[op] = box.schema.index_mt[op .. "_ffi"]
+ local_index_mt[op] = local_index_mt[op .. "_ffi"]
end
end
- --
+ -- Lua 5.2 compatibility
+ local_index_mt.__pairs = local_index_mt.pairs
+ local_index_mt.__ipairs = local_index_mt.ipairs
- setmetatable(space, box.schema.space_mt)
+ setmetatable(space, space_mt)
if type(space.index) == 'table' and space.enabled then
for j, index in pairs(space.index) do
if type(j) == 'number' then
- setmetatable(index, index_mt)
+ setmetatable(index, local_index_mt)
end
end
end
diff --git a/test/box-tap/schema_mt.test.lua b/test/box-tap/schema_mt.test.lua
index 7e531dd01..096c4ad60 100755
--- a/test/box-tap/schema_mt.test.lua
+++ b/test/box-tap/schema_mt.test.lua
@@ -71,4 +71,9 @@ test:is(
)
test:check()
+
+sp1:drop()
+sp2:drop()
+sp3:drop()
+sp4:drop()
os.exit(0)
--
2.14.3 (Apple Git-98)
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 0/2] schema: expose space_mt and index_mt on table
2018-03-23 13:07 [PATCH 0/2] schema: expose space_mt and index_mt on table Vladislav Shpilevoy
2018-03-23 13:07 ` [PATCH 1/2] schema: expose space_mt and index_mt on `box.schema` table Vladislav Shpilevoy
2018-03-23 13:07 ` [PATCH 2/2] schema: review fixes for box.schema.space/index metatables Vladislav Shpilevoy
@ 2018-03-29 10:54 ` Vladimir Davydov
2018-03-29 14:31 ` [PATCH v2 0/2] schema: expose space_mt and index_mt on box.schema table Vladislav Shpilevoy
2 siblings, 1 reply; 20+ messages in thread
From: Vladimir Davydov @ 2018-03-29 10:54 UTC (permalink / raw)
To: Vladislav Shpilevoy, Konstantin Osipov; +Cc: tarantool-patches
On Fri, Mar 23, 2018 at 04:07:07PM +0300, Vladislav Shpilevoy wrote:
> Branch: http://github.com/tarantool/tarantool/tree/pr-3204-expose-space-index-mt
> Issue: https://github.com/tarantool/tarantool/issues/3204
>
> Some of users want to extend space and index methods with their
> own, but do not want to do it for each space, because a metatable
> are created individually for a space.
I don't understand why anyone would do that. If one wants to
implement some extra methods, they can always create a wrapper
in Lua. Overwriting a metatable for this looks like a dirty hack.
Anyway, the patch is nearly impossible to review, because it is
basically a 100% diff. If you still think it's useful and really
want it to get reviewed, please split it properly: first move
index_mt and space_mt definitions from box.schema.space.bless
without introducing any functional changes, then export them.
>
> Move these metatables to box.schema, and lookup into them, when
> a method was not found in an original metatable.
>
> Vladislav Shpilevoy (1):
> schema: review fixes for box.schema.space/index metatables
>
> aleclarson (1):
> schema: expose space_mt and index_mt on `box.schema` table
>
> src/box/lua/schema.lua | 631 ++++++++++++++++++++--------------------
> test/box-tap/schema_mt.test.lua | 79 +++++
> 2 files changed, 391 insertions(+), 319 deletions(-)
> create mode 100755 test/box-tap/schema_mt.test.lua
^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH v2 0/2] schema: expose space_mt and index_mt on box.schema table
2018-03-29 10:54 ` [PATCH 0/2] schema: expose space_mt and index_mt on table Vladimir Davydov
@ 2018-03-29 14:31 ` Vladislav Shpilevoy
2018-03-29 14:31 ` [PATCH v2 1/2] schema: move space_mt and index_mt definition out of space bless Vladislav Shpilevoy
` (2 more replies)
0 siblings, 3 replies; 20+ messages in thread
From: Vladislav Shpilevoy @ 2018-03-29 14:31 UTC (permalink / raw)
To: tarantool-patches; +Cc: vdavydov.dev, Vladislav Shpilevoy
Branch: http://github.com/tarantool/tarantool/tree/pr-3204-expose-space-index-mt
Issue: https://github.com/tarantool/tarantool/issues/3204
Vladislav Shpilevoy (1):
schema: expose space_mt and index_mt on `box.schema` table
aleclarson (1):
schema: move space_mt and index_mt definition out of space bless
src/box/lua/schema.lua | 631 ++++++++++++++++++++--------------------
test/box-tap/schema_mt.test.lua | 79 +++++
2 files changed, 391 insertions(+), 319 deletions(-)
create mode 100755 test/box-tap/schema_mt.test.lua
--
2.14.3 (Apple Git-98)
^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH v2 1/2] schema: move space_mt and index_mt definition out of space bless
2018-03-29 14:31 ` [PATCH v2 0/2] schema: expose space_mt and index_mt on box.schema table Vladislav Shpilevoy
@ 2018-03-29 14:31 ` Vladislav Shpilevoy
2018-04-01 9:33 ` Vladimir Davydov
2018-03-29 14:31 ` [PATCH v2 2/2] schema: expose space_mt and index_mt on `box.schema` table Vladislav Shpilevoy
2018-04-01 9:37 ` [PATCH v2 0/2] schema: expose space_mt and index_mt on box.schema table Vladimir Davydov
2 siblings, 1 reply; 20+ messages in thread
From: Vladislav Shpilevoy @ 2018-03-29 14:31 UTC (permalink / raw)
To: tarantool-patches; +Cc: vdavydov.dev, aleclarson
From: aleclarson <alec.stanford.larson@gmail.com>
Space_mt and index_mt are created individually for each space
inside a giant space.bless function. It makes impossible to
implement #3204: exposing space and index metatables to a user,
because for this they must be global and shared between all
spaces and indexes.
Lets move their definition out of space.bless function, and do
their duplicate inside.
Needed #3204
---
src/box/lua/schema.lua | 608 +++++++++++++++++++---------------------
test/box-tap/schema_mt.test.lua | 79 ++++++
2 files changed, 373 insertions(+), 314 deletions(-)
create mode 100755 test/box-tap/schema_mt.test.lua
diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua
index 5a0f71559..ff89c8e58 100644
--- a/src/box/lua/schema.lua
+++ b/src/box/lua/schema.lua
@@ -1074,221 +1074,321 @@ end
internal.check_iterator_type = check_iterator_type -- export for net.box
-function box.schema.space.bless(space)
- local index_mt = {}
- -- __len and __index
- index_mt.len = function(index)
- check_index_arg(index, 'len')
- local ret = builtin.box_index_len(index.space_id, index.id)
- if ret == -1 then
- box.error()
- end
- return tonumber(ret)
- end
- -- index.bsize
- index_mt.bsize = function(index)
- check_index_arg(index, 'bsize')
- local ret = builtin.box_index_bsize(index.space_id, index.id)
- if ret == -1 then
- box.error()
- end
- return tonumber(ret)
- end
- index_mt.__len = index_mt.len -- Lua 5.2 compatibility
- index_mt.__newindex = function(table, index)
- return error('Attempt to modify a read-only table') end
- index_mt.__index = index_mt
- -- min and max
- index_mt.min_ffi = function(index, key)
- check_index_arg(index, 'min')
- local pkey, pkey_end = tuple_encode(key)
- if builtin.box_index_min(index.space_id, index.id,
- pkey, pkey_end, ptuple) ~= 0 then
- box.error() -- error
- elseif ptuple[0] ~= nil then
- return tuple_bless(ptuple[0])
- else
- return
- end
+local space_mt = {}
+space_mt.__index = space_mt
+box.schema.space_mt = space_mt
+
+function space_mt:len()
+ check_space_arg(self, 'len')
+ local pk = self.index[0]
+ if pk == nil then
+ return 0 -- empty space without indexes, return 0
end
- index_mt.min_luac = function(index, key)
- check_index_arg(index, 'min')
- key = keify(key)
- return internal.min(index.space_id, index.id, key);
+ return self.index[0]:len()
+end
+function space_mt:count(key, opts)
+ check_space_arg(self, 'count')
+ local pk = self.index[0]
+ if pk == nil then
+ return 0 -- empty space without indexes, return 0
end
- index_mt.max_ffi = function(index, key)
- check_index_arg(index, 'max')
- local pkey, pkey_end = tuple_encode(key)
- if builtin.box_index_max(index.space_id, index.id,
- pkey, pkey_end, ptuple) ~= 0 then
- box.error() -- error
- elseif ptuple[0] ~= nil then
- return tuple_bless(ptuple[0])
- else
- return
- end
+ return pk:count(key, opts)
+end
+function space_mt:bsize()
+ check_space_arg(self, 'bsize')
+ local s = builtin.space_by_id(self.id)
+ if s == nil then
+ box.error(box.error.NO_SUCH_SPACE, self.name)
end
- index_mt.max_luac = function(index, key)
- check_index_arg(index, 'max')
- key = keify(key)
- return internal.max(index.space_id, index.id, key);
+ return builtin.space_bsize(s)
+end
+function space_mt:get(key)
+ check_space_arg(self, 'get')
+ return check_primary_index(self):get(key)
+end
+function space_mt:select(key, opts)
+ check_space_arg(self, 'select')
+ return check_primary_index(self):select(key, opts)
+end
+function space_mt:insert(tuple)
+ check_space_arg(self, 'insert')
+ return internal.insert(self.id, tuple)
+end
+function space_mt:replace(tuple)
+ check_space_arg(self, 'replace')
+ return internal.replace(self.id, tuple)
+end
+space_mt.put = space_mt.replace -- put is an alias for replace
+function space_mt:update(key, ops)
+ check_space_arg(self, 'update')
+ return check_primary_index(self):update(key, ops)
+end
+function space_mt:upsert(tuple_key, ops, deprecated)
+ check_space_arg(self, '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
- index_mt.random_ffi = function(index, rnd)
- check_index_arg(index, 'random')
- rnd = rnd or math.random()
- if builtin.box_index_random(index.space_id, index.id, rnd,
- ptuple) ~= 0 then
- box.error() -- error
- elseif ptuple[0] ~= nil then
- return tuple_bless(ptuple[0])
- else
- return
- end
+ return internal.upsert(self.id, tuple_key, ops)
+end
+function space_mt:delete(key)
+ check_space_arg(self, 'delete')
+ return check_primary_index(self):delete(key)
+end
+-- Assumes that spaceno has a TREE (NUM) primary key
+-- inserts a tuple after getting the next value of the
+-- primary key and returns it back to the user
+function space_mt:auto_increment(tuple)
+ check_space_arg(self, 'auto_increment')
+ local max_tuple = check_primary_index(self):max()
+ local max = 0
+ if max_tuple ~= nil then
+ max = max_tuple[1]
+ end
+ table.insert(tuple, 1, max + 1)
+ return self:insert(tuple)
+end
+function space_mt:pairs(key, opts)
+ check_space_arg(self, 'pairs')
+ local pk = self.index[0]
+ if pk == nil then
+ -- empty space without indexes, return empty iterator
+ return fun.iter({})
+ end
+ return pk:pairs(key, opts)
+end
+space_mt.__pairs = space_mt.pairs -- Lua 5.2 compatibility
+space_mt.__ipairs = space_mt.pairs -- Lua 5.2 compatibility
+function space_mt:truncate()
+ check_space_arg(self, 'truncate')
+ return internal.truncate(self.id)
+end
+function space_mt:format(format)
+ check_space_arg(self, 'format')
+ return box.schema.space.format(self.id, format)
+end
+function space_mt:drop()
+ check_space_arg(self, 'drop')
+ check_space_exists(self)
+ return box.schema.space.drop(self.id, self.name)
+end
+function space_mt:rename(name)
+ check_space_arg(self, 'rename')
+ check_space_exists(self)
+ return box.schema.space.rename(self.id, name)
+end
+function space_mt:create_index(name, options)
+ check_space_arg(self, 'create_index')
+ check_space_exists(self)
+ return box.schema.index.create(self.id, name, options)
+end
+function space_mt:run_triggers(yesno)
+ check_space_arg(self, 'run_triggers')
+ local s = builtin.space_by_id(self.id)
+ if s == nil then
+ box.error(box.error.NO_SUCH_SPACE, self.name)
end
- index_mt.random_luac = function(index, rnd)
- check_index_arg(index, 'random')
- rnd = rnd or math.random()
- return internal.random(index.space_id, index.id, rnd);
- end
- -- iteration
- index_mt.pairs_ffi = function(index, key, opts)
- check_index_arg(index, 'pairs')
- local pkey, pkey_end = tuple_encode(key)
- local itype = check_iterator_type(opts, pkey + 1 >= pkey_end);
-
- local keybuf = ffi.string(pkey, pkey_end - pkey)
- local pkeybuf = ffi.cast('const char *', keybuf)
- local cdata = builtin.box_index_iterator(index.space_id, index.id,
- itype, pkeybuf, pkeybuf + #keybuf);
- if cdata == nil then
- box.error()
- end
- return fun.wrap(iterator_gen, keybuf,
- ffi.gc(cdata, builtin.box_iterator_free))
- end
- index_mt.pairs_luac = function(index, key, opts)
- check_index_arg(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 = internal.iterator(index.space_id, index.id, itype, keymp);
- return fun.wrap(iterator_gen_luac, keybuf,
- ffi.gc(cdata, builtin.box_iterator_free))
- end
-
- -- index subtree size
- index_mt.count_ffi = function(index, key, opts)
- check_index_arg(index, 'count')
- local pkey, pkey_end = tuple_encode(key)
- local itype = check_iterator_type(opts, pkey + 1 >= pkey_end);
- local count = builtin.box_index_count(index.space_id, index.id,
- itype, pkey, pkey_end);
- if count == -1 then
- box.error()
- end
- return tonumber(count)
- end
- index_mt.count_luac = function(index, key, opts)
- check_index_arg(index, 'count')
- key = keify(key)
- local itype = check_iterator_type(opts, #key == 0);
- return internal.count(index.space_id, index.id, itype, key);
- end
-
- index_mt.get_ffi = function(index, key)
- check_index_arg(index, 'get')
- local key, key_end = tuple_encode(key)
- if builtin.box_index_get(index.space_id, index.id,
- key, key_end, ptuple) ~= 0 then
- return box.error() -- error
- elseif ptuple[0] ~= nil then
- return tuple_bless(ptuple[0])
- else
- return
- end
+ builtin.space_run_triggers(s, yesno)
+end
+
+local index_mt = {}
+index_mt.__index = index_mt
+box.schema.index_mt = index_mt
+
+function index_mt:len()
+ check_index_arg(self, 'len')
+ local ret = builtin.box_index_len(self.space_id, self.id)
+ if ret == -1 then
+ box.error()
end
- index_mt.get_luac = function(index, key)
- check_index_arg(index, 'get')
- key = keify(key)
- return internal.get(index.space_id, index.id, key)
+ return tonumber(ret)
+end
+function index_mt:bsize()
+ check_index_arg(self, 'bsize')
+ local ret = builtin.box_index_bsize(self.space_id, self.id)
+ if ret == -1 then
+ box.error()
end
-
- local function check_select_opts(opts, key_is_nil)
- local offset = 0
- local limit = 4294967295
- local iterator = check_iterator_type(opts, key_is_nil)
- if opts ~= nil then
- if opts.offset ~= nil then
- offset = opts.offset
- end
- if opts.limit ~= nil then
- limit = opts.limit
- end
- end
- return iterator, offset, limit
+ return tonumber(ret)
+end
+index_mt.__len = index_mt.len -- Lua 5.2 compatibility
+index_mt.__index = index_mt
+function index_mt:min_ffi(key)
+ check_index_arg(self, 'min')
+ local pkey, pkey_end = tuple_encode(key)
+ if builtin.box_index_min(self.space_id, self.id,
+ pkey, pkey_end, ptuple) ~= 0 then
+ box.error()
+ elseif ptuple[0] ~= nil then
+ return tuple_bless(ptuple[0])
end
+end
+function index_mt:min_luac(key)
+ check_index_arg(self, 'min')
+ key = keify(key)
+ return internal.min(self.space_id, self.id, key)
+end
+function index_mt:max_ffi(key)
+ check_index_arg(self, 'max')
+ local pkey, pkey_end = tuple_encode(key)
+ if builtin.box_index_max(self.space_id, self.id,
+ pkey, pkey_end, ptuple) ~= 0 then
+ box.error()
+ elseif ptuple[0] ~= nil then
+ return tuple_bless(ptuple[0])
+ end
+end
+function index_mt:max_luac(key)
+ check_index_arg(self, 'max')
+ key = keify(key)
+ return internal.max(self.space_id, self.id, key)
+end
+function index_mt:random_ffi(rnd)
+ check_index_arg(self, 'random')
+ rnd = rnd or math.random()
+ if builtin.box_index_random(self.space_id, self.id, rnd, ptuple) ~= 0 then
+ box.error()
+ elseif ptuple[0] ~= nil then
+ return tuple_bless(ptuple[0])
+ end
+end
+function index_mt:random_luac(rnd)
+ check_index_arg(self, 'random')
+ rnd = rnd or math.random()
+ return internal.random(self.space_id, self.id, rnd)
+end
+function index_mt:pairs_ffi(key, opts)
+ check_index_arg(self, 'pairs')
+ local pkey, pkey_end = tuple_encode(key)
+ local itype = check_iterator_type(opts, pkey + 1 >= pkey_end)
- index_mt.select_ffi = function(index, key, opts)
- check_index_arg(index, 'select')
- local key, key_end = tuple_encode(key)
- local iterator, offset, limit = check_select_opts(opts, key + 1 >= key_end)
-
- local port = ffi.cast('struct port *', port_tuple)
+ local keybuf = ffi.string(pkey, pkey_end - pkey)
+ local pkeybuf = ffi.cast('const char *', keybuf)
+ local cdata = builtin.box_index_iterator(self.space_id, self.id, itype,
+ pkeybuf, pkeybuf + #keybuf)
+ if cdata == nil then
+ box.error()
+ end
+ return fun.wrap(iterator_gen, keybuf,
+ ffi.gc(cdata, builtin.box_iterator_free))
+end
+function index_mt:pairs_luac(key, opts)
+ check_index_arg(self, '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 = internal.iterator(self.space_id, self.id, itype, keymp)
+ return fun.wrap(iterator_gen_luac, keybuf,
+ ffi.gc(cdata, builtin.box_iterator_free))
+end
+function index_mt:count_ffi(key, opts)
+ check_index_arg(self, 'count')
+ local pkey, pkey_end = tuple_encode(key)
+ local itype = check_iterator_type(opts, pkey + 1 >= pkey_end)
+ local count = builtin.box_index_count(self.space_id, self.id, itype, pkey,
+ pkey_end)
+ if count == -1 then
+ box.error()
+ end
+ return tonumber(count)
+end
+function index_mt:count_luac(key, opts)
+ check_index_arg(self, 'count')
+ key = keify(key)
+ local itype = check_iterator_type(opts, #key == 0)
+ return internal.count(self.space_id, self.id, itype, key)
+end
+function index_mt:get_ffi(key)
+ check_index_arg(self, 'get')
+ local key, key_end = tuple_encode(key)
+ if builtin.box_index_get(self.space_id, self.id, key, key_end,
+ ptuple) ~= 0 then
+ return box.error()
+ elseif ptuple[0] ~= nil then
+ return tuple_bless(ptuple[0])
+ end
+end
+function index_mt:get_luac(key)
+ check_index_arg(self, 'get')
+ key = keify(key)
+ return internal.get(self.space_id, self.id, key)
+end
- if builtin.box_select(index.space_id, index.id,
- iterator, offset, limit, key, key_end, port) ~= 0 then
- return box.error()
+local function check_select_opts(opts, key_is_nil)
+ local offset = 0
+ local limit = 4294967295
+ local iterator = check_iterator_type(opts, key_is_nil)
+ if opts ~= nil then
+ if opts.offset ~= nil then
+ offset = opts.offset
end
-
- local ret = {}
- local entry = port_tuple.first
- for i=1,tonumber(port_tuple.size),1 do
- ret[i] = tuple_bless(entry.tuple)
- entry = entry.next
+ if opts.limit ~= nil then
+ limit = opts.limit
end
- builtin.port_destroy(port);
- return ret
end
+ return iterator, offset, limit
+end
- index_mt.select_luac = function(index, key, opts)
- check_index_arg(index, 'select')
- local key = keify(key)
- local iterator, offset, limit = check_select_opts(opts, #key == 0)
- return internal.select(index.space_id, index.id, iterator,
- offset, limit, key)
- end
+function index_mt:select_ffi(key, opts)
+ check_index_arg(self, 'select')
+ local key, key_end = tuple_encode(key)
+ local iterator, offset, limit = check_select_opts(opts, key + 1 >= key_end)
- index_mt.update = function(index, key, ops)
- check_index_arg(index, 'update')
- return internal.update(index.space_id, index.id, keify(key), ops);
- end
- index_mt.delete = function(index, key)
- check_index_arg(index, 'delete')
- return internal.delete(index.space_id, index.id, keify(key));
- end
+ local port = ffi.cast('struct port *', port_tuple)
- index_mt.info = function(index)
- return internal.info(index.space_id, index.id);
+ if builtin.box_select(self.space_id, self.id, iterator, offset, limit, key,
+ key_end, port) ~= 0 then
+ return box.error()
end
- index_mt.drop = function(index)
- check_index_arg(index, 'drop')
- return box.schema.index.drop(index.space_id, index.id)
- end
- index_mt.rename = function(index, name)
- check_index_arg(index, 'rename')
- return box.schema.index.rename(index.space_id, index.id, name)
+ local ret = {}
+ local entry = port_tuple.first
+ for i=1,tonumber(port_tuple.size),1 do
+ ret[i] = tuple_bless(entry.tuple)
+ entry = entry.next
end
- index_mt.alter = function(index, options)
- check_index_arg(index, 'alter')
- if index.id == nil or index.space_id == nil then
- box.error(box.error.PROC_LUA, "Usage: index:alter{opts}")
- end
- return box.schema.index.alter(index.space_id, index.id, options)
+ builtin.port_destroy(port)
+ return ret
+end
+function index_mt:select_luac(key, opts)
+ check_index_arg(self, 'select')
+ local key = keify(key)
+ local iterator, offset, limit = check_select_opts(opts, #key == 0)
+ return internal.select(self.space_id, self.id, iterator, offset, limit, key)
+end
+function index_mt:update(key, ops)
+ check_index_arg(self, 'update')
+ return internal.update(self.space_id, self.id, keify(key), ops)
+end
+function index_mt:delete(key)
+ check_index_arg(self, 'delete')
+ return internal.delete(self.space_id, self.id, keify(key))
+end
+function index_mt:info()
+ return internal.info(self.space_id, self.id)
+end
+function index_mt:drop()
+ check_index_arg(self, 'drop')
+ return box.schema.index.drop(self.space_id, self.id)
+end
+function index_mt:rename(name)
+ check_index_arg(self, 'rename')
+ return box.schema.index.rename(self.space_id, self.id, name)
+end
+function index_mt:alter(options)
+ check_index_arg(self, 'alter')
+ if self.id == nil or self.space_id == nil then
+ box.error(box.error.PROC_LUA, "Usage: index:alter{opts}")
end
+ return box.schema.index.alter(self.space_id, self.id, options)
+end
- -- true if reading operations may yield
+function box.schema.space.bless(space)
+ local space_mt = table.deepcopy(space_mt)
+ local index_mt = table.deepcopy(index_mt)
local read_yields = space.engine == 'vinyl'
local read_ops = {'select', 'get', 'min', 'max', 'count', 'random', 'pairs'}
for _, op in ipairs(read_ops) do
@@ -1302,126 +1402,6 @@ function box.schema.space.bless(space)
end
index_mt.__pairs = index_mt.pairs -- Lua 5.2 compatibility
index_mt.__ipairs = index_mt.pairs -- Lua 5.2 compatibility
- --
- local space_mt = {}
- space_mt.len = function(space)
- check_space_arg(space, 'len')
- local pk = space.index[0]
- if pk == nil then
- return 0 -- empty space without indexes, return 0
- end
- return space.index[0]:len()
- end
- space_mt.count = function(space, key, opts)
- check_space_arg(space, 'count')
- local pk = space.index[0]
- if pk == nil then
- return 0 -- empty space without indexes, return 0
- end
- return pk:count(key, opts)
- end
- space_mt.bsize = function(space)
- check_space_arg(space, 'bsize')
- local s = builtin.space_by_id(space.id)
- if s == nil then
- box.error(box.error.NO_SUCH_SPACE, space.name)
- end
- return builtin.space_bsize(s)
- end
- space_mt.__newindex = index_mt.__newindex
-
- space_mt.get = function(space, key)
- check_space_arg(space, 'get')
- return check_primary_index(space):get(key)
- end
- space_mt.select = function(space, key, opts)
- check_space_arg(space, 'select')
- return check_primary_index(space):select(key, opts)
- end
- space_mt.insert = function(space, tuple)
- check_space_arg(space, 'insert')
- return internal.insert(space.id, tuple);
- end
- space_mt.replace = function(space, tuple)
- check_space_arg(space, 'replace')
- return internal.replace(space.id, tuple);
- end
- space_mt.put = space_mt.replace; -- put is an alias for replace
- space_mt.update = function(space, key, ops)
- check_space_arg(space, 'update')
- return check_primary_index(space):update(key, ops)
- end
- space_mt.upsert = function(space, tuple_key, ops, deprecated)
- check_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 internal.upsert(space.id, tuple_key, ops);
- end
- space_mt.delete = function(space, key)
- check_space_arg(space, 'delete')
- return check_primary_index(space):delete(key)
- end
--- Assumes that spaceno has a TREE (NUM) primary key
--- inserts a tuple after getting the next value of the
--- primary key and returns it back to the user
- space_mt.auto_increment = function(space, tuple)
- check_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_mt.pairs = function(space, key, opts)
- check_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_mt.__pairs = space_mt.pairs -- Lua 5.2 compatibility
- space_mt.__ipairs = space_mt.pairs -- Lua 5.2 compatibility
- space_mt.truncate = function(space)
- check_space_arg(space, 'truncate')
- return internal.truncate(space.id)
- end
- space_mt.format = function(space, format)
- check_space_arg(space, 'format')
- return box.schema.space.format(space.id, format)
- end
- space_mt.drop = function(space)
- check_space_arg(space, 'drop')
- check_space_exists(space)
- return box.schema.space.drop(space.id, space.name)
- end
- space_mt.rename = function(space, name)
- check_space_arg(space, 'rename')
- check_space_exists(space)
- return box.schema.space.rename(space.id, name)
- end
- space_mt.create_index = function(space, name, options)
- check_space_arg(space, 'create_index')
- check_space_exists(space)
- return box.schema.index.create(space.id, name, options)
- end
- space_mt.run_triggers = function(space, yesno)
- check_space_arg(space, 'run_triggers')
- local s = builtin.space_by_id(space.id)
- if s == nil then
- box.error(box.error.NO_SUCH_SPACE, space.name)
- end
- builtin.space_run_triggers(s, yesno)
- end
- space_mt.__index = space_mt
setmetatable(space, space_mt)
if type(space.index) == 'table' and space.enabled then
diff --git a/test/box-tap/schema_mt.test.lua b/test/box-tap/schema_mt.test.lua
new file mode 100755
index 000000000..096c4ad60
--- /dev/null
+++ b/test/box-tap/schema_mt.test.lua
@@ -0,0 +1,79 @@
+#!/usr/bin/env tarantool
+--
+-- pr-3204: expose space_mt, index_mt into box.schema.
+--
+
+local tap = require('tap')
+local test = tap.test('schema_mt')
+
+test:plan(7)
+
+box.cfg{
+ log="tarantool.log",
+}
+
+local sp1 = box.schema.space.create('test')
+local sp2 = box.schema.space.create('test2')
+
+test:is(
+ getmetatable(sp1),
+ getmetatable(sp2),
+ 'all spaces use the same metatable'
+)
+
+local idx1 = sp1:create_index('primary')
+local idx2 = sp2:create_index('primary')
+
+test:isnt(
+ getmetatable(idx1),
+ getmetatable(idx2),
+ 'all indexes have their own metatable'
+)
+
+test:is(
+ idx1.get,
+ idx2.get,
+ 'memtx indexes share read methods'
+)
+
+local sp3 = box.schema.space.create('test3', {engine='vinyl'})
+local sp4 = box.schema.space.create('test4', {engine='vinyl'})
+
+local idx3 = sp3:create_index('primary')
+local idx4 = sp4:create_index('primary')
+
+test:is(
+ idx3.get,
+ idx4.get,
+ 'vinyl indexes share read methods'
+)
+
+test:isnt(
+ idx1.get,
+ idx3.get,
+ 'memtx and vinyl indexes have separate read methods'
+)
+
+function box.schema.space_mt.foo() end
+
+test:is(
+ sp1.foo,
+ box.schema.space_mt.foo,
+ 'box.schema.space_mt is mutable'
+)
+
+function box.schema.index_mt.foo() end
+
+test:is(
+ idx1.foo,
+ box.schema.index_mt.foo,
+ 'box.schema.index_mt is mutable'
+)
+
+test:check()
+
+sp1:drop()
+sp2:drop()
+sp3:drop()
+sp4:drop()
+os.exit(0)
--
2.14.3 (Apple Git-98)
^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH v2 2/2] schema: expose space_mt and index_mt on `box.schema` table
2018-03-29 14:31 ` [PATCH v2 0/2] schema: expose space_mt and index_mt on box.schema table Vladislav Shpilevoy
2018-03-29 14:31 ` [PATCH v2 1/2] schema: move space_mt and index_mt definition out of space bless Vladislav Shpilevoy
@ 2018-03-29 14:31 ` Vladislav Shpilevoy
2018-04-01 9:37 ` [PATCH v2 0/2] schema: expose space_mt and index_mt on box.schema table Vladimir Davydov
2 siblings, 0 replies; 20+ messages in thread
From: Vladislav Shpilevoy @ 2018-03-29 14:31 UTC (permalink / raw)
To: tarantool-patches; +Cc: vdavydov.dev, Vladislav Shpilevoy
This commit allows userland to extend the space and index
metatables with their own functions or even metamethods. Reducing
barriers for this kind of experimentation is vital for user
contribution toward the improvement of Tarantool's API.
Closes #3204
---
src/box/lua/schema.lua | 27 ++++++++++++++++++++-------
1 file changed, 20 insertions(+), 7 deletions(-)
diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua
index ff89c8e58..2f85bc541 100644
--- a/src/box/lua/schema.lua
+++ b/src/box/lua/schema.lua
@@ -1387,27 +1387,40 @@ function index_mt:alter(options)
end
function box.schema.space.bless(space)
- local space_mt = table.deepcopy(space_mt)
- local index_mt = table.deepcopy(index_mt)
+ local local_index_mt = {}
+ -- At first, reference all global index functions. At second,
+ -- choose an implementation for read operations. They are not
+ -- in a global index_mt, because they are engine specific.
+ -- All common index_mt functions must be referenced here to
+ -- be able to get them using getmetatable(index_object) - if
+ -- they are only in index_mt, then getmetatable() returns only
+ -- read ops.
+ for k, v in pairs(index_mt) do
+ local_index_mt[k] = v
+ end
+ local_index_mt.__index = function(index, key)
+ return local_index_mt[key] or index_mt[key]
+ end
local read_yields = space.engine == 'vinyl'
local read_ops = {'select', 'get', 'min', 'max', 'count', 'random', 'pairs'}
for _, op in ipairs(read_ops) do
if read_yields then
-- use Lua/C implmenetation
- index_mt[op] = index_mt[op .. "_luac"]
+ local_index_mt[op] = local_index_mt[op .. "_luac"]
else
-- use FFI implementation
- index_mt[op] = index_mt[op .. "_ffi"]
+ local_index_mt[op] = local_index_mt[op .. "_ffi"]
end
end
- index_mt.__pairs = index_mt.pairs -- Lua 5.2 compatibility
- index_mt.__ipairs = index_mt.pairs -- Lua 5.2 compatibility
+ -- Lua 5.2 compatibility
+ local_index_mt.__pairs = local_index_mt.pairs
+ local_index_mt.__ipairs = local_index_mt.pairs
setmetatable(space, space_mt)
if type(space.index) == 'table' and space.enabled then
for j, index in pairs(space.index) do
if type(j) == 'number' then
- setmetatable(index, index_mt)
+ setmetatable(index, local_index_mt)
end
end
end
--
2.14.3 (Apple Git-98)
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2 1/2] schema: move space_mt and index_mt definition out of space bless
2018-03-29 14:31 ` [PATCH v2 1/2] schema: move space_mt and index_mt definition out of space bless Vladislav Shpilevoy
@ 2018-04-01 9:33 ` Vladimir Davydov
2018-04-01 11:02 ` [PATCH 0/2] schema: expose space_mt and index_mt on box.schema table Vladislav Shpilevoy
0 siblings, 1 reply; 20+ messages in thread
From: Vladimir Davydov @ 2018-04-01 9:33 UTC (permalink / raw)
To: Vladislav Shpilevoy; +Cc: tarantool-patches, Konstantin Osipov
For the record, as I said before
> I don't understand why anyone would do that. If one wants to
> implement some extra methods, they can always create a wrapper
> in Lua. Overwriting a metatable for this looks like a dirty hack.
so this is a purely technical review.
On Thu, Mar 29, 2018 at 05:31:44PM +0300, Vladislav Shpilevoy wrote:
> +function space_mt:len()
> + check_space_arg(self, 'len')
> + local pk = self.index[0]
> + if pk == nil then
> + return 0 -- empty space without indexes, return 0
> end
> +end
> - space_mt.len = function(space)
> - check_space_arg(space, 'len')
> - local pk = space.index[0]
> - if pk == nil then
> - return 0 -- empty space without indexes, return 0
> - end
> - return space.index[0]:len()
> - end
This isn't what I meant when I wrote
> move index_mt and space_mt definitions from box.schema.space.bless
> without introducing any functional changes, then export them
I meant that in the first patch you should just move these functions to
the top-level, without doing ANY changes to them, otherwise the patch
will be rather difficult to review.
Anyway, I don't think the piece of syntax sugar you're trying to push
(passing the object to a class method implicitly) is really necessary.
We never do it in schema.lua, why do it for space_mt/index_mt now?
And it's definitely doesn't belong to this patch. Please remove.
> diff --git a/test/box-tap/schema_mt.test.lua b/test/box-tap/schema_mt.test.lua
The test should go to patch 2, the one that exports the metatables.
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2 0/2] schema: expose space_mt and index_mt on box.schema table
2018-03-29 14:31 ` [PATCH v2 0/2] schema: expose space_mt and index_mt on box.schema table Vladislav Shpilevoy
2018-03-29 14:31 ` [PATCH v2 1/2] schema: move space_mt and index_mt definition out of space bless Vladislav Shpilevoy
2018-03-29 14:31 ` [PATCH v2 2/2] schema: expose space_mt and index_mt on `box.schema` table Vladislav Shpilevoy
@ 2018-04-01 9:37 ` Vladimir Davydov
2 siblings, 0 replies; 20+ messages in thread
From: Vladimir Davydov @ 2018-04-01 9:37 UTC (permalink / raw)
To: Vladislav Shpilevoy; +Cc: tarantool-patches
On Thu, Mar 29, 2018 at 05:31:43PM +0300, Vladislav Shpilevoy wrote:
> Branch: http://github.com/tarantool/tarantool/tree/pr-3204-expose-space-index-mt
> Issue: https://github.com/tarantool/tarantool/issues/3204
Nit-picking: change log is missing, see
https://tarantool.io/en/doc/1.9/dev_guide/developer_guidelines.html#how-to-submit-a-patch-for-review
>
> Vladislav Shpilevoy (1):
> schema: expose space_mt and index_mt on `box.schema` table
>
> aleclarson (1):
> schema: move space_mt and index_mt definition out of space bless
Please remove Alec from here.
>
> src/box/lua/schema.lua | 631 ++++++++++++++++++++--------------------
> test/box-tap/schema_mt.test.lua | 79 +++++
> 2 files changed, 391 insertions(+), 319 deletions(-)
> create mode 100755 test/box-tap/schema_mt.test.lua
^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH 0/2] schema: expose space_mt and index_mt on box.schema table
2018-04-01 9:33 ` Vladimir Davydov
@ 2018-04-01 11:02 ` Vladislav Shpilevoy
2018-04-01 11:02 ` [PATCH 1/2] schema: move space_mt and index_mt definition out of space bless Vladislav Shpilevoy
2018-04-01 11:02 ` [PATCH 2/2] schema: expose space_mt and index_mt on `box.schema` table Vladislav Shpilevoy
0 siblings, 2 replies; 20+ messages in thread
From: Vladislav Shpilevoy @ 2018-04-01 11:02 UTC (permalink / raw)
To: tarantool-patches; +Cc: vdavydov.dev, Vladislav Shpilevoy
Branch: http://github.com/tarantool/tarantool/tree/pr-3204-expose-space-index-mt
Issue: https://github.com/tarantool/tarantool/issues/3204
Space_mt and index_mt are created individually for each space
inside a giant space.bless function. It consumes lua memory by
duplicating these metatables (which is very limited), and does not
allow to add new functions, which would be visible in all spaces
and indexes.
Lets move space_mt and index_mt definitions out of space.bless
function in box.schema.space/index_mt, and allow to expand them
with user functions.
Vladislav Shpilevoy (2):
schema: move space_mt and index_mt definition out of space bless
schema: expose space_mt and index_mt on `box.schema` table
src/box/lua/schema.lua | 636 ++++++++++++++++++++--------------------
test/box-tap/schema_mt.test.lua | 79 +++++
2 files changed, 404 insertions(+), 311 deletions(-)
create mode 100755 test/box-tap/schema_mt.test.lua
--
2.14.3 (Apple Git-98)
^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH 1/2] schema: move space_mt and index_mt definition out of space bless
2018-04-01 11:02 ` [PATCH 0/2] schema: expose space_mt and index_mt on box.schema table Vladislav Shpilevoy
@ 2018-04-01 11:02 ` Vladislav Shpilevoy
2018-04-01 11:02 ` [PATCH 2/2] schema: expose space_mt and index_mt on `box.schema` table Vladislav Shpilevoy
1 sibling, 0 replies; 20+ messages in thread
From: Vladislav Shpilevoy @ 2018-04-01 11:02 UTC (permalink / raw)
To: tarantool-patches; +Cc: vdavydov.dev, Vladislav Shpilevoy
Space_mt and index_mt are created individually for each space
inside a giant space.bless function. It makes impossible to
implement #3204: exposing space and index metatables to a user,
because for this they must be global and shared between all
spaces and indexes.
Lets move their definition out of space.bless function, and do
their duplicate inside.
Needed #3204
---
src/box/lua/schema.lua | 619 +++++++++++++++++++++++++------------------------
1 file changed, 311 insertions(+), 308 deletions(-)
diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua
index 5a0f71559..8b6f69ad8 100644
--- a/src/box/lua/schema.lua
+++ b/src/box/lua/schema.lua
@@ -1074,220 +1074,343 @@ end
internal.check_iterator_type = check_iterator_type -- export for net.box
-function box.schema.space.bless(space)
- local index_mt = {}
- -- __len and __index
- index_mt.len = function(index)
- check_index_arg(index, 'len')
- local ret = builtin.box_index_len(index.space_id, index.id)
- if ret == -1 then
- box.error()
- end
- return tonumber(ret)
- end
- -- index.bsize
- index_mt.bsize = function(index)
- check_index_arg(index, 'bsize')
- local ret = builtin.box_index_bsize(index.space_id, index.id)
- if ret == -1 then
- box.error()
- end
- return tonumber(ret)
- end
- index_mt.__len = index_mt.len -- Lua 5.2 compatibility
- index_mt.__newindex = function(table, index)
- return error('Attempt to modify a read-only table') end
- index_mt.__index = index_mt
- -- min and max
- index_mt.min_ffi = function(index, key)
- check_index_arg(index, 'min')
- local pkey, pkey_end = tuple_encode(key)
- if builtin.box_index_min(index.space_id, index.id,
- pkey, pkey_end, ptuple) ~= 0 then
- box.error() -- error
- elseif ptuple[0] ~= nil then
- return tuple_bless(ptuple[0])
- else
- return
- end
+local index_mt = {}
+-- __len and __index
+index_mt.len = function(index)
+ check_index_arg(index, 'len')
+ local ret = builtin.box_index_len(index.space_id, index.id)
+ if ret == -1 then
+ box.error()
end
- index_mt.min_luac = function(index, key)
- check_index_arg(index, 'min')
- key = keify(key)
- return internal.min(index.space_id, index.id, key);
+ return tonumber(ret)
+end
+-- index.bsize
+index_mt.bsize = function(index)
+ check_index_arg(index, 'bsize')
+ local ret = builtin.box_index_bsize(index.space_id, index.id)
+ if ret == -1 then
+ box.error()
end
- index_mt.max_ffi = function(index, key)
- check_index_arg(index, 'max')
- local pkey, pkey_end = tuple_encode(key)
- if builtin.box_index_max(index.space_id, index.id,
- pkey, pkey_end, ptuple) ~= 0 then
- box.error() -- error
- elseif ptuple[0] ~= nil then
- return tuple_bless(ptuple[0])
- else
- return
- end
+ return tonumber(ret)
+end
+index_mt.__len = index_mt.len -- Lua 5.2 compatibility
+index_mt.__newindex = function(table, index)
+ return error('Attempt to modify a read-only table')
+end
+index_mt.__index = index_mt
+-- min and max
+index_mt.min_ffi = function(index, key)
+ check_index_arg(index, 'min')
+ local pkey, pkey_end = tuple_encode(key)
+ if builtin.box_index_min(index.space_id, index.id,
+ pkey, pkey_end, ptuple) ~= 0 then
+ box.error() -- error
+ elseif ptuple[0] ~= nil then
+ return tuple_bless(ptuple[0])
+ else
+ return
end
- index_mt.max_luac = function(index, key)
- check_index_arg(index, 'max')
- key = keify(key)
- return internal.max(index.space_id, index.id, key);
+end
+index_mt.min_luac = function(index, key)
+ check_index_arg(index, 'min')
+ key = keify(key)
+ return internal.min(index.space_id, index.id, key);
+end
+index_mt.max_ffi = function(index, key)
+ check_index_arg(index, 'max')
+ local pkey, pkey_end = tuple_encode(key)
+ if builtin.box_index_max(index.space_id, index.id,
+ pkey, pkey_end, ptuple) ~= 0 then
+ box.error() -- error
+ elseif ptuple[0] ~= nil then
+ return tuple_bless(ptuple[0])
+ else
+ return
end
- index_mt.random_ffi = function(index, rnd)
- check_index_arg(index, 'random')
- rnd = rnd or math.random()
- if builtin.box_index_random(index.space_id, index.id, rnd,
- ptuple) ~= 0 then
- box.error() -- error
- elseif ptuple[0] ~= nil then
- return tuple_bless(ptuple[0])
- else
- return
- end
+end
+index_mt.max_luac = function(index, key)
+ check_index_arg(index, 'max')
+ key = keify(key)
+ return internal.max(index.space_id, index.id, key);
+end
+index_mt.random_ffi = function(index, rnd)
+ check_index_arg(index, 'random')
+ rnd = rnd or math.random()
+ if builtin.box_index_random(index.space_id, index.id, rnd,
+ ptuple) ~= 0 then
+ box.error() -- error
+ elseif ptuple[0] ~= nil then
+ return tuple_bless(ptuple[0])
+ else
+ return
end
- index_mt.random_luac = function(index, rnd)
- check_index_arg(index, 'random')
- rnd = rnd or math.random()
- return internal.random(index.space_id, index.id, rnd);
- end
- -- iteration
- index_mt.pairs_ffi = function(index, key, opts)
- check_index_arg(index, 'pairs')
- local pkey, pkey_end = tuple_encode(key)
- local itype = check_iterator_type(opts, pkey + 1 >= pkey_end);
-
- local keybuf = ffi.string(pkey, pkey_end - pkey)
- local pkeybuf = ffi.cast('const char *', keybuf)
- local cdata = builtin.box_index_iterator(index.space_id, index.id,
- itype, pkeybuf, pkeybuf + #keybuf);
- if cdata == nil then
- box.error()
- end
- return fun.wrap(iterator_gen, keybuf,
- ffi.gc(cdata, builtin.box_iterator_free))
- end
- index_mt.pairs_luac = function(index, key, opts)
- check_index_arg(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 = internal.iterator(index.space_id, index.id, itype, keymp);
- return fun.wrap(iterator_gen_luac, keybuf,
- ffi.gc(cdata, builtin.box_iterator_free))
- end
-
- -- index subtree size
- index_mt.count_ffi = function(index, key, opts)
- check_index_arg(index, 'count')
- local pkey, pkey_end = tuple_encode(key)
- local itype = check_iterator_type(opts, pkey + 1 >= pkey_end);
- local count = builtin.box_index_count(index.space_id, index.id,
- itype, pkey, pkey_end);
- if count == -1 then
- box.error()
- end
- return tonumber(count)
- end
- index_mt.count_luac = function(index, key, opts)
- check_index_arg(index, 'count')
- key = keify(key)
- local itype = check_iterator_type(opts, #key == 0);
- return internal.count(index.space_id, index.id, itype, key);
- end
-
- index_mt.get_ffi = function(index, key)
- check_index_arg(index, 'get')
- local key, key_end = tuple_encode(key)
- if builtin.box_index_get(index.space_id, index.id,
- key, key_end, ptuple) ~= 0 then
- return box.error() -- error
- elseif ptuple[0] ~= nil then
- return tuple_bless(ptuple[0])
- else
- return
- end
+end
+index_mt.random_luac = function(index, rnd)
+ check_index_arg(index, 'random')
+ rnd = rnd or math.random()
+ return internal.random(index.space_id, index.id, rnd);
+end
+-- iteration
+index_mt.pairs_ffi = function(index, key, opts)
+ check_index_arg(index, 'pairs')
+ local pkey, pkey_end = tuple_encode(key)
+ local itype = check_iterator_type(opts, pkey + 1 >= pkey_end);
+
+ local keybuf = ffi.string(pkey, pkey_end - pkey)
+ local pkeybuf = ffi.cast('const char *', keybuf)
+ local cdata = builtin.box_index_iterator(index.space_id, index.id,
+ itype, pkeybuf, pkeybuf + #keybuf);
+ if cdata == nil then
+ box.error()
end
- index_mt.get_luac = function(index, key)
- check_index_arg(index, 'get')
- key = keify(key)
- return internal.get(index.space_id, index.id, key)
+ return fun.wrap(iterator_gen, keybuf,
+ ffi.gc(cdata, builtin.box_iterator_free))
+end
+index_mt.pairs_luac = function(index, key, opts)
+ check_index_arg(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 = internal.iterator(index.space_id, index.id, itype, keymp);
+ return fun.wrap(iterator_gen_luac, keybuf,
+ ffi.gc(cdata, builtin.box_iterator_free))
+end
+
+-- index subtree size
+index_mt.count_ffi = function(index, key, opts)
+ check_index_arg(index, 'count')
+ local pkey, pkey_end = tuple_encode(key)
+ local itype = check_iterator_type(opts, pkey + 1 >= pkey_end);
+ local count = builtin.box_index_count(index.space_id, index.id,
+ itype, pkey, pkey_end);
+ if count == -1 then
+ box.error()
end
+ return tonumber(count)
+end
+index_mt.count_luac = function(index, key, opts)
+ check_index_arg(index, 'count')
+ key = keify(key)
+ local itype = check_iterator_type(opts, #key == 0);
+ return internal.count(index.space_id, index.id, itype, key);
+end
- local function check_select_opts(opts, key_is_nil)
- local offset = 0
- local limit = 4294967295
- local iterator = check_iterator_type(opts, key_is_nil)
- if opts ~= nil then
- if opts.offset ~= nil then
- offset = opts.offset
- end
- if opts.limit ~= nil then
- limit = opts.limit
- end
+index_mt.get_ffi = function(index, key)
+ check_index_arg(index, 'get')
+ local key, key_end = tuple_encode(key)
+ if builtin.box_index_get(index.space_id, index.id,
+ key, key_end, ptuple) ~= 0 then
+ return box.error() -- error
+ elseif ptuple[0] ~= nil then
+ return tuple_bless(ptuple[0])
+ else
+ return
+ end
+end
+index_mt.get_luac = function(index, key)
+ check_index_arg(index, 'get')
+ key = keify(key)
+ return internal.get(index.space_id, index.id, key)
+end
+
+local function check_select_opts(opts, key_is_nil)
+ local offset = 0
+ local limit = 4294967295
+ local iterator = check_iterator_type(opts, key_is_nil)
+ if opts ~= nil then
+ if opts.offset ~= nil then
+ offset = opts.offset
+ end
+ if opts.limit ~= nil then
+ limit = opts.limit
end
- return iterator, offset, limit
end
+ return iterator, offset, limit
+end
- index_mt.select_ffi = function(index, key, opts)
- check_index_arg(index, 'select')
- local key, key_end = tuple_encode(key)
- local iterator, offset, limit = check_select_opts(opts, key + 1 >= key_end)
+index_mt.select_ffi = function(index, key, opts)
+ check_index_arg(index, 'select')
+ local key, key_end = tuple_encode(key)
+ local iterator, offset, limit = check_select_opts(opts, key + 1 >= key_end)
- local port = ffi.cast('struct port *', port_tuple)
+ local port = ffi.cast('struct port *', port_tuple)
- if builtin.box_select(index.space_id, index.id,
- iterator, offset, limit, key, key_end, port) ~= 0 then
- return box.error()
- end
+ if builtin.box_select(index.space_id, index.id,
+ iterator, offset, limit, key, key_end, port) ~= 0 then
+ return box.error()
+ end
- local ret = {}
- local entry = port_tuple.first
- for i=1,tonumber(port_tuple.size),1 do
- ret[i] = tuple_bless(entry.tuple)
- entry = entry.next
- end
- builtin.port_destroy(port);
- return ret
+ local ret = {}
+ local entry = port_tuple.first
+ for i=1,tonumber(port_tuple.size),1 do
+ ret[i] = tuple_bless(entry.tuple)
+ entry = entry.next
end
+ builtin.port_destroy(port);
+ return ret
+end
+
+index_mt.select_luac = function(index, key, opts)
+ check_index_arg(index, 'select')
+ local key = keify(key)
+ local iterator, offset, limit = check_select_opts(opts, #key == 0)
+ return internal.select(index.space_id, index.id, iterator,
+ offset, limit, key)
+end
- index_mt.select_luac = function(index, key, opts)
- check_index_arg(index, 'select')
- local key = keify(key)
- local iterator, offset, limit = check_select_opts(opts, #key == 0)
- return internal.select(index.space_id, index.id, iterator,
- offset, limit, key)
+index_mt.update = function(index, key, ops)
+ check_index_arg(index, 'update')
+ return internal.update(index.space_id, index.id, keify(key), ops);
+end
+index_mt.delete = function(index, key)
+ check_index_arg(index, 'delete')
+ return internal.delete(index.space_id, index.id, keify(key));
+end
+
+index_mt.info = function(index)
+ return internal.info(index.space_id, index.id);
+end
+
+index_mt.drop = function(index)
+ check_index_arg(index, 'drop')
+ return box.schema.index.drop(index.space_id, index.id)
+end
+index_mt.rename = function(index, name)
+ check_index_arg(index, 'rename')
+ return box.schema.index.rename(index.space_id, index.id, name)
+end
+index_mt.alter = function(index, options)
+ check_index_arg(index, 'alter')
+ if index.id == nil or index.space_id == nil then
+ box.error(box.error.PROC_LUA, "Usage: index:alter{opts}")
end
+ return box.schema.index.alter(index.space_id, index.id, options)
+end
- index_mt.update = function(index, key, ops)
- check_index_arg(index, 'update')
- return internal.update(index.space_id, index.id, keify(key), ops);
+local space_mt = {}
+space_mt.len = function(space)
+ check_space_arg(space, 'len')
+ local pk = space.index[0]
+ if pk == nil then
+ return 0 -- empty space without indexes, return 0
end
- index_mt.delete = function(index, key)
- check_index_arg(index, 'delete')
- return internal.delete(index.space_id, index.id, keify(key));
+ return space.index[0]:len()
+end
+space_mt.count = function(space, key, opts)
+ check_space_arg(space, 'count')
+ local pk = space.index[0]
+ if pk == nil then
+ return 0 -- empty space without indexes, return 0
end
-
- index_mt.info = function(index)
- return internal.info(index.space_id, index.id);
+ return pk:count(key, opts)
+end
+space_mt.bsize = function(space)
+ check_space_arg(space, 'bsize')
+ local s = builtin.space_by_id(space.id)
+ if s == nil then
+ box.error(box.error.NO_SUCH_SPACE, space.name)
end
+ return builtin.space_bsize(s)
+end
+space_mt.__newindex = index_mt.__newindex
- index_mt.drop = function(index)
- check_index_arg(index, 'drop')
- return box.schema.index.drop(index.space_id, index.id)
+space_mt.get = function(space, key)
+ check_space_arg(space, 'get')
+ return check_primary_index(space):get(key)
+end
+space_mt.select = function(space, key, opts)
+ check_space_arg(space, 'select')
+ return check_primary_index(space):select(key, opts)
+end
+space_mt.insert = function(space, tuple)
+ check_space_arg(space, 'insert')
+ return internal.insert(space.id, tuple);
+end
+space_mt.replace = function(space, tuple)
+ check_space_arg(space, 'replace')
+ return internal.replace(space.id, tuple);
+end
+space_mt.put = space_mt.replace; -- put is an alias for replace
+space_mt.update = function(space, key, ops)
+ check_space_arg(space, 'update')
+ return check_primary_index(space):update(key, ops)
+end
+space_mt.upsert = function(space, tuple_key, ops, deprecated)
+ check_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
- index_mt.rename = function(index, name)
- check_index_arg(index, 'rename')
- return box.schema.index.rename(index.space_id, index.id, name)
+ return internal.upsert(space.id, tuple_key, ops);
+end
+space_mt.delete = function(space, key)
+ check_space_arg(space, 'delete')
+ return check_primary_index(space):delete(key)
+end
+-- Assumes that spaceno has a TREE (NUM) primary key
+-- inserts a tuple after getting the next value of the
+-- primary key and returns it back to the user
+space_mt.auto_increment = function(space, tuple)
+ check_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
- index_mt.alter = function(index, options)
- check_index_arg(index, 'alter')
- if index.id == nil or index.space_id == nil then
- box.error(box.error.PROC_LUA, "Usage: index:alter{opts}")
- end
- return box.schema.index.alter(index.space_id, index.id, options)
+ table.insert(tuple, 1, max + 1)
+ return space:insert(tuple)
+end
+
+space_mt.pairs = function(space, key, opts)
+ check_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_mt.__pairs = space_mt.pairs -- Lua 5.2 compatibility
+space_mt.__ipairs = space_mt.pairs -- Lua 5.2 compatibility
+space_mt.truncate = function(space)
+ check_space_arg(space, 'truncate')
+ return internal.truncate(space.id)
+end
+space_mt.format = function(space, format)
+ check_space_arg(space, 'format')
+ return box.schema.space.format(space.id, format)
+end
+space_mt.drop = function(space)
+ check_space_arg(space, 'drop')
+ check_space_exists(space)
+ return box.schema.space.drop(space.id, space.name)
+end
+space_mt.rename = function(space, name)
+ check_space_arg(space, 'rename')
+ check_space_exists(space)
+ return box.schema.space.rename(space.id, name)
+end
+space_mt.create_index = function(space, name, options)
+ check_space_arg(space, 'create_index')
+ check_space_exists(space)
+ return box.schema.index.create(space.id, name, options)
+end
+space_mt.run_triggers = function(space, yesno)
+ check_space_arg(space, 'run_triggers')
+ local s = builtin.space_by_id(space.id)
+ if s == nil then
+ box.error(box.error.NO_SUCH_SPACE, space.name)
end
+ builtin.space_run_triggers(s, yesno)
+end
+space_mt.__index = space_mt
+function box.schema.space.bless(space)
+ local index_mt = table.deepcopy(index_mt)
+ local space_mt = table.deepcopy(space_mt)
-- true if reading operations may yield
local read_yields = space.engine == 'vinyl'
local read_ops = {'select', 'get', 'min', 'max', 'count', 'random', 'pairs'}
@@ -1302,126 +1425,6 @@ function box.schema.space.bless(space)
end
index_mt.__pairs = index_mt.pairs -- Lua 5.2 compatibility
index_mt.__ipairs = index_mt.pairs -- Lua 5.2 compatibility
- --
- local space_mt = {}
- space_mt.len = function(space)
- check_space_arg(space, 'len')
- local pk = space.index[0]
- if pk == nil then
- return 0 -- empty space without indexes, return 0
- end
- return space.index[0]:len()
- end
- space_mt.count = function(space, key, opts)
- check_space_arg(space, 'count')
- local pk = space.index[0]
- if pk == nil then
- return 0 -- empty space without indexes, return 0
- end
- return pk:count(key, opts)
- end
- space_mt.bsize = function(space)
- check_space_arg(space, 'bsize')
- local s = builtin.space_by_id(space.id)
- if s == nil then
- box.error(box.error.NO_SUCH_SPACE, space.name)
- end
- return builtin.space_bsize(s)
- end
- space_mt.__newindex = index_mt.__newindex
-
- space_mt.get = function(space, key)
- check_space_arg(space, 'get')
- return check_primary_index(space):get(key)
- end
- space_mt.select = function(space, key, opts)
- check_space_arg(space, 'select')
- return check_primary_index(space):select(key, opts)
- end
- space_mt.insert = function(space, tuple)
- check_space_arg(space, 'insert')
- return internal.insert(space.id, tuple);
- end
- space_mt.replace = function(space, tuple)
- check_space_arg(space, 'replace')
- return internal.replace(space.id, tuple);
- end
- space_mt.put = space_mt.replace; -- put is an alias for replace
- space_mt.update = function(space, key, ops)
- check_space_arg(space, 'update')
- return check_primary_index(space):update(key, ops)
- end
- space_mt.upsert = function(space, tuple_key, ops, deprecated)
- check_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 internal.upsert(space.id, tuple_key, ops);
- end
- space_mt.delete = function(space, key)
- check_space_arg(space, 'delete')
- return check_primary_index(space):delete(key)
- end
--- Assumes that spaceno has a TREE (NUM) primary key
--- inserts a tuple after getting the next value of the
--- primary key and returns it back to the user
- space_mt.auto_increment = function(space, tuple)
- check_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_mt.pairs = function(space, key, opts)
- check_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_mt.__pairs = space_mt.pairs -- Lua 5.2 compatibility
- space_mt.__ipairs = space_mt.pairs -- Lua 5.2 compatibility
- space_mt.truncate = function(space)
- check_space_arg(space, 'truncate')
- return internal.truncate(space.id)
- end
- space_mt.format = function(space, format)
- check_space_arg(space, 'format')
- return box.schema.space.format(space.id, format)
- end
- space_mt.drop = function(space)
- check_space_arg(space, 'drop')
- check_space_exists(space)
- return box.schema.space.drop(space.id, space.name)
- end
- space_mt.rename = function(space, name)
- check_space_arg(space, 'rename')
- check_space_exists(space)
- return box.schema.space.rename(space.id, name)
- end
- space_mt.create_index = function(space, name, options)
- check_space_arg(space, 'create_index')
- check_space_exists(space)
- return box.schema.index.create(space.id, name, options)
- end
- space_mt.run_triggers = function(space, yesno)
- check_space_arg(space, 'run_triggers')
- local s = builtin.space_by_id(space.id)
- if s == nil then
- box.error(box.error.NO_SUCH_SPACE, space.name)
- end
- builtin.space_run_triggers(s, yesno)
- end
- space_mt.__index = space_mt
setmetatable(space, space_mt)
if type(space.index) == 'table' and space.enabled then
--
2.14.3 (Apple Git-98)
^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH 2/2] schema: expose space_mt and index_mt on `box.schema` table
2018-04-01 11:02 ` [PATCH 0/2] schema: expose space_mt and index_mt on box.schema table Vladislav Shpilevoy
2018-04-01 11:02 ` [PATCH 1/2] schema: move space_mt and index_mt definition out of space bless Vladislav Shpilevoy
@ 2018-04-01 11:02 ` Vladislav Shpilevoy
2018-04-02 11:28 ` Vladimir Davydov
1 sibling, 1 reply; 20+ messages in thread
From: Vladislav Shpilevoy @ 2018-04-01 11:02 UTC (permalink / raw)
To: tarantool-patches; +Cc: vdavydov.dev, Vladislav Shpilevoy
This commit allows userland to extend the space and index
metatables with their own functions or even metamethods. Reducing
barriers for this kind of experimentation is vital for user
contribution toward the improvement of Tarantool's API.
Closes #3204
---
src/box/lua/schema.lua | 21 ++++++++---
test/box-tap/schema_mt.test.lua | 79 +++++++++++++++++++++++++++++++++++++++++
2 files changed, 95 insertions(+), 5 deletions(-)
create mode 100755 test/box-tap/schema_mt.test.lua
diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua
index 8b6f69ad8..6e325a5c5 100644
--- a/src/box/lua/schema.lua
+++ b/src/box/lua/schema.lua
@@ -1075,6 +1075,7 @@ end
internal.check_iterator_type = check_iterator_type -- export for net.box
local index_mt = {}
+box.schema.index_mt = index_mt
-- __len and __index
index_mt.len = function(index)
check_index_arg(index, 'len')
@@ -1289,6 +1290,7 @@ index_mt.alter = function(index, options)
end
local space_mt = {}
+box.schema.space_mt = space_mt
space_mt.len = function(space)
check_space_arg(space, 'len')
local pk = space.index[0]
@@ -1409,18 +1411,27 @@ end
space_mt.__index = space_mt
function box.schema.space.bless(space)
- local index_mt = table.deepcopy(index_mt)
- local space_mt = table.deepcopy(space_mt)
+ -- At first, reference all global index functions. At second,
+ -- choose an implementation for read operations. They are not
+ -- in a global index_mt, because they are engine specific.
+ -- All common index_mt functions must be referenced here to
+ -- be able to get them using getmetatable(index_object) - if
+ -- they are only in index_mt, then getmetatable() returns only
+ -- read ops.
+ local local_index_mt = table.deepcopy(index_mt)
+ local_index_mt.__index = function(index, key)
+ return local_index_mt[key] or index_mt[key]
+ end
-- true if reading operations may yield
local read_yields = space.engine == 'vinyl'
local read_ops = {'select', 'get', 'min', 'max', 'count', 'random', 'pairs'}
for _, op in ipairs(read_ops) do
if read_yields then
-- use Lua/C implmenetation
- index_mt[op] = index_mt[op .. "_luac"]
+ local_index_mt[op] = index_mt[op .. "_luac"]
else
-- use FFI implementation
- index_mt[op] = index_mt[op .. "_ffi"]
+ local_index_mt[op] = index_mt[op .. "_ffi"]
end
end
index_mt.__pairs = index_mt.pairs -- Lua 5.2 compatibility
@@ -1430,7 +1441,7 @@ function box.schema.space.bless(space)
if type(space.index) == 'table' and space.enabled then
for j, index in pairs(space.index) do
if type(j) == 'number' then
- setmetatable(index, index_mt)
+ setmetatable(index, local_index_mt)
end
end
end
diff --git a/test/box-tap/schema_mt.test.lua b/test/box-tap/schema_mt.test.lua
new file mode 100755
index 000000000..096c4ad60
--- /dev/null
+++ b/test/box-tap/schema_mt.test.lua
@@ -0,0 +1,79 @@
+#!/usr/bin/env tarantool
+--
+-- pr-3204: expose space_mt, index_mt into box.schema.
+--
+
+local tap = require('tap')
+local test = tap.test('schema_mt')
+
+test:plan(7)
+
+box.cfg{
+ log="tarantool.log",
+}
+
+local sp1 = box.schema.space.create('test')
+local sp2 = box.schema.space.create('test2')
+
+test:is(
+ getmetatable(sp1),
+ getmetatable(sp2),
+ 'all spaces use the same metatable'
+)
+
+local idx1 = sp1:create_index('primary')
+local idx2 = sp2:create_index('primary')
+
+test:isnt(
+ getmetatable(idx1),
+ getmetatable(idx2),
+ 'all indexes have their own metatable'
+)
+
+test:is(
+ idx1.get,
+ idx2.get,
+ 'memtx indexes share read methods'
+)
+
+local sp3 = box.schema.space.create('test3', {engine='vinyl'})
+local sp4 = box.schema.space.create('test4', {engine='vinyl'})
+
+local idx3 = sp3:create_index('primary')
+local idx4 = sp4:create_index('primary')
+
+test:is(
+ idx3.get,
+ idx4.get,
+ 'vinyl indexes share read methods'
+)
+
+test:isnt(
+ idx1.get,
+ idx3.get,
+ 'memtx and vinyl indexes have separate read methods'
+)
+
+function box.schema.space_mt.foo() end
+
+test:is(
+ sp1.foo,
+ box.schema.space_mt.foo,
+ 'box.schema.space_mt is mutable'
+)
+
+function box.schema.index_mt.foo() end
+
+test:is(
+ idx1.foo,
+ box.schema.index_mt.foo,
+ 'box.schema.index_mt is mutable'
+)
+
+test:check()
+
+sp1:drop()
+sp2:drop()
+sp3:drop()
+sp4:drop()
+os.exit(0)
--
2.14.3 (Apple Git-98)
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 2/2] schema: expose space_mt and index_mt on `box.schema` table
2018-04-01 11:02 ` [PATCH 2/2] schema: expose space_mt and index_mt on `box.schema` table Vladislav Shpilevoy
@ 2018-04-02 11:28 ` Vladimir Davydov
2018-04-03 16:50 ` [PATCH v2 0/3] schema: expose space_mt and index_mt on box.schema table Vladislav Shpilevoy
0 siblings, 1 reply; 20+ messages in thread
From: Vladimir Davydov @ 2018-04-02 11:28 UTC (permalink / raw)
To: Vladislav Shpilevoy; +Cc: tarantool-patches
On Sun, Apr 01, 2018 at 02:02:46PM +0300, Vladislav Shpilevoy wrote:
> function box.schema.space.bless(space)
> - local index_mt = table.deepcopy(index_mt)
> - local space_mt = table.deepcopy(space_mt)
> + -- At first, reference all global index functions. At second,
> + -- choose an implementation for read operations. They are not
> + -- in a global index_mt, because they are engine specific.
> + -- All common index_mt functions must be referenced here to
> + -- be able to get them using getmetatable(index_object) - if
> + -- they are only in index_mt, then getmetatable() returns only
> + -- read ops.
> + local local_index_mt = table.deepcopy(index_mt)
> + local_index_mt.__index = function(index, key)
> + return local_index_mt[key] or index_mt[key]
> + end
I don't really like that you use the same metatable for all spaces, but
a separate metatable for each index. Besides, because of this one can't
overwrite a built-in method (e.g. 'get') as it will be masked by
local_index_mt. AFAIU you do this, because vinyl and memtx use different
methods (luac vs ffi) for certain operations.
I see the following alternatives:
1. Leave as is. This isn't all so bad. After all, I doubt anybody's
actually willing to overwrite built-in methods.
2. Use two metatables, one for all memtx indexes, another for vinyl,
and push all changes from the public index_mt to both of these
metatables. Not quite sure it's technically possible in Lua.
3. Same as #2, but proscribe modification of built-in methods.
4. Add a separate engine-specific metatable for the method that differ
between memtx and vinyl, then define __index as:
local engine_index_mt = (engine == 'memtx' and
memtx_index_mt or vinyl_index_mt)
index_mt.__index = function(index, key)
return index_mt[key] or engine_index_mt[key]
Personally, I like #4 most, but it may affect performance.
Anyway, I guess this is up to Kostja to decide which one to choose.
I don't see any other technical problems in the patch, nor in the test.
> -- true if reading operations may yield
> local read_yields = space.engine == 'vinyl'
> local read_ops = {'select', 'get', 'min', 'max', 'count', 'random', 'pairs'}
> for _, op in ipairs(read_ops) do
> if read_yields then
> -- use Lua/C implmenetation
> - index_mt[op] = index_mt[op .. "_luac"]
> + local_index_mt[op] = index_mt[op .. "_luac"]
> else
> -- use FFI implementation
> - index_mt[op] = index_mt[op .. "_ffi"]
> + local_index_mt[op] = index_mt[op .. "_ffi"]
> end
> end
> index_mt.__pairs = index_mt.pairs -- Lua 5.2 compatibility
> @@ -1430,7 +1441,7 @@ function box.schema.space.bless(space)
> if type(space.index) == 'table' and space.enabled then
> for j, index in pairs(space.index) do
> if type(j) == 'number' then
> - setmetatable(index, index_mt)
> + setmetatable(index, local_index_mt)
> end
> end
> end
^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH v2 0/3] schema: expose space_mt and index_mt on box.schema table
2018-04-02 11:28 ` Vladimir Davydov
@ 2018-04-03 16:50 ` Vladislav Shpilevoy
2018-04-03 16:50 ` [PATCH v2 1/3] schema: move space_mt and index_mt definition out of space bless Vladislav Shpilevoy
` (2 more replies)
0 siblings, 3 replies; 20+ messages in thread
From: Vladislav Shpilevoy @ 2018-04-03 16:50 UTC (permalink / raw)
To: tarantool-patches; +Cc: vdavydov.dev, Vladislav Shpilevoy
Branch: http://github.com/tarantool/tarantool/tree/pr-3204-expose-space-index-mt
Issue: https://github.com/tarantool/tarantool/issues/3204
Space_mt and index_mt are created individually for each space
inside a giant space.bless function. It consumes lua memory by
duplicating these metatables (which is very limited), and does not
allow to add new functions or redefine existing ones, which would
be visible in all spaces and indexes. Lets solve the problems one
by one.
At first, move space_mt and index_mt definitions out of
space.bless function. Now space.bless just copy space_mt and
inherit a needed index_mt depending on egine.
At second, instead of customization of index_mt copy on each
space.bless lets once create separate index metatables for memtx
and for vinyl, and just copy needed in space.bless. With no
selecting yielding/non-yielding methods inside the function.
At third, just stop copying in bless, start reference metatables
instead, and open them to users via box.schema:
box.schema.space_mt - metatable of all spaces;
box.schema.index_mt - base metatable of all indexes - replicated
into the vinyl and memtx. See below how.
box.schema.vinyl_index_mt - metatable of all vinyl indexes;
box.schema.memtx_index_mt - metatable of all memtx indexes.
Once metatables become global and shared, they can be easy
extended. However there is a non-trival task how to make index
metatable extension be transparent for a user, if a user modifies
not vinyl or memtx metatable, but box.schema.index_mt. It would be
good to replicate all changes of a base metatable over engine
specific ones.
This is solved using "metatable for a metatable". When a user
modifies box.schema.index_mt, these changes via __newindex method
of box.schema.index_mt are replicated both into memtx and vinyl
index metatables.
Vladislav Shpilevoy (3):
schema: move space_mt and index_mt definition out of space bless
schema: inherit vinyl/memtx_index_mt from base index mt
schema: expose space_mt and index_mt on box.schema table
src/box/lua/schema.lua | 663 +++++++++++++++++++++-------------------
test/box-tap/schema_mt.test.lua | 80 +++++
2 files changed, 424 insertions(+), 319 deletions(-)
create mode 100755 test/box-tap/schema_mt.test.lua
--
2.14.3 (Apple Git-98)
^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH v2 1/3] schema: move space_mt and index_mt definition out of space bless
2018-04-03 16:50 ` [PATCH v2 0/3] schema: expose space_mt and index_mt on box.schema table Vladislav Shpilevoy
@ 2018-04-03 16:50 ` Vladislav Shpilevoy
2018-04-03 16:50 ` [PATCH v2 2/3] schema: inherit vinyl/memtx_index_mt from base index mt Vladislav Shpilevoy
2018-04-03 16:50 ` [PATCH v2 3/3] schema: expose space_mt and index_mt on box.schema table Vladislav Shpilevoy
2 siblings, 0 replies; 20+ messages in thread
From: Vladislav Shpilevoy @ 2018-04-03 16:50 UTC (permalink / raw)
To: tarantool-patches; +Cc: vdavydov.dev, Vladislav Shpilevoy
Space_mt and index_mt are created individually for each space
inside a giant space.bless function. It makes impossible to
implement #3204: exposing space and index metatables to a user,
because for this they must be global and shared between all
spaces and indexes.
Lets move their definition out of space.bless function, and do
their duplicate inside.
Needed #3204
---
src/box/lua/schema.lua | 619 +++++++++++++++++++++++++------------------------
1 file changed, 311 insertions(+), 308 deletions(-)
diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua
index 5a0f71559..8b6f69ad8 100644
--- a/src/box/lua/schema.lua
+++ b/src/box/lua/schema.lua
@@ -1074,220 +1074,343 @@ end
internal.check_iterator_type = check_iterator_type -- export for net.box
-function box.schema.space.bless(space)
- local index_mt = {}
- -- __len and __index
- index_mt.len = function(index)
- check_index_arg(index, 'len')
- local ret = builtin.box_index_len(index.space_id, index.id)
- if ret == -1 then
- box.error()
- end
- return tonumber(ret)
- end
- -- index.bsize
- index_mt.bsize = function(index)
- check_index_arg(index, 'bsize')
- local ret = builtin.box_index_bsize(index.space_id, index.id)
- if ret == -1 then
- box.error()
- end
- return tonumber(ret)
- end
- index_mt.__len = index_mt.len -- Lua 5.2 compatibility
- index_mt.__newindex = function(table, index)
- return error('Attempt to modify a read-only table') end
- index_mt.__index = index_mt
- -- min and max
- index_mt.min_ffi = function(index, key)
- check_index_arg(index, 'min')
- local pkey, pkey_end = tuple_encode(key)
- if builtin.box_index_min(index.space_id, index.id,
- pkey, pkey_end, ptuple) ~= 0 then
- box.error() -- error
- elseif ptuple[0] ~= nil then
- return tuple_bless(ptuple[0])
- else
- return
- end
+local index_mt = {}
+-- __len and __index
+index_mt.len = function(index)
+ check_index_arg(index, 'len')
+ local ret = builtin.box_index_len(index.space_id, index.id)
+ if ret == -1 then
+ box.error()
end
- index_mt.min_luac = function(index, key)
- check_index_arg(index, 'min')
- key = keify(key)
- return internal.min(index.space_id, index.id, key);
+ return tonumber(ret)
+end
+-- index.bsize
+index_mt.bsize = function(index)
+ check_index_arg(index, 'bsize')
+ local ret = builtin.box_index_bsize(index.space_id, index.id)
+ if ret == -1 then
+ box.error()
end
- index_mt.max_ffi = function(index, key)
- check_index_arg(index, 'max')
- local pkey, pkey_end = tuple_encode(key)
- if builtin.box_index_max(index.space_id, index.id,
- pkey, pkey_end, ptuple) ~= 0 then
- box.error() -- error
- elseif ptuple[0] ~= nil then
- return tuple_bless(ptuple[0])
- else
- return
- end
+ return tonumber(ret)
+end
+index_mt.__len = index_mt.len -- Lua 5.2 compatibility
+index_mt.__newindex = function(table, index)
+ return error('Attempt to modify a read-only table')
+end
+index_mt.__index = index_mt
+-- min and max
+index_mt.min_ffi = function(index, key)
+ check_index_arg(index, 'min')
+ local pkey, pkey_end = tuple_encode(key)
+ if builtin.box_index_min(index.space_id, index.id,
+ pkey, pkey_end, ptuple) ~= 0 then
+ box.error() -- error
+ elseif ptuple[0] ~= nil then
+ return tuple_bless(ptuple[0])
+ else
+ return
end
- index_mt.max_luac = function(index, key)
- check_index_arg(index, 'max')
- key = keify(key)
- return internal.max(index.space_id, index.id, key);
+end
+index_mt.min_luac = function(index, key)
+ check_index_arg(index, 'min')
+ key = keify(key)
+ return internal.min(index.space_id, index.id, key);
+end
+index_mt.max_ffi = function(index, key)
+ check_index_arg(index, 'max')
+ local pkey, pkey_end = tuple_encode(key)
+ if builtin.box_index_max(index.space_id, index.id,
+ pkey, pkey_end, ptuple) ~= 0 then
+ box.error() -- error
+ elseif ptuple[0] ~= nil then
+ return tuple_bless(ptuple[0])
+ else
+ return
end
- index_mt.random_ffi = function(index, rnd)
- check_index_arg(index, 'random')
- rnd = rnd or math.random()
- if builtin.box_index_random(index.space_id, index.id, rnd,
- ptuple) ~= 0 then
- box.error() -- error
- elseif ptuple[0] ~= nil then
- return tuple_bless(ptuple[0])
- else
- return
- end
+end
+index_mt.max_luac = function(index, key)
+ check_index_arg(index, 'max')
+ key = keify(key)
+ return internal.max(index.space_id, index.id, key);
+end
+index_mt.random_ffi = function(index, rnd)
+ check_index_arg(index, 'random')
+ rnd = rnd or math.random()
+ if builtin.box_index_random(index.space_id, index.id, rnd,
+ ptuple) ~= 0 then
+ box.error() -- error
+ elseif ptuple[0] ~= nil then
+ return tuple_bless(ptuple[0])
+ else
+ return
end
- index_mt.random_luac = function(index, rnd)
- check_index_arg(index, 'random')
- rnd = rnd or math.random()
- return internal.random(index.space_id, index.id, rnd);
- end
- -- iteration
- index_mt.pairs_ffi = function(index, key, opts)
- check_index_arg(index, 'pairs')
- local pkey, pkey_end = tuple_encode(key)
- local itype = check_iterator_type(opts, pkey + 1 >= pkey_end);
-
- local keybuf = ffi.string(pkey, pkey_end - pkey)
- local pkeybuf = ffi.cast('const char *', keybuf)
- local cdata = builtin.box_index_iterator(index.space_id, index.id,
- itype, pkeybuf, pkeybuf + #keybuf);
- if cdata == nil then
- box.error()
- end
- return fun.wrap(iterator_gen, keybuf,
- ffi.gc(cdata, builtin.box_iterator_free))
- end
- index_mt.pairs_luac = function(index, key, opts)
- check_index_arg(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 = internal.iterator(index.space_id, index.id, itype, keymp);
- return fun.wrap(iterator_gen_luac, keybuf,
- ffi.gc(cdata, builtin.box_iterator_free))
- end
-
- -- index subtree size
- index_mt.count_ffi = function(index, key, opts)
- check_index_arg(index, 'count')
- local pkey, pkey_end = tuple_encode(key)
- local itype = check_iterator_type(opts, pkey + 1 >= pkey_end);
- local count = builtin.box_index_count(index.space_id, index.id,
- itype, pkey, pkey_end);
- if count == -1 then
- box.error()
- end
- return tonumber(count)
- end
- index_mt.count_luac = function(index, key, opts)
- check_index_arg(index, 'count')
- key = keify(key)
- local itype = check_iterator_type(opts, #key == 0);
- return internal.count(index.space_id, index.id, itype, key);
- end
-
- index_mt.get_ffi = function(index, key)
- check_index_arg(index, 'get')
- local key, key_end = tuple_encode(key)
- if builtin.box_index_get(index.space_id, index.id,
- key, key_end, ptuple) ~= 0 then
- return box.error() -- error
- elseif ptuple[0] ~= nil then
- return tuple_bless(ptuple[0])
- else
- return
- end
+end
+index_mt.random_luac = function(index, rnd)
+ check_index_arg(index, 'random')
+ rnd = rnd or math.random()
+ return internal.random(index.space_id, index.id, rnd);
+end
+-- iteration
+index_mt.pairs_ffi = function(index, key, opts)
+ check_index_arg(index, 'pairs')
+ local pkey, pkey_end = tuple_encode(key)
+ local itype = check_iterator_type(opts, pkey + 1 >= pkey_end);
+
+ local keybuf = ffi.string(pkey, pkey_end - pkey)
+ local pkeybuf = ffi.cast('const char *', keybuf)
+ local cdata = builtin.box_index_iterator(index.space_id, index.id,
+ itype, pkeybuf, pkeybuf + #keybuf);
+ if cdata == nil then
+ box.error()
end
- index_mt.get_luac = function(index, key)
- check_index_arg(index, 'get')
- key = keify(key)
- return internal.get(index.space_id, index.id, key)
+ return fun.wrap(iterator_gen, keybuf,
+ ffi.gc(cdata, builtin.box_iterator_free))
+end
+index_mt.pairs_luac = function(index, key, opts)
+ check_index_arg(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 = internal.iterator(index.space_id, index.id, itype, keymp);
+ return fun.wrap(iterator_gen_luac, keybuf,
+ ffi.gc(cdata, builtin.box_iterator_free))
+end
+
+-- index subtree size
+index_mt.count_ffi = function(index, key, opts)
+ check_index_arg(index, 'count')
+ local pkey, pkey_end = tuple_encode(key)
+ local itype = check_iterator_type(opts, pkey + 1 >= pkey_end);
+ local count = builtin.box_index_count(index.space_id, index.id,
+ itype, pkey, pkey_end);
+ if count == -1 then
+ box.error()
end
+ return tonumber(count)
+end
+index_mt.count_luac = function(index, key, opts)
+ check_index_arg(index, 'count')
+ key = keify(key)
+ local itype = check_iterator_type(opts, #key == 0);
+ return internal.count(index.space_id, index.id, itype, key);
+end
- local function check_select_opts(opts, key_is_nil)
- local offset = 0
- local limit = 4294967295
- local iterator = check_iterator_type(opts, key_is_nil)
- if opts ~= nil then
- if opts.offset ~= nil then
- offset = opts.offset
- end
- if opts.limit ~= nil then
- limit = opts.limit
- end
+index_mt.get_ffi = function(index, key)
+ check_index_arg(index, 'get')
+ local key, key_end = tuple_encode(key)
+ if builtin.box_index_get(index.space_id, index.id,
+ key, key_end, ptuple) ~= 0 then
+ return box.error() -- error
+ elseif ptuple[0] ~= nil then
+ return tuple_bless(ptuple[0])
+ else
+ return
+ end
+end
+index_mt.get_luac = function(index, key)
+ check_index_arg(index, 'get')
+ key = keify(key)
+ return internal.get(index.space_id, index.id, key)
+end
+
+local function check_select_opts(opts, key_is_nil)
+ local offset = 0
+ local limit = 4294967295
+ local iterator = check_iterator_type(opts, key_is_nil)
+ if opts ~= nil then
+ if opts.offset ~= nil then
+ offset = opts.offset
+ end
+ if opts.limit ~= nil then
+ limit = opts.limit
end
- return iterator, offset, limit
end
+ return iterator, offset, limit
+end
- index_mt.select_ffi = function(index, key, opts)
- check_index_arg(index, 'select')
- local key, key_end = tuple_encode(key)
- local iterator, offset, limit = check_select_opts(opts, key + 1 >= key_end)
+index_mt.select_ffi = function(index, key, opts)
+ check_index_arg(index, 'select')
+ local key, key_end = tuple_encode(key)
+ local iterator, offset, limit = check_select_opts(opts, key + 1 >= key_end)
- local port = ffi.cast('struct port *', port_tuple)
+ local port = ffi.cast('struct port *', port_tuple)
- if builtin.box_select(index.space_id, index.id,
- iterator, offset, limit, key, key_end, port) ~= 0 then
- return box.error()
- end
+ if builtin.box_select(index.space_id, index.id,
+ iterator, offset, limit, key, key_end, port) ~= 0 then
+ return box.error()
+ end
- local ret = {}
- local entry = port_tuple.first
- for i=1,tonumber(port_tuple.size),1 do
- ret[i] = tuple_bless(entry.tuple)
- entry = entry.next
- end
- builtin.port_destroy(port);
- return ret
+ local ret = {}
+ local entry = port_tuple.first
+ for i=1,tonumber(port_tuple.size),1 do
+ ret[i] = tuple_bless(entry.tuple)
+ entry = entry.next
end
+ builtin.port_destroy(port);
+ return ret
+end
+
+index_mt.select_luac = function(index, key, opts)
+ check_index_arg(index, 'select')
+ local key = keify(key)
+ local iterator, offset, limit = check_select_opts(opts, #key == 0)
+ return internal.select(index.space_id, index.id, iterator,
+ offset, limit, key)
+end
- index_mt.select_luac = function(index, key, opts)
- check_index_arg(index, 'select')
- local key = keify(key)
- local iterator, offset, limit = check_select_opts(opts, #key == 0)
- return internal.select(index.space_id, index.id, iterator,
- offset, limit, key)
+index_mt.update = function(index, key, ops)
+ check_index_arg(index, 'update')
+ return internal.update(index.space_id, index.id, keify(key), ops);
+end
+index_mt.delete = function(index, key)
+ check_index_arg(index, 'delete')
+ return internal.delete(index.space_id, index.id, keify(key));
+end
+
+index_mt.info = function(index)
+ return internal.info(index.space_id, index.id);
+end
+
+index_mt.drop = function(index)
+ check_index_arg(index, 'drop')
+ return box.schema.index.drop(index.space_id, index.id)
+end
+index_mt.rename = function(index, name)
+ check_index_arg(index, 'rename')
+ return box.schema.index.rename(index.space_id, index.id, name)
+end
+index_mt.alter = function(index, options)
+ check_index_arg(index, 'alter')
+ if index.id == nil or index.space_id == nil then
+ box.error(box.error.PROC_LUA, "Usage: index:alter{opts}")
end
+ return box.schema.index.alter(index.space_id, index.id, options)
+end
- index_mt.update = function(index, key, ops)
- check_index_arg(index, 'update')
- return internal.update(index.space_id, index.id, keify(key), ops);
+local space_mt = {}
+space_mt.len = function(space)
+ check_space_arg(space, 'len')
+ local pk = space.index[0]
+ if pk == nil then
+ return 0 -- empty space without indexes, return 0
end
- index_mt.delete = function(index, key)
- check_index_arg(index, 'delete')
- return internal.delete(index.space_id, index.id, keify(key));
+ return space.index[0]:len()
+end
+space_mt.count = function(space, key, opts)
+ check_space_arg(space, 'count')
+ local pk = space.index[0]
+ if pk == nil then
+ return 0 -- empty space without indexes, return 0
end
-
- index_mt.info = function(index)
- return internal.info(index.space_id, index.id);
+ return pk:count(key, opts)
+end
+space_mt.bsize = function(space)
+ check_space_arg(space, 'bsize')
+ local s = builtin.space_by_id(space.id)
+ if s == nil then
+ box.error(box.error.NO_SUCH_SPACE, space.name)
end
+ return builtin.space_bsize(s)
+end
+space_mt.__newindex = index_mt.__newindex
- index_mt.drop = function(index)
- check_index_arg(index, 'drop')
- return box.schema.index.drop(index.space_id, index.id)
+space_mt.get = function(space, key)
+ check_space_arg(space, 'get')
+ return check_primary_index(space):get(key)
+end
+space_mt.select = function(space, key, opts)
+ check_space_arg(space, 'select')
+ return check_primary_index(space):select(key, opts)
+end
+space_mt.insert = function(space, tuple)
+ check_space_arg(space, 'insert')
+ return internal.insert(space.id, tuple);
+end
+space_mt.replace = function(space, tuple)
+ check_space_arg(space, 'replace')
+ return internal.replace(space.id, tuple);
+end
+space_mt.put = space_mt.replace; -- put is an alias for replace
+space_mt.update = function(space, key, ops)
+ check_space_arg(space, 'update')
+ return check_primary_index(space):update(key, ops)
+end
+space_mt.upsert = function(space, tuple_key, ops, deprecated)
+ check_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
- index_mt.rename = function(index, name)
- check_index_arg(index, 'rename')
- return box.schema.index.rename(index.space_id, index.id, name)
+ return internal.upsert(space.id, tuple_key, ops);
+end
+space_mt.delete = function(space, key)
+ check_space_arg(space, 'delete')
+ return check_primary_index(space):delete(key)
+end
+-- Assumes that spaceno has a TREE (NUM) primary key
+-- inserts a tuple after getting the next value of the
+-- primary key and returns it back to the user
+space_mt.auto_increment = function(space, tuple)
+ check_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
- index_mt.alter = function(index, options)
- check_index_arg(index, 'alter')
- if index.id == nil or index.space_id == nil then
- box.error(box.error.PROC_LUA, "Usage: index:alter{opts}")
- end
- return box.schema.index.alter(index.space_id, index.id, options)
+ table.insert(tuple, 1, max + 1)
+ return space:insert(tuple)
+end
+
+space_mt.pairs = function(space, key, opts)
+ check_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_mt.__pairs = space_mt.pairs -- Lua 5.2 compatibility
+space_mt.__ipairs = space_mt.pairs -- Lua 5.2 compatibility
+space_mt.truncate = function(space)
+ check_space_arg(space, 'truncate')
+ return internal.truncate(space.id)
+end
+space_mt.format = function(space, format)
+ check_space_arg(space, 'format')
+ return box.schema.space.format(space.id, format)
+end
+space_mt.drop = function(space)
+ check_space_arg(space, 'drop')
+ check_space_exists(space)
+ return box.schema.space.drop(space.id, space.name)
+end
+space_mt.rename = function(space, name)
+ check_space_arg(space, 'rename')
+ check_space_exists(space)
+ return box.schema.space.rename(space.id, name)
+end
+space_mt.create_index = function(space, name, options)
+ check_space_arg(space, 'create_index')
+ check_space_exists(space)
+ return box.schema.index.create(space.id, name, options)
+end
+space_mt.run_triggers = function(space, yesno)
+ check_space_arg(space, 'run_triggers')
+ local s = builtin.space_by_id(space.id)
+ if s == nil then
+ box.error(box.error.NO_SUCH_SPACE, space.name)
end
+ builtin.space_run_triggers(s, yesno)
+end
+space_mt.__index = space_mt
+function box.schema.space.bless(space)
+ local index_mt = table.deepcopy(index_mt)
+ local space_mt = table.deepcopy(space_mt)
-- true if reading operations may yield
local read_yields = space.engine == 'vinyl'
local read_ops = {'select', 'get', 'min', 'max', 'count', 'random', 'pairs'}
@@ -1302,126 +1425,6 @@ function box.schema.space.bless(space)
end
index_mt.__pairs = index_mt.pairs -- Lua 5.2 compatibility
index_mt.__ipairs = index_mt.pairs -- Lua 5.2 compatibility
- --
- local space_mt = {}
- space_mt.len = function(space)
- check_space_arg(space, 'len')
- local pk = space.index[0]
- if pk == nil then
- return 0 -- empty space without indexes, return 0
- end
- return space.index[0]:len()
- end
- space_mt.count = function(space, key, opts)
- check_space_arg(space, 'count')
- local pk = space.index[0]
- if pk == nil then
- return 0 -- empty space without indexes, return 0
- end
- return pk:count(key, opts)
- end
- space_mt.bsize = function(space)
- check_space_arg(space, 'bsize')
- local s = builtin.space_by_id(space.id)
- if s == nil then
- box.error(box.error.NO_SUCH_SPACE, space.name)
- end
- return builtin.space_bsize(s)
- end
- space_mt.__newindex = index_mt.__newindex
-
- space_mt.get = function(space, key)
- check_space_arg(space, 'get')
- return check_primary_index(space):get(key)
- end
- space_mt.select = function(space, key, opts)
- check_space_arg(space, 'select')
- return check_primary_index(space):select(key, opts)
- end
- space_mt.insert = function(space, tuple)
- check_space_arg(space, 'insert')
- return internal.insert(space.id, tuple);
- end
- space_mt.replace = function(space, tuple)
- check_space_arg(space, 'replace')
- return internal.replace(space.id, tuple);
- end
- space_mt.put = space_mt.replace; -- put is an alias for replace
- space_mt.update = function(space, key, ops)
- check_space_arg(space, 'update')
- return check_primary_index(space):update(key, ops)
- end
- space_mt.upsert = function(space, tuple_key, ops, deprecated)
- check_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 internal.upsert(space.id, tuple_key, ops);
- end
- space_mt.delete = function(space, key)
- check_space_arg(space, 'delete')
- return check_primary_index(space):delete(key)
- end
--- Assumes that spaceno has a TREE (NUM) primary key
--- inserts a tuple after getting the next value of the
--- primary key and returns it back to the user
- space_mt.auto_increment = function(space, tuple)
- check_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_mt.pairs = function(space, key, opts)
- check_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_mt.__pairs = space_mt.pairs -- Lua 5.2 compatibility
- space_mt.__ipairs = space_mt.pairs -- Lua 5.2 compatibility
- space_mt.truncate = function(space)
- check_space_arg(space, 'truncate')
- return internal.truncate(space.id)
- end
- space_mt.format = function(space, format)
- check_space_arg(space, 'format')
- return box.schema.space.format(space.id, format)
- end
- space_mt.drop = function(space)
- check_space_arg(space, 'drop')
- check_space_exists(space)
- return box.schema.space.drop(space.id, space.name)
- end
- space_mt.rename = function(space, name)
- check_space_arg(space, 'rename')
- check_space_exists(space)
- return box.schema.space.rename(space.id, name)
- end
- space_mt.create_index = function(space, name, options)
- check_space_arg(space, 'create_index')
- check_space_exists(space)
- return box.schema.index.create(space.id, name, options)
- end
- space_mt.run_triggers = function(space, yesno)
- check_space_arg(space, 'run_triggers')
- local s = builtin.space_by_id(space.id)
- if s == nil then
- box.error(box.error.NO_SUCH_SPACE, space.name)
- end
- builtin.space_run_triggers(s, yesno)
- end
- space_mt.__index = space_mt
setmetatable(space, space_mt)
if type(space.index) == 'table' and space.enabled then
--
2.14.3 (Apple Git-98)
^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH v2 2/3] schema: inherit vinyl/memtx_index_mt from base index mt
2018-04-03 16:50 ` [PATCH v2 0/3] schema: expose space_mt and index_mt on box.schema table Vladislav Shpilevoy
2018-04-03 16:50 ` [PATCH v2 1/3] schema: move space_mt and index_mt definition out of space bless Vladislav Shpilevoy
@ 2018-04-03 16:50 ` Vladislav Shpilevoy
2018-04-03 16:50 ` [PATCH v2 3/3] schema: expose space_mt and index_mt on box.schema table Vladislav Shpilevoy
2 siblings, 0 replies; 20+ messages in thread
From: Vladislav Shpilevoy @ 2018-04-03 16:50 UTC (permalink / raw)
To: tarantool-patches; +Cc: vdavydov.dev, Vladislav Shpilevoy
Now space.bless() in Lua serves to choose a correct index
metatable that depends on a space engine. Vinyl index methods
must not use FFI since yield breaks it. Lets do not choose
correct methods one by one in space.bless, create them only
once on start, and then just do copy of needed table.
---
src/box/lua/schema.lua | 109 ++++++++++++++++++++++++++++++-------------------
1 file changed, 66 insertions(+), 43 deletions(-)
diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua
index 8b6f69ad8..4b5eeeda9 100644
--- a/src/box/lua/schema.lua
+++ b/src/box/lua/schema.lua
@@ -1074,9 +1074,32 @@ end
internal.check_iterator_type = check_iterator_type -- export for net.box
-local index_mt = {}
+local function forbid_new_index(t, k, v)
+ return error('Attempt to modify a read-only table')
+end
+
+local base_index_mt = {}
+base_index_mt.__index = base_index_mt
+--
+-- Inherit engine specific index metatables from a base one.
+--
+local vinyl_index_mt = {}
+vinyl_index_mt.__index = vinyl_index_mt
+local memtx_index_mt = {}
+memtx_index_mt.__index = memtx_index_mt
+--
+-- When a new method is added below to base index mt, the same
+-- method is added both to vinyl and memtx index mt.
+--
+setmetatable(base_index_mt, {
+ __newindex = function(t, k, v)
+ vinyl_index_mt[k] = v
+ memtx_index_mt[k] = v
+ rawset(t, k, v)
+ end
+})
-- __len and __index
-index_mt.len = function(index)
+base_index_mt.len = function(index)
check_index_arg(index, 'len')
local ret = builtin.box_index_len(index.space_id, index.id)
if ret == -1 then
@@ -1085,7 +1108,7 @@ index_mt.len = function(index)
return tonumber(ret)
end
-- index.bsize
-index_mt.bsize = function(index)
+base_index_mt.bsize = function(index)
check_index_arg(index, 'bsize')
local ret = builtin.box_index_bsize(index.space_id, index.id)
if ret == -1 then
@@ -1093,13 +1116,11 @@ index_mt.bsize = function(index)
end
return tonumber(ret)
end
-index_mt.__len = index_mt.len -- Lua 5.2 compatibility
-index_mt.__newindex = function(table, index)
- return error('Attempt to modify a read-only table')
-end
-index_mt.__index = index_mt
+-- Lua 5.2 compatibility
+base_index_mt.__len = base_index_mt.len
+base_index_mt.__newindex = forbid_new_index
-- min and max
-index_mt.min_ffi = function(index, key)
+base_index_mt.min_ffi = function(index, key)
check_index_arg(index, 'min')
local pkey, pkey_end = tuple_encode(key)
if builtin.box_index_min(index.space_id, index.id,
@@ -1111,12 +1132,12 @@ index_mt.min_ffi = function(index, key)
return
end
end
-index_mt.min_luac = function(index, key)
+base_index_mt.min_luac = function(index, key)
check_index_arg(index, 'min')
key = keify(key)
return internal.min(index.space_id, index.id, key);
end
-index_mt.max_ffi = function(index, key)
+base_index_mt.max_ffi = function(index, key)
check_index_arg(index, 'max')
local pkey, pkey_end = tuple_encode(key)
if builtin.box_index_max(index.space_id, index.id,
@@ -1128,12 +1149,12 @@ index_mt.max_ffi = function(index, key)
return
end
end
-index_mt.max_luac = function(index, key)
+base_index_mt.max_luac = function(index, key)
check_index_arg(index, 'max')
key = keify(key)
return internal.max(index.space_id, index.id, key);
end
-index_mt.random_ffi = function(index, rnd)
+base_index_mt.random_ffi = function(index, rnd)
check_index_arg(index, 'random')
rnd = rnd or math.random()
if builtin.box_index_random(index.space_id, index.id, rnd,
@@ -1145,13 +1166,13 @@ index_mt.random_ffi = function(index, rnd)
return
end
end
-index_mt.random_luac = function(index, rnd)
+base_index_mt.random_luac = function(index, rnd)
check_index_arg(index, 'random')
rnd = rnd or math.random()
return internal.random(index.space_id, index.id, rnd);
end
-- iteration
-index_mt.pairs_ffi = function(index, key, opts)
+base_index_mt.pairs_ffi = function(index, key, opts)
check_index_arg(index, 'pairs')
local pkey, pkey_end = tuple_encode(key)
local itype = check_iterator_type(opts, pkey + 1 >= pkey_end);
@@ -1166,7 +1187,7 @@ index_mt.pairs_ffi = function(index, key, opts)
return fun.wrap(iterator_gen, keybuf,
ffi.gc(cdata, builtin.box_iterator_free))
end
-index_mt.pairs_luac = function(index, key, opts)
+base_index_mt.pairs_luac = function(index, key, opts)
check_index_arg(index, 'pairs')
key = keify(key)
local itype = check_iterator_type(opts, #key == 0);
@@ -1178,7 +1199,7 @@ index_mt.pairs_luac = function(index, key, opts)
end
-- index subtree size
-index_mt.count_ffi = function(index, key, opts)
+base_index_mt.count_ffi = function(index, key, opts)
check_index_arg(index, 'count')
local pkey, pkey_end = tuple_encode(key)
local itype = check_iterator_type(opts, pkey + 1 >= pkey_end);
@@ -1189,14 +1210,14 @@ index_mt.count_ffi = function(index, key, opts)
end
return tonumber(count)
end
-index_mt.count_luac = function(index, key, opts)
+base_index_mt.count_luac = function(index, key, opts)
check_index_arg(index, 'count')
key = keify(key)
local itype = check_iterator_type(opts, #key == 0);
return internal.count(index.space_id, index.id, itype, key);
end
-index_mt.get_ffi = function(index, key)
+base_index_mt.get_ffi = function(index, key)
check_index_arg(index, 'get')
local key, key_end = tuple_encode(key)
if builtin.box_index_get(index.space_id, index.id,
@@ -1208,7 +1229,7 @@ index_mt.get_ffi = function(index, key)
return
end
end
-index_mt.get_luac = function(index, key)
+base_index_mt.get_luac = function(index, key)
check_index_arg(index, 'get')
key = keify(key)
return internal.get(index.space_id, index.id, key)
@@ -1229,7 +1250,7 @@ local function check_select_opts(opts, key_is_nil)
return iterator, offset, limit
end
-index_mt.select_ffi = function(index, key, opts)
+base_index_mt.select_ffi = function(index, key, opts)
check_index_arg(index, 'select')
local key, key_end = tuple_encode(key)
local iterator, offset, limit = check_select_opts(opts, key + 1 >= key_end)
@@ -1251,7 +1272,7 @@ index_mt.select_ffi = function(index, key, opts)
return ret
end
-index_mt.select_luac = function(index, key, opts)
+base_index_mt.select_luac = function(index, key, opts)
check_index_arg(index, 'select')
local key = keify(key)
local iterator, offset, limit = check_select_opts(opts, #key == 0)
@@ -1259,28 +1280,28 @@ index_mt.select_luac = function(index, key, opts)
offset, limit, key)
end
-index_mt.update = function(index, key, ops)
+base_index_mt.update = function(index, key, ops)
check_index_arg(index, 'update')
return internal.update(index.space_id, index.id, keify(key), ops);
end
-index_mt.delete = function(index, key)
+base_index_mt.delete = function(index, key)
check_index_arg(index, 'delete')
return internal.delete(index.space_id, index.id, keify(key));
end
-index_mt.info = function(index)
+base_index_mt.info = function(index)
return internal.info(index.space_id, index.id);
end
-index_mt.drop = function(index)
+base_index_mt.drop = function(index)
check_index_arg(index, 'drop')
return box.schema.index.drop(index.space_id, index.id)
end
-index_mt.rename = function(index, name)
+base_index_mt.rename = function(index, name)
check_index_arg(index, 'rename')
return box.schema.index.rename(index.space_id, index.id, name)
end
-index_mt.alter = function(index, options)
+base_index_mt.alter = function(index, options)
check_index_arg(index, 'alter')
if index.id == nil or index.space_id == nil then
box.error(box.error.PROC_LUA, "Usage: index:alter{opts}")
@@ -1288,6 +1309,17 @@ index_mt.alter = function(index, options)
return box.schema.index.alter(index.space_id, index.id, options)
end
+local read_ops = {'select', 'get', 'min', 'max', 'count', 'random', 'pairs'}
+for _, op in ipairs(read_ops) do
+ vinyl_index_mt[op] = base_index_mt[op..'_luac']
+ memtx_index_mt[op] = base_index_mt[op..'_ffi']
+end
+-- Lua 5.2 compatibility
+vinyl_index_mt.__pairs = vinyl_index_mt.pairs
+vinyl_index_mt.__ipairs = vinyl_index_mt.pairs
+memtx_index_mt.__pairs = memtx_index_mt.pairs
+memtx_index_mt.__ipairs = memtx_index_mt.pairs
+
local space_mt = {}
space_mt.len = function(space)
check_space_arg(space, 'len')
@@ -1313,7 +1345,7 @@ space_mt.bsize = function(space)
end
return builtin.space_bsize(s)
end
-space_mt.__newindex = index_mt.__newindex
+space_mt.__newindex = forbid_new_index
space_mt.get = function(space, key)
check_space_arg(space, 'get')
@@ -1409,22 +1441,13 @@ end
space_mt.__index = space_mt
function box.schema.space.bless(space)
- local index_mt = table.deepcopy(index_mt)
local space_mt = table.deepcopy(space_mt)
- -- true if reading operations may yield
- local read_yields = space.engine == 'vinyl'
- local read_ops = {'select', 'get', 'min', 'max', 'count', 'random', 'pairs'}
- for _, op in ipairs(read_ops) do
- if read_yields then
- -- use Lua/C implmenetation
- index_mt[op] = index_mt[op .. "_luac"]
- else
- -- use FFI implementation
- index_mt[op] = index_mt[op .. "_ffi"]
- end
+ local index_mt
+ if space.engine == 'vinyl' then
+ index_mt = table.deepcopy(vinyl_index_mt)
+ else
+ index_mt = table.deepcopy(memtx_index_mt)
end
- index_mt.__pairs = index_mt.pairs -- Lua 5.2 compatibility
- index_mt.__ipairs = index_mt.pairs -- Lua 5.2 compatibility
setmetatable(space, space_mt)
if type(space.index) == 'table' and space.enabled then
--
2.14.3 (Apple Git-98)
^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH v2 3/3] schema: expose space_mt and index_mt on box.schema table
2018-04-03 16:50 ` [PATCH v2 0/3] schema: expose space_mt and index_mt on box.schema table Vladislav Shpilevoy
2018-04-03 16:50 ` [PATCH v2 1/3] schema: move space_mt and index_mt definition out of space bless Vladislav Shpilevoy
2018-04-03 16:50 ` [PATCH v2 2/3] schema: inherit vinyl/memtx_index_mt from base index mt Vladislav Shpilevoy
@ 2018-04-03 16:50 ` Vladislav Shpilevoy
2018-05-05 12:47 ` [tarantool-patches] " Vladislav Shpilevoy
2 siblings, 1 reply; 20+ messages in thread
From: Vladislav Shpilevoy @ 2018-04-03 16:50 UTC (permalink / raw)
To: tarantool-patches; +Cc: vdavydov.dev, Vladislav Shpilevoy
This commit allows userland to extend the space and index
metatables with their own functions or even metamethods. Reducing
barriers for this kind of experimentation is vital for user
contribution toward the improvement of Tarantool's API.
There are 4 metatables available for extending:
box.schema.space_mt - metatable of all spaces;
box.schema.index_mt - base metatable of all indexes - replicated
into the vinyl and memtx. See below how.
box.schema.vinyl_index_mt - metatable of all vinyl indexes;
box.schema.memtx_index_mt - metatable of all memtx indexes.
Closes #3204
---
src/box/lua/schema.lua | 17 +++++----
test/box-tap/schema_mt.test.lua | 80 +++++++++++++++++++++++++++++++++++++++++
2 files changed, 88 insertions(+), 9 deletions(-)
create mode 100755 test/box-tap/schema_mt.test.lua
diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua
index 4b5eeeda9..5d5b0dc49 100644
--- a/src/box/lua/schema.lua
+++ b/src/box/lua/schema.lua
@@ -1074,10 +1074,6 @@ end
internal.check_iterator_type = check_iterator_type -- export for net.box
-local function forbid_new_index(t, k, v)
- return error('Attempt to modify a read-only table')
-end
-
local base_index_mt = {}
base_index_mt.__index = base_index_mt
--
@@ -1098,6 +1094,7 @@ setmetatable(base_index_mt, {
rawset(t, k, v)
end
})
+
-- __len and __index
base_index_mt.len = function(index)
check_index_arg(index, 'len')
@@ -1118,7 +1115,6 @@ base_index_mt.bsize = function(index)
end
-- Lua 5.2 compatibility
base_index_mt.__len = base_index_mt.len
-base_index_mt.__newindex = forbid_new_index
-- min and max
base_index_mt.min_ffi = function(index, key)
check_index_arg(index, 'min')
@@ -1345,7 +1341,6 @@ space_mt.bsize = function(space)
end
return builtin.space_bsize(s)
end
-space_mt.__newindex = forbid_new_index
space_mt.get = function(space, key)
check_space_arg(space, 'get')
@@ -1440,13 +1435,17 @@ space_mt.run_triggers = function(space, yesno)
end
space_mt.__index = space_mt
+box.schema.index_mt = base_index_mt
+box.schema.memtx_index_mt = memtx_index_mt
+box.schema.vinyl_index_mt = vinyl_index_mt
+box.schema.space_mt = space_mt
+
function box.schema.space.bless(space)
- local space_mt = table.deepcopy(space_mt)
local index_mt
if space.engine == 'vinyl' then
- index_mt = table.deepcopy(vinyl_index_mt)
+ index_mt = vinyl_index_mt
else
- index_mt = table.deepcopy(memtx_index_mt)
+ index_mt = memtx_index_mt
end
setmetatable(space, space_mt)
diff --git a/test/box-tap/schema_mt.test.lua b/test/box-tap/schema_mt.test.lua
new file mode 100755
index 000000000..b785b8e21
--- /dev/null
+++ b/test/box-tap/schema_mt.test.lua
@@ -0,0 +1,80 @@
+#!/usr/bin/env tarantool
+--
+-- pr-3204: expose space_mt, index_mt into box.schema.
+--
+
+local tap = require('tap')
+local test = tap.test('schema_mt')
+
+test:plan(18)
+
+box.cfg{
+ log="tarantool.log",
+}
+
+--
+-- Check that space metatable is shared between all spaces,
+-- regardless of engine.
+--
+local sp1 = box.schema.create_space('test1', {engine = 'memtx'})
+local sp2 = box.schema.create_space('test2', {engine = 'vinyl'})
+test:is(getmetatable(sp1), getmetatable(sp2), 'spaces share metatables')
+
+function box.schema.space_mt.myfunc(space, args)
+ return args
+end
+test:is(sp1:myfunc(123), 123, 'space_mt can be extended')
+test:is(sp1.myfunc, sp2.myfunc, 'space_mt can be extended')
+
+--
+-- Check that index metatable is shared in a scope of an engine.
+--
+local sp1_pk = sp1:create_index('pk')
+local sp1_sk = sp1:create_index('sk')
+local sp2_pk = sp2:create_index('pk')
+local sp2_sk = sp2:create_index('sk')
+test:is(getmetatable(sp1_pk), getmetatable(sp1_sk), 'memtx indexes share metatables')
+test:is(getmetatable(sp2_pk), getmetatable(sp2_sk), 'vinyl indexes share metatables')
+test:isnt(getmetatable(sp1_pk), getmetatable(sp2_pk), 'engines do not share metatables')
+
+--
+-- Check that there are two ways to extend index metatable:
+-- extend base index metatable, or extend engine specific.
+--
+function box.schema.index_mt.common_func(index, args)
+ return args
+end
+function box.schema.vinyl_index_mt.vinyl_func(index, args)
+ return args
+end
+function box.schema.memtx_index_mt.memtx_func(index, args)
+ return args
+end
+test:is(box.schema.index_mt.common_func, box.schema.vinyl_index_mt.common_func,
+ 'base index_mt is replicated into vinyl index_mt')
+test:is(box.schema.index_mt.common_func, box.schema.memtx_index_mt.common_func,
+ 'base index_mt is replicated into memtx index_mt')
+test:is(box.schema.index_mt.vinyl_func, nil, 'vinyl index_mt is not replicated')
+test:is(box.schema.index_mt.memtx_func, nil, 'memtx index_mt is not replicated')
+
+test:is(sp1_pk.common_func, box.schema.index_mt.common_func,
+ 'new common methods are visible in memtx index')
+test:is(sp2_pk.common_func, box.schema.index_mt.common_func,
+ 'new common methods are visible in vinyl index')
+
+test:is(sp1_pk.memtx_func, box.schema.memtx_index_mt.memtx_func,
+ 'new memtx methods are visible in memtx index')
+test:is(sp2_pk.vinyl_func, box.schema.vinyl_index_mt.vinyl_func,
+ 'new vinyl methods are visible in vinyl index')
+
+test:is(sp1_pk:memtx_func(100), 100, 'memtx local methods work')
+test:is(sp1_sk:common_func(200), 200, 'memtx common methods work')
+test:is(sp2_pk:vinyl_func(300), 300, 'vinyl local methods work')
+test:is(sp2_sk:common_func(400), 400, 'vinyl common methods work')
+
+sp1:drop()
+sp2:drop()
+
+test:check()
+
+os.exit(0)
--
2.14.3 (Apple Git-98)
^ permalink raw reply [flat|nested] 20+ messages in thread
* [tarantool-patches] Re: [PATCH v2 3/3] schema: expose space_mt and index_mt on box.schema table
2018-04-03 16:50 ` [PATCH v2 3/3] schema: expose space_mt and index_mt on box.schema table Vladislav Shpilevoy
@ 2018-05-05 12:47 ` Vladislav Shpilevoy
2018-05-08 16:48 ` Konstantin Osipov
0 siblings, 1 reply; 20+ messages in thread
From: Vladislav Shpilevoy @ 2018-05-05 12:47 UTC (permalink / raw)
To: tarantool-patches; +Cc: Konstantin Osipov
Users requested to be able to extend spaces individually. The
patch was reworked to allow this.
On 03/04/2018 19:50, Vladislav Shpilevoy wrote:
> This commit allows userland to extend the space and index
> metatables with their own functions or even metamethods. Reducing
> barriers for this kind of experimentation is vital for user
> contribution toward the improvement of Tarantool's API.
>
> There are 4 metatables available for extending:
> box.schema.space_mt - metatable of all spaces;
> box.schema.index_mt - base metatable of all indexes - replicated
> into the vinyl and memtx. See below how.
> box.schema.vinyl_index_mt - metatable of all vinyl indexes;
> box.schema.memtx_index_mt - metatable of all memtx indexes.
>
> Closes #3204
> ---
> src/box/lua/schema.lua | 17 +++++----
> test/box-tap/schema_mt.test.lua | 80 +++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 88 insertions(+), 9 deletions(-)
> create mode 100755 test/box-tap/schema_mt.test.lua
>
schema: expose space_mt and index_mt on box.schema table
This commit allows userland to extend the space and index
metatables with their own functions or even metamethods. Reducing
barriers for this kind of experimentation is vital for user
contribution toward the improvement of Tarantool's API.
There are 4 metatables available for extending:
box.schema.space_mt - metatable of all spaces;
box.schema.index_mt - base metatable of all indexes - replicated
into the vinyl and memtx. See below how.
box.schema.vinyl_index_mt - metatable of all vinyl indexes;
box.schema.memtx_index_mt - metatable of all memtx indexes.
On the other hand local space/index metatables still can be
extended individually to save compatibility with existing
modules. Routinely space/index metatable is just a proxy for a
global mt. When a user attempts to extend a space or index
methods via local space/index metatable instead of from
box.schema mt, the local metatable is transformed. Its __index
metamethod starts looking up at first in self, and only then into
the global mt.
Closes #3204
diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua
index 8a1d23b00..6a3da4897 100644
--- a/src/box/lua/schema.lua
+++ b/src/box/lua/schema.lua
@@ -1074,10 +1074,6 @@ end
internal.check_iterator_type = check_iterator_type -- export for net.box
-local function forbid_new_index(t, k, v)
- return error('Attempt to modify a read-only table')
-end
-
local base_index_mt = {}
base_index_mt.__index = base_index_mt
--
@@ -1118,7 +1114,6 @@ base_index_mt.bsize = function(index)
end
-- Lua 5.2 compatibility
base_index_mt.__len = base_index_mt.len
-base_index_mt.__newindex = forbid_new_index
-- min and max
base_index_mt.min_ffi = function(index, key)
check_index_arg(index, 'min')
@@ -1345,7 +1340,6 @@ space_mt.bsize = function(space)
end
return builtin.space_bsize(s)
end
-space_mt.__newindex = forbid_new_index
space_mt.get = function(space, key)
check_space_arg(space, 'get')
@@ -1441,14 +1435,41 @@ end
space_mt.frommap = box.internal.space.frommap
space_mt.__index = space_mt
+box.schema.index_mt = base_index_mt
+box.schema.memtx_index_mt = memtx_index_mt
+box.schema.vinyl_index_mt = vinyl_index_mt
+box.schema.space_mt = space_mt
+
+--
+-- Wrap a global space/index metatable into a space/index local
+-- one. Routinely this metatable just indexes the global one. When
+-- a user attempts to extend a space or index methods via local
+-- space/index metatable instead of from box.schema mt, the local
+-- metatable is transformed. Its __index metamethod starts looking
+-- up at first in self, and only then into the global mt.
+--
+local function wrap_schema_object_mt(name)
+ local mt = { __index = box.schema[name] }
+ local mt_mt = {}
+ mt_mt.__newindex = function(t, k, v)
+ mt_mt.__newindex = nil
+ mt.__index = function(t, k)
+ return mt[k] or box.schema[name][k]
+ end
+ rawset(mt, k, v)
+ end
+ setmetatable(mt, mt_mt)
+ return mt
+end
+
function box.schema.space.bless(space)
- local space_mt = table.deepcopy(space_mt)
local index_mt
if space.engine == 'vinyl' then
- index_mt = table.deepcopy(vinyl_index_mt)
+ index_mt = wrap_schema_object_mt('vinyl_index_mt')
else
- index_mt = table.deepcopy(memtx_index_mt)
+ index_mt = wrap_schema_object_mt('memtx_index_mt')
end
+ local space_mt = wrap_schema_object_mt('space_mt')
setmetatable(space, space_mt)
if type(space.index) == 'table' and space.enabled then
diff --git a/test/box-tap/schema_mt.test.lua b/test/box-tap/schema_mt.test.lua
new file mode 100755
index 000000000..5a635262f
--- /dev/null
+++ b/test/box-tap/schema_mt.test.lua
@@ -0,0 +1,106 @@
+#!/usr/bin/env tarantool
+--
+-- pr-3204: expose space_mt, index_mt into box.schema.
+--
+
+local tap = require('tap')
+local test = tap.test('schema_mt')
+
+test:plan(26)
+
+box.cfg{
+ log="tarantool.log",
+}
+
+--
+-- Check that space metatable is shared between all spaces,
+-- regardless of engine.
+--
+local sp1 = box.schema.create_space('test1', {engine = 'memtx'})
+local sp2 = box.schema.create_space('test2', {engine = 'vinyl'})
+test:is(getmetatable(sp1).__index, getmetatable(sp2).__index, 'spaces share metatables __index')
+
+function box.schema.space_mt.myfunc(space, args)
+ return args
+end
+test:is(sp1:myfunc(123), 123, 'space_mt can be extended')
+test:is(sp1.myfunc, sp2.myfunc, 'space_mt can be extended')
+
+--
+-- Check that index metatable is shared in a scope of an engine.
+--
+local sp1_pk = sp1:create_index('pk')
+local sp1_sk = sp1:create_index('sk')
+local sp2_pk = sp2:create_index('pk')
+local sp2_sk = sp2:create_index('sk')
+test:is(getmetatable(sp1_pk).__index, getmetatable(sp1_sk).__index, 'memtx indexes share metatables __index')
+test:is(getmetatable(sp2_pk).__index, getmetatable(sp2_sk).__index, 'vinyl indexes share metatables __index')
+test:isnt(getmetatable(sp1_pk).__index, getmetatable(sp2_pk).__index, 'engines do not share metatables __index')
+
+--
+-- Check that there are two ways to extend index metatable:
+-- extend base index metatable, or extend engine specific.
+--
+function box.schema.index_mt.common_func(index, args)
+ return args
+end
+function box.schema.vinyl_index_mt.vinyl_func(index, args)
+ return args
+end
+function box.schema.memtx_index_mt.memtx_func(index, args)
+ return args
+end
+test:is(box.schema.index_mt.common_func, box.schema.vinyl_index_mt.common_func,
+ 'base index_mt is replicated into vinyl index_mt')
+test:is(box.schema.index_mt.common_func, box.schema.memtx_index_mt.common_func,
+ 'base index_mt is replicated into memtx index_mt')
+test:is(box.schema.index_mt.vinyl_func, nil, 'vinyl index_mt is not replicated')
+test:is(box.schema.index_mt.memtx_func, nil, 'memtx index_mt is not replicated')
+
+test:is(sp1_pk.common_func, box.schema.index_mt.common_func,
+ 'new common methods are visible in memtx index')
+test:is(sp2_pk.common_func, box.schema.index_mt.common_func,
+ 'new common methods are visible in vinyl index')
+
+test:is(sp1_pk.memtx_func, box.schema.memtx_index_mt.memtx_func,
+ 'new memtx methods are visible in memtx index')
+test:is(sp2_pk.vinyl_func, box.schema.vinyl_index_mt.vinyl_func,
+ 'new vinyl methods are visible in vinyl index')
+
+test:is(sp1_pk:memtx_func(100), 100, 'memtx local methods work')
+test:is(sp1_sk:common_func(200), 200, 'memtx common methods work')
+test:is(sp2_pk:vinyl_func(300), 300, 'vinyl local methods work')
+test:is(sp2_sk:common_func(400), 400, 'vinyl common methods work')
+
+--
+-- Test space/index-local methods.
+-- A space local metatable can extended so it does not affect
+-- other spaces. Same about index.
+--
+sp3 = box.schema.create_space('test3', {engine = 'memtx'})
+sp3_pk = sp3:create_index('pk')
+sp3_sk = sp3:create_index('sk')
+mt1 = getmetatable(sp1)
+mt2 = getmetatable(sp2)
+test:isnt(mt1, mt2, 'spaces do not share metatables')
+index_mt1 = getmetatable(sp3_pk)
+index_mt2 = getmetatable(sp3_sk)
+test:isnt(index_mt1, index_mt2, 'indexes do not share metatables')
+
+mt1.my_func = function(a) return a end
+test:isnil(mt2.my_func, 'extend local space metatable')
+test:is(sp1.my_func(100), 100, 'extend local space metatable')
+test:isnil(sp2.my_func, 'extend local space metatable')
+
+index_mt1.my_func = function(a) return a + 100 end
+test:isnil(index_mt2.my_func, 'extend local index metatable')
+test:is(sp3_pk.my_func(100), 200, 'extend local index metatable')
+test:isnil(sp3_sk.my_func, 'extend local index metatable')
+
+sp1:drop()
+sp2:drop()
+sp3:drop()
+
+test:check()
+
+os.exit(0)
^ permalink raw reply [flat|nested] 20+ messages in thread
* [tarantool-patches] Re: [PATCH v2 3/3] schema: expose space_mt and index_mt on box.schema table
2018-05-05 12:47 ` [tarantool-patches] " Vladislav Shpilevoy
@ 2018-05-08 16:48 ` Konstantin Osipov
2018-05-08 17:33 ` Vladislav Shpilevoy
0 siblings, 1 reply; 20+ messages in thread
From: Konstantin Osipov @ 2018-05-08 16:48 UTC (permalink / raw)
To: Vladislav Shpilevoy; +Cc: tarantool-patches
* Vladislav Shpilevoy <v.shpilevoy@tarantool.org> [18/05/05 23:23]:
This branch breaks app/luafun.test.lua
> Users requested to be able to extend spaces individually. The
> patch was reworked to allow this.
>
> On 03/04/2018 19:50, Vladislav Shpilevoy wrote:
> >This commit allows userland to extend the space and index
> >metatables with their own functions or even metamethods. Reducing
> >barriers for this kind of experimentation is vital for user
> >contribution toward the improvement of Tarantool's API.
> >
> >There are 4 metatables available for extending:
> >box.schema.space_mt - metatable of all spaces;
> >box.schema.index_mt - base metatable of all indexes - replicated
> > into the vinyl and memtx. See below how.
> >box.schema.vinyl_index_mt - metatable of all vinyl indexes;
> >box.schema.memtx_index_mt - metatable of all memtx indexes.
> >
> >Closes #3204
--
Konstantin Osipov, Moscow, Russia, +7 903 626 22 32
http://tarantool.io - www.twitter.com/kostja_osipov
^ permalink raw reply [flat|nested] 20+ messages in thread
* [tarantool-patches] Re: [PATCH v2 3/3] schema: expose space_mt and index_mt on box.schema table
2018-05-08 16:48 ` Konstantin Osipov
@ 2018-05-08 17:33 ` Vladislav Shpilevoy
0 siblings, 0 replies; 20+ messages in thread
From: Vladislav Shpilevoy @ 2018-05-08 17:33 UTC (permalink / raw)
To: Konstantin Osipov; +Cc: tarantool-patches
Hello. Thanks for review!
On 08/05/2018 19:48, Konstantin Osipov wrote:
> * Vladislav Shpilevoy <v.shpilevoy@tarantool.org> [18/05/05 23:23]:
>
> This branch breaks app/luafun.test.lua
Sorry, just noticed that my auto test-runner script skips app/ directory.
Fixed on the branch. The problem was that luafun expects that space/index
metatable contains __ipairs/__pairs metamethods with not proxy via __index.
@@ -1449,7 +1449,12 @@ box.schema.space_mt = space_mt
-- up at first in self, and only then into the global mt.
--
local function wrap_schema_object_mt(name)
- local mt = { __index = box.schema[name] }
+ local global_mt = box.schema[name]
+ local mt = {
+ __index = global_mt,
+ __ipairs = global_mt.__ipairs,
+ __pairs = global_mt.__pairs
+ }
local mt_mt = {}
mt_mt.__newindex = function(t, k, v)
mt_mt.__newindex = nil
>
>> Users requested to be able to extend spaces individually. The
>> patch was reworked to allow this.
>>
>> On 03/04/2018 19:50, Vladislav Shpilevoy wrote:
>>> This commit allows userland to extend the space and index
>>> metatables with their own functions or even metamethods. Reducing
>>> barriers for this kind of experimentation is vital for user
>>> contribution toward the improvement of Tarantool's API.
>>>
>>> There are 4 metatables available for extending:
>>> box.schema.space_mt - metatable of all spaces;
>>> box.schema.index_mt - base metatable of all indexes - replicated
>>> into the vinyl and memtx. See below how.
>>> box.schema.vinyl_index_mt - metatable of all vinyl indexes;
>>> box.schema.memtx_index_mt - metatable of all memtx indexes.
>>>
>>> Closes #3204
>
^ permalink raw reply [flat|nested] 20+ messages in thread
end of thread, other threads:[~2018-05-08 17:33 UTC | newest]
Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-03-23 13:07 [PATCH 0/2] schema: expose space_mt and index_mt on table Vladislav Shpilevoy
2018-03-23 13:07 ` [PATCH 1/2] schema: expose space_mt and index_mt on `box.schema` table Vladislav Shpilevoy
2018-03-23 13:07 ` [PATCH 2/2] schema: review fixes for box.schema.space/index metatables Vladislav Shpilevoy
2018-03-29 10:54 ` [PATCH 0/2] schema: expose space_mt and index_mt on table Vladimir Davydov
2018-03-29 14:31 ` [PATCH v2 0/2] schema: expose space_mt and index_mt on box.schema table Vladislav Shpilevoy
2018-03-29 14:31 ` [PATCH v2 1/2] schema: move space_mt and index_mt definition out of space bless Vladislav Shpilevoy
2018-04-01 9:33 ` Vladimir Davydov
2018-04-01 11:02 ` [PATCH 0/2] schema: expose space_mt and index_mt on box.schema table Vladislav Shpilevoy
2018-04-01 11:02 ` [PATCH 1/2] schema: move space_mt and index_mt definition out of space bless Vladislav Shpilevoy
2018-04-01 11:02 ` [PATCH 2/2] schema: expose space_mt and index_mt on `box.schema` table Vladislav Shpilevoy
2018-04-02 11:28 ` Vladimir Davydov
2018-04-03 16:50 ` [PATCH v2 0/3] schema: expose space_mt and index_mt on box.schema table Vladislav Shpilevoy
2018-04-03 16:50 ` [PATCH v2 1/3] schema: move space_mt and index_mt definition out of space bless Vladislav Shpilevoy
2018-04-03 16:50 ` [PATCH v2 2/3] schema: inherit vinyl/memtx_index_mt from base index mt Vladislav Shpilevoy
2018-04-03 16:50 ` [PATCH v2 3/3] schema: expose space_mt and index_mt on box.schema table Vladislav Shpilevoy
2018-05-05 12:47 ` [tarantool-patches] " Vladislav Shpilevoy
2018-05-08 16:48 ` Konstantin Osipov
2018-05-08 17:33 ` Vladislav Shpilevoy
2018-03-29 14:31 ` [PATCH v2 2/2] schema: expose space_mt and index_mt on `box.schema` table Vladislav Shpilevoy
2018-04-01 9:37 ` [PATCH v2 0/2] schema: expose space_mt and index_mt on box.schema table Vladimir Davydov
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox