From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: From: Kirill Shcherbatov Subject: [PATCH v9 2/6] box: introduce tuple_field_raw_by_path routine Date: Sun, 3 Feb 2019 13:20:22 +0300 Message-Id: <8637583706afb4df090124ecf5d498b40a3c28fc.1549187339.git.kshcherbatov@tarantool.org> 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: Introduced a new function tuple_field_raw_by_path is used to get tuple fields by field index and relative JSON path. This routine uses tuple_format's field_map if possible. It will be further extended to use JSON indexes. The old tuple_field_raw_by_path routine used to work with full JSON paths, renamed tuple_field_raw_by_full_path. It's return value type is changed to const char * because the other similar functions tuple_field_raw and tuple_field_by_part_raw use this convention. Got rid of reporting error position for 'invalid JSON path' error in lbox_tuple_field_by_path because we can't extend other routines to behave such way that makes an API inconsistent, moreover such error are useless and confusing. Needed for #1012 --- src/box/lua/tuple.c | 9 +-- src/box/tuple.h | 60 +++++++++--------- src/box/tuple_format.c | 63 ++++++------------- src/box/tuple_format.h | 127 +++++++++++++++++++++------------------ test/engine/tuple.result | 16 ++--- 5 files changed, 129 insertions(+), 146 deletions(-) diff --git a/src/box/lua/tuple.c b/src/box/lua/tuple.c index 1867f810f..7d377b69e 100644 --- a/src/box/lua/tuple.c +++ b/src/box/lua/tuple.c @@ -463,13 +463,10 @@ lbox_tuple_field_by_path(struct lua_State *L) const char *field = NULL, *path = lua_tolstring(L, 2, &len); if (len == 0) return 0; - if (tuple_field_by_path(tuple, path, (uint32_t) len, - lua_hashstring(L, 2), &field) != 0) { - return luaT_error(L); - } else if (field == NULL) { + field = tuple_field_by_full_path(tuple, path, (uint32_t)len, + lua_hashstring(L, 2)); + if (field == NULL) return 0; - } - assert(field != NULL); luamp_decode(L, luaL_msgpack_default, &field); return 1; } diff --git a/src/box/tuple.h b/src/box/tuple.h index 83e5b7013..c3cd689fd 100644 --- a/src/box/tuple.h +++ b/src/box/tuple.h @@ -513,6 +513,24 @@ tuple_field(const struct tuple *tuple, uint32_t fieldno) tuple_field_map(tuple), fieldno); } +/** + * Get tuple field by its root field index and relative + * JSON path. + * @param tuple Tuple to get the field from. + * @param fieldno The index of root field. + * @param path Relative JSON path to field data. + * @param path_len The length of the @path. + * @retval Field data if the field exists or NULL. + */ +static inline const char * +tuple_field_by_path(const struct tuple *tuple, uint32_t fieldno, + const char *path, uint32_t path_len) +{ + return tuple_field_raw_by_path(tuple_format(tuple), tuple_data(tuple), + tuple_field_map(tuple), fieldno, + path, path_len); +} + /** * Get a field refereed by index @part in tuple. * @param tuple Tuple to get the field from. @@ -527,43 +545,25 @@ tuple_field_by_part(const struct tuple *tuple, struct key_part *part) } /** - * Get tuple field by its JSON path. + * Get tuple field by full JSON path. + * Unlike tuple_field_by_path this function works with full JSON + * paths, performing root field index resolve on its own. + * When the first JSON path token has JSON_TOKEN_STR type, routine + * uses tuple format dictionary to get field index by field name. * @param tuple Tuple to get field from. - * @param path Field JSON path. + * @param path Full JSON path to field. * @param path_len Length of @a path. * @param path_hash Hash of @a path. - * @param[out] field Found field, or NULL, if not found. - * - * @retval 0 Success. - * @retval -1 Error in JSON path. - */ -static inline int -tuple_field_by_path(const struct tuple *tuple, const char *path, - uint32_t path_len, uint32_t path_hash, - const char **field) -{ - return tuple_field_raw_by_path(tuple_format(tuple), tuple_data(tuple), - tuple_field_map(tuple), path, path_len, - path_hash, field); -} - -/** - * Get tuple field by its name. - * @param tuple Tuple to get field from. - * @param name Field name. - * @param name_len Length of @a name. - * @param name_hash Hash of @a name. * - * @retval not NULL MessagePack field. - * @retval NULL No field with @a name. + * @retval field data if field exists or NULL. */ static inline const char * -tuple_field_by_name(const struct tuple *tuple, const char *name, - uint32_t name_len, uint32_t name_hash) +tuple_field_by_full_path(const struct tuple *tuple, const char *path, + uint32_t path_len, uint32_t path_hash) { - return tuple_field_raw_by_name(tuple_format(tuple), tuple_data(tuple), - tuple_field_map(tuple), name, name_len, - name_hash); + return tuple_field_raw_by_full_path(tuple_format(tuple), tuple_data(tuple), + tuple_field_map(tuple), + path, path_len, path_hash); } /** diff --git a/src/box/tuple_format.c b/src/box/tuple_format.c index 214760247..4d10b0918 100644 --- a/src/box/tuple_format.c +++ b/src/box/tuple_format.c @@ -879,15 +879,7 @@ tuple_field_go_to_key(const char **field, const char *key, int len) return -1; } -/** - * Retrieve msgpack data by JSON path. - * @param data Pointer to msgpack with data. - * @param path The path to process. - * @param path_len The length of the @path. - * @retval 0 On success. - * @retval >0 On path parsing error, invalid character position. - */ -static int +int tuple_field_go_to_path(const char **data, const char *path, uint32_t path_len) { int rc; @@ -911,14 +903,13 @@ tuple_field_go_to_path(const char **data, const char *path, uint32_t path_len) return 0; } } - return rc; + return rc != 0 ? -1 : 0; } -int -tuple_field_raw_by_path(struct tuple_format *format, const char *tuple, - const uint32_t *field_map, const char *path, - uint32_t path_len, uint32_t path_hash, - const char **field) +const char * +tuple_field_raw_by_full_path(struct tuple_format *format, const char *tuple, + const uint32_t *field_map, const char *path, + uint32_t path_len, uint32_t path_hash) { assert(path_len > 0); uint32_t fieldno; @@ -929,22 +920,16 @@ tuple_field_raw_by_path(struct tuple_format *format, const char *tuple, * use the path as a field name. */ if (tuple_fieldno_by_name(format->dict, path, path_len, path_hash, - &fieldno) == 0) { - *field = tuple_field_raw(format, tuple, field_map, fieldno); - return 0; - } + &fieldno) == 0) + return tuple_field_raw(format, tuple, field_map, fieldno); struct json_lexer lexer; struct json_token token; json_lexer_create(&lexer, path, path_len, TUPLE_INDEX_BASE); - int rc = json_lexer_next_token(&lexer, &token); - if (rc != 0) - goto error; + if (json_lexer_next_token(&lexer, &token) != 0) + return NULL; switch(token.type) { case JSON_TOKEN_NUM: { - int index = token.num; - *field = tuple_field_raw(format, tuple, field_map, index); - if (*field == NULL) - return 0; + fieldno = token.num; break; } case JSON_TOKEN_STR: { @@ -961,28 +946,16 @@ tuple_field_raw_by_path(struct tuple_format *format, const char *tuple, */ name_hash = field_name_hash(token.str, token.len); } - *field = tuple_field_raw_by_name(format, tuple, field_map, - token.str, token.len, - name_hash); - if (*field == NULL) - return 0; + if (tuple_fieldno_by_name(format->dict, token.str, token.len, + name_hash, &fieldno) != 0) + return NULL; break; } default: assert(token.type == JSON_TOKEN_END); - *field = NULL; - return 0; + return NULL; } - rc = tuple_field_go_to_path(field, path + lexer.offset, - path_len - lexer.offset); - if (rc == 0) - return 0; - /* Setup absolute error position. */ - rc += lexer.offset; - -error: - assert(rc > 0); - diag_set(ClientError, ER_ILLEGAL_PARAMS, - tt_sprintf("error in path on position %d", rc)); - return -1; + return tuple_field_raw_by_path(format, tuple, field_map, fieldno, + path + lexer.offset, + path_len - lexer.offset); } diff --git a/src/box/tuple_format.h b/src/box/tuple_format.h index 0d91119db..60b019194 100644 --- a/src/box/tuple_format.h +++ b/src/box/tuple_format.h @@ -374,88 +374,101 @@ tuple_init_field_map(struct tuple_format *format, uint32_t *field_map, const char *tuple, bool validate); /** - * Get a field at the specific position in this MessagePack array. - * Returns a pointer to MessagePack data. - * @param format tuple format - * @param tuple a pointer to MessagePack array - * @param field_map a pointer to the LAST element of field map - * @param field_no the index of field to return - * - * @returns field data if field exists or NULL - * @sa tuple_init_field_map() + * Retrieve msgpack data by JSON path. + * @param data[in, out] Pointer to msgpack with data. + * If the field cannot be retrieved be the + * specified path @path, it is overwritten + * with NULL. + * @param path The path to process. + * @param path_len The length of the @path. + * @retval 0 On success. + * @retval -1 In case of error in JSON path. + */ +int +tuple_field_go_to_path(const char **data, const char *path, uint32_t path_len); + +/** + * Get tuple field by field index and relative JSON path. + * @param format Tuple format. + * @param tuple MessagePack tuple's body. + * @param field_map Tuple field map. + * @param path Relative JSON path to field. + * @param path_len Length of @a path. */ static inline const char * -tuple_field_raw(struct tuple_format *format, const char *tuple, - const uint32_t *field_map, uint32_t field_no) +tuple_field_raw_by_path(struct tuple_format *format, const char *tuple, + const uint32_t *field_map, uint32_t fieldno, + const char *path, uint32_t path_len) { - if (likely(field_no < format->index_field_count)) { - /* Indexed field */ - - if (field_no == 0) { + if (likely(fieldno < format->index_field_count)) { + if (fieldno == 0) { mp_decode_array(&tuple); - return tuple; - } - - int32_t offset_slot = tuple_format_field(format, - field_no)->offset_slot; - if (offset_slot != TUPLE_OFFSET_SLOT_NIL) { - if (field_map[offset_slot] != 0) - return tuple + field_map[offset_slot]; - else - return NULL; + goto parse_path; } + struct tuple_field *field = tuple_format_field(format, fieldno); + assert(field != NULL); + int32_t offset_slot = field->offset_slot; + if (offset_slot == TUPLE_OFFSET_SLOT_NIL) + goto parse; + /* Indexed field */ + if (field_map[offset_slot] == 0) + return NULL; + tuple += field_map[offset_slot]; + } else { + uint32_t field_count; +parse: + ERROR_INJECT(ERRINJ_TUPLE_FIELD, return NULL); + field_count = mp_decode_array(&tuple); + if (unlikely(fieldno >= field_count)) + return NULL; + for (uint32_t k = 0; k < fieldno; k++) + mp_next(&tuple); } - ERROR_INJECT(ERRINJ_TUPLE_FIELD, return NULL); - uint32_t field_count = mp_decode_array(&tuple); - if (unlikely(field_no >= field_count)) +parse_path: + if (path != NULL && + unlikely(tuple_field_go_to_path(&tuple, path, path_len) != 0)) return NULL; - for (uint32_t k = 0; k < field_no; k++) - mp_next(&tuple); return tuple; } /** - * Get tuple field by its name. - * @param format Tuple format. - * @param tuple MessagePack tuple's body. - * @param field_map Tuple field map. - * @param name Field name. - * @param name_len Length of @a name. - * @param name_hash Hash of @a name. + * Get a field at the specific position in this MessagePack array. + * Returns a pointer to MessagePack data. + * @param format tuple format + * @param tuple a pointer to MessagePack array + * @param field_map a pointer to the LAST element of field map + * @param field_no the index of field to return * - * @retval not NULL MessagePack field. - * @retval NULL No field with @a name. + * @returns field data if field exists or NULL + * @sa tuple_init_field_map() */ static inline const char * -tuple_field_raw_by_name(struct tuple_format *format, const char *tuple, - const uint32_t *field_map, const char *name, - uint32_t name_len, uint32_t name_hash) +tuple_field_raw(struct tuple_format *format, const char *tuple, + const uint32_t *field_map, uint32_t field_no) { - uint32_t fieldno; - if (tuple_fieldno_by_name(format->dict, name, name_len, name_hash, - &fieldno) != 0) - return NULL; - return tuple_field_raw(format, tuple, field_map, fieldno); + return tuple_field_raw_by_path(format, tuple, field_map, field_no, + NULL, 0); } /** - * Get tuple field by its path. + * Get tuple field by full JSON path. + * Unlike tuple_field_raw_by_path this function works with full + * JSON paths, performing root field index resolve on its own. + * When the first JSON path token has JSON_TOKEN_STR type, routine + * uses tuple format dictionary to get field index by field name. * @param format Tuple format. * @param tuple MessagePack tuple's body. * @param field_map Tuple field map. - * @param path Field path. + * @param path Full JSON path to field. * @param path_len Length of @a path. * @param path_hash Hash of @a path. - * @param[out] field Found field, or NULL, if not found. * - * @retval 0 Success. - * @retval -1 Error in JSON path. + * @retval field data if field exists or NULL */ -int -tuple_field_raw_by_path(struct tuple_format *format, const char *tuple, - const uint32_t *field_map, const char *path, - uint32_t path_len, uint32_t path_hash, - const char **field); +const char * +tuple_field_raw_by_full_path(struct tuple_format *format, const char *tuple, + const uint32_t *field_map, const char *path, + uint32_t path_len, uint32_t path_hash); /** * Get a tuple field pointed to by an index part. diff --git a/test/engine/tuple.result b/test/engine/tuple.result index 7ca3985c7..daf57d1f5 100644 --- a/test/engine/tuple.result +++ b/test/engine/tuple.result @@ -823,7 +823,7 @@ t[0] ... t["[0]"] --- -- error: Illegal parameters, error in path on position 2 +- null ... t["[1000]"] --- @@ -847,7 +847,7 @@ t["[2][6].key100"] ... t["[2][0]"] -- 0-based index in array. --- -- error: Illegal parameters, error in path on position 5 +- null ... t["[4][3]"] -- Can not index string. --- @@ -866,27 +866,27 @@ t["a.b.c d.e.f"] -- Sytax errors. t["[2].[5]"] --- -- error: Illegal parameters, error in path on position 5 +- null ... t["[-1]"] --- -- error: Illegal parameters, error in path on position 2 +- null ... t[".."] --- -- error: Illegal parameters, error in path on position 2 +- null ... t["[["] --- -- error: Illegal parameters, error in path on position 2 +- null ... t["]]"] --- -- error: Illegal parameters, error in path on position 1 +- null ... t["{"] --- -- error: Illegal parameters, error in path on position 1 +- null ... s:drop() --- -- 2.20.1