From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: From: Kirill Shcherbatov Subject: [PATCH v1 3/8] schema: rework _func system space format Date: Thu, 30 May 2019 13:45:30 +0300 Message-Id: In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit To: tarantool-patches@freelists.org, vdavydov.dev@gmail.com Cc: Kirill Shcherbatov List-ID: This patch updates a _func system space to prepare it for working with persistent functions. The format of the _func system space is [ UINT, UINT, STR, UINT, STR, STR, STR, BOOL] It is preparational step to introduce persistent Lua function in Tarantool. The new field is a string that represents the function body, returns is the type of value that it returns, and the is_deterministic flag is responsible whether this routine deterministic or not (can produce only one result for a given list of parameters). Updated function definition structure, decode operation and migration script correspondingly. Part of #4182 Needed for #1260 --- src/box/alter.cc | 63 +++++++++++++++++++++++++++++------ src/box/bootstrap.snap | Bin 4393 -> 4447 bytes src/box/func_def.h | 22 ++++++++---- src/box/lua/schema.lua | 10 ++++-- src/box/lua/upgrade.lua | 22 +++++++++++- src/box/schema_def.h | 3 ++ test/box-py/bootstrap.result | 6 ++-- test/box/access_misc.result | 6 ++-- 8 files changed, 106 insertions(+), 26 deletions(-) diff --git a/src/box/alter.cc b/src/box/alter.cc index 3b57a7d82..c9446bfe0 100644 --- a/src/box/alter.cc +++ b/src/box/alter.cc @@ -2426,26 +2426,47 @@ func_def_get_ids_from_tuple(struct tuple *tuple, uint32_t *fid, uint32_t *uid) static struct func_def * func_def_new_from_tuple(struct tuple *tuple) { - uint32_t len; - const char *name = tuple_field_str_xc(tuple, BOX_FUNC_FIELD_NAME, - &len); - if (len > BOX_NAME_MAX) + uint32_t field_count = tuple_field_count(tuple); + uint32_t name_len, body_len; + const char *name, *body; + name = tuple_field_str_xc(tuple, BOX_FUNC_FIELD_NAME, &name_len); + if (name_len > BOX_NAME_MAX) { tnt_raise(ClientError, ER_CREATE_FUNCTION, tt_cstr(name, BOX_INVALID_NAME_MAX), "function name is too long"); - identifier_check_xc(name, len); - struct func_def *def = (struct func_def *) malloc(func_def_sizeof(len)); + } + identifier_check_xc(name, name_len); + if (field_count > BOX_FUNC_FIELD_BODY) { + body = tuple_field_str_xc(tuple, BOX_FUNC_FIELD_BODY, + &body_len); + } else { + body = NULL; + body_len = 0; + } + + uint32_t def_sz = func_def_sizeof(name_len, body_len); + struct func_def *def = (struct func_def *) malloc(def_sz); if (def == NULL) - tnt_raise(OutOfMemory, func_def_sizeof(len), "malloc", "def"); + tnt_raise(OutOfMemory, def_sz, "malloc", "def"); auto def_guard = make_scoped_guard([=] { free(def); }); func_def_get_ids_from_tuple(tuple, &def->fid, &def->uid); - memcpy(def->name, name, len); - def->name[len] = 0; - if (tuple_field_count(tuple) > BOX_FUNC_FIELD_SETUID) + + def->name = (char *)def + sizeof(struct func_def); + memcpy(def->name, name, name_len); + def->name[name_len] = 0; + if (body_len > 0) { + def->body = def->name + name_len + 1; + memcpy(def->body, body, body_len); + def->body[body_len] = 0; + } else { + def->body = NULL; + } + + if (field_count > BOX_FUNC_FIELD_SETUID) def->setuid = tuple_field_u32_xc(tuple, BOX_FUNC_FIELD_SETUID); else def->setuid = false; - if (tuple_field_count(tuple) > BOX_FUNC_FIELD_LANGUAGE) { + if (field_count > BOX_FUNC_FIELD_LANGUAGE) { const char *language = tuple_field_cstr_xc(tuple, BOX_FUNC_FIELD_LANGUAGE); def->language = STR2ENUM(func_language, language); @@ -2457,6 +2478,26 @@ func_def_new_from_tuple(struct tuple *tuple) /* Lua is the default. */ def->language = FUNC_LANGUAGE_LUA; } + if (def->language != FUNC_LANGUAGE_LUA && body_len > 0) { + tnt_raise(ClientError, ER_CREATE_FUNCTION, name, + "function body may be specified only for " + "Lua language"); + } + if (field_count > BOX_FUNC_FIELD_BODY) { + assert(field_count > BOX_FUNC_FIELD_IS_DETERMINISTIC); + const char *type = + tuple_field_cstr_xc(tuple, BOX_FUNC_FIELD_RETURNS); + def->returns = STR2ENUM(field_type, type); + if (def->returns == field_type_MAX) { + tnt_raise(ClientError, ER_CREATE_FUNCTION, name, + "function return value has unknown field type"); + } + def->is_deterministic = + tuple_field_bool_xc(tuple, BOX_FUNC_FIELD_IS_DETERMINISTIC); + } else { + def->returns = FIELD_TYPE_ANY; + def->is_deterministic = false; + } def_guard.is_active = false; return def; } diff --git a/src/box/bootstrap.snap b/src/box/bootstrap.snap diff --git a/src/box/func_def.h b/src/box/func_def.h index 5b52ab498..78fef9d22 100644 --- a/src/box/func_def.h +++ b/src/box/func_def.h @@ -32,6 +32,7 @@ */ #include "trivia/util.h" +#include "field_def.h" #include /** @@ -54,17 +55,21 @@ struct func_def { uint32_t fid; /** Owner of the function. */ uint32_t uid; + /** Function name. */ + char *name; + /** Definition of the routine. */ + char *body; /** * True if the function requires change of user id before * invocation. */ bool setuid; - /** - * The language of the stored function. - */ + /** The language of the stored function. */ enum func_language language; - /** Function name. */ - char name[0]; + /** The type of the returned value. */ + enum field_type returns; + /** Whether this function is deterministic. */ + bool is_deterministic; }; /** @@ -73,10 +78,13 @@ struct func_def { * for a function of length @a a name_len. */ static inline size_t -func_def_sizeof(uint32_t name_len) +func_def_sizeof(uint32_t name_len, uint32_t body_len) { /* +1 for '\0' name terminating. */ - return sizeof(struct func_def) + name_len + 1; + size_t sz = sizeof(struct func_def) + name_len + 1; + if (body_len > 0) + sz += body_len + 1; + return sz; } /** diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua index 39bd8da6d..960ea0fc7 100644 --- a/src/box/lua/schema.lua +++ b/src/box/lua/schema.lua @@ -2105,7 +2105,8 @@ box.schema.func.create = function(name, opts) opts = opts or {} check_param_table(opts, { setuid = 'boolean', if_not_exists = 'boolean', - language = 'string'}) + language = 'string', body = 'string', + returns = 'string', is_deterministic = 'boolean'}) local _func = box.space[box.schema.FUNC_ID] local _vfunc = box.space[box.schema.VFUNC_ID] local func = _vfunc.index.name:get{name} @@ -2115,10 +2116,13 @@ box.schema.func.create = function(name, opts) end return end - opts = update_param_table(opts, { setuid = false, language = 'lua'}) + opts = update_param_table(opts, { setuid = false, language = 'lua', + body = '', returns = 'any', + is_deterministic = false}) opts.language = string.upper(opts.language) opts.setuid = opts.setuid and 1 or 0 - _func:auto_increment{session.euid(), name, opts.setuid, opts.language} + _func:auto_increment{session.euid(), name, opts.setuid, opts.language, + opts.body, opts.returns, opts.is_deterministic} end box.schema.func.drop = function(name, opts) diff --git a/src/box/lua/upgrade.lua b/src/box/lua/upgrade.lua index 070662698..c38e5e3ba 100644 --- a/src/box/lua/upgrade.lua +++ b/src/box/lua/upgrade.lua @@ -324,7 +324,7 @@ local function initial_1_7_5() -- create "box.schema.user.info" function log.info('create function "box.schema.user.info" with setuid') - _func:replace{1, ADMIN, 'box.schema.user.info', 1, 'LUA'} + _func:replace{1, ADMIN, 'box.schema.user.info', 1, 'LUA', '', 'any', false} -- grant 'public' role access to 'box.schema.user.info' function log.info('grant execute on function "box.schema.user.info" to public') @@ -774,8 +774,28 @@ local function upgrade_sequence_to_2_2_1() _space_sequence:format(format) end +local function upgrade_func_to_2_2_1() + log.info("Update _func format") + local _func = box.space[box.schema.FUNC_ID] + local format = {} + format[1] = {name='id', type='unsigned'} + format[2] = {name='owner', type='unsigned'} + format[3] = {name='name', type='string'} + format[4] = {name='setuid', type='unsigned'} + format[5] = {name='language', type='string'} + format[6] = {name='body', type='string'} + format[7] = {name='returns', type='string'} + format[8] = {name='is_deterministic', type='boolean'} + for _, v in box.space._func:pairs() do + _ = box.space._func:replace({v.id, v.owner, v.name, v.setuid, + v[5] or 'LUA', '', 'any', false}) + end + _func:format(format) +end + local function upgrade_to_2_2_1() upgrade_sequence_to_2_2_1() + upgrade_func_to_2_2_1() end -------------------------------------------------------------------------------- diff --git a/src/box/schema_def.h b/src/box/schema_def.h index b817b49f6..25f6b3d52 100644 --- a/src/box/schema_def.h +++ b/src/box/schema_def.h @@ -163,6 +163,9 @@ enum { BOX_FUNC_FIELD_NAME = 2, BOX_FUNC_FIELD_SETUID = 3, BOX_FUNC_FIELD_LANGUAGE = 4, + BOX_FUNC_FIELD_BODY = 5, + BOX_FUNC_FIELD_RETURNS = 6, + BOX_FUNC_FIELD_IS_DETERMINISTIC = 7, }; /** _collation fields. */ diff --git a/test/box-py/bootstrap.result b/test/box-py/bootstrap.result index 0684914c0..84c03c6fe 100644 --- a/test/box-py/bootstrap.result +++ b/test/box-py/bootstrap.result @@ -49,7 +49,9 @@ box.space._space:select{} 'type': 'string'}, {'name': 'opts', 'type': 'map'}, {'name': 'parts', 'type': 'array'}]] - [296, 1, '_func', 'memtx', 0, {}, [{'name': 'id', 'type': 'unsigned'}, {'name': 'owner', 'type': 'unsigned'}, {'name': 'name', 'type': 'string'}, {'name': 'setuid', - 'type': 'unsigned'}]] + 'type': 'unsigned'}, {'name': 'language', 'type': 'string'}, {'name': 'body', + 'type': 'string'}, {'name': 'returns', 'type': 'string'}, {'name': 'is_deterministic', + 'type': 'boolean'}]] - [297, 1, '_vfunc', 'sysview', 0, {}, [{'name': 'id', 'type': 'unsigned'}, {'name': 'owner', 'type': 'unsigned'}, {'name': 'name', 'type': 'string'}, {'name': 'setuid', 'type': 'unsigned'}]] @@ -142,7 +144,7 @@ box.space._user:select{} ... box.space._func:select{} --- -- - [1, 1, 'box.schema.user.info', 1, 'LUA'] +- - [1, 1, 'box.schema.user.info', 1, 'LUA', '', 'any', false] ... box.space._priv:select{} --- diff --git a/test/box/access_misc.result b/test/box/access_misc.result index 24bdd9d63..3c8318b0c 100644 --- a/test/box/access_misc.result +++ b/test/box/access_misc.result @@ -789,7 +789,9 @@ box.space._space:select() 'type': 'string'}, {'name': 'opts', 'type': 'map'}, {'name': 'parts', 'type': 'array'}]] - [296, 1, '_func', 'memtx', 0, {}, [{'name': 'id', 'type': 'unsigned'}, {'name': 'owner', 'type': 'unsigned'}, {'name': 'name', 'type': 'string'}, {'name': 'setuid', - 'type': 'unsigned'}]] + 'type': 'unsigned'}, {'name': 'language', 'type': 'string'}, {'name': 'body', + 'type': 'string'}, {'name': 'returns', 'type': 'string'}, {'name': 'is_deterministic', + 'type': 'boolean'}]] - [297, 1, '_vfunc', 'sysview', 0, {}, [{'name': 'id', 'type': 'unsigned'}, {'name': 'owner', 'type': 'unsigned'}, {'name': 'name', 'type': 'string'}, {'name': 'setuid', 'type': 'unsigned'}]] @@ -822,7 +824,7 @@ box.space._space:select() ... box.space._func:select() --- -- - [1, 1, 'box.schema.user.info', 1, 'LUA'] +- - [1, 1, 'box.schema.user.info', 1, 'LUA', '', 'any', false] ... session = nil --- -- 2.21.0