Tarantool development patches archive
 help / color / mirror / Atom feed
From: Vladislav Shpilevoy <v.shpilevoy@tarantool.org>
To: tarantool-patches@freelists.org
Cc: Konstantin Osipov <kostja@tarantool.org>
Subject: [tarantool-patches] Re: [PATCH v2 3/3] schema: expose space_mt and index_mt on box.schema table
Date: Sat, 5 May 2018 15:47:34 +0300	[thread overview]
Message-ID: <a4b824d3-7b0c-5243-fe10-d5ffb47df0c2@tarantool.org> (raw)
In-Reply-To: <8053011f0e2036fdb811dd7ad6097ccc150b76d0.1522774214.git.v.shpilevoy@tarantool.org>

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)

  reply	other threads:[~2018-05-05 12:47 UTC|newest]

Thread overview: 20+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
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                   ` Vladislav Shpilevoy [this message]
2018-05-08 16:48                     ` [tarantool-patches] " 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

Reply instructions:

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

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

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

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

  git send-email \
    --in-reply-to=a4b824d3-7b0c-5243-fe10-d5ffb47df0c2@tarantool.org \
    --to=v.shpilevoy@tarantool.org \
    --cc=kostja@tarantool.org \
    --cc=tarantool-patches@freelists.org \
    --subject='[tarantool-patches] Re: [PATCH v2 3/3] schema: expose space_mt and index_mt on box.schema table' \
    /path/to/YOUR_REPLY

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

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

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