diff --git a/changelogs/unreleased/space-type.md b/changelogs/unreleased/space-type.md new file mode 100644 index 0000000000000000000000000000000000000000..2f2b4bae8977a736541d81855ffc3cf6ba9901f8 --- /dev/null +++ b/changelogs/unreleased/space-type.md @@ -0,0 +1,3 @@ +## feature/space + +* Introduces space type: a new space definition field. diff --git a/src/box/alter.cc b/src/box/alter.cc index a1780983c47abddbb59b700a006d17833e8f7bf2..a09102be3cee9b99f590e3ebdc3f5390ddb40aa6 100644 --- a/src/box/alter.cc +++ b/src/box/alter.cc @@ -416,11 +416,18 @@ space_opts_decode(struct space_opts *opts, const char *map, struct region *region) { space_opts_create(opts); + opts->type = SPACE_TYPE_DEFAULT; if (opts_decode(opts, space_opts_reg, &map, region) != 0) { diag_set(ClientError, ER_WRONG_SPACE_OPTIONS, diag_last_error(diag_get())->errmsg); return -1; } + /* + * This can only be SPACE_TYPE_DEFAULT if neither 'type' nor 'temporary' + * was specified, which means the space type is normal. + */ + if (opts->type == SPACE_TYPE_DEFAULT) + opts->type = SPACE_TYPE_NORMAL; return 0; } @@ -2072,8 +2079,8 @@ space_check_alter(struct space *old_space, struct space_def *new_space_def) */ assert(old_space->def->opts.group_id == new_space_def->opts.group_id); /* Only alter from non-temporary to temporary can cause problems. */ - if (old_space->def->opts.is_temporary || - !new_space_def->opts.is_temporary) + if (space_is_temporary(old_space) || + !space_opts_is_temporary(&new_space_def->opts)) return 0; /* Check for foreign keys that refers to this space. */ struct space_cache_holder *h; @@ -2090,7 +2097,7 @@ space_check_alter(struct space *old_space, struct space_def *new_space_def) * If the referring space is temporary too then the alter * can't break foreign key consistency after restart. */ - if (other_space->def->opts.is_temporary) + if (space_opts_is_temporary(&other_space->def->opts)) continue; diag_set(ClientError, ER_ALTER_SPACE, space_name(old_space), diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua index 12a439ad3cca86b4740f14d4e0aea364d6089dae..2097128f55d03a3d9301492bba736db6235eed51 100644 --- a/src/box/lua/schema.lua +++ b/src/box/lua/schema.lua @@ -674,6 +674,24 @@ local function denormalize_format(format) return result end +local space_types = { + 'normal', + 'data-temporary', +} +local function check_space_type(space_type) + if space_type == nil then + return + end + for _, t in ipairs(space_types) do + if t == space_type then + return + end + end + box.error(box.error.ILLEGAL_PARAMS, + "unknown space type, must be one of: '" .. + table.concat(space_types, "', '") .. "'.") +end + box.schema.space = {} box.schema.space.create = function(name, options) check_param(name, 'name', 'string') @@ -684,6 +702,7 @@ box.schema.space.create = function(name, options) field_count = 'number', user = 'string, number', format = 'table', + type = 'string', is_local = 'boolean', temporary = 'boolean', is_sync = 'boolean', @@ -694,10 +713,14 @@ box.schema.space.create = function(name, options) local options_defaults = { engine = 'memtx', field_count = 0, - temporary = false, } check_param_table(options, options_template) options = update_param_table(options, options_defaults) + check_space_type(options.type) + if options.type ~= nil and options.temporary ~= nil then + box.error(box.error.ILLEGAL_PARAMS, + "only one of 'type' or 'temporary' may be specified") + end if options.engine == 'vinyl' then options = update_param_table(options, { defer_deletes = box.cfg.vinyl_defer_deletes, @@ -732,7 +755,8 @@ box.schema.space.create = function(name, options) -- filter out global parameters from the options array local space_options = setmap({ group_id = options.is_local and 1 or nil, - temporary = options.temporary and true or nil, + temporary = options.temporary, + type = options.type, is_sync = options.is_sync, defer_deletes = options.defer_deletes and true or nil, constraint = constraint, @@ -825,6 +849,7 @@ local alter_space_template = { field_count = 'number', user = 'string, number', format = 'table', + type = 'string', temporary = 'boolean', is_sync = 'boolean', defer_deletes = 'boolean', @@ -858,6 +883,11 @@ box.schema.space.alter = function(space_id, options) local field_count = options.field_count or tuple.field_count local flags = tuple.flags + if options.type ~= nil then + check_space_type(options.type) + flags.type = options.type + end + if options.temporary ~= nil then flags.temporary = options.temporary end diff --git a/src/box/lua/space.cc b/src/box/lua/space.cc index 923cabd41b14bb19a310a7f0700fcf6b6e67791d..e6bb82859adbbdfb17b1911d83790672315cb4d8 100644 --- a/src/box/lua/space.cc +++ b/src/box/lua/space.cc @@ -304,11 +304,16 @@ lbox_fillspace(struct lua_State *L, struct space *space, int i) lua_pushboolean(L, space_is_local(space)); lua_settable(L, i); - /* space.is_temp */ + /* space.temporary */ lua_pushstring(L, "temporary"); lua_pushboolean(L, space_is_temporary(space)); lua_settable(L, i); + /* space.type */ + lua_pushstring(L, "type"); + lua_pushstring(L, space_type_name(space->def->opts.type)); + lua_settable(L, i); + /* space.name */ lua_pushstring(L, "name"); lua_pushstring(L, space_name(space)); diff --git a/src/box/space.c b/src/box/space.c index 58dfd41726cdf1c56cab2fa82b2fe2bbe56705d2..1a2bbe68e8c51e42751776160dc58a3fe17b9f1a 100644 --- a/src/box/space.c +++ b/src/box/space.c @@ -429,7 +429,7 @@ space_new(struct space_def *def, struct rlist *key_list) struct space * space_new_ephemeral(struct space_def *def, struct rlist *key_list) { - assert(def->opts.is_temporary); + assert(space_opts_is_temporary(&def->opts)); assert(def->opts.is_ephemeral); struct space *space = space_new(def, key_list); if (space == NULL) diff --git a/src/box/space.h b/src/box/space.h index 8941a469497600407ada61013a8f9289a4ec671b..d60daa3fa0c059ee2cf2a18b3b67267e3b22d5ea 100644 --- a/src/box/space.h +++ b/src/box/space.h @@ -344,7 +344,7 @@ space_name(const struct space *space) static inline bool space_is_temporary(const struct space *space) { - return space->def->opts.is_temporary; + return space_opts_is_temporary(&space->def->opts); } /** Return true if space is synchronous. */ diff --git a/src/box/space_def.c b/src/box/space_def.c index 8f9b1c8df2d702ec08f6086e904d29541790456d..48bc26813a793757cd56e0c0bc31ac3067bba560 100644 --- a/src/box/space_def.c +++ b/src/box/space_def.c @@ -40,7 +40,7 @@ const struct space_opts space_opts_default = { /* .group_id = */ 0, - /* .is_temporary = */ false, + /* .type = */ SPACE_TYPE_NORMAL, /* .is_ephemeral = */ false, /* .view = */ false, /* .is_sync = */ false, @@ -75,9 +75,25 @@ static int space_opts_parse_upgrade(const char **data, void *vopts, struct region *region); +/** + * Callback to parse a value with 'temporary' key in msgpack space opts + * definition. See function definition below. + */ +static int +space_opts_parse_temporary(const char **data, void *vopts, + struct region *region); + +/** + * Callback to parse a value with 'type' key in msgpack space opts + * definition. See function definition below. + */ +static int +space_opts_parse_type(const char **data, void *vopts, struct region *region); + const struct opt_def space_opts_reg[] = { + OPT_DEF_CUSTOM("type", space_opts_parse_type), OPT_DEF("group_id", OPT_UINT32, struct space_opts, group_id), - OPT_DEF("temporary", OPT_BOOL, struct space_opts, is_temporary), + OPT_DEF_CUSTOM("temporary", space_opts_parse_temporary), OPT_DEF("view", OPT_BOOL, struct space_opts, is_view), OPT_DEF("is_sync", OPT_BOOL, struct space_opts, is_sync), OPT_DEF("defer_deletes", OPT_BOOL, struct space_opts, defer_deletes), @@ -97,7 +113,8 @@ space_tuple_format_new(struct tuple_format_vtab *vtab, void *engine, return tuple_format_new(vtab, engine, keys, key_count, def->fields, def->field_count, def->exact_field_count, def->dict, - def->opts.is_temporary, def->opts.is_ephemeral, + space_opts_is_temporary(&def->opts), + def->opts.is_ephemeral, def->opts.constraint_def, def->opts.constraint_count); } @@ -168,7 +185,7 @@ struct space_def* space_def_new_ephemeral(uint32_t exact_field_count, struct field_def *fields) { struct space_opts opts = space_opts_default; - opts.is_temporary = true; + opts.type = SPACE_TYPE_DATA_TEMPORARY; opts.is_ephemeral = true; uint32_t field_count = exact_field_count; if (fields == NULL) { @@ -242,3 +259,56 @@ space_opts_parse_upgrade(const char **data, void *vopts, opts->upgrade_def = space_upgrade_def_decode(data, region); return opts->upgrade_def == NULL ? -1 : 0; } + +static int +space_opts_parse_temporary(const char **data, void *vopts, + struct region *region) +{ + (void)region; + if (mp_typeof(**data) != MP_BOOL) { + diag_set(IllegalParams, "'temporary' must be boolean"); + return -1; + } + struct space_opts *opts = vopts; + if (opts->type != SPACE_TYPE_DEFAULT) { + /* This means 'type' was specified. */ + diag_set(IllegalParams, + "only one of 'type' or 'temporary' may be specified"); + return -1; + } + bool is_temporary = mp_decode_bool(data); + opts->type = is_temporary ? + SPACE_TYPE_DATA_TEMPORARY : SPACE_TYPE_NORMAL; + return 0; +} + +const char *space_type_strs[] = { + /* [SPACE_TYPE_NORMAL] = */ "normal", + /* [SPACE_TYPE_DATA_TEMPORARY] = */ "data-temporary", +}; + +static int +space_opts_parse_type(const char **data, void *vopts, struct region *region) +{ + (void)region; + if (mp_typeof(**data) != MP_STR) { + diag_set(IllegalParams, "'type' must be a string"); + return -1; + } + uint32_t str_len = 0; + const char *str = mp_decode_str(data, &str_len); + enum space_type space_type = STRN2ENUM(space_type, str, str_len); + if (space_type == SPACE_TYPE_DEFAULT) { + diag_set(IllegalParams, "unknown space type"); + return -1; + } + struct space_opts *opts = vopts; + if (opts->type != SPACE_TYPE_DEFAULT) { + /* This means 'temporary' was specified. */ + diag_set(IllegalParams, + "only one of 'type' or 'temporary' may be specified"); + return -1; + } + opts->type = space_type; + return 0; +} diff --git a/src/box/space_def.h b/src/box/space_def.h index 5db266e2ad96dc1e40a4a338cbca928e0c054d10..2b40585eac5774f78daddc4a0373c61c69d6bac9 100644 --- a/src/box/space_def.h +++ b/src/box/space_def.h @@ -42,6 +42,30 @@ extern "C" { struct space_upgrade_def; +/** Space type names. */ +extern const char *space_type_strs[]; + +/** See space_opts::type. */ +enum space_type { + /** + * SPACE_TYPE_DEFAULT is a special value which is used when decoding + * space options from a tuple. After the options have been parsed + * SPACE_TYPE_DEFAULT will be replaced with SPACE_TYPE_NORMAL. + * No live space should ever have this type. + */ + SPACE_TYPE_DEFAULT = -1, + SPACE_TYPE_NORMAL = 0, + SPACE_TYPE_DATA_TEMPORARY = 1, + space_type_MAX, +}; + +static inline const char * +space_type_name(enum space_type space_type) +{ + assert(space_type != SPACE_TYPE_DEFAULT); + return space_type_strs[space_type]; +} + /** Space options */ struct space_opts { /** @@ -49,15 +73,15 @@ struct space_opts { * made to a space are replicated. */ uint32_t group_id; - /** - * The space is a temporary: + /** + * If set to SPACE_TYPE_DATA_TEMPORARY: * - it is empty at server start * - changes are not written to WAL * - changes are not part of a snapshot - * - in SQL: space_def memory is allocated on region and - * does not require manual release. + * - in SQL: space_def memory is allocated on region and + * does not require manual release. */ - bool is_temporary; + enum space_type type; /** * This flag is set if space is ephemeral and hence * its format might be re-used. @@ -104,6 +128,16 @@ space_opts_create(struct space_opts *opts) *opts = space_opts_default; } +/** + * Check if the space is temporary. + */ +static inline bool +space_opts_is_temporary(const struct space_opts *opts) +{ + assert(opts->type != SPACE_TYPE_DEFAULT); + return opts->type != SPACE_TYPE_NORMAL; +} + /** Space metadata. */ struct space_def { /** Space id. */ diff --git a/src/box/sql/build.c b/src/box/sql/build.c index f3b48bbfa18ccad2306838a26d7a419f047b650a..38c7e08cba43020b2680fbd061542b80f5ac56d9 100644 --- a/src/box/sql/build.c +++ b/src/box/sql/build.c @@ -285,7 +285,7 @@ sql_shallow_space_copy(struct Parse *parse, struct space *space) memcpy(ret->index, space->index, sizeof(struct index *) * space->index_count); memcpy(ret->def, space->def, sizeof(struct space_def)); - ret->def->opts.is_temporary = true; + ret->def->opts.type = SPACE_TYPE_DATA_TEMPORARY; ret->def->opts.is_ephemeral = true; if (ret->def->field_count != 0) { ret->def->fields = diff --git a/src/box/vinyl.c b/src/box/vinyl.c index 018f2a409b9845ea3a1d5ef3598180313f46fe0d..ed8118b29d7284090719070896b83953d3da5b94 100644 --- a/src/box/vinyl.c +++ b/src/box/vinyl.c @@ -586,7 +586,7 @@ vinyl_engine_check_space_def(struct space_def *def) return -1; } } - if (def->opts.is_temporary) { + if (space_opts_is_temporary(&def->opts)) { diag_set(ClientError, ER_ALTER_SPACE, def->name, "engine does not support temporary flag"); return -1;