diff --git a/src/box/alter.cc b/src/box/alter.cc index 013d3e0e516cb2f3e950d33a0922c756fc97a0e3..809a45db575d4d1f78fefe920557892fb6052680 100644 --- a/src/box/alter.cc +++ b/src/box/alter.cc @@ -35,16 +35,18 @@ #include "scoped_guard.h" #include <new> /* for placement new */ #include <stdio.h> /* snprintf() */ +#include <ctype.h> /** _space columns */ -#define ID 0 -#define ARITY 1 -#define NAME 2 +#define ID 0 +#define ARITY 1 +#define NAME 2 +#define FLAGS 3 /** _index columns */ -#define INDEX_ID 1 -#define INDEX_TYPE 3 -#define INDEX_IS_UNIQUE 4 -#define INDEX_PART_COUNT 5 +#define INDEX_ID 1 +#define INDEX_TYPE 3 +#define INDEX_IS_UNIQUE 4 +#define INDEX_PART_COUNT 5 /* {{{ Auxiliary functions and methods. */ @@ -94,6 +96,28 @@ key_def_new_from_tuple(struct tuple *tuple) return key_def; } +static void +space_def_init_flags(struct space_def *def, struct tuple *tuple) +{ + /* default values of flags */ + def->temporary = false; + + /* there is no property in the space */ + if (tuple->field_count <= FLAGS) + return; + + const char *flags = tuple_field_cstr(tuple, FLAGS); + while (flags && *flags) { + while (isspace(*flags)) /* skip space */ + flags++; + if (strncmp(flags, "temporary", strlen("temporary")) == 0) + def->temporary = true; + flags = strchr(flags, ','); + if (flags) + flags++; + } +} + /** * Fill space_def structure from struct tuple. */ @@ -105,6 +129,8 @@ space_def_create_from_tuple(struct space_def *def, struct tuple *tuple, def->arity = tuple_field_u32(tuple, ARITY); int n = snprintf(def->name, sizeof(def->name), "%s", tuple_field_cstr(tuple, NAME)); + + space_def_init_flags(def, tuple); space_def_check(def, n, errcode); if (errcode != ER_ALTER_SPACE && def->id >= SC_SYSTEM_ID_MIN && def->id < SC_SYSTEM_ID_MAX) { @@ -449,6 +475,13 @@ ModifySpace::prepare(struct alter_space *alter) (unsigned) def.id, "can not change arity on a non-empty space"); } + if (def.temporary != alter->old_space->def.temporary && + alter->old_space->engine.state != READY_NO_KEYS && + space_size(alter->old_space) > 0) { + tnt_raise(ClientError, ER_ALTER_SPACE, + (unsigned) space_id(alter->old_space), + "can not switch temporary flag on a non-empty space"); + } } /** Amend the definition of the new space. */ diff --git a/src/box/box.cc b/src/box/box.cc index ab20ae872615770e4baeba26a241006215dd9ae9..324255cf65ff5c07aa47333783b24f0c0350e8c4 100644 --- a/src/box/box.cc +++ b/src/box/box.cc @@ -309,6 +309,8 @@ struct snapshot_space_param { static void snapshot_space(struct space *sp, void *udata) { + if (space_is_temporary(sp)) + return; struct tuple *tuple; struct snapshot_space_param *ud = (struct snapshot_space_param *) udata; Index *pk = space_index(sp, 0); diff --git a/src/box/box_lua_space.cc b/src/box/box_lua_space.cc index 4a6b39c813fe32e0e9fe8cb77b6e6085c44809bf..4a153a45d52eb3821dc03e1e0e678062692dc3e6 100644 --- a/src/box/box_lua_space.cc +++ b/src/box/box_lua_space.cc @@ -57,6 +57,11 @@ lbox_fillspace(struct lua_State *L, struct space *space, int i) lua_pushnumber(L, space_id(space)); lua_settable(L, i); + /* space.is_temp */ + lua_pushstring(L, "temporary"); + lua_pushboolean(L, space_is_temporary(space)); + lua_settable(L, i); + /* space.name */ lua_pushstring(L, "name"); lua_pushstring(L, space_name(space)); diff --git a/src/box/key_def.h b/src/box/key_def.h index 56bfc26dff50a2472b9bcd69ad2638bf5044fdc2..ee0f61c98a55df6e0646615bcbd752facde145f2 100644 --- a/src/box/key_def.h +++ b/src/box/key_def.h @@ -194,6 +194,13 @@ struct space_def { */ uint32_t arity; char name[BOX_NAME_MAX + 1]; + /** + * The space is a temporary: + * - it is empty at server start + * - changes are not written to WAL + * - changes are not part of a snapshot + */ + bool temporary; }; /** Check space definition structure for errors. */ diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua index f92066fb79e468691642401ccac3989f91ed2e30..ca07fc2cf81f7ce0780a6878cfb8fb767db960c9 100644 --- a/src/box/lua/schema.lua +++ b/src/box/lua/schema.lua @@ -13,6 +13,9 @@ box.schema.space.create = function(name, options) options = {} end local if_not_exists = options.if_not_exists + + local temporary = options.temporary and "temporary" or "" + if box.space[name] then if options.if_not_exists then return box.space[name], "not created" @@ -35,7 +38,7 @@ box.schema.space.create = function(name, options) if options.arity == nil then options.arity = 0 end - _space:insert(id, options.arity, name) + _space:insert(id, options.arity, name, temporary) return box.space[id], "created" end box.schema.create_space = box.schema.space.create diff --git a/src/box/schema.cc b/src/box/schema.cc index 065f28ec10c5dfc5de59a2d76afa3f00255fe820..3b69278e0f219a4e9a4a3d740f2dc6b02f8c927c 100644 --- a/src/box/schema.cc +++ b/src/box/schema.cc @@ -217,7 +217,7 @@ schema_init() * (and re-created) first. */ /* _schema - key/value space with schema description */ - struct space_def def = { SC_SCHEMA_ID, 0, "_schema" }; + struct space_def def = { SC_SCHEMA_ID, 0, "_schema", false }; struct key_def *key_def = key_def_new(def.id, 0 /* index id */, "primary", /* name */ diff --git a/src/box/space.h b/src/box/space.h index 98ba9fb211440270bbd56e56e645a9514ce6810e..c62558e6c57cab21a1d0085f02205e7fce5fdc37 100644 --- a/src/box/space.h +++ b/src/box/space.h @@ -130,6 +130,11 @@ space_id(struct space *space) { return space->def.id; } static inline const char * space_name(struct space *space) { return space->def.name; } + +/** Return true if space is temporary. */ +static inline bool +space_is_temporary(struct space *space) { return space->def.temporary; } + /** * @brief A single method to handle REPLACE, DELETE and UPDATE. * diff --git a/src/box/txn.cc b/src/box/txn.cc index 96f4f2316c911b13a682a5e2a80fbd972f466fa7..1dcfd99f3c2d66f81b16639bbe923afe86ed8820 100644 --- a/src/box/txn.cc +++ b/src/box/txn.cc @@ -82,7 +82,8 @@ txn_begin() void txn_commit(struct txn *txn) { - if (txn->old_tuple || txn->new_tuple) { + if ((txn->old_tuple || txn->new_tuple) && + !space_is_temporary(txn->space)) { int64_t lsn = next_lsn(recovery_state); ev_tstamp start = ev_now(), stop; diff --git a/test/box/alter_limits.result b/test/box/alter_limits.result index d6ef57175960da88c851969e0bfe4fae88fd94f7..6dbc6c985477625730dbf5846e629d209b3fb008 100644 --- a/test/box/alter_limits.result +++ b/test/box/alter_limits.result @@ -133,7 +133,7 @@ s:select(0) ... s:select_range(0, 0, 0) --- -- error: '[string "-- schema.lua (internal file)..."]:205: attempt to index a nil +- error: '[string "-- schema.lua (internal file)..."]:208: attempt to index a nil value' ... s:delete(0) @@ -266,7 +266,7 @@ box.space['_space']:update(s.n, "=p", 1, 1) -- remove arity - ok box.space['_space']:update(s.n, "=p", 1, 0) --- -- [512, 0, 1953719668] +- [512, 0, 1953719668, ''] ... s:select(0) --- @@ -286,7 +286,7 @@ s:select(0) -- set arity of an empty space box.space['_space']:update(s.n, "=p", 1, 3) --- -- [512, 3, 1953719668] +- [512, 3, 1953719668, ''] ... s:select(0) --- diff --git a/test/box/lua.result b/test/box/lua.result index 85d52d76664b1fb2895c412fc1c513725f8b939b..d0a3a5b6832350b6ce4c0d3a050ff1802c80309a 100644 --- a/test/box/lua.result +++ b/test/box/lua.result @@ -529,10 +529,11 @@ t = {} for k,v in pairs(box.space[0]) do if type(v) ~= 'table' then table.insert ... t --- -- - 'arity: 0' +- - 'temporary: false' - 'n: 0' - 'enabled: true' - 'name: tweedledum' + - 'arity: 0' ... box.cfg.reload() --- @@ -578,10 +579,11 @@ t = {} for k,v in pairs(box.space[0]) do if type(v) ~= 'table' then table.insert ... t --- -- - 'arity: 0' +- - 'temporary: false' - 'n: 0' - 'enabled: true' - 'name: tweedledum' + - 'arity: 0' ... box.cfg.nosuchoption = 1 --- diff --git a/test/box/temp_spaces.result b/test/box/temp_spaces.result new file mode 100644 index 0000000000000000000000000000000000000000..eb11a93fb044a13e8d32d70e99ea5756aac0e08e --- /dev/null +++ b/test/box/temp_spaces.result @@ -0,0 +1,131 @@ +-- temporary spaces +-- not a temporary +s = box.schema.create_space('t', { temporary = true }) +--- +... +s.temporary +--- +- true +... +s:drop() +--- +... +-- not a temporary, too +s = box.schema.create_space('t', { temporary = false }) +--- +... +s.temporary +--- +- false +... +s:drop() +--- +... +-- not a temporary, too +s = box.schema.create_space('t', { temporary = nil }) +--- +... +s.temporary +--- +- false +... +s:drop() +--- +... +s = box.schema.create_space('t', { temporary = true }) +--- +... +s:create_index('primary', 'hash') +--- +... +s:insert(1, 2, 3) +--- +- [1, 2, 3] +... +s:select(0, 1) +--- +- [1, 2, 3] +... +s:len() +--- +- 1 +... +box.space[box.schema.SPACE_ID]:update(s.n, '=p', 3, 'temporary') +--- +- [512, 0, 't', 'temporary'] +... +box.space[box.schema.SPACE_ID]:update(s.n, '=p', 3, '') +--- +- error: 'Can''t modify space 512: can not switch temporary flag on a non-empty space' +... +--# stop server default +--# start server default +s = box.space.t +--- +... +s:len() +--- +- 0 +... +s.temporary +--- +- true +... +box.space[box.schema.SPACE_ID]:update(s.n, '=p', 3, 'no-temporary') +--- +- [512, 0, 't', 'no-temporary'] +... +s.temporary +--- +- false +... +box.space[box.schema.SPACE_ID]:update(s.n, '=p', 3, ',:asfda:temporary') +--- +- [512, 0, 't', ',:asfda:temporary'] +... +s.temporary +--- +- false +... +box.space[box.schema.SPACE_ID]:update(s.n, '=p', 3, 'a,b,c,d,e') +--- +- [512, 0, 't', 'a,b,c,d,e'] +... +s.temporary +--- +- false +... +box.space[box.schema.SPACE_ID]:update(s.n, '=p', 3, 'temporary') +--- +- [512, 0, 't', 'temporary'] +... +s.temporary +--- +- true +... +s:select(0, 1) +--- +... +s:insert(1, 2, 3) +--- +- [1, 2, 3] +... +box.space[box.schema.SPACE_ID]:update(s.n, '=p', 3, 'temporary') +--- +- [512, 0, 't', 'temporary'] +... +box.space[box.schema.SPACE_ID]:update(s.n, '=p', 3, 'no-temporary') +--- +- error: 'Can''t modify space 512: can not switch temporary flag on a non-empty space' +... +s:delete(1) +--- +- [1, 2, 3] +... +box.space[box.schema.SPACE_ID]:update(s.n, '=p', 3, 'no-temporary') +--- +- [512, 0, 't', 'no-temporary'] +... +s:drop() +--- +... diff --git a/test/box/temp_spaces.test.lua b/test/box/temp_spaces.test.lua new file mode 100644 index 0000000000000000000000000000000000000000..a21ae36bdfd80800acdfa2725fdb71a4c41043e8 --- /dev/null +++ b/test/box/temp_spaces.test.lua @@ -0,0 +1,51 @@ +-- temporary spaces +-- not a temporary +s = box.schema.create_space('t', { temporary = true }) +s.temporary +s:drop() + +-- not a temporary, too +s = box.schema.create_space('t', { temporary = false }) +s.temporary +s:drop() + +-- not a temporary, too +s = box.schema.create_space('t', { temporary = nil }) +s.temporary +s:drop() + +s = box.schema.create_space('t', { temporary = true }) +s:create_index('primary', 'hash') + +s:insert(1, 2, 3) +s:select(0, 1) +s:len() + +box.space[box.schema.SPACE_ID]:update(s.n, '=p', 3, 'temporary') +box.space[box.schema.SPACE_ID]:update(s.n, '=p', 3, '') + +--# stop server default +--# start server default + +s = box.space.t +s:len() +s.temporary + +box.space[box.schema.SPACE_ID]:update(s.n, '=p', 3, 'no-temporary') +s.temporary +box.space[box.schema.SPACE_ID]:update(s.n, '=p', 3, ',:asfda:temporary') +s.temporary +box.space[box.schema.SPACE_ID]:update(s.n, '=p', 3, 'a,b,c,d,e') +s.temporary +box.space[box.schema.SPACE_ID]:update(s.n, '=p', 3, 'temporary') +s.temporary + +s:select(0, 1) +s:insert(1, 2, 3) + +box.space[box.schema.SPACE_ID]:update(s.n, '=p', 3, 'temporary') +box.space[box.schema.SPACE_ID]:update(s.n, '=p', 3, 'no-temporary') + +s:delete(1) +box.space[box.schema.SPACE_ID]:update(s.n, '=p', 3, 'no-temporary') +s:drop()