Tarantool development patches archive
 help / color / mirror / Atom feed
From: Vladislav Shpilevoy <v.shpilevoy@tarantool.org>
To: tarantool-patches@freelists.org
Cc: vdavydov.dev@gmail.com
Subject: [PATCH 5/5] tuple: implement update by field name
Date: Sun, 14 Jul 2019 00:11:08 +0200	[thread overview]
Message-ID: <8ffb22759c7c9d6d60b74b1bf280d16344afeefc.1563054879.git.v.shpilevoy@tarantool.org> (raw)
In-Reply-To: <cover.1563054879.git.v.shpilevoy@tarantool.org>

Tuple fields can be named, accessed by name, indexed by name, but
till this commit field names couldn't be used in update
operations. Now it is possible.

This patch is a teaser of updates by JSON path.

Part of #1261
---
 src/box/errcode.h           |   4 +-
 src/box/lua/tuple.c         |   5 +-
 src/box/memtx_space.c       |  17 ++--
 src/box/space.c             |   9 ++-
 src/box/sql/insert.c        |   3 +-
 src/box/sql/resolve.c       |   4 +-
 src/box/sql/update.c        |   2 +-
 src/box/tuple.c             |  12 +--
 src/box/tuple_update.c      | 152 ++++++++++++++++++++++--------------
 src/box/tuple_update.h      |  15 ++--
 src/box/vinyl.c             |   9 ++-
 src/box/vy_upsert.c         |   6 +-
 test/box/misc.result        |   3 +-
 test/box/update.result      |   2 +-
 test/engine/update.result   |  67 ++++++++++++++++
 test/engine/update.test.lua |  28 +++++++
 test/engine/upsert.result   |   2 +-
 test/unit/column_mask.c     |   5 +-
 18 files changed, 247 insertions(+), 98 deletions(-)

diff --git a/src/box/errcode.h b/src/box/errcode.h
index be8dab27d..ccbc43b02 100644
--- a/src/box/errcode.h
+++ b/src/box/errcode.h
@@ -205,14 +205,14 @@ struct errcode_record {
 	/*150 */_(ER_CANT_CREATE_COLLATION,	"Failed to initialize collation: %s.") \
 	/*151 */_(ER_WRONG_COLLATION_OPTIONS,	"Wrong collation options (field %u): %s") \
 	/*152 */_(ER_NULLABLE_PRIMARY,		"Primary index of the space '%s' can not contain nullable parts") \
-	/*153 */_(ER_NO_SUCH_FIELD_NAME,	"Field '%s' was not found in the space '%s' format") \
+	/*153 */_(ER_NO_SUCH_FIELD_NAME_IN_SPACE, "Field '%s' was not found in the space '%s' format") \
 	/*154 */_(ER_TRANSACTION_YIELD,		"Transaction has been aborted by a fiber yield") \
 	/*155 */_(ER_NO_SUCH_GROUP,		"Replication group '%s' does not exist") \
 	/*156 */_(ER_SQL_BIND_VALUE,            "Bind value for parameter %s is out of range for type %s") \
 	/*157 */_(ER_SQL_BIND_TYPE,             "Bind value type %s for parameter %s is not supported") \
 	/*158 */_(ER_SQL_BIND_PARAMETER_MAX,    "SQL bind parameter limit reached: %d") \
 	/*159 */_(ER_SQL_EXECUTE,               "Failed to execute SQL statement: %s") \
-	/*160 */_(ER_UNUSED,			"") \
+	/*160 */_(ER_NO_SUCH_FIELD_NAME,	"Field '%s' was not found in the tuple") \
 	/*161 */_(ER_SQL_BIND_NOT_FOUND,	"Parameter %s was not found in the statement") \
 	/*162 */_(ER_ACTION_MISMATCH,		"Field %s contains %s on conflict action, but %s in index parts") \
 	/*163 */_(ER_VIEW_MISSING_SQL,		"Space declared as a view must have SQL statement") \
diff --git a/src/box/lua/tuple.c b/src/box/lua/tuple.c
index 2fbfb1473..3902288bf 100644
--- a/src/box/lua/tuple.c
+++ b/src/box/lua/tuple.c
@@ -439,6 +439,7 @@ lbox_tuple_transform(struct lua_State *L)
 	const char *old_data = tuple_data_range(tuple, &bsize);
 	struct region *region = &fiber()->gc;
 	size_t used = region_used(region);
+	struct tuple_format *format = tuple_format(tuple);
 	struct tuple *new_tuple = NULL;
 	/*
 	 * Can't use box_tuple_update() since transform must reset
@@ -449,8 +450,8 @@ lbox_tuple_transform(struct lua_State *L)
 	 */
 	const char *new_data =
 		tuple_update_execute(buf->buf, buf->buf + ibuf_used(buf),
-				     old_data, old_data + bsize, &new_size, 1,
-				     NULL);
+				     old_data, old_data + bsize, format->dict,
+				     &new_size, 1, NULL);
 	if (new_data != NULL)
 		new_tuple = tuple_new(box_tuple_format_default(),
 				      new_data, new_data + new_size);
diff --git a/src/box/memtx_space.c b/src/box/memtx_space.c
index 54b379c43..d6286924e 100644
--- a/src/box/memtx_space.c
+++ b/src/box/memtx_space.c
@@ -417,15 +417,16 @@ memtx_space_execute_update(struct space *space, struct txn *txn,
 
 	/* Update the tuple; legacy, request ops are in request->tuple */
 	uint32_t new_size = 0, bsize;
+	struct tuple_format *format = space->format;
 	const char *old_data = tuple_data_range(old_tuple, &bsize);
 	const char *new_data =
 		tuple_update_execute(request->tuple, request->tuple_end,
-				     old_data, old_data + bsize,
+				     old_data, old_data + bsize, format->dict,
 				     &new_size, request->index_base, NULL);
 	if (new_data == NULL)
 		return -1;
 
-	stmt->new_tuple = memtx_tuple_new(space->format, new_data,
+	stmt->new_tuple = memtx_tuple_new(format, new_data,
 					  new_data + new_size);
 	if (stmt->new_tuple == NULL)
 		return -1;
@@ -471,6 +472,7 @@ memtx_space_execute_upsert(struct space *space, struct txn *txn,
 	if (index_get(index, key, part_count, &old_tuple) != 0)
 		return -1;
 
+	struct tuple_format *format = space->format;
 	if (old_tuple == NULL) {
 		/**
 		 * Old tuple was not found. A write optimized
@@ -489,11 +491,11 @@ memtx_space_execute_upsert(struct space *space, struct txn *txn,
 		 * @sa https://github.com/tarantool/tarantool/issues/1156
 		 */
 		if (tuple_update_check_ops(request->ops, request->ops_end,
+					   format->dict,
 					   request->index_base) != 0) {
 			return -1;
 		}
-		stmt->new_tuple = memtx_tuple_new(space->format,
-						  request->tuple,
+		stmt->new_tuple = memtx_tuple_new(format, request->tuple,
 						  request->tuple_end);
 		if (stmt->new_tuple == NULL)
 			return -1;
@@ -511,12 +513,13 @@ memtx_space_execute_upsert(struct space *space, struct txn *txn,
 		const char *new_data =
 			tuple_upsert_execute(request->ops, request->ops_end,
 					     old_data, old_data + bsize,
-					     &new_size, request->index_base,
-					     false, &column_mask);
+					     format->dict, &new_size,
+					     request->index_base, false,
+					     &column_mask);
 		if (new_data == NULL)
 			return -1;
 
-		stmt->new_tuple = memtx_tuple_new(space->format, new_data,
+		stmt->new_tuple = memtx_tuple_new(format, new_data,
 						  new_data + new_size);
 		if (stmt->new_tuple == NULL)
 			return -1;
diff --git a/src/box/space.c b/src/box/space.c
index e881aaf32..d4ac90ad9 100644
--- a/src/box/space.c
+++ b/src/box/space.c
@@ -359,7 +359,8 @@ space_before_replace(struct space *space, struct txn *txn,
 		old_data_end = old_data + old_size;
 		new_data = tuple_update_execute(request->tuple,
 						request->tuple_end, old_data,
-						old_data_end, &new_size,
+						old_data_end,
+						space->format->dict, &new_size,
 						request->index_base, NULL);
 		if (new_data == NULL)
 			return -1;
@@ -382,6 +383,7 @@ space_before_replace(struct space *space, struct txn *txn,
 			new_data_end = request->tuple_end;
 			if (tuple_update_check_ops(request->ops,
 						   request->ops_end,
+						   space->format->dict,
 						   request->index_base) != 0)
 				return -1;
 			break;
@@ -390,8 +392,9 @@ space_before_replace(struct space *space, struct txn *txn,
 		old_data_end = old_data + old_size;
 		new_data = tuple_upsert_execute(request->ops, request->ops_end,
 						old_data, old_data_end,
-						&new_size, request->index_base,
-						false, NULL);
+						space->format->dict, &new_size,
+						request->index_base, false,
+						NULL);
 		new_data_end = new_data + new_size;
 		break;
 	default:
diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c
index b35314855..c8ad2f7f7 100644
--- a/src/box/sql/insert.c
+++ b/src/box/sql/insert.c
@@ -373,7 +373,8 @@ sqlInsert(Parse * pParse,	/* Parser context */
 				}
 			}
 			if (j >= (int) space_def->field_count) {
-				diag_set(ClientError, ER_NO_SUCH_FIELD_NAME,
+				diag_set(ClientError,
+					 ER_NO_SUCH_FIELD_NAME_IN_SPACE,
 					 pColumn->a[i].zName,
 					 pTabList->a[0].zName);
 				pParse->is_aborted = true;
diff --git a/src/box/sql/resolve.c b/src/box/sql/resolve.c
index 0b90edd06..bb2e94e9e 100644
--- a/src/box/sql/resolve.c
+++ b/src/box/sql/resolve.c
@@ -431,8 +431,8 @@ lookupName(Parse * pParse,	/* The parsing context */
 		if (zTab == NULL) {
 			diag_set(ClientError, ER_SQL_CANT_RESOLVE_FIELD, zCol);
 		} else {
-			diag_set(ClientError, ER_NO_SUCH_FIELD_NAME, zCol,
-				 zTab);
+			diag_set(ClientError, ER_NO_SUCH_FIELD_NAME_IN_SPACE,
+				 zCol, zTab);
 		}
 		pParse->is_aborted = true;
 		pTopNC->nErr++;
diff --git a/src/box/sql/update.c b/src/box/sql/update.c
index d77bee051..e9262de2e 100644
--- a/src/box/sql/update.c
+++ b/src/box/sql/update.c
@@ -192,7 +192,7 @@ sqlUpdate(Parse * pParse,		/* The parser context */
 			}
 		}
 		if (j >= (int)def->field_count) {
-			diag_set(ClientError, ER_NO_SUCH_FIELD_NAME,
+			diag_set(ClientError, ER_NO_SUCH_FIELD_NAME_IN_SPACE,
 				 pChanges->a[i].zName, def->name);
 			pParse->is_aborted = true;
 			goto update_cleanup;
diff --git a/src/box/tuple.c b/src/box/tuple.c
index 1e5b2fb24..f1225d3d5 100644
--- a/src/box/tuple.c
+++ b/src/box/tuple.c
@@ -705,15 +705,15 @@ box_tuple_update(box_tuple_t *tuple, const char *expr, const char *expr_end)
 	const char *old_data = tuple_data_range(tuple, &bsize);
 	struct region *region = &fiber()->gc;
 	size_t used = region_used(region);
+	struct tuple_format *format = tuple_format(tuple);
 	const char *new_data =
 		tuple_update_execute(expr, expr_end, old_data, old_data + bsize,
-				     &new_size, 1, NULL);
+				     format->dict, &new_size, 1, NULL);
 	if (new_data == NULL) {
 		region_truncate(region, used);
 		return NULL;
 	}
-	struct tuple *ret = tuple_new(tuple_format(tuple), new_data,
-				      new_data + new_size);
+	struct tuple *ret = tuple_new(format, new_data, new_data + new_size);
 	region_truncate(region, used);
 	if (ret != NULL)
 		return tuple_bless(ret);
@@ -727,16 +727,16 @@ box_tuple_upsert(box_tuple_t *tuple, const char *expr, const char *expr_end)
 	const char *old_data = tuple_data_range(tuple, &bsize);
 	struct region *region = &fiber()->gc;
 	size_t used = region_used(region);
+	struct tuple_format *format = tuple_format(tuple);
 	const char *new_data =
 		tuple_upsert_execute(expr, expr_end, old_data, old_data + bsize,
-				     &new_size, 1, false, NULL);
+				     format->dict, &new_size, 1, false, NULL);
 	if (new_data == NULL) {
 		region_truncate(region, used);
 		return NULL;
 	}
 
-	struct tuple *ret = tuple_new(tuple_format(tuple),
-				      new_data, new_data + new_size);
+	struct tuple *ret = tuple_new(format, new_data, new_data + new_size);
 	region_truncate(region, used);
 	if (ret != NULL)
 		return tuple_bless(ret);
diff --git a/src/box/tuple_update.c b/src/box/tuple_update.c
index 21ea876c9..34e0e767f 100644
--- a/src/box/tuple_update.c
+++ b/src/box/tuple_update.c
@@ -41,6 +41,8 @@
 #include <bit/int96.h>
 #include "column_mask.h"
 #include "fiber.h"
+#include "tuple_dictionary.h"
+#include "tt_static.h"
 
 /** UPDATE request implementation.
  * UPDATE request is represented by a sequence of operations, each
@@ -929,12 +931,87 @@ update_op_by(char opcode)
 	}
 }
 
+/**
+ * Decode an update operation from MessagePack.
+ * @param[out] op Update operation.
+ * @param index_base Field numbers base: 0 or 1.
+ * @param dict Dictionary to lookup field number by a name.
+ * @param expr MessagePack.
+ *
+ * @retval 0 Success.
+ * @retval -1 Client error.
+ */
+static inline int
+update_op_decode(struct update_op *op, int index_base,
+		 struct tuple_dictionary *dict, const char **expr)
+{
+	if (mp_typeof(**expr) != MP_ARRAY) {
+		diag_set(ClientError, ER_ILLEGAL_PARAMS, "update operation "
+			 "must be an array {op,..}");
+		return -1;
+	}
+	uint32_t len, args = mp_decode_array(expr);
+	if (args < 1) {
+		diag_set(ClientError, ER_ILLEGAL_PARAMS, "update operation "\
+			 "must be an array {op,..}, got empty array");
+		return -1;
+	}
+	if (mp_typeof(**expr) != MP_STR) {
+		diag_set(ClientError, ER_ILLEGAL_PARAMS,
+			 "update operation name must be a string");
+		return -1;
+	}
+	op->opcode = *mp_decode_str(expr, &len);
+	op->meta = update_op_by(op->opcode);
+	if (op->meta == NULL)
+		return -1;
+	if (args != op->meta->args) {
+		diag_set(ClientError, ER_UNKNOWN_UPDATE_OP);
+		return -1;
+	}
+	int32_t field_no;
+	switch(mp_typeof(**expr)) {
+	case MP_INT:
+	case MP_UINT: {
+		if (mp_read_i32(index_base, op, expr, &field_no) != 0)
+			return -1;
+		if (field_no - index_base >= 0) {
+			op->field_no = field_no - index_base;
+		} else if (field_no < 0) {
+			op->field_no = field_no;
+		} else {
+			diag_set(ClientError, ER_NO_SUCH_FIELD_NO, field_no);
+			return -1;
+		}
+		break;
+	}
+	case MP_STR: {
+		const char *path = mp_decode_str(expr, &len);
+		uint32_t field_no, hash = field_name_hash(path, len);
+		if (tuple_fieldno_by_name(dict, path, len, hash,
+					  &field_no) != 0) {
+			diag_set(ClientError, ER_NO_SUCH_FIELD_NAME,
+				 tt_cstr(path, len));
+			return -1;
+		}
+		op->field_no = (int32_t) field_no;
+		break;
+	}
+	default:
+		diag_set(ClientError, ER_ILLEGAL_PARAMS,
+			 "field id must be a number or a string");
+		return -1;
+	}
+	return op->meta->read_arg(index_base, op, expr);
+}
+
 /**
  * Read and check update operations and fill column mask.
  *
  * @param[out] update Update meta.
  * @param expr MessagePack array of operations.
  * @param expr_end End of the @expr.
+ * @param dict Dictionary to lookup field number by a name.
  * @param field_count_hint Field count in the updated tuple. If
  *        there is no tuple at hand (for example, when we are
  *        reading UPSERT operations), then 0 for field count will
@@ -948,7 +1025,8 @@ update_op_by(char opcode)
  */
 static int
 update_read_ops(struct tuple_update *update, const char *expr,
-		const char *expr_end, int32_t field_count_hint)
+		const char *expr_end, struct tuple_dictionary *dict,
+		int32_t field_count_hint)
 {
 	if (mp_typeof(*expr) != MP_ARRAY) {
 		diag_set(ClientError, ER_ILLEGAL_PARAMS,
@@ -974,59 +1052,14 @@ update_read_ops(struct tuple_update *update, const char *expr,
 	struct update_op *op = update->ops;
 	struct update_op *ops_end = op + update->op_count;
 	for (; op < ops_end; op++) {
-		if (mp_typeof(*expr) != MP_ARRAY) {
-			diag_set(ClientError, ER_ILLEGAL_PARAMS,
-				 "update operation"
-				 " must be an array {op,..}");
-			return -1;
-		}
-		/* Read operation */
-		uint32_t args, len;
-		args = mp_decode_array(&expr);
-		if (args < 1) {
-			diag_set(ClientError, ER_ILLEGAL_PARAMS,
-				 "update operation must be an "
-				 "array {op,..}, got empty array");
-			return -1;
-		}
-		if (mp_typeof(*expr) != MP_STR) {
-			diag_set(ClientError, ER_ILLEGAL_PARAMS,
-				 "update operation name must be a string");
-			return -1;
-		}
-
-		op->opcode = *mp_decode_str(&expr, &len);
-		op->meta = update_op_by(op->opcode);
-		if (op->meta == NULL)
-			return -1;
-		if (args != op->meta->args) {
-			diag_set(ClientError, ER_UNKNOWN_UPDATE_OP);
-			return -1;
-		}
-		if (mp_typeof(*expr) != MP_INT && mp_typeof(*expr) != MP_UINT) {
-			diag_set(ClientError, ER_ILLEGAL_PARAMS,
-				 "field id must be a number");
-			return -1;
-		}
-		int32_t field_no = 0;
-		if (mp_read_i32(update->index_base, op, &expr, &field_no))
-			return -1;
-		if (field_no - update->index_base >= 0) {
-			op->field_no = field_no - update->index_base;
-		} else if (field_no < 0) {
-			op->field_no = field_no;
-		} else {
-			diag_set(ClientError, ER_NO_SUCH_FIELD_NO, field_no);
+		if (update_op_decode(op, update->index_base, dict, &expr) != 0)
 			return -1;
-		}
-		if (op->meta->read_arg(update->index_base, op, &expr))
-			return -1;
-
 		/*
 		 * Continue collecting the changed columns
 		 * only if there are unset bits in the mask.
 		 */
 		if (column_mask != COLUMN_MASK_FULL) {
+			int32_t field_no;
 			if (op->field_no >= 0)
 				field_no = op->field_no;
 			else if (op->opcode != '!')
@@ -1184,24 +1217,25 @@ update_finish(struct tuple_update *update, uint32_t *p_tuple_len)
 }
 
 int
-tuple_update_check_ops(const char *expr, const char *expr_end, int index_base)
+tuple_update_check_ops(const char *expr, const char *expr_end,
+		       struct tuple_dictionary *dict, int index_base)
 {
 	struct tuple_update update;
 	update_init(&update, index_base);
-	return update_read_ops(&update, expr, expr_end, 0);
+	return update_read_ops(&update, expr, expr_end, dict, 0);
 }
 
 const char *
 tuple_update_execute(const char *expr,const char *expr_end,
 		     const char *old_data, const char *old_data_end,
-		     uint32_t *p_tuple_len, int index_base,
-		     uint64_t *column_mask)
+		     struct tuple_dictionary *dict, uint32_t *p_tuple_len,
+		     int index_base, uint64_t *column_mask)
 {
 	struct tuple_update update;
 	update_init(&update, index_base);
 	uint32_t field_count = mp_decode_array(&old_data);
 
-	if (update_read_ops(&update, expr, expr_end, field_count) != 0)
+	if (update_read_ops(&update, expr, expr_end, dict, field_count) != 0)
 		return NULL;
 	if (update_do_ops(&update, old_data, old_data_end, field_count))
 		return NULL;
@@ -1214,14 +1248,14 @@ tuple_update_execute(const char *expr,const char *expr_end,
 const char *
 tuple_upsert_execute(const char *expr,const char *expr_end,
 		     const char *old_data, const char *old_data_end,
-		     uint32_t *p_tuple_len, int index_base, bool suppress_error,
-		     uint64_t *column_mask)
+		     struct tuple_dictionary *dict, uint32_t *p_tuple_len,
+		     int index_base, bool suppress_error, uint64_t *column_mask)
 {
 	struct tuple_update update;
 	update_init(&update, index_base);
 	uint32_t field_count = mp_decode_array(&old_data);
 
-	if (update_read_ops(&update, expr, expr_end, field_count) != 0)
+	if (update_read_ops(&update, expr, expr_end, dict, field_count) != 0)
 		return NULL;
 	if (upsert_do_ops(&update, old_data, old_data_end, field_count,
 			  suppress_error))
@@ -1235,14 +1269,16 @@ tuple_upsert_execute(const char *expr,const char *expr_end,
 const char *
 tuple_upsert_squash(const char *expr1, const char *expr1_end,
 		    const char *expr2, const char *expr2_end,
-		    size_t *result_size, int index_base)
+		    struct tuple_dictionary *dict, size_t *result_size,
+		    int index_base)
 {
 	const char *expr[2] = {expr1, expr2};
 	const char *expr_end[2] = {expr1_end, expr2_end};
 	struct tuple_update update[2];
 	for (int j = 0; j < 2; j++) {
 		update_init(&update[j], index_base);
-		if (update_read_ops(&update[j], expr[j], expr_end[j], 0))
+		if (update_read_ops(&update[j], expr[j], expr_end[j],
+				    dict, 0) != 0)
 			return NULL;
 		mp_decode_array(&expr[j]);
 		int32_t prev_field_no = index_base - 1;
diff --git a/src/box/tuple_update.h b/src/box/tuple_update.h
index 37faa1918..b6210dd38 100644
--- a/src/box/tuple_update.h
+++ b/src/box/tuple_update.h
@@ -44,19 +44,23 @@ enum {
 	BOX_UPDATE_OP_CNT_MAX = 4000,
 };
 
+struct tuple_dictionary;
+
 int
-tuple_update_check_ops(const char *expr, const char *expr_end, int index_base);
+tuple_update_check_ops(const char *expr, const char *expr_end,
+		       struct tuple_dictionary *dict, int index_base);
 
 const char *
 tuple_update_execute(const char *expr,const char *expr_end,
 		     const char *old_data, const char *old_data_end,
-		     uint32_t *p_new_size, int index_base,
-		     uint64_t *column_mask);
+		     struct tuple_dictionary *dict, uint32_t *p_new_size,
+		     int index_base, uint64_t *column_mask);
 
 const char *
 tuple_upsert_execute(const char *expr, const char *expr_end,
 		     const char *old_data, const char *old_data_end,
-		     uint32_t *p_new_size, int index_base, bool suppress_error,
+		     struct tuple_dictionary *dict, uint32_t *p_new_size,
+		     int index_base, bool suppress_error,
 		     uint64_t *column_mask);
 
 /**
@@ -72,7 +76,8 @@ tuple_upsert_execute(const char *expr, const char *expr_end,
 const char *
 tuple_upsert_squash(const char *expr1, const char *expr1_end,
 		    const char *expr2, const char *expr2_end,
-		    size_t *result_size, int index_base);
+		    struct tuple_dictionary *dict, size_t *result_size,
+		    int index_base);
 
 #if defined(__cplusplus)
 } /* extern "C" */
diff --git a/src/box/vinyl.c b/src/box/vinyl.c
index d82cae766..3be999caf 100644
--- a/src/box/vinyl.c
+++ b/src/box/vinyl.c
@@ -1938,7 +1938,8 @@ vy_update(struct vy_env *env, struct vy_tx *tx, struct txn_stmt *stmt,
 	const char *old_tuple = tuple_data_range(stmt->old_tuple, &old_size);
 	const char *old_tuple_end = old_tuple + old_size;
 	new_tuple = tuple_update_execute(request->tuple, request->tuple_end,
-					 old_tuple, old_tuple_end, &new_size,
+					 old_tuple, old_tuple_end,
+					 pk->mem_format->dict, &new_size,
 					 request->index_base, &column_mask);
 	if (new_tuple == NULL)
 		return -1;
@@ -2117,7 +2118,8 @@ vy_upsert(struct vy_env *env, struct vy_tx *tx, struct txn_stmt *stmt,
 		return 0;
 	/* Check update operations. */
 	if (tuple_update_check_ops(request->ops, request->ops_end,
-				   request->index_base)) {
+				   pk->mem_format->dict,
+				   request->index_base) != 0) {
 		return -1;
 	}
 	if (request->index_base != 0) {
@@ -2175,7 +2177,8 @@ vy_upsert(struct vy_env *env, struct vy_tx *tx, struct txn_stmt *stmt,
 
 	/* Apply upsert operations to the old tuple. */
 	new_tuple = tuple_upsert_execute(ops, ops_end, old_tuple, old_tuple_end,
-					 &new_size, 0, false, &column_mask);
+					 pk->mem_format->dict, &new_size, 0,
+					 false, &column_mask);
 	if (new_tuple == NULL)
 		return -1;
 	/*
diff --git a/src/box/vy_upsert.c b/src/box/vy_upsert.c
index 1c55f86c2..817d29cfd 100644
--- a/src/box/vy_upsert.c
+++ b/src/box/vy_upsert.c
@@ -58,7 +58,7 @@ vy_upsert_try_to_squash(struct tuple_format *format,
 	size_t squashed_size;
 	const char *squashed =
 		tuple_upsert_squash(old_serie, old_serie_end,
-				    new_serie, new_serie_end,
+				    new_serie, new_serie_end, format->dict,
 				    &squashed_size, 0);
 	if (squashed == NULL)
 		return 0;
@@ -119,8 +119,8 @@ vy_apply_upsert(struct tuple *new_stmt, struct tuple *old_stmt,
 	uint8_t old_type = vy_stmt_type(old_stmt);
 	uint64_t column_mask = COLUMN_MASK_FULL;
 	result_mp = tuple_upsert_execute(new_ops, new_ops_end, result_mp,
-					 result_mp_end, &mp_size, 0,
-					 suppress_error, &column_mask);
+					 result_mp_end, format->dict, &mp_size,
+					 0, suppress_error, &column_mask);
 	result_mp_end = result_mp + mp_size;
 	if (old_type != IPROTO_UPSERT) {
 		assert(old_type == IPROTO_INSERT ||
diff --git a/test/box/misc.result b/test/box/misc.result
index dab8549bd..02482ef6d 100644
--- a/test/box/misc.result
+++ b/test/box/misc.result
@@ -482,13 +482,14 @@ t;
   150: box.error.CANT_CREATE_COLLATION
   151: box.error.WRONG_COLLATION_OPTIONS
   152: box.error.NULLABLE_PRIMARY
-  153: box.error.NO_SUCH_FIELD_NAME
+  153: box.error.NO_SUCH_FIELD_NAME_IN_SPACE
   154: box.error.TRANSACTION_YIELD
   155: box.error.NO_SUCH_GROUP
   156: box.error.SQL_BIND_VALUE
   157: box.error.SQL_BIND_TYPE
   158: box.error.SQL_BIND_PARAMETER_MAX
   159: box.error.SQL_EXECUTE
+  160: box.error.NO_SUCH_FIELD_NAME
   161: box.error.SQL_BIND_NOT_FOUND
   162: box.error.ACTION_MISMATCH
   163: box.error.VIEW_MISSING_SQL
diff --git a/test/box/update.result b/test/box/update.result
index a3f731b55..f5de3bc09 100644
--- a/test/box/update.result
+++ b/test/box/update.result
@@ -794,7 +794,7 @@ s:update({0}, {{'+', 0}})
 ...
 s:update({0}, {{'+', '+', '+'}})
 ---
-- error: Illegal parameters, field id must be a number
+- error: Field '+' was not found in the tuple
 ...
 s:update({0}, {{0, 0, 0}})
 ---
diff --git a/test/engine/update.result b/test/engine/update.result
index 69293e468..f181924f3 100644
--- a/test/engine/update.result
+++ b/test/engine/update.result
@@ -788,3 +788,70 @@ sk:select()
 s:drop()
 ---
 ...
+--
+-- gh-1261: tuple update by JSON.
+-- At first, test tuple update by field names.
+--
+format = {}
+---
+...
+format[1] = {'field1', 'unsigned'}
+---
+...
+format[2] = {'field2', 'array'}
+---
+...
+format[3] = {'field3', 'map'}
+---
+...
+format[4] = {'field4', 'string'}
+---
+...
+format[5] = {'field5', 'any'}
+---
+...
+format[6] = {'field6', 'integer'}
+---
+...
+format[7] = {'[1]', 'unsigned'}
+---
+...
+s = box.schema.create_space('test', {format = format})
+---
+...
+pk = s:create_index('pk')
+---
+...
+t = s:replace{1, {10, 11, 12}, {a = 20, b = 21, c = 22}, 'abcdefgh', true, -100, 200}
+---
+...
+t:update({{'+', 'field1', 1}})
+---
+- [2, [10, 11, 12], {'b': 21, 'a': 20, 'c': 22}, 'abcdefgh', true, -100, 200]
+...
+t:update({{'=', 'field2', {13, 14, 15}}})
+---
+- [1, [13, 14, 15], {'b': 21, 'a': 20, 'c': 22}, 'abcdefgh', true, -100, 200]
+...
+t:update({{':', 'field4', 3, 3, 'bbccdd'}, {'+', 'field6', 50}, {'!', 7, 300}})
+---
+- [1, [10, 11, 12], {'b': 21, 'a': 20, 'c': 22}, 'abbbccddfgh', true, -50, 300, 200]
+...
+-- Any path is interpreted as a field name first. And only then
+-- as JSON.
+t:update({{'+', '[1]', 50}})
+---
+- [1, [10, 11, 12], {'b': 21, 'a': 20, 'c': 22}, 'abcdefgh', true, -100, 250]
+...
+-- JSON paths are not allowed yet.
+t:update({{'=', 'field2[1]', 13}})
+---
+- error: Field 'field2[1]' was not found in the tuple
+...
+s:update({1}, {{'=', 'field3', {d = 30, e = 31, f = 32}}})
+---
+- [1, [10, 11, 12], {'d': 30, 'f': 32, 'e': 31}, 'abcdefgh', true, -100, 200]
+...
+s:drop()
+---
+...
diff --git a/test/engine/update.test.lua b/test/engine/update.test.lua
index 51263f577..4ca2589e4 100644
--- a/test/engine/update.test.lua
+++ b/test/engine/update.test.lua
@@ -134,3 +134,31 @@ box.begin() s:update(1, {{'=', 2, 2}}) s:update(1, {{'=', 3, 2}}) box.commit()
 pk:select()
 sk:select()
 s:drop()
+
+--
+-- gh-1261: tuple update by JSON.
+-- At first, test tuple update by field names.
+--
+format = {}
+format[1] = {'field1', 'unsigned'}
+format[2] = {'field2', 'array'}
+format[3] = {'field3', 'map'}
+format[4] = {'field4', 'string'}
+format[5] = {'field5', 'any'}
+format[6] = {'field6', 'integer'}
+format[7] = {'[1]', 'unsigned'}
+s = box.schema.create_space('test', {format = format})
+pk = s:create_index('pk')
+t = s:replace{1, {10, 11, 12}, {a = 20, b = 21, c = 22}, 'abcdefgh', true, -100, 200}
+t:update({{'+', 'field1', 1}})
+t:update({{'=', 'field2', {13, 14, 15}}})
+t:update({{':', 'field4', 3, 3, 'bbccdd'}, {'+', 'field6', 50}, {'!', 7, 300}})
+-- Any path is interpreted as a field name first. And only then
+-- as JSON.
+t:update({{'+', '[1]', 50}})
+-- JSON paths are not allowed yet.
+t:update({{'=', 'field2[1]', 13}})
+
+s:update({1}, {{'=', 'field3', {d = 30, e = 31, f = 32}}})
+
+s:drop()
diff --git a/test/engine/upsert.result b/test/engine/upsert.result
index b35545588..47da307fa 100644
--- a/test/engine/upsert.result
+++ b/test/engine/upsert.result
@@ -2108,7 +2108,7 @@ test(t, {{'&', 3, 1}, {'&', 2, 1}, {'&', 4, 1}}, {{1, '1', 1, '1'}})
 ...
 test(t, {{'&', 'a', 3}, {'+', 3, 3}}, {{1, '1', 1, '1'}})
 ---
-- error: Illegal parameters, field id must be a number
+- error: Field 'a' was not found in the tuple
 ...
 test(t, {{'+', 3, 3}, {'&', 3, 'a'}}, {{1, '1', 1, '1'}})
 ---
diff --git a/test/unit/column_mask.c b/test/unit/column_mask.c
index 3ffd351ac..5ee8b7332 100644
--- a/test/unit/column_mask.c
+++ b/test/unit/column_mask.c
@@ -129,8 +129,9 @@ check_update_result(const struct tuple_template *original,
 	uint64_t column_mask;
 	struct region *region = &fiber()->gc;
 	const char *actual =
-		tuple_update_execute(ops, ops_end, old, old_end, &actual_len, 1,
-				     &column_mask);
+		tuple_update_execute(ops, ops_end, old, old_end,
+				     box_tuple_format_default()->dict,
+				     &actual_len, 1, &column_mask);
 	fail_if(actual == NULL);
 	is((int32_t)actual_len, new_end - new, "check result length");
 	is(memcmp(actual, new, actual_len), 0, "tuple update is correct");
-- 
2.20.1 (Apple Git-117)

  parent reply	other threads:[~2019-07-13 22:11 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-07-13 22:11 [PATCH 0/5] JSON update preparation Vladislav Shpilevoy
2019-07-13 22:11 ` [PATCH 1/5] tuple: remove alloc and alloc_ctx args from update() Vladislav Shpilevoy
2019-07-13 22:11 ` [PATCH 2/5] rope: make rope library macro template Vladislav Shpilevoy
2019-07-13 22:11 ` [PATCH 3/5] tuple: relax struct tuple_update dependency on rope Vladislav Shpilevoy
2019-07-13 22:11 ` [PATCH 4/5] int96: add a missing header Vladislav Shpilevoy
2019-07-13 22:11 ` Vladislav Shpilevoy [this message]
2019-07-31 12:15 ` [PATCH 0/5] JSON update preparation Vladimir Davydov
2019-07-31 20:36   ` [tarantool-patches] " Vladislav Shpilevoy
2019-08-22 21:18 ` Vladislav Shpilevoy
2019-08-27 22:08 ` [tarantool-patches] " Kirill Yukhin

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=8ffb22759c7c9d6d60b74b1bf280d16344afeefc.1563054879.git.v.shpilevoy@tarantool.org \
    --to=v.shpilevoy@tarantool.org \
    --cc=tarantool-patches@freelists.org \
    --cc=vdavydov.dev@gmail.com \
    --subject='Re: [PATCH 5/5] tuple: implement update by field name' \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox