[PATCH v2 5/9] schema: rework _func system space format

Kirill Shcherbatov kshcherbatov at tarantool.org
Thu Jun 6 15:04:01 MSK 2019


This patch updates a _func system space to prepare it for
working with persistent functions. The format of the _func
system space is
[<id> UINT, <owner> UINT, <name> STR, <setuid> UINT,
 <language> STR, <body> STR, <returns> STR,
 <is_deterministic> BOOL, <opts> MAP]

It is preparational step to introduce persistent Lua function
in Tarantool.

The new <body> 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             |  61 ++++++++++++++++++++++++++++-------
 src/box/bootstrap.snap       | Bin 4393 -> 4449 bytes
 src/box/func_def.h           |  14 ++++++--
 src/box/lua/schema.lua       |  13 ++++++--
 src/box/lua/upgrade.lua      |  25 +++++++++++++-
 src/box/schema_def.h         |   4 +++
 test/box-py/bootstrap.result |   6 ++--
 test/box/access_misc.result  |   6 ++--
 8 files changed, 108 insertions(+), 21 deletions(-)

diff --git a/src/box/alter.cc b/src/box/alter.cc
index 3b57a7d82..11cad77c3 100644
--- a/src/box/alter.cc
+++ b/src/box/alter.cc
@@ -2426,26 +2426,45 @@ 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)
+	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 +2476,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_OPTS);
+		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
index bb8fbeba114b1e72a5585548fb7f22796931d90f..440495684401541854e9d9180f55cc9a4bc45b25 100644

diff --git a/src/box/func_def.h b/src/box/func_def.h
index 5b52ab498..7a920f65e 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 <stdbool.h>
 
 /**
@@ -54,11 +55,17 @@ struct func_def {
 	uint32_t fid;
 	/** Owner of the function. */
 	uint32_t uid;
+	/** Definition of the persistent function. */
+	char *body;
 	/**
 	 * True if the function requires change of user id before
 	 * invocation.
 	 */
 	bool setuid;
+	/** The type of the returned value. */
+	enum field_type returns;
+	/** Whether this function is deterministic. */
+	bool is_deterministic;
 	/**
 	 * The language of the stored function.
 	 */
@@ -73,10 +80,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..b5393e19a 100644
--- a/src/box/lua/schema.lua
+++ b/src/box/lua/schema.lua
@@ -2105,7 +2105,9 @@ 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',
+                              opts = 'table'} )
     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 +2117,15 @@ 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 = setmap{}})
     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,
+                         opts.opts}
 end
 
 box.schema.func.drop = function(name, opts)
diff --git a/src/box/lua/upgrade.lua b/src/box/lua/upgrade.lua
index 070662698..6f35e0b5a 100644
--- a/src/box/lua/upgrade.lua
+++ b/src/box/lua/upgrade.lua
@@ -324,7 +324,8 @@ 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, MAP}
 
     -- grant 'public' role access to 'box.schema.user.info' function
     log.info('grant execute on function "box.schema.user.info" to public')
@@ -774,8 +775,30 @@ 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'}
+    format[9] = {name='opts', type='map'}
+    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,
+                                     setmap({})})
+    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..b8cbdb12e 100644
--- a/src/box/schema_def.h
+++ b/src/box/schema_def.h
@@ -163,6 +163,10 @@ 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,
+	BOX_FUNC_FIELD_OPTS = 8,
 };
 
 /** _collation fields. */
diff --git a/test/box-py/bootstrap.result b/test/box-py/bootstrap.result
index 0684914c0..305bb9276 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'}, {'name': 'opts', 'type': 'map'}]]
   - [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..6e4558a19 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'}, {'name': 'opts', 'type': 'map'}]]
   - [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




More information about the Tarantool-patches mailing list