From b9550f192673581dcbd8432867ae2e55ec264676 Mon Sep 17 00:00:00 2001 From: Georgiy Lebedev <g.lebedev@tarantool.org> Date: Sat, 15 Apr 2023 20:16:22 +0300 Subject: [PATCH] box: support space and index names in IPROTO requests Add support for accepting IPROTO requests with space or index name instead of identifier (name is preferred over identifier to disambiguate missing identifiers from zero identifiers): mark space identifier request key as present upon encountering space name, and delay resolution of identifier until request gets to transaction thread. Add support for sending DML requests from net.box connection objects with disabled schema fetching by manually specifying space or index name or identifier: when schema fetching is disabled, the space and index tables of connections return wrapper tables that store necessary context (space or index name or identifier, determined by type, connection object and space for indexes) for performing requests. The space and index tables cache the wrapper table they return. Closes #8146 @TarantoolBot document Title: Space and index name in IPROTO requests Refer to design document for details: https://www.notion.so/tarantool/Schemafull-IPROTO-cc315ad6bdd641dea66ad854992d8cbf?pvs=4#f4d4b3fa2b3646f1949319866428b6c0 --- ...pace-and-index-names-in-iproto-requests.md | 4 + src/box/iproto.cc | 44 ++- src/box/iproto_constants.c | 2 + src/box/iproto_constants.h | 12 + src/box/iproto_features.c | 2 + src/box/iproto_features.h | 10 +- src/box/lua/net_box.c | 82 +++-- src/box/lua/net_box.lua | 94 ++++-- src/box/lua/schema.lua | 4 +- src/box/xrow.c | 17 +- src/box/xrow.h | 31 +- ...ort_iproto_constants_and_features_test.lua | 6 +- .../gh_8146_data/00000000000000000004.snap | Bin 0 -> 5093 bytes ...and_index_name_in_iproto_requests_test.lua | 304 ++++++++++++++++++ test/box-luatest/net_box_test.lua | 2 +- test/box-py/iproto.result | 4 +- test/box/net.box_iproto_id.result | 11 +- 17 files changed, 560 insertions(+), 69 deletions(-) create mode 100644 changelogs/unreleased/gh-8146-space-and-index-names-in-iproto-requests.md create mode 100644 test/box-luatest/gh_8146_data/00000000000000000004.snap create mode 100644 test/box-luatest/gh_8146_space_and_index_name_in_iproto_requests_test.lua diff --git a/changelogs/unreleased/gh-8146-space-and-index-names-in-iproto-requests.md b/changelogs/unreleased/gh-8146-space-and-index-names-in-iproto-requests.md new file mode 100644 index 0000000000..9e14e1c365 --- /dev/null +++ b/changelogs/unreleased/gh-8146-space-and-index-names-in-iproto-requests.md @@ -0,0 +1,4 @@ +## feature/box + +* Added support for accepting IPROTO requests with a space or index name instead + of an identifier (gh-8146). diff --git a/src/box/iproto.cc b/src/box/iproto.cc index 30dcce91f1..951c1d283f 100644 --- a/src/box/iproto.cc +++ b/src/box/iproto.cc @@ -1666,8 +1666,8 @@ iproto_msg_decode(struct iproto_msg *msg, struct cmsg_hop **route) assert(type < sizeof(iproto_thread->dml_route) / sizeof(*iproto_thread->dml_route)); *route = iproto_thread->dml_route[type]; - if (xrow_decode_dml(&msg->header, &msg->dml, - dml_request_key_map(type))) + if (xrow_decode_dml_iproto(&msg->header, &msg->dml, + dml_request_key_map(type)) != 0) return -1; /* * In contrast to replication requests, for a client request @@ -2143,6 +2143,42 @@ tx_process_rollback(struct cmsg *m) tx_end_msg(msg, &header); } +/* + * In case the request does not contain a space or identifier but contains a + * corresponding name, tries to resolve the name. + */ +static int +tx_resolve_space_and_index_name(struct request *dml) +{ + struct space *space = NULL; + if (dml->space_name != NULL) { + space = space_by_name(dml->space_name, dml->space_name_len); + if (space == NULL) { + diag_set(ClientError, ER_NO_SUCH_SPACE, + tt_cstr(dml->space_name, dml->space_name_len)); + return -1; + } + dml->space_id = space->def->id; + } + if ((dml->type == IPROTO_SELECT || dml->type == IPROTO_UPDATE || + dml->type == IPROTO_DELETE) && dml->index_name != NULL) { + if (space == NULL) + space = space_cache_find(dml->space_id); + if (space == NULL) + return -1; + struct index *idx = space_index_by_name(space, dml->index_name, + dml->index_name_len); + if (idx == NULL) { + diag_set(ClientError, ER_NO_SUCH_INDEX_NAME, + tt_cstr(dml->index_name, dml->index_name_len), + space->def->name); + return -1; + } + dml->index_id = idx->dense_id; + } + return 0; +} + static void tx_process1(struct cmsg *m) { @@ -2154,6 +2190,8 @@ tx_process1(struct cmsg *m) struct obuf_svp svp; struct obuf *out; tx_inject_delay(); + if (tx_resolve_space_and_index_name(&msg->dml) != 0) + goto error; if (box_process1(&msg->dml, &tuple) != 0) goto error; out = msg->connection->tx.p_obuf; @@ -2190,6 +2228,8 @@ tx_process_select(struct cmsg *m) goto error; tx_inject_delay(); + if (tx_resolve_space_and_index_name(&msg->dml) != 0) + goto error; packed_pos = req->after_position; packed_pos_end = req->after_position_end; if (packed_pos != NULL) { diff --git a/src/box/iproto_constants.c b/src/box/iproto_constants.c index 7cd41f145c..ecbdaeb06d 100644 --- a/src/box/iproto_constants.c +++ b/src/box/iproto_constants.c @@ -154,6 +154,8 @@ const unsigned char iproto_key_type[iproto_key_MAX] = /* 0x5b */ MP_STR, /* IPROTO_AUTH_TYPE */ /* 0x5c */ MP_STR, /* IPROTO_REPLICASET_NAME */ /* 0x5d */ MP_STR, /* IPROTO_INSTANCE_NAME */ + /* 0x5e */ MP_STR, /* IPROTO_SPACE_NAME */ + /* 0x5f */ MP_STR, /* IPROTO_INDEX_NAME */ /* }}} */ }; diff --git a/src/box/iproto_constants.h b/src/box/iproto_constants.h index 6b441f28dd..29f48b4a3a 100644 --- a/src/box/iproto_constants.h +++ b/src/box/iproto_constants.h @@ -195,6 +195,18 @@ extern const size_t iproto_flag_constants_size; _(IPROTO_AUTH_TYPE, 0x5b) \ _(IPROTO_REPLICASET_NAME, 0x5c) \ _(IPROTO_INSTANCE_NAME, 0x5d) \ + /** + * Space name used instead of identifier (IPROTO_SPACE_ID) in DML + * requests. Preferred when identifier is present (i.e., the identifier + * is ignored). + */ \ + _(IPROTO_SPACE_NAME, 0x5e) \ + /** + * Index name used instead of identifier (IPROTO_INDEX_ID) in + * IPROTO_SELECT, IPROTO_UPDATE, and IPROTO_DELETE requests. Preferred + * when identifier is present (i.e., the identifier is ignored). + */ \ + _(IPROTO_INDEX_NAME, 0x5f) \ ENUM(iproto_key, IPROTO_KEYS); /** diff --git a/src/box/iproto_features.c b/src/box/iproto_features.c index b5724381fb..9fcc731046 100644 --- a/src/box/iproto_features.c +++ b/src/box/iproto_features.c @@ -75,4 +75,6 @@ iproto_features_init(void) IPROTO_FEATURE_WATCHERS); iproto_features_set(&IPROTO_CURRENT_FEATURES, IPROTO_FEATURE_PAGINATION); + iproto_features_set(&IPROTO_CURRENT_FEATURES, + IPROTO_FEATURE_SPACE_AND_INDEX_NAMES); } diff --git a/src/box/iproto_features.h b/src/box/iproto_features.h index 755fde7a67..39eece83d3 100644 --- a/src/box/iproto_features.h +++ b/src/box/iproto_features.h @@ -50,6 +50,14 @@ extern "C" { * request fields and IPROTO_POSITION response field. */ \ _(IPROTO_FEATURE_PAGINATION, 4) \ + /** + * Using space [index] names instead of identifiers support: + * IPROTO_SPACE_NAME and IPROTO_INDEX_NAME fields in IPROTO_SELECT, + * IPROTO_UPDATE and IPROTO_DELETE request body; + * IPROTO_SPACE_NAME field in IPROTO_INSERT, IPROTO_REPLACE, + * IPROTO_UPDATE and IPROTO_UPSERT request body. + */ \ + _(IPROTO_FEATURE_SPACE_AND_INDEX_NAMES, 5) \ ENUM(iproto_feature_id, IPROTO_FEATURES); @@ -72,7 +80,7 @@ struct iproto_features { * `box.iproto.protocol_version` needs to be updated correspondingly. */ enum { - IPROTO_CURRENT_VERSION = 4, + IPROTO_CURRENT_VERSION = 5, }; /** diff --git a/src/box/lua/net_box.c b/src/box/lua/net_box.c index 7ca5f17f5a..f43cb97834 100644 --- a/src/box/lua/net_box.c +++ b/src/box/lua/net_box.c @@ -80,7 +80,7 @@ enum { /** * IPROTO protocol version supported by the netbox connector. */ - NETBOX_IPROTO_VERSION = 4, + NETBOX_IPROTO_VERSION = 5, }; /** @@ -781,6 +781,44 @@ netbox_encode_eval(lua_State *L, int idx, struct mpstream *stream, netbox_end_encode(stream, svp); } +/* + * Depending on the type of the argument (see also net.box space metatable) + * encode either a space identifier or a space name. + */ +static void +netbox_encode_space_id_or_name(lua_State *L, int idx, struct mpstream *stream) +{ + if (lua_type(L, idx) == LUA_TNUMBER) { + uint32_t space_id = lua_tonumber(L, idx); + mpstream_encode_uint(stream, IPROTO_SPACE_ID); + mpstream_encode_uint(stream, space_id); + } else { + size_t len; + const char *space_name = lua_tolstring(L, idx, &len); + mpstream_encode_uint(stream, IPROTO_SPACE_NAME); + mpstream_encode_strn(stream, space_name, len); + } +} + +/* + * Depending on the type of the argument (see also net.box index metatable) + * encode either a index identifier or an index name. + */ +static void +netbox_encode_index_id_or_name(lua_State *L, int idx, struct mpstream *stream) +{ + if (lua_type(L, idx) == LUA_TNUMBER) { + uint32_t space_id = lua_tonumber(L, idx); + mpstream_encode_uint(stream, IPROTO_INDEX_ID); + mpstream_encode_uint(stream, space_id); + } else { + size_t len; + const char *space_name = lua_tolstring(L, idx, &len); + mpstream_encode_uint(stream, IPROTO_INDEX_NAME); + mpstream_encode_strn(stream, space_name, len); + } +} + /* Encode select request. */ static void netbox_encode_select(lua_State *L, int idx, struct mpstream *stream, @@ -801,19 +839,13 @@ netbox_encode_select(lua_State *L, int idx, struct mpstream *stream, if (fetch_pos) map_size++; mpstream_encode_map(stream, map_size); - uint32_t space_id = lua_tonumber(L, idx); - uint32_t index_id = lua_tonumber(L, idx + 1); int iterator = lua_tointeger(L, idx + 2); uint32_t offset = lua_tonumber(L, idx + 3); uint32_t limit = lua_tonumber(L, idx + 4); - /* encode space_id */ - mpstream_encode_uint(stream, IPROTO_SPACE_ID); - mpstream_encode_uint(stream, space_id); + netbox_encode_space_id_or_name(L, idx, stream); - /* encode index_id */ - mpstream_encode_uint(stream, IPROTO_INDEX_ID); - mpstream_encode_uint(stream, index_id); + netbox_encode_index_id_or_name(L, idx + 1, stream); /* encode iterator */ mpstream_encode_uint(stream, IPROTO_ITERATOR); @@ -865,10 +897,7 @@ netbox_encode_insert_or_replace(lua_State *L, int idx, struct mpstream *stream, mpstream_encode_map(stream, 2); - /* encode space_id */ - uint32_t space_id = lua_tonumber(L, idx); - mpstream_encode_uint(stream, IPROTO_SPACE_ID); - mpstream_encode_uint(stream, space_id); + netbox_encode_space_id_or_name(L, idx, stream); /* encode args */ mpstream_encode_uint(stream, IPROTO_TUPLE); @@ -903,15 +932,9 @@ netbox_encode_delete(lua_State *L, int idx, struct mpstream *stream, mpstream_encode_map(stream, 3); - /* encode space_id */ - uint32_t space_id = lua_tonumber(L, idx); - mpstream_encode_uint(stream, IPROTO_SPACE_ID); - mpstream_encode_uint(stream, space_id); + netbox_encode_space_id_or_name(L, idx, stream); - /* encode space_id */ - uint32_t index_id = lua_tonumber(L, idx + 1); - mpstream_encode_uint(stream, IPROTO_INDEX_ID); - mpstream_encode_uint(stream, index_id); + netbox_encode_index_id_or_name(L, idx + 1, stream); /* encode key */ mpstream_encode_uint(stream, IPROTO_KEY); @@ -930,15 +953,9 @@ netbox_encode_update(lua_State *L, int idx, struct mpstream *stream, mpstream_encode_map(stream, 5); - /* encode space_id */ - uint32_t space_id = lua_tonumber(L, idx); - mpstream_encode_uint(stream, IPROTO_SPACE_ID); - mpstream_encode_uint(stream, space_id); + netbox_encode_space_id_or_name(L, idx, stream); - /* encode index_id */ - uint32_t index_id = lua_tonumber(L, idx + 1); - mpstream_encode_uint(stream, IPROTO_INDEX_ID); - mpstream_encode_uint(stream, index_id); + netbox_encode_index_id_or_name(L, idx + 1, stream); /* encode index_id */ mpstream_encode_uint(stream, IPROTO_INDEX_BASE); @@ -965,10 +982,7 @@ netbox_encode_upsert(lua_State *L, int idx, struct mpstream *stream, mpstream_encode_map(stream, 4); - /* encode space_id */ - uint32_t space_id = lua_tonumber(L, idx); - mpstream_encode_uint(stream, IPROTO_SPACE_ID); - mpstream_encode_uint(stream, space_id); + netbox_encode_space_id_or_name(L, idx, stream); /* encode index_base */ mpstream_encode_uint(stream, IPROTO_INDEX_BASE); @@ -2994,6 +3008,8 @@ luaopen_net_box(struct lua_State *L) IPROTO_FEATURE_WATCHERS); iproto_features_set(&NETBOX_IPROTO_FEATURES, IPROTO_FEATURE_PAGINATION); + iproto_features_set(&NETBOX_IPROTO_FEATURES, + IPROTO_FEATURE_SPACE_AND_INDEX_NAMES); lua_pushcfunction(L, luaT_netbox_request_iterator_next); luaT_netbox_request_iterator_next_ref = luaL_ref(L, LUA_REGISTRYINDEX); diff --git a/src/box/lua/net_box.lua b/src/box/lua/net_box.lua index 670cfdf59e..abf5654666 100644 --- a/src/box/lua/net_box.lua +++ b/src/box/lua/net_box.lua @@ -58,6 +58,7 @@ local IPROTO_FEATURE_NAMES = { [2] = 'error_extension', [3] = 'watchers', [4] = 'pagination', + [5] = 'space_and_index_names', } local REQUEST_OPTION_TYPES = { @@ -388,6 +389,49 @@ local function new_sm(uri, opts) }) end end + if not opts.fetch_schema then + remote.space = setmetatable({}, { + __index = function(_, space_key) + local id_or_name + if type(space_key) == 'number' then + id_or_name = 'id' + elseif type(space_key) == 'string' then + if not features.space_and_index_names then + return nil + end + id_or_name = 'name' + else + return nil + end + local space = {[id_or_name] = space_key, + _id_or_name = space_key} + space.index = setmetatable({}, { + __index = function(_, idx_key) + local id_or_name + if type(idx_key) == 'number' then + id_or_name = 'id' + elseif type(idx_key) == 'string' then + if not features.space_and_index_names then + return nil + end + id_or_name = 'name' + else + return nil + end + local idx_wrapper = setmetatable({ + [id_or_name] = idx_key, + _id_or_name = idx_key, + space = space + }, remote._index_mt) + space.index[idx_key] = idx_wrapper + return idx_wrapper + end}) + local space_wrapper = setmetatable(space, + remote._space_mt) + remote.space[space_key] = space_wrapper + return space_wrapper + end}) + end elseif what == 'did_fetch_schema' then remote:_install_schema(...) elseif what == 'event' then @@ -932,6 +976,7 @@ function remote_methods:_install_schema(schema_version, spaces, indices, end s.id = id s.name = name + s._id_or_name = id s.engine = engine s.field_count = field_count s.enabled = true @@ -969,6 +1014,7 @@ function remote_methods:_install_schema(schema_version, spaces, indices, space = index[1], id = index[2], name = index[3], + _id_or_name = index[2], type = string.upper(index[4]), parts = {}, } @@ -1057,14 +1103,14 @@ space_metatable = function(remote) check_space_arg(self, 'insert') check_param_table(opts, REQUEST_OPTION_TYPES) return remote:_request(M_INSERT, opts, self._format_cdata, - self._stream_id, self.id, tuple) + self._stream_id, self._id_or_name, tuple) end function methods:replace(tuple, opts) check_space_arg(self, 'replace') check_param_table(opts, REQUEST_OPTION_TYPES) return remote:_request(M_REPLACE, opts, self._format_cdata, - self._stream_id, self.id, tuple) + self._stream_id, self._id_or_name, tuple) end function methods:select(key, opts) @@ -1086,7 +1132,8 @@ space_metatable = function(remote) check_space_arg(self, 'upsert') check_param_table(opts, REQUEST_OPTION_TYPES) return nothing_or_data(remote:_request(M_UPSERT, opts, nil, - self._stream_id, self.id, + self._stream_id, + self._id_or_name, key, oplist)) end @@ -1125,8 +1172,9 @@ index_metatable = function(remote) local res local method = fetch_pos and M_SELECT_FETCH_POS or M_SELECT res = (remote:_request(method, opts, self.space._format_cdata, - self._stream_id, self.space.id, self.id, - iterator, offset, limit, key, after, fetch_pos)) + self._stream_id, self.space._id_or_name, + self._id_or_name, iterator, offset, limit, key, + after, fetch_pos)) if type(res) ~= 'table' or not fetch_pos or opts and opts.is_async then return res end @@ -1142,9 +1190,9 @@ index_metatable = function(remote) return nothing_or_data(remote:_request(M_GET, opts, self.space._format_cdata, self._stream_id, - self.space.id, self.id, - box.index.EQ, 0, 2, key, - nil, false)) + self.space._id_or_name, + self._id_or_name, box.index.EQ, + 0, 2, key, nil, false)) end function methods:min(key, opts) @@ -1156,9 +1204,9 @@ index_metatable = function(remote) return nothing_or_data(remote:_request(M_MIN, opts, self.space._format_cdata, self._stream_id, - self.space.id, self.id, - box.index.GE, 0, 1, key, - nil, false)) + self.space._id_or_name, + self._id_or_name, box.index.GE, + 0, 1, key, nil, false)) end function methods:max(key, opts) @@ -1170,9 +1218,9 @@ index_metatable = function(remote) return nothing_or_data(remote:_request(M_MAX, opts, self.space._format_cdata, self._stream_id, - self.space.id, self.id, - box.index.LE, 0, 1, key, - nil, false)) + self.space._id_or_name, + self._id_or_name, box.index.LE, + 0, 1, key, nil, false)) end function methods:count(key, opts) @@ -1181,8 +1229,12 @@ index_metatable = function(remote) if opts and opts.buffer then error("index:count() doesn't support `buffer` argument") end - local code = string.format('box.space.%s.index.%s:count', - self.space.name, self.name) + local code = 'box.space[' .. (self.space.name ~= nil and + '"' .. self.space.name .. '"' or + self.space.id) .. + '].index[' .. (self.name ~= nil and + '"' .. self.name .. '"' or self.id) .. + ']:count' return remote:_request(M_COUNT, opts, nil, self._stream_id, code, { key, opts }) end @@ -1192,8 +1244,9 @@ index_metatable = function(remote) check_param_table(opts, REQUEST_OPTION_TYPES) return nothing_or_data(remote:_request(M_DELETE, opts, self.space._format_cdata, - self._stream_id, self.space.id, - self.id, key)) + self._stream_id, + self.space._id_or_name, + self._id_or_name, key)) end function methods:update(key, oplist, opts) @@ -1201,8 +1254,9 @@ index_metatable = function(remote) check_param_table(opts, REQUEST_OPTION_TYPES) return nothing_or_data(remote:_request(M_UPDATE, opts, self.space._format_cdata, - self._stream_id, self.space.id, - self.id, key, oplist)) + self._stream_id, + self.space._id_or_name, + self._id_or_name, key, oplist)) end return { __index = methods, __metatable = false } diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua index b8642d4e53..53d456b1b4 100644 --- a/src/box/lua/schema.lua +++ b/src/box/lua/schema.lua @@ -1858,7 +1858,7 @@ local port_c = ffi.cast('struct port_c *', port) -- Helper function to check space:method() usage local function check_space_arg(space, method) - if type(space) ~= 'table' or space.id == nil then + if type(space) ~= 'table' or (space.id == nil and space.name == nil) then local fmt = 'Use space:%s(...) instead of space.%s(...)' error(string.format(fmt, method, method)) end @@ -1877,7 +1877,7 @@ end -- Helper function to check index:method() usage local function check_index_arg(index, method) - if type(index) ~= 'table' or index.id == nil then + if type(index) ~= 'table' or (index.id == nil and index.name == nil) then local fmt = 'Use index:%s(...) instead of index.%s(...)' error(string.format(fmt, method, method)) end diff --git a/src/box/xrow.c b/src/box/xrow.c index dcaec90607..a7805c1317 100644 --- a/src/box/xrow.c +++ b/src/box/xrow.c @@ -894,8 +894,8 @@ iproto_send_event(struct obuf *out, uint64_t sync, } int -xrow_decode_dml(struct xrow_header *row, struct request *request, - uint64_t key_map) +xrow_decode_dml_internal(struct xrow_header *row, struct request *request, + uint64_t key_map, bool accept_space_name) { memset(request, 0, sizeof(*request)); request->header = row; @@ -925,7 +925,8 @@ xrow_decode_dml(struct xrow_header *row, struct request *request, if (key >= iproto_key_MAX || iproto_key_type[key] != mp_typeof(*value)) goto error; - key_map &= ~iproto_key_bit(key); + if (key < 64) + key_map &= ~iproto_key_bit(key); switch (key) { case IPROTO_SPACE_ID: request->space_id = mp_decode_uint(&value); @@ -980,10 +981,20 @@ xrow_decode_dml(struct xrow_header *row, struct request *request, request->after_tuple = value; request->after_tuple_end = data; break; + case IPROTO_SPACE_NAME: + request->space_name = + mp_decode_str(&value, &request->space_name_len); + break; + case IPROTO_INDEX_NAME: + request->index_name = + mp_decode_str(&value, &request->index_name_len); + break; default: break; } } + if (accept_space_name && request->space_name != NULL) + key_map &= ~iproto_key_bit(IPROTO_SPACE_ID); done: if (key_map) { enum iproto_key key = (enum iproto_key) bit_ctz_u64(key_map); diff --git a/src/box/xrow.h b/src/box/xrow.h index e74ec75c49..a7b6190ce0 100644 --- a/src/box/xrow.h +++ b/src/box/xrow.h @@ -216,6 +216,14 @@ struct request { int index_base; /** Send position of last selected tuple in response if true. */ bool fetch_position; + /** Name of requested space, points to the request's input buffer. */ + const char *space_name; + /** Length of @space_name. */ + uint32_t space_name_len; + /** Name of requested index, points to the request's input buffer. */ + const char *index_name; + /** Length of @index_name. */ + uint32_t index_name_len; }; /** @@ -229,12 +237,33 @@ request_str(const struct request *request); * @param[out] request DML request to decode to. * @param key_map a bit map of keys that are required by the caller, * @sa request_key_map(). + * @param accept_space_name space name is accepted instead of space identifier. * @retval 0 on success * @retval -1 on error */ int +xrow_decode_dml_internal(struct xrow_header *xrow, struct request *request, + uint64_t key_map, bool accept_space_name); + +/** + * Decode DML from system request (recovery or replication). + */ +static inline int xrow_decode_dml(struct xrow_header *xrow, struct request *request, - uint64_t key_map); + uint64_t key_map) +{ + return xrow_decode_dml_internal(xrow, request, key_map, false); +} + +/** + * Decode DML from IPROTO request. + */ +static inline int +xrow_decode_dml_iproto(struct xrow_header *xrow, struct request *request, + uint64_t key_map) +{ + return xrow_decode_dml_internal(xrow, request, key_map, true); +} /** * Encode the request fields to iovec using region_alloc(). diff --git a/test/box-luatest/gh_7894_export_iproto_constants_and_features_test.lua b/test/box-luatest/gh_7894_export_iproto_constants_and_features_test.lua index e784ae7fab..457667775a 100644 --- a/test/box-luatest/gh_7894_export_iproto_constants_and_features_test.lua +++ b/test/box-luatest/gh_7894_export_iproto_constants_and_features_test.lua @@ -82,6 +82,8 @@ local reference_table = { AUTH_TYPE = 0x5b, REPLICASET_NAME = 0x5c, INSTANCE_NAME = 0x5d, + SPACE_NAME = 0x5e, + INDEX_NAME = 0x5f, }, -- `iproto_metadata_key` enumeration. @@ -160,7 +162,7 @@ local reference_table = { }, -- `IPROTO_CURRENT_VERSION` constant - protocol_version = 4, + protocol_version = 5, -- `feature_id` enumeration protocol_features = { @@ -169,6 +171,7 @@ local reference_table = { error_extension = true, watchers = true, pagination = true, + space_and_index_names = true, }, feature = { streams = 0, @@ -176,6 +179,7 @@ local reference_table = { error_extension = 2, watchers = 3, pagination = 4, + space_and_index_names = 5, }, } diff --git a/test/box-luatest/gh_8146_data/00000000000000000004.snap b/test/box-luatest/gh_8146_data/00000000000000000004.snap new file mode 100644 index 0000000000000000000000000000000000000000..d5dfe125e06889e9caa9fe769c55b0eb11d0fb93 GIT binary patch literal 5093 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 H)ex=i0PnX5 literal 0 HcmV?d00001 diff --git a/test/box-luatest/gh_8146_space_and_index_name_in_iproto_requests_test.lua b/test/box-luatest/gh_8146_space_and_index_name_in_iproto_requests_test.lua new file mode 100644 index 0000000000..5f9dfb9332 --- /dev/null +++ b/test/box-luatest/gh_8146_space_and_index_name_in_iproto_requests_test.lua @@ -0,0 +1,304 @@ +local fio = require('fio') +local msgpack = require('msgpack') +local netbox = require('net.box') +local server = require('luatest.server') +local t = require('luatest') + +local g = t.group(nil, t.helpers.matrix{connection = {'connection', 'stream'}}) + +g.before_all(function(cg) + cg.server = server:new() + cg.server:start() +end) + +g.after_all(function(cg) + cg.server:drop() +end) + +-- Checks contents of net.box connections with disabled schema fetching. +g.test_net_box_conn_with_disabled_schema_fetching = function(cg) + t.skip_if(cg.params.connection == 'stream') + + local c = netbox:connect(cg.server.net_box_uri, {fetch_schema = false}) + t.assert_equals(c.space, {}) + t.assert_not_equals(getmetatable(c.space).__index, nil) + local s = c.space.s + t.assert_equals(c.space, {s = s}) + t.assert_equals(s, {_id_or_name = 's', name = 's', index = {}}) + t.assert_not_equals(getmetatable(s.index).__index, nil) + local i = s.index.i + t.assert_equals(s.index, {i = i}) + t.assert_equals(i, {_id_or_name = 'i', name = 'i', space = s}) + + local s512 = c.space[512] + t.assert_equals(c.space, {[512] = s512, s = s}) + t.assert_equals(s512, {_id_or_name = 512, id = 512, index = {}}) + local i1 = s.index[1] + t.assert_equals(s.index, {[1] = i1, i = i}) + t.assert_equals(i1, {_id_or_name = 1, id = 1, space = s}) + + t.assert_equals(c.space[{}], nil) + t.assert_equals(c.space[true], nil) + t.assert_equals(c.space[function() end], nil) + t.assert_equals(c.space.s.index[{}], nil) + t.assert_equals(c.space.s.index[true], nil) + t.assert_equals(c.space.s.index[function() end], nil) +end + +g.before_test('test_net_box_conn_with_disabled_schema_fetching_errinj', + function(cg) + cg.server:exec(function() + box.error.injection.set('ERRINJ_IPROTO_FLIP_FEATURE', + box.iproto.feature.space_and_index_names) + end) +end) + +-- Checks contents of net.box connections with disabled schema fetching when +-- space and index names IPROTO feature is disabled via error injection. +g.test_net_box_conn_with_disabled_schema_fetching_errinj = function(cg) + t.tarantool.skip_if_not_debug() + t.skip_if(cg.params.connection == 'stream') + + local c = netbox:connect(cg.server.net_box_uri, {fetch_schema = false}) + + t.assert_equals(c.space.s, nil) + + local s512 = c.space[512] + t.assert_equals(c.space, {[512] = s512}) + t.assert_equals(s512, {_id_or_name = 512, id = 512, index = {}}) + t.assert_equals(s512.index.i, nil) + local i1 = s512.index[1] + t.assert_equals(s512.index, {[1] = i1}) + t.assert_equals(i1, {_id_or_name = 1, id = 1, space = s512}) +end + +g.after_test('test_net_box_conn_with_disabled_schema_fetching_errinj', + function(cg) + cg.server:exec(function() + box.error.injection.set('ERRINJ_IPROTO_FLIP_FEATURE', -1) + end) +end) + +g.before_test('test_net_box_conn_space_and_index_wrapper_tables', function(cg) + cg.s_id = cg.server:exec(function() + local s = box.schema.create_space('s') + s:create_index('i') + s:create_index('s', {parts = {2, 'unsigned'}}) + s:insert{0, 1} + s:insert{1, 0} + local s_space = box.schema.create_space(' s ') + s_space:create_index(' i ') + s_space:insert{0} + return s.id + end) +end) + +-- Checks that space and index wrapper tables of net.box connections with +-- disabled schema fetching work correctly. +g.test_net_box_conn_space_and_index_wrapper_tables = function(cg) + local c = netbox:connect(cg.server.net_box_uri, {fetch_schema = false}) + if cg.params.connection == 'stream' then + c = c:new_stream() + end + local s = c.space.s + local i = s.index.i + + t.assert_equals(s:select(), {{0, 1}, {1, 0}}) + t.assert_equals(s:insert{2, 2}, {2, 2}) + t.assert_equals(s:update({2}, {{'=', 2, 3}}), {2, 3}) + t.assert_equals(i:update({2}, {{'=', 2, 2}}), {2, 2}) + t.assert_equals(i:select({0}), {{0, 1}}) + t.assert_equals(i:delete{2}, {2, 2}) + t.assert_equals(i:count(), 2) + t.assert_equals(i:min(), {0, 1}) + t.assert_equals(i:max(), {1, 0}) + t.assert_equals(i:get{0}, {0, 1}) + + s = c.space[cg.s_id] + i = s.index[1] + t.assert_equals(s:replace{1, 2}, {1, 2}) + t.assert_equals(i:select{2}, {{1, 2}}) + t.assert_equals(s:update({0}, {{'=', 2, 3}}), {0, 3}) + t.assert_equals(i:update({3}, {{'=', 2, 0}}), {0, 0}) + t.assert_equals(s:upsert({2, 4}, {{'=', 2, 5}}), nil) + t.assert_equals(s:upsert({2, 4}, {{'=', 2, 5}}), nil) + t.assert_equals(i:delete{5}, {2, 5}) + t.assert_equals(i:count(), 2) + t.assert_equals(i:min(), {0, 0}) + t.assert_equals(i:max(), {1, 2}) + t.assert_equals(i:get{2}, {1, 2}) + + t.assert_equals(c.space[cg.s_id].index.i:count(), 2) + t.assert_equals(c.space.s.index[1]:count(), 2) + t.assert_equals(c.space[' s '].index[' i ']:count(), 1) + + local err_msg = "Space 'nonexistent' does not exist" + t.assert_error_msg_content_equals(err_msg, function() + c.space.nonexistent:select{} + end) + local err_msg = "Space 'nonexistent' does not exist" + t.assert_error_msg_content_equals(err_msg, function() + c.space.nonexistent:insert{} + end) + err_msg = "Space '777' does not exist" + t.assert_error_msg_content_equals(err_msg, function() + c.space[777]:select{} + end) + err_msg = "Space '777' does not exist" + t.assert_error_msg_content_equals(err_msg, function() + c.space[777]:insert{} + end) + err_msg = "Space '777' does not exist" + t.assert_error_msg_content_equals(err_msg, function() + c.space[777].index.i:select{} + end) + err_msg = "No index 'nonexistent' is defined in space 's'" + t.assert_error_msg_content_equals(err_msg, function() + c.space[cg.s_id].index.nonexistent:select{} + end) + err_msg = "No index #777 is defined in space 's'" + t.assert_error_msg_content_equals(err_msg, function() + c.space[cg.s_id].index[777]:select{} + end) +end + +g.after_test('test_net_box_conn_space_and_index_wrapper_tables', function(cg) + cg.server:exec(function() + box.space.s:drop() + box.space[' s ']:drop() + end) +end) + +g.before_test('test_space_and_index_name_resolution', function(cg) + cg.s_id, cg.s1_id, cg.s2_id = cg.server:exec(function() + local s = box.schema.create_space('s') + s:create_index('i1') + s:create_index('i2', {parts = {2}}) + s:insert{1, 2} + s:insert{2, 1} + local s1 = box.schema.create_space('s1') + s1:create_index('i') + s1:insert{1} + local s2 = box.schema.create_space('s2') + s2:create_index('i') + s2:insert{2} + return s.id, s1.id + end) +end) + +local function inject_select(c, sid, space_name, iid, idx_name, key) + local header = msgpack.encode({ + [box.iproto.key.REQUEST_TYPE] = box.iproto.type.SELECT, + [box.iproto.key.SYNC] = c:_next_sync(), + [box.iproto.key.STREAM_ID] = c._stream_id or 0, + }) + local body = msgpack.encode({ + [box.iproto.key.SPACE_ID] = sid, + [box.iproto.key.SPACE_NAME] = space_name, + [box.iproto.key.INDEX_ID] = iid, + [box.iproto.key.INDEX_NAME] = idx_name, + [box.iproto.key.LIMIT] = 1, + [box.iproto.key.KEY] = setmetatable({key}, {__serialize = 'array'}), + }) + local size = msgpack.encode(#header + #body) + local request = size .. header .. body + return c:_inject(request) +end + +local function inject_insert_or_replace(c, request, space_name, index_name) + local header = msgpack.encode({ + [box.iproto.key.REQUEST_TYPE] = box.iproto.type[request], + [box.iproto.key.SYNC] = c:_next_sync(), + [box.iproto.key.STREAM_ID] = c._stream_id or 0, + }) + local body = msgpack.encode({ + [box.iproto.key.SPACE_NAME] = space_name, + [box.iproto.key.INDEX_NAME] = index_name, + [box.iproto.key.TUPLE] = {3, 3}, + }) + local size = msgpack.encode(#header + #body) + local request = size .. header .. body + return c:_inject(request) +end + +-- Checks that space and index name resolution works correctly. +g.test_space_and_index_name_resolution = function(cg) + local c = netbox:connect(cg.server.net_box_uri, {fetch_schema = false}) + if cg.params.connection == 'stream' then + c = c:new_stream() + end + + t.assert_equals(inject_select(c, cg.s1_id, 's2'), {{2}}) + t.assert_equals(inject_select(c, 777, 's2'), {{2}}) + t.assert_equals(inject_select(c, 0, 's2'), {{2}}) + t.assert_equals(inject_select(c, cg.s2_id, 's1'), {{1}}) + t.assert_equals(inject_select(c, 777, 's1'), {{1}}) + t.assert_equals(inject_select(c, 0, 's1'), {{1}}) + + t.assert_equals(inject_select(c, nil, 's', 0, 'i2', 2), {{1, 2}}) + t.assert_equals(inject_select(c, nil, 's', 777, 'i2', 2), {{1, 2}}) + t.assert_equals(inject_select(c, nil, 's', 1, 'i1', 2), {{2, 1}}) + t.assert_equals(inject_select(c, nil, 's', 777, 'i1', 2), {{2, 1}}) + + local err_msg = "Space 'nonexistent' does not exist" + t.assert_error_msg_content_equals(err_msg, function() + inject_select(c, cg.s_id, 'nonexistent') + end) + err_msg = "No index 'nonexistent' is defined in space 's'" + t.assert_error_msg_content_equals(err_msg, function() + inject_select(c, nil, 's', 0, 'nonexistent') + end) + + t.assert_equals(inject_insert_or_replace(c, 'INSERT', 's', 'nonexistent'), + {{3, 3}}) + t.assert_equals(inject_insert_or_replace(c, 'REPLACE', 's', 'nonexistent'), + {{3, 3}}) +end + +g.after_test('test_space_and_index_name_resolution', function(cg) + cg.server:exec(function() + box.space.s:drop() + end) +end) + +-- Checks that space name is not accepted instead of space identifier in +-- recovery requests. +-- +-- Snapshot generation instruction: +-- 1. Patch this place make the replace request body contain IPROTO_SPACE_NAME +-- instead OF IPROTO_SPACE_ID: +-- luacheck: no max comment line length +-- https://github.com/tarantool/tarantool/blob/5ce3114436bc94ab8414c88e4675e3e50923c199/src/box/iproto_constants.h#L497-L499 +-- For instance, add this snippet: +-- ``` +-- if (space_id == 512) { +-- body->k_space_id = 0x5c; +-- body->m_space_id = 0xa4; /* 4-byte string */ +-- char space_name[5] = {}; +-- strcpy(space_name, "name"); +-- space_id = *(uint32_t *)space_name; +-- body->v_space_id = space_id; +-- } else { +-- body->k_space_id = IPROTO_SPACE_ID; +-- body->m_space_id = 0xce; /* uint32 */ +-- body->v_space_id = mp_bswap_u32(space_id); +-- } +-- ``` +-- 2. Build and run Tarantool, call `box.cfg`; +-- 3. Create a user space, a primary index for it and insert a tuple into it. +-- 4. Call `box.snapshot`. +g.test_space_name_in_snapshot = function(cg) + local s = server:new{ + alias = 'recovery_' .. cg.params.connection, + datadir = 'test/box-luatest/gh_8146_data' + } + s:start{wait_until_ready = false} + local log = fio.pathjoin(s.workdir, s.alias .. '.log') + t.helpers.retrying({}, function() + t.assert_not_equals(s:grep_log("can't initialize storage: " .. + "Missing mandatory field 'space id' " .. + "in request", nil, + {filename = log}), nil) + end) + s:drop() +end diff --git a/test/box-luatest/net_box_test.lua b/test/box-luatest/net_box_test.lua index 47072dc6db..1eb9175dc7 100644 --- a/test/box-luatest/net_box_test.lua +++ b/test/box-luatest/net_box_test.lua @@ -268,7 +268,7 @@ g.test_schemaless = function() c = net.connect(g.server.net_box_uri, {fetch_schema = false}) t.assert_equals(c.state, 'active') t.assert_equals(c.opts.fetch_schema, false) - t.assert_equals(c.space, nil) + t.assert_not_equals(c.space, nil) c:on_schema_reload(function() schema_update_counter = schema_update_counter + 1 diff --git a/test/box-py/iproto.result b/test/box-py/iproto.result index 9c2f65c1ae..840fa71a80 100644 --- a/test/box-py/iproto.result +++ b/test/box-py/iproto.result @@ -210,9 +210,9 @@ Invalid MsgPack - request body # Invalid auth_type Invalid MsgPack - request body # Empty request body -version=4, features=[0, 1, 2, 3, 4], auth_type=chap-sha1 +version=5, features=[0, 1, 2, 3, 4, 5], auth_type=chap-sha1 # Unknown version and features -version=4, features=[0, 1, 2, 3, 4], auth_type=chap-sha1 +version=5, features=[0, 1, 2, 3, 4, 5], auth_type=chap-sha1 # # gh-6257 Watchers diff --git a/test/box/net.box_iproto_id.result b/test/box/net.box_iproto_id.result index 77e4a5d798..052b37b45a 100644 --- a/test/box/net.box_iproto_id.result +++ b/test/box/net.box_iproto_id.result @@ -15,7 +15,7 @@ c = net.connect(box.cfg.listen) | ... c.peer_protocol_version | --- - | - 4 + | - 5 | ... c.peer_protocol_features | --- @@ -24,6 +24,7 @@ c.peer_protocol_features | error_extension: true | streams: true | pagination: true + | space_and_index_names: true | ... c:close() | --- @@ -52,6 +53,7 @@ c.peer_protocol_features | error_extension: false | streams: false | pagination: false + | space_and_index_names: false | ... errinj.set('ERRINJ_IPROTO_DISABLE_ID', false) | --- @@ -101,6 +103,7 @@ c.peer_protocol_features | error_extension: true | streams: true | pagination: true + | space_and_index_names: true | ... c:close() | --- @@ -146,7 +149,7 @@ c.error -- error | ... c.peer_protocol_version | --- - | - 4 + | - 5 | ... c.peer_protocol_features | --- @@ -155,6 +158,7 @@ c.peer_protocol_features | error_extension: true | streams: true | pagination: true + | space_and_index_names: true | ... c:close() | --- @@ -173,7 +177,7 @@ c.error -- error | ... c.peer_protocol_version | --- - | - 4 + | - 5 | ... c.peer_protocol_features | --- @@ -182,6 +186,7 @@ c.peer_protocol_features | error_extension: true | streams: true | pagination: true + | space_and_index_names: true | ... c:close() | --- -- GitLab