Skip to content

Commit

Permalink
Add functions from ddl for sharding func module
Browse files Browse the repository at this point in the history
  • Loading branch information
AnaNek committed Dec 22, 2021
1 parent 0d37a6e commit 8ee652d
Show file tree
Hide file tree
Showing 3 changed files with 340 additions and 0 deletions.
101 changes: 101 additions & 0 deletions crud/common/sharding/sharding_func.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
local errors = require('errors')
local log = require('log')

local dev_checks = require('crud.common.dev_checks')
local utils = require('crud.common.utils')
local cache = require('crud.common.sharding.sharding_metadata_cache')

local ShardingFuncError = errors.new_class('ShardingFuncError', {capture_stack = false})

local sharding_func_module = {}

local function is_callable(object)
if type(object) == 'function' then
return true
end

-- all objects with type `cdata` are allowed
-- because there is no easy way to get
-- metatable.__call of object with type `cdata`
if type(object) == 'cdata' then
return true
end

local object_metatable = getmetatable(object)
if (type(object) == 'table' or type(object) == 'userdata') then
-- if metatable type is not `table` -> metatable is protected ->
-- cannot detect metamethod `__call` exists
if object_metatable and type(object_metatable) ~= 'table' then
return true
end

-- `__call` metamethod can be only the `function`
-- and cannot be a `table` | `userdata` | `cdata`
-- with `__call` methamethod on its own
if object_metatable and object_metatable.__call then
return type(object_metatable.__call) == 'function'
end
end

return false
end

local function get_function_from_G(func_name)
local chunks = string.split(func_name, '.')
local sharding_func = _G

-- check is the each chunk an identifier
for _, chunk in pairs(chunks) do
if not utils.check_name_isident(chunk) or sharding_func == nil then
return nil
end
sharding_func = rawget(sharding_func, chunk)
end

return sharding_func
end

local function as_callable_object(sharding_func_def, space_name)
if type(sharding_func_def) == 'string' then
local sharding_func = get_function_from_G(sharding_func_def)
if sharding_func ~= nil and is_callable(sharding_func) == true then
return sharding_func
end
end

if type(sharding_func_def) == 'table' then
local sharding_func, err = loadstring('return ' .. sharding_func_def.body)
if sharding_func == nil then
return nil, ShardingFuncError:new(
"Body is incorrect in sharding_func for space (%s): %s", space_name, err)
end
return sharding_func()
end

return nil, ShardingFuncError:new(
"Wrong sharding function specified in _ddl_sharding_func space for (%s) space", space_name
)
end

function sharding_func_module.extract_function_def(tuple)
if not tuple then
return nil
end

if tuple.sharding_func_body ~= nil then
return {body = tuple.sharding_func_body}
end

if tuple.sharding_func_name ~= nil then
return tuple.sharding_func_name
end

return nil
end

sharding_func_module.internal = {
as_callable_object = as_callable_object,
is_callable = is_callable,
}

return sharding_func_module
85 changes: 85 additions & 0 deletions crud/common/utils.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ local errors = require('errors')
local ffi = require('ffi')
local vshard = require('vshard')
local fun = require('fun')
local bit = require('bit')

local schema = require('crud.common.schema')
local dev_checks = require('crud.common.dev_checks')
Expand All @@ -17,6 +18,54 @@ local utils = {}

local space_format_cache = setmetatable({}, {__mode = 'k'})

-- copy from LuaJIT lj_char.c
local lj_char_bits = {
0,
1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
152,152,152,152,152,152,152,152,152,152, 4, 4, 4, 4, 4, 4,
4,176,176,176,176,176,176,160,160,160,160,160,160,160,160,160,
160,160,160,160,160,160,160,160,160,160,160, 4, 4, 4, 4,132,
4,208,208,208,208,208,208,192,192,192,192,192,192,192,192,192,
192,192,192,192,192,192,192,192,192,192,192, 4, 4, 4, 4, 1,
128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,
128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,
128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,
128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,
128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,
128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,
128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,
128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128
}

local LJ_CHAR_IDENT = 0x80
local LJ_CHAR_DIGIT = 0x08

local LUA_KEYWORDS = {
['and'] = true,
['end'] = true,
['in'] = true,
['repeat'] = true,
['break'] = true,
['false'] = true,
['local'] = true,
['return'] = true,
['do'] = true,
['for'] = true,
['nil'] = true,
['then'] = true,
['else'] = true,
['function'] = true,
['not'] = true,
['true'] = true,
['elseif'] = true,
['if'] = true,
['or'] = true,
['until'] = true,
['while'] = true,
}

function utils.table_count(table)
dev_checks("table")

Expand Down Expand Up @@ -606,4 +655,40 @@ function utils.merge_options(opts_a, opts_b)
return fun.chain(opts_a or {}, opts_b or {}):tomap()
end

local function lj_char_isident(n)
return bit.band(lj_char_bits[n + 2], LJ_CHAR_IDENT) == LJ_CHAR_IDENT
end

local function lj_char_isdigit(n)
return bit.band(lj_char_bits[n + 2], LJ_CHAR_DIGIT) == LJ_CHAR_DIGIT
end

function utils.check_name_isident(name)
dev_checks('string')

-- sharding function name cannot
-- be equal to lua keyword
if LUA_KEYWORDS[name] then
return false
end

-- sharding function name cannot
-- begin with a digit
local char_number = string.byte(name:sub(1,1))
if lj_char_isdigit(char_number) then
return false
end

-- sharding func name must be sequence
-- of letters, digits, or underscore symbols
for i = 1, #name do
local char_number = string.byte(name:sub(i,i))
if not lj_char_isident(char_number) then
return false
end
end

return true
end

return utils
154 changes: 154 additions & 0 deletions test/unit/sharding_metadata_test.lua
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
local t = require('luatest')
local ffi = require('ffi')
local sharding_metadata_module = require('crud.common.sharding.sharding_metadata')
local sharding_key_module = require('crud.common.sharding.sharding_key')
local sharding_func_module = require('crud.common.sharding.sharding_func')
local cache = require('crud.common.sharding.sharding_metadata_cache')
local utils = require('crud.common.utils')

Expand Down Expand Up @@ -249,3 +251,155 @@ g.test_is_part_of_pk_negative = function()
local res = is_part_of_pk(space_name, index_parts, sharding_key_as_index_obj)
t.assert_equals(res, false)
end

g.test_as_callable_object_func_body = function()
local sharding_func_def = {body = 'function(key) return key end'}

local callable_obj, err = sharding_func_module.internal.as_callable_object(sharding_func_def,
'space_name')
t.assert_equals(err, nil)
t.assert_equals(type(callable_obj), 'function')
t.assert_equals(callable_obj(5), 5)
end

g.test_as_callable_object_G_func = function()
local some_module = {
sharding_func = function(key) return key % 10 end
}
local module_name = 'some_module'
local sharding_func_def = 'some_module.sharding_func'
rawset(_G, module_name, some_module)

local callable_obj, err = sharding_func_module.internal.as_callable_object(sharding_func_def,
'space_name')
t.assert_equals(err, nil)
t.assert_equals(callable_obj, some_module.sharding_func)

rawset(_G, module_name, nil)
end

g.test_as_callable_object_func_body_negative = function()
local sharding_func_def = {body = 'function(key) return key'}

local callable_obj, err = sharding_func_module.internal.as_callable_object(sharding_func_def,
'space_name')
t.assert_equals(callable_obj, nil)
t.assert_str_contains(err.err,
'Body is incorrect in sharding_func for space (space_name)')
end

g.test_as_callable_object_G_func_not_exist = function()
local sharding_func_def = 'some_module.sharding_func'

local callable_obj, err = sharding_func_module.internal.as_callable_object(sharding_func_def,
'space_name')
t.assert_equals(callable_obj, nil)
t.assert_str_contains(err.err,
'Wrong sharding function specified in _ddl_sharding_func space for (space_name) space')
end

g.test_as_callable_object_G_func_keyword = function()
local sharding_func_def = 'and'
rawset(_G, sharding_func_def, function(key) return key % 10 end)

local callable_obj, err = sharding_func_module.internal.as_callable_object(sharding_func_def,
'space_name')
t.assert_equals(callable_obj, nil)
t.assert_str_contains(err.err,
'Wrong sharding function specified in _ddl_sharding_func space for (space_name) space')

rawset(_G, sharding_func_def, nil)
end

g.test_as_callable_object_G_func_begin_with_digit = function()
local sharding_func_def = '5incorrect_name'
rawset(_G, sharding_func_def, function(key) return key % 10 end)

local callable_obj, err = sharding_func_module.internal.as_callable_object(sharding_func_def,
'space_name')
t.assert_equals(callable_obj, nil)
t.assert_str_contains(err.err,
'Wrong sharding function specified in _ddl_sharding_func space for (space_name) space')

rawset(_G, sharding_func_def, nil)
end

g.test_as_callable_object_G_func_incorrect_symbol = function()
local sharding_func_def = 'incorrect-name'
rawset(_G, sharding_func_def, function(key) return key % 10 end)

local callable_obj, err = sharding_func_module.internal.as_callable_object(sharding_func_def,
'space_name')
t.assert_equals(callable_obj, nil)
t.assert_str_contains(err.err,
'Wrong sharding function specified in _ddl_sharding_func space for (space_name) space')

rawset(_G, sharding_func_def, nil)
end

g.test_as_callable_object_invalid_type = function()
local sharding_func_def = 5

local callable_obj, err = sharding_func_module.internal.as_callable_object(sharding_func_def,
'space_name')
t.assert_equals(callable_obj, nil)
t.assert_str_contains(err.err,
'Wrong sharding function specified in _ddl_sharding_func space for (space_name) space')
end

g.test_is_callable_func = function()
local sharding_func_obj = function(key) return key end

local ok = sharding_func_module.internal.is_callable(sharding_func_obj)
t.assert_equals(ok, true)
end

g.test_is_callable_table_positive = function()
local sharding_func_table = setmetatable({}, {
__call = function(_, key) return key end
})

local ok = sharding_func_module.internal.is_callable(sharding_func_table)
t.assert_equals(ok, true)
end

g.test_is_callable_table_negative = function()
local sharding_func_table = setmetatable({}, {})

local ok = sharding_func_module.internal.is_callable(sharding_func_table)
t.assert_equals(ok, false)
end

g.test_is_callable_userdata_positive = function()
local sharding_func_userdata = newproxy(true)
local mt = getmetatable(sharding_func_userdata)
mt.__call = function(_, key) return key end

local ok = sharding_func_module.internal.is_callable(sharding_func_userdata)
t.assert_equals(ok, true)
end

g.test_is_callable_userdata_negative = function()
local sharding_func_userdata = newproxy(true)
local mt = getmetatable(sharding_func_userdata)
mt.__call = {}

local ok = sharding_func_module.internal.is_callable(sharding_func_userdata)
t.assert_equals(ok, false)
end

g.test_is_callable_cdata = function()
ffi.cdef[[
typedef struct
{
int data;
} test_check_struct_t;
]]
ffi.metatype('test_check_struct_t', {
__call = function(_, key) return key end
})
local sharding_func_cdata = ffi.new('test_check_struct_t')

local ok = sharding_func_module.internal.is_callable(sharding_func_cdata)
t.assert_equals(ok, true)
end

0 comments on commit 8ee652d

Please sign in to comment.