From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from localhost (localhost [127.0.0.1]) by turing.freelists.org (Avenir Technologies Mail Multiplex) with ESMTP id DC04A25219 for ; Mon, 12 Aug 2019 19:02:58 -0400 (EDT) Received: from turing.freelists.org ([127.0.0.1]) by localhost (turing.freelists.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id XZK65h-eFzu9 for ; Mon, 12 Aug 2019 19:02:58 -0400 (EDT) Received: from smtp55.i.mail.ru (smtp55.i.mail.ru [217.69.128.35]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by turing.freelists.org (Avenir Technologies Mail Multiplex) with ESMTPS id 6F84A24FC8 for ; Mon, 12 Aug 2019 19:02:58 -0400 (EDT) Received: by smtp55.i.mail.ru with esmtpa (envelope-from ) id 1hxJL5-0007dc-O2 for tarantool-patches@freelists.org; Tue, 13 Aug 2019 02:02:57 +0300 From: Vladislav Shpilevoy Subject: [tarantool-patches] [PATCH 05/13] tuple: implement update by field name Date: Tue, 13 Aug 2019 01:05:15 +0200 Message-Id: In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Sender: tarantool-patches-bounce@freelists.org Errors-to: tarantool-patches-bounce@freelists.org Reply-To: tarantool-patches@freelists.org List-Help: List-Unsubscribe: List-software: Ecartis version 1.0.0 List-Id: tarantool-patches List-Subscribe: List-Owner: List-post: List-Archive: To: tarantool-patches@freelists.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 #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)