From: Vladislav Shpilevoy <v.shpilevoy@tarantool.org>
To: tarantool-patches@freelists.org
Subject: [tarantool-patches] [PATCH 05/13] tuple: implement update by field name
Date: Tue, 13 Aug 2019 01:05:15 +0200 [thread overview]
Message-ID: <bc5dfa0cab86ec88c8124b09e28f3c8766793e82.1565649886.git.v.shpilevoy@tarantool.org> (raw)
In-Reply-To: <cover.1565649886.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 817275b97..dd55bd42a 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 space '%s' can not contain nullable parts") \
- /*153 */_(ER_NO_SUCH_FIELD_NAME, "Field '%s' was not found in space '%s' format") \
+ /*153 */_(ER_NO_SUCH_FIELD_NAME_IN_SPACE, "Field '%s' was not found in 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 d63e8a5ca..29956c059 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 fffe03c41..b9a830e78 100644
--- a/src/box/space.c
+++ b/src/box/space.c
@@ -397,7 +397,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;
@@ -420,6 +421,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;
@@ -428,8 +430,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 1c956567f..42b839166 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 7f9358c05..2d7ebf8cd 100644
--- a/src/box/sql/update.c
+++ b/src/box/sql/update.c
@@ -167,7 +167,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 beb290a9f..bf4ea711d 100644
--- a/src/box/tuple.c
+++ b/src/box/tuple.c
@@ -707,15 +707,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);
@@ -729,16 +729,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 4c5c08852..5fdfe0aa7 100644
--- a/src/box/vinyl.c
+++ b/src/box/vinyl.c
@@ -1943,7 +1943,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;
@@ -2122,7 +2123,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) {
@@ -2180,7 +2182,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 7a15dabf0..f45e0514c 100644
--- a/test/box/misc.result
+++ b/test/box/misc.result
@@ -483,13 +483,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)
next prev parent reply other threads:[~2019-08-12 23:02 UTC|newest]
Thread overview: 15+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-08-12 23:05 [tarantool-patches] [PATCH 00/13] JSON updates Vladislav Shpilevoy
2019-08-12 23:05 ` [tarantool-patches] [PATCH 01/13] tuple: remove alloc and alloc_ctx args from update() Vladislav Shpilevoy
2019-08-12 23:05 ` [tarantool-patches] [PATCH 10/13] tuple: enable JSON bar updates Vladislav Shpilevoy
2019-08-12 23:05 ` [tarantool-patches] [PATCH 11/13] tuple: make update operation tokens consumable Vladislav Shpilevoy
2019-08-12 23:05 ` [tarantool-patches] [PATCH 12/13] tuple: JSON updates support intersection by arrays Vladislav Shpilevoy
2019-08-12 23:05 ` [tarantool-patches] [PATCH 13/13] tuple: JSON updates support intersection by maps Vladislav Shpilevoy
2019-08-12 23:05 ` [tarantool-patches] [PATCH 02/13] rope: make rope library macro template Vladislav Shpilevoy
2019-08-12 23:05 ` [tarantool-patches] [PATCH 03/13] tuple: relax struct tuple_update dependency on rope Vladislav Shpilevoy
2019-08-12 23:05 ` [tarantool-patches] [PATCH 04/13] int96: add a missing header Vladislav Shpilevoy
2019-08-12 23:05 ` Vladislav Shpilevoy [this message]
2019-08-12 23:05 ` [tarantool-patches] [PATCH 06/13] tuple: expose JSON go_to_key and go_to_index functions Vladislav Shpilevoy
2019-08-12 23:05 ` [tarantool-patches] [PATCH 07/13] tuple: rework updates to improve code extendibility Vladislav Shpilevoy
2019-08-12 23:05 ` [tarantool-patches] [PATCH 08/13] json: lexer_eof and token_cmp helper functions Vladislav Shpilevoy
2019-08-12 23:05 ` [tarantool-patches] [PATCH 09/13] tuple: account the whole array in field.data and size Vladislav Shpilevoy
2019-08-20 18:49 ` [tarantool-patches] Re: [PATCH 00/13] JSON updates Vladislav Shpilevoy
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=bc5dfa0cab86ec88c8124b09e28f3c8766793e82.1565649886.git.v.shpilevoy@tarantool.org \
--to=v.shpilevoy@tarantool.org \
--cc=tarantool-patches@freelists.org \
--subject='Re: [tarantool-patches] [PATCH 05/13] 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