[PATCH v9 2/6] box: introduce tuple_field_raw_by_path routine

Kirill Shcherbatov kshcherbatov at tarantool.org
Sun Feb 3 13:20:22 MSK 2019


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




More information about the Tarantool-patches mailing list