diff --git a/src/box/iproto_constants.c b/src/box/iproto_constants.c index 329331a92a6b4fcdb4f3ec19d4cd8f7e563209da..8cdf759ea2d07d42bea886657e42c7859590f834 100644 --- a/src/box/iproto_constants.c +++ b/src/box/iproto_constants.c @@ -83,6 +83,7 @@ const unsigned char iproto_key_type[IPROTO_KEY_MAX] = /* 0x25 */ MP_STR, /* IPROTO_CLUSTER_UUID */ /* 0x26 */ MP_MAP, /* IPROTO_VCLOCK */ /* 0x27 */ MP_STR, /* IPROTO_EXPR */ + /* 0x28 */ MP_ARRAY, /* IPROTO_OPS */ /* }}} */ }; @@ -97,11 +98,11 @@ const char *iproto_type_strs[] = "CALL", "AUTH", "EVAL", - "UPSERT" + "UPSERT", }; #define bit(c) (1ULL<<IPROTO_##c) -const uint64_t iproto_body_key_map[IPROTO_EVAL + 1] = { +const uint64_t iproto_body_key_map[IPROTO_UPSERT + 1] = { 0, /* unused */ bit(SPACE_ID) | bit(LIMIT) | bit(KEY), /* SELECT */ bit(SPACE_ID) | bit(TUPLE), /* INSERT */ @@ -109,8 +110,9 @@ const uint64_t iproto_body_key_map[IPROTO_EVAL + 1] = { bit(SPACE_ID) | bit(KEY) | bit(TUPLE), /* UPDATE */ bit(SPACE_ID) | bit(KEY), /* DELETE */ bit(FUNCTION_NAME) | bit(TUPLE), /* CALL */ - bit(USER_NAME) | bit(TUPLE), /* AUTH */ - bit(EXPR) | bit(TUPLE), /* EVAL */ + bit(USER_NAME)| bit(TUPLE), /* AUTH */ + bit(EXPR) | bit(TUPLE), /* EVAL */ + bit(SPACE_ID) | bit(KEY) | bit(OPS) | bit(TUPLE), /* UPSERT */ }; #undef bit @@ -155,5 +157,6 @@ const char *iproto_key_strs[IPROTO_KEY_MAX] = { "cluster UUID", /* 0x25 */ "vector clock", /* 0x26 */ "expression", /* 0x27 */ + "operations", /* 0x28 */ }; diff --git a/src/box/iproto_constants.h b/src/box/iproto_constants.h index a4ea276a19608e748db2245524cc0a31635090b6..f87752ec660d39eec741e2dbc5b5b352c6d02781 100644 --- a/src/box/iproto_constants.h +++ b/src/box/iproto_constants.h @@ -69,7 +69,7 @@ enum iproto_key { IPROTO_CLUSTER_UUID = 0x25, IPROTO_VCLOCK = 0x26, IPROTO_EXPR = 0x27, /* EVAL */ - IPROTO_DEF_TUPLE = 0x28, + IPROTO_OPS = 0x28, /* UPSERT but not UPDATE ops, because of legacy */ /* Leave a gap between request keys and response keys */ IPROTO_DATA = 0x30, IPROTO_ERROR = 0x31, @@ -83,7 +83,7 @@ enum iproto_key { #define IPROTO_BODY_BMAP (bit(SPACE_ID) | bit(INDEX_ID) | bit(LIMIT) |\ bit(OFFSET) | bit(ITERATOR) | bit(INDEX_BASE) |\ bit(KEY) | bit(TUPLE) | bit(FUNCTION_NAME) | \ - bit(USER_NAME) | bit(EXPR)) + bit(USER_NAME) | bit(EXPR) | bit(OPS)) static inline bool xrow_header_has_key(const char *pos, const char *end) @@ -121,14 +121,11 @@ enum iproto_type { IPROTO_REPLACE = 3, IPROTO_UPDATE = 4, IPROTO_DELETE = 5, - IPROTO_TYPE_DML_MAX = IPROTO_DELETE + 1, IPROTO_CALL = 6, IPROTO_AUTH = 7, IPROTO_EVAL = 8, IPROTO_UPSERT = 9, - IPROTO_TYPE_EXT_DML_MAX = IPROTO_UPSERT + 1, IPROTO_TYPE_STAT_MAX = IPROTO_UPSERT + 1, - /* new range of dml command codes */ /* admin command codes */ IPROTO_PING = 64, IPROTO_JOIN = 65, @@ -161,8 +158,8 @@ iproto_type_is_select(uint32_t type) static inline bool iproto_type_is_dml(uint32_t type) { - return (type >= IPROTO_SELECT && type < IPROTO_TYPE_DML_MAX) || - (type == IPROTO_UPSERT && type < IPROTO_TYPE_EXT_DML_MAX); + return (type >= IPROTO_SELECT && type <= IPROTO_DELETE) || + type == IPROTO_UPSERT; } static inline bool diff --git a/src/box/lua/call.cc b/src/box/lua/call.cc index c43e35b36400baf72eb9c0d7dc60c1a9c2a74f0e..14366f043d677d48a96b7f8c23a91de03f443ca5 100644 --- a/src/box/lua/call.cc +++ b/src/box/lua/call.cc @@ -189,7 +189,7 @@ lbox_process(lua_State *L) void lbox_request_create(struct request *request, struct lua_State *L, enum iproto_type type, - int key, int tuple, int default_tuple) + int key, int tuple, int ops) { request_create(request, type); request->space_id = lua_tointeger(L, 1); @@ -219,15 +219,14 @@ lbox_request_create(struct request *request, request->tuple = (char *) region_join(gc, tuple_len); request->tuple_end = request->tuple + tuple_len; } - if (default_tuple > 0) { + if (ops > 0) { size_t used = region_used(gc); mpstream_reset(&stream); - luamp_encode_tuple(L, luaL_msgpack_default, &stream, - default_tuple); + luamp_encode_tuple(L, luaL_msgpack_default, &stream, ops); mpstream_flush(&stream); - size_t tuple_len = region_used(gc) - used; - request->default_tuple = (char *) region_join(gc, tuple_len); - request->default_tuple_end = request->default_tuple + tuple_len; + size_t ops_len = region_used(gc) - used; + request->ops = (char *) region_join(gc, ops_len); + request->ops_end = request->ops + ops_len; } } @@ -313,6 +312,7 @@ lbox_update(lua_State *L) struct request request; struct port_lua port; + /** Legacy: in case of update, ops are passed in in request tuple */ lbox_request_create(&request, L, IPROTO_UPDATE, 3, 4, -1); request.index_base = 1; /* field ids are one-indexed */ port_lua_create(&port, L); @@ -327,11 +327,11 @@ lbox_upsert(lua_State *L) if (lua_gettop(L) != 5 || !lua_isnumber(L, 1) || !lua_isnumber(L, 2) || lua_type(L, 3) != LUA_TTABLE || lua_type(L, 4) != LUA_TTABLE || lua_type(L, 5) != LUA_TTABLE) - return luaL_error(L, "Usage space:upsert(key, ops, def_tuple)"); + return luaL_error(L, "Usage space:upsert(key, ops, tuple)"); struct request request; struct port_lua port; - lbox_request_create(&request, L, IPROTO_UPSERT, 3, 4, 5); + lbox_request_create(&request, L, IPROTO_UPSERT, 3, 5, 4); request.index_base = 1; /* field ids are one-indexed */ port_lua_create(&port, L); /* Ignore index_id for now */ diff --git a/src/box/lua/tuple.cc b/src/box/lua/tuple.cc index 323cc18673d264dcc53b8ff870e2aedccdd29bad..631dd265dfce085368d48b6583d0826dad8f0119 100644 --- a/src/box/lua/tuple.cc +++ b/src/box/lua/tuple.cc @@ -367,4 +367,18 @@ boxffi_tuple_update(struct tuple *tuple, const char *expr, const char *expr_end) } catch (ClientError *e) { return NULL; } + +} +struct tuple * +boxffi_tuple_upsert(struct tuple *tuple, const char *expr, const char *expr_end) +{ + RegionGuard region_guard(&fiber()->gc); + try { + struct tuple *new_tuple = tuple_upsert(tuple_format_ber, + region_alloc_cb, &fiber()->gc, tuple, expr, expr_end, 1); + tuple_ref(new_tuple); /* must not throw in this case */ + return new_tuple; + } catch (ClientError *e) { + return NULL; + } } diff --git a/src/box/lua/tuple.lua b/src/box/lua/tuple.lua index 7a87974f67f41f159bc32bbd2b203d9f0083d482..7ca694feff8fea1c518ff0e05231124680e901e0 100644 --- a/src/box/lua/tuple.lua +++ b/src/box/lua/tuple.lua @@ -42,7 +42,12 @@ void tuple_to_buf(struct tuple *tuple, char *buf); struct tuple * -boxffi_tuple_update(struct tuple *tuple, const char *expr, const char *expr_end); +boxffi_tuple_update(struct tuple *tuple, const char *expr, + const char *expr_end); + +struct tuple * +boxffi_tuple_upsert(struct tuple *tuple, const char *expr, + const char *expr_end); ]]) local builtin = ffi.C @@ -167,6 +172,18 @@ local function tuple_update(tuple, expr) return tuple_bless(tuple) end +local function tuple_upsert(tuple, expr) + if type(expr) ~= 'table' then + error("Usage: tuple:upsert({ { op, field, arg}+ })") + end + local pexpr, pexpr_end = msgpackffi.encode_tuple(expr) + local tuple = builtin.boxffi_tuple_upsert(tuple, pexpr, pexpr_end) + if tuple == nil then + return box.error() + end + return tuple_bless(tuple) +end + -- Set encode hooks for msgpackffi local function tuple_to_msgpack(buf, tuple) local data = buf:alloc(tuple._bsize) @@ -189,6 +206,7 @@ local methods = { ["unpack"] = tuple_unpack; ["totable"] = tuple_totable; ["update"] = tuple_update; + ["upsert"] = tuple_upsert; ["bsize"] = function(tuple) return tonumber(tuple._bsize) end; diff --git a/src/box/request.cc b/src/box/request.cc index b127f63185abad237b99d5ecfda8543b16b05ad6..2e7295b2a2641a4a87f9b1993436721f67fad059 100644 --- a/src/box/request.cc +++ b/src/box/request.cc @@ -93,7 +93,7 @@ execute_update(struct request *request, struct port *port) } TupleGuard old_guard(old_tuple); - /* Update the tuple. */ + /* Update the tuple; legacy, request ops are in request->tuple */ struct tuple *new_tuple = tuple_update(space->format, region_alloc_cb, &fiber()->gc, @@ -115,7 +115,7 @@ execute_update(struct request *request, struct port *port) } static void -execute_upsert(struct request *request, struct port *port) +execute_upsert(struct request *request, struct port * /* port */) { struct space *space = space_cache_find(request->space_id); struct txn *txn = txn_begin_stmt(request, space); @@ -128,39 +128,33 @@ execute_upsert(struct request *request, struct port *port) primary_key_validate(pk->key_def, key, part_count); struct tuple *old_tuple = pk->findByKey(key, part_count); - TupleGuardSafe old_guard(old_tuple); + if (old_tuple == NULL) { + struct tuple *new_tuple = tuple_new(space->format, + request->tuple, + request->tuple_end); + TupleGuard guard(new_tuple); + space_validate_tuple(space, new_tuple); + + txn_replace(txn, space, NULL, new_tuple, DUP_INSERT); + } else { + TupleGuard old_guard(old_tuple); + + /* Update the tuple. */ + struct tuple *new_tuple = + tuple_upsert(space->format, region_alloc_cb, + &fiber()->gc, old_tuple, + request->ops, request->ops_end, + request->index_base); + TupleGuard guard(new_tuple); - /* Update the tuple. */ - struct tuple *new_tuple = - tuple_upsert(space->format, region_alloc_cb, - &fiber()->gc, old_tuple, - request->default_tuple, request->default_tuple_end, - request->tuple, request->tuple_end, - request->index_base); - TupleGuard guard(new_tuple); - try { space_validate_tuple(space, new_tuple); - if (old_tuple && - !engine_auto_check_update(space->handler->engine->flags)) + if (!engine_auto_check_update(space->handler->engine->flags)) space_check_update(space, old_tuple, new_tuple); - txn_replace(txn, space, old_tuple, new_tuple, - old_tuple ? DUP_REPLACE : DUP_INSERT); - } catch (ClientError *e) { - say_error("The following error occured during UPSERT " - "operation:"); - e->log(); - txn_rollback_stmt(); - if (old_tuple) - port_add_tuple(port, old_tuple); - return; + txn_replace(txn, space, old_tuple, new_tuple, DUP_REPLACE); } + txn_commit_stmt(txn); - /* - * Adding result to port must be after possible WAL write. - * The reason is that any yield between port_add_tuple and port_eof - * calls could lead to sending not finished response to iproto socket. - */ - port_add_tuple(port, new_tuple); + /* Return nothing: upsert does not return data. */ } static void @@ -318,9 +312,11 @@ request_decode(struct request *request, const char *data, uint32_t len) case IPROTO_EXPR: request->key = value; request->key_end = data; - case IPROTO_DEF_TUPLE: - request->default_tuple = value; - request->default_tuple_end = data; + break; + case IPROTO_OPS: + request->ops = value; + request->ops_end = data; + break; default: break; } @@ -339,9 +335,10 @@ int request_encode(struct request *request, struct iovec *iov) { int iovcnt = 1; - const int HEADER_LEN_MAX = 32; + const int MAP_LEN_MAX = 40; uint32_t key_len = request->key_end - request->key; - uint32_t len = HEADER_LEN_MAX + key_len; + uint32_t ops_len = request->ops_end - request->ops; + uint32_t len = MAP_LEN_MAX + key_len; char *begin = (char *) region_alloc(&fiber()->gc, len); char *pos = begin + 1; /* skip 1 byte for MP_MAP */ int map_size = 0; @@ -355,21 +352,27 @@ request_encode(struct request *request, struct iovec *iov) pos = mp_encode_uint(pos, request->index_id); map_size++; } + if (request->index_base) { /* UPDATE/UPSERT */ + pos = mp_encode_uint(pos, IPROTO_INDEX_BASE); + pos = mp_encode_uint(pos, request->index_base); + map_size++; + } if (request->key) { pos = mp_encode_uint(pos, IPROTO_KEY); memcpy(pos, request->key, key_len); pos += key_len; map_size++; } - if (request->index_base) { /* only for UPDATE */ - pos = mp_encode_uint(pos, IPROTO_INDEX_BASE); - pos = mp_encode_uint(pos, request->index_base); + if (request->ops) { + pos = mp_encode_uint(pos, IPROTO_OPS); + memcpy(pos, request->ops, ops_len); + pos += ops_len; map_size++; } if (request->tuple) { pos = mp_encode_uint(pos, IPROTO_TUPLE); - iov[1].iov_base = (void *) request->tuple; - iov[1].iov_len = (request->tuple_end - request->tuple); + iov[iovcnt].iov_base = (void *) request->tuple; + iov[iovcnt].iov_len = (request->tuple_end - request->tuple); iovcnt++; map_size++; } diff --git a/src/box/request.h b/src/box/request.h index 16d6d81d2a84de8c02b178621a35951708414b90..82e3d477dd3ad92e0332882f191a0fd64523f745 100644 --- a/src/box/request.h +++ b/src/box/request.h @@ -55,13 +55,13 @@ struct request /** Search key or proc name. */ const char *key; const char *key_end; - /** Insert/replace tuple or proc argument or update operations. */ + /** Insert/replace/upsert tuple or proc argument or update operations. */ const char *tuple; const char *tuple_end; - /** Additional tuple of request, currently used by UPSERT */ - const char *default_tuple; - const char *default_tuple_end; - /** Base field offset for UPDATE, e.g. 0 for C and 1 for Lua. */ + /** Upsert operations. */ + const char *ops; + const char *ops_end; + /** Base field offset for UPDATE/UPSERT, e.g. 0 for C and 1 for Lua. */ int index_base; }; diff --git a/src/box/tuple.cc b/src/box/tuple.cc index 0589714b0fd4e1016aa50eca73e8dc4d36b0f52d..6fee7b7fbb394e22648612abd1179d62f9beaf9b 100644 --- a/src/box/tuple.cc +++ b/src/box/tuple.cc @@ -399,18 +399,9 @@ tuple_field_cstr(struct tuple *tuple, uint32_t i) } struct tuple * -tuple_update(struct tuple_format *format, - tuple_update_alloc_func f, void *alloc_ctx, - const struct tuple *old_tuple, const char *expr, - const char *expr_end, int field_base) +tuple_bless(struct tuple_format *format, + const char *new_data, size_t new_size) { - uint32_t new_size = 0; - const char *new_data = - tuple_update_execute(f, alloc_ctx, - expr, expr_end, old_tuple->data, - old_tuple->data + old_tuple->bsize, - &new_size, field_base); - /* Allocate a new tuple. */ assert(mp_typeof(*new_data) == MP_ARRAY); struct tuple *new_tuple = tuple_new(format, new_data, @@ -425,39 +416,37 @@ tuple_update(struct tuple_format *format, return new_tuple; } +struct tuple * +tuple_update(struct tuple_format *format, + tuple_update_alloc_func f, void *alloc_ctx, + const struct tuple *old_tuple, const char *expr, + const char *expr_end, int field_base) +{ + uint32_t new_size = 0; + const char *new_data = + tuple_update_execute(f, alloc_ctx, + expr, expr_end, old_tuple->data, + old_tuple->data + old_tuple->bsize, + &new_size, field_base); + + return tuple_bless(format, new_data, new_size); +} + struct tuple * tuple_upsert(struct tuple_format *format, void *(*region_alloc)(void *, size_t), void *alloc_ctx, const struct tuple *old_tuple, - const char *plan_b_tuple_data, const char *plan_b_tuple_data_end, const char *expr, const char *expr_end, int field_base) { uint32_t new_size = 0; - const char *old_data, *old_data_end; - if (old_tuple) { - old_data = old_tuple->data; - old_data_end = old_tuple->data + old_tuple->bsize; - } else { - old_data = plan_b_tuple_data; - old_data_end = plan_b_tuple_data_end; - } + const char *new_data = tuple_upsert_execute(region_alloc, alloc_ctx, expr, expr_end, - old_data, old_data_end, + old_tuple->data, + old_tuple->data + old_tuple->bsize, &new_size, field_base); - /* Allocate a new tuple. */ - assert(mp_typeof(*new_data) == MP_ARRAY); - struct tuple *new_tuple = tuple_new(format, new_data, - new_data + new_size); - - try { - tuple_init_field_map(format, new_tuple, (uint32_t *)new_tuple); - } catch (Exception *e) { - tuple_delete(new_tuple); - throw; - } - return new_tuple; + return tuple_bless(format, new_data, new_size); } struct tuple * diff --git a/src/box/tuple.h b/src/box/tuple.h index 002eed68c101f1e2ed47261b3398603c18aa6e44..11c0fd6f6c22617500d283cc32ab45fe272bead4 100644 --- a/src/box/tuple.h +++ b/src/box/tuple.h @@ -232,29 +232,12 @@ tuple_unref(struct tuple *tuple) /** Make tuple references exception-friendly in absence of @finally. */ struct TupleGuard { struct tuple *tuple; - TupleGuard(struct tuple *arg) : tuple(arg) { tuple_ref(tuple); } + TupleGuard(struct tuple *arg) :tuple(arg) { tuple_ref(tuple); } ~TupleGuard() { tuple_unref(tuple); } TupleGuard(const TupleGuard&) = delete; void operator=(const TupleGuard&) = delete; }; -/** Same as TupleGuard, but accepts normally NULL pointers */ -struct TupleGuardSafe { - struct tuple *tuple; - TupleGuardSafe(struct tuple *arg) : tuple(arg) - { - if (tuple) - tuple_ref(tuple); - } - ~TupleGuardSafe() - { - if (tuple) - tuple_unref(tuple); - } - TupleGuardSafe(const TupleGuardSafe&) = delete; - void operator=(const TupleGuardSafe&) = delete; -}; - /** * @brief Return a tuple format instance * @param tuple tuple @@ -483,7 +466,6 @@ struct tuple * tuple_upsert(struct tuple_format *new_format, void *(*region_alloc)(void *, size_t), void *alloc_ctx, const struct tuple *old_tuple, - const char *plan_b_tuple_data, const char *plan_b_tuple_data_end, const char *expr, const char *expr_end, int field_base); /** diff --git a/src/box/tuple_update.cc b/src/box/tuple_update.cc index 8c633d890d33d35cbcb39186dd15961100042db3..93cdb830a9f0a81d205078a718d4582e852644cd 100644 --- a/src/box/tuple_update.cc +++ b/src/box/tuple_update.cc @@ -928,9 +928,8 @@ upsert_do_ops(struct tuple_update *update) for (; op < ops_end; op++) { try { op->meta->do_op(update, op); - } catch(ClientError *e) { - say_error("The following error occured during UPSERT " - "operation:"); + } catch (ClientError *e) { + say_error("UPSERT operation failed:"); e->log(); } } diff --git a/src/box/tuple_update.h b/src/box/tuple_update.h index e847096275844fe2a0b25e26a4a1ded14d91f151..14deacfdcfed6910cdb335053c544c1b420da640 100644 --- a/src/box/tuple_update.h +++ b/src/box/tuple_update.h @@ -49,7 +49,7 @@ tuple_update_execute(tuple_update_alloc_func alloc, void *alloc_ctx, const char * tuple_upsert_execute(tuple_update_alloc_func alloc, void *alloc_ctx, - const char *expr,const char *expr_end, + const char *expr, const char *expr_end, const char *old_data, const char *old_data_end, uint32_t *p_new_size, int index_base); diff --git a/test/big/lua.result b/test/big/lua.result index 6bf25aeb88b3046b5d2eef726faaef23d6c40b21..07b817ce80f7cb9ca16f630bc232fcd92612d24b 100644 --- a/test/big/lua.result +++ b/test/big/lua.result @@ -734,7 +734,7 @@ t:find(2, '2') ... t:find(89, '2') --- -- error: '[string "-- tuple.lua (internal file)..."]:84: error: invalid key to ''next''' +- error: '[string "-- tuple.lua (internal file)..."]:89: error: invalid key to ''next''' ... t:findall(4, '3') --- diff --git a/test/box/tuple.result b/test/box/tuple.result index 2c9e15e1a8bbc5b4874ea79cae635ffaf8a53940..9bb0e2fde2bd31ded68c240e0e20be7471fa122c 100644 --- a/test/box/tuple.result +++ b/test/box/tuple.result @@ -305,12 +305,12 @@ t:unpack(2, 1) ... t:totable(0) --- -- error: '[string "-- tuple.lua (internal file)..."]:111: tuple.totable: invalid second +- error: '[string "-- tuple.lua (internal file)..."]:116: tuple.totable: invalid second argument' ... t:totable(1, 0) --- -- error: '[string "-- tuple.lua (internal file)..."]:120: tuple.totable: invalid third +- error: '[string "-- tuple.lua (internal file)..."]:125: tuple.totable: invalid third argument' ... -- @@ -435,15 +435,15 @@ t:next(3) ... t:next(4) --- -- error: '[string "-- tuple.lua (internal file)..."]:84: error: invalid key to ''next''' +- error: '[string "-- tuple.lua (internal file)..."]:89: error: invalid key to ''next''' ... t:next(-1) --- -- error: '[string "-- tuple.lua (internal file)..."]:84: error: invalid key to ''next''' +- error: '[string "-- tuple.lua (internal file)..."]:89: error: invalid key to ''next''' ... t:next("fdsaf") --- -- error: '[string "-- tuple.lua (internal file)..."]:67: error: invalid key to ''next''' +- error: '[string "-- tuple.lua (internal file)..."]:72: error: invalid key to ''next''' ... box.tuple.new({'x', 'y', 'z'}):next() --- @@ -455,7 +455,7 @@ t=space:insert{1953719668} ... t:next(1684234849) --- -- error: '[string "-- tuple.lua (internal file)..."]:84: error: invalid key to ''next''' +- error: '[string "-- tuple.lua (internal file)..."]:89: error: invalid key to ''next''' ... t:next(1) --- @@ -611,7 +611,7 @@ r = {} ... for _state, val in t:pairs(10) do table.insert(r, val) end --- -- error: '[string "-- tuple.lua (internal file)..."]:84: error: invalid key to ''next''' +- error: '[string "-- tuple.lua (internal file)..."]:89: error: invalid key to ''next''' ... r --- @@ -697,19 +697,19 @@ t:findall(1, 'xxxxx') ... t:find(100, 'a') --- -- error: '[string "-- tuple.lua (internal file)..."]:84: error: invalid key to ''next''' +- error: '[string "-- tuple.lua (internal file)..."]:89: error: invalid key to ''next''' ... t:findall(100, 'a') --- -- error: '[string "-- tuple.lua (internal file)..."]:84: error: invalid key to ''next''' +- error: '[string "-- tuple.lua (internal file)..."]:89: error: invalid key to ''next''' ... t:find(100, 'xxxxx') --- -- error: '[string "-- tuple.lua (internal file)..."]:84: error: invalid key to ''next''' +- error: '[string "-- tuple.lua (internal file)..."]:89: error: invalid key to ''next''' ... t:findall(100, 'xxxxx') --- -- error: '[string "-- tuple.lua (internal file)..."]:84: error: invalid key to ''next''' +- error: '[string "-- tuple.lua (internal file)..."]:89: error: invalid key to ''next''' ... --- -- Lua type coercion @@ -803,12 +803,12 @@ t = box.tuple.new({'a', 'b', 'c', 'd', 'e'}) ... t:update() --- -- error: '[string "-- tuple.lua (internal file)..."]:160: Usage: tuple:update({ { +- error: '[string "-- tuple.lua (internal file)..."]:165: Usage: tuple:update({ { op, field, arg}+ })' ... t:update(10) --- -- error: '[string "-- tuple.lua (internal file)..."]:160: Usage: tuple:update({ { +- error: '[string "-- tuple.lua (internal file)..."]:165: Usage: tuple:update({ { op, field, arg}+ })' ... t:update({}) diff --git a/test/box/update.result b/test/box/update.result index 576f6f57e9e7a09ed603868988a9a6b7d894509f..30be9e9fe3b35ab656a998ca4137f7394cc277a4 100644 --- a/test/box/update.result +++ b/test/box/update.result @@ -767,27 +767,40 @@ s:delete{0} ... s:upsert({0}, {{'+', 2, 2}}) -- wrong usage --- -- error: Usage space:upsert(key, ops, def_tuple) +- error: Usage space:upsert(key, ops, tuple) +... +s:select{0} +--- +- [] ... s:upsert({0}, {{'+', 2, 2}}, {0, 0}) --- -- [0, 2] +... +s:select{0} +--- +- - [0, 0] ... s:delete{0} --- -- [0, 2] +- [0, 0] ... s:upsert({0}, {{'+', 2, 2}}, {0, 0, 0}) --- -- [0, 2, 0] +... +s:select{0} +--- +- - [0, 0, 0] ... s:delete{0} --- -- [0, 2, 0] +- [0, 0, 0] ... s:upsert({0}, {{'+', 2, 2}}, {0}) --- -- [0] +... +s:select{0} +--- +- - [0] ... s:replace{0, 1, 2, 4} --- @@ -795,7 +808,10 @@ s:replace{0, 1, 2, 4} ... s:upsert({0}, {{'+', 2, 2}}, {0, 0, "you will not see it"}) --- -- [0, 3, 2, 4] +... +s:select{0} +--- +- - [0, 3, 2, 4] ... s:replace{0, -0x4000000000000000ll} --- @@ -803,7 +819,10 @@ s:replace{0, -0x4000000000000000ll} ... s:upsert({0}, {{'+', 2, -0x4000000000000001ll}}, {0}) -- overflow --- -- [0, -4611686018427387904] +... +s:select{0} +--- +- - [0, -4611686018427387904] ... s:replace{0, "thing"} --- @@ -811,7 +830,10 @@ s:replace{0, "thing"} ... s:upsert({0}, {{'+', 2, 2}}, {0, "nothing"}) --- -- [0, 'thing'] +... +s:select{0} +--- +- - [0, 'thing'] ... s:delete{0} --- @@ -819,7 +841,10 @@ s:delete{0} ... s:upsert({0}, {{'+', 2, 2}}, {0, "thing"}) --- -- [0, 'thing'] +... +s:select{0} +--- +- - [0, 'thing'] ... s:replace{0, 1, 2} --- @@ -827,15 +852,24 @@ s:replace{0, 1, 2} ... s:upsert({0}, {{'!', 42, 42}}, {0}) --- -- [0, 1, 2] +... +s:select{0} +--- +- - [0, 1, 2] ... s:upsert({0}, {{'#', 42, 42}}, {0}) --- -- [0, 1, 2] +... +s:select{0} +--- +- - [0, 1, 2] ... s:upsert({0}, {{'=', 42, 42}}, {0}) --- -- [0, 1, 2] +... +s:select{0} +--- +- - [0, 1, 2] ... s:replace{0, 1.5} --- @@ -843,7 +877,11 @@ s:replace{0, 1.5} ... s:upsert({0}, {{'|', 1, 255}}, {0}) --- -- [0, 1.5] +- error: Attempt to modify a tuple field which is part of index 'pk' in space 'tweedledum' +... +s:select{0} +--- +- - [0, 1.5] ... s:replace{0, 1.5} --- @@ -855,15 +893,24 @@ s:replace{0, 'something to splice'} ... s:upsert({0}, {{':', 2, 1, 4, 'no'}}, {0}) --- -- [0, 'nothing to splice'] +... +s:select{0} +--- +- - [0, 'nothing to splice'] ... s:upsert({0}, {{':', 2, 1, 2, 'every'}}, {0}) --- -- [0, 'everything to splice'] +... +s:select{0} +--- +- - [0, 'everything to splice'] ... s:upsert({0}, {{':', 2, -100, 2, 'every'}}, {0}) --- -- [0, 'everything to splice'] +... +s:select{0} +--- +- - [0, 'everything to splice'] ... s:drop() --- diff --git a/test/box/update.test.lua b/test/box/update.test.lua index e6373bb02c5f6f4229d2f9c082fe54190d95dbf6..f9ec4e781f93bfa3a2bebd1b182a435cbaa2791d 100644 --- a/test/box/update.test.lua +++ b/test/box/update.test.lua @@ -236,30 +236,45 @@ s:update({0}, {{'+', 2, -0x4000000000000001ll}}) -- overflow --UPSERT https://github.com/tarantool/tarantool/issues/905 s:delete{0} s:upsert({0}, {{'+', 2, 2}}) -- wrong usage +s:select{0} s:upsert({0}, {{'+', 2, 2}}, {0, 0}) +s:select{0} s:delete{0} s:upsert({0}, {{'+', 2, 2}}, {0, 0, 0}) +s:select{0} s:delete{0} s:upsert({0}, {{'+', 2, 2}}, {0}) +s:select{0} s:replace{0, 1, 2, 4} s:upsert({0}, {{'+', 2, 2}}, {0, 0, "you will not see it"}) +s:select{0} s:replace{0, -0x4000000000000000ll} s:upsert({0}, {{'+', 2, -0x4000000000000001ll}}, {0}) -- overflow +s:select{0} s:replace{0, "thing"} s:upsert({0}, {{'+', 2, 2}}, {0, "nothing"}) +s:select{0} s:delete{0} s:upsert({0}, {{'+', 2, 2}}, {0, "thing"}) +s:select{0} s:replace{0, 1, 2} s:upsert({0}, {{'!', 42, 42}}, {0}) +s:select{0} s:upsert({0}, {{'#', 42, 42}}, {0}) +s:select{0} s:upsert({0}, {{'=', 42, 42}}, {0}) +s:select{0} s:replace{0, 1.5} s:upsert({0}, {{'|', 1, 255}}, {0}) +s:select{0} s:replace{0, 1.5} s:replace{0, 'something to splice'} s:upsert({0}, {{':', 2, 1, 4, 'no'}}, {0}) +s:select{0} s:upsert({0}, {{':', 2, 1, 2, 'every'}}, {0}) +s:select{0} s:upsert({0}, {{':', 2, -100, 2, 'every'}}, {0}) +s:select{0} s:drop() diff --git a/test/replication/init_storage.result b/test/replication/init_storage.result index 852144da15a733000e78ea0860da4c034212b5f4..5660e969ce6421775e85ce701fb979764846fda5 100644 --- a/test/replication/init_storage.result +++ b/test/replication/init_storage.result @@ -54,6 +54,9 @@ replica test 2 (must be ok) for k = 10, 19 do box.space[42]:insert{k, k*k*k} end --- ... +for k = 20, 29 do box.space[42]:upsert({k}, {}, {k}) end +--- +... space = box.space.test --- ... diff --git a/test/replication/init_storage.test.py b/test/replication/init_storage.test.py index d1bb875a012861fced25181fba660e67cbb464f3..ab556986d9597b87e896ccbd9ad229ef934685d8 100644 --- a/test/replication/init_storage.test.py +++ b/test/replication/init_storage.test.py @@ -57,6 +57,7 @@ print '-------------------------------------------------------------' master.restart() master.admin('for k = 10, 19 do box.space[42]:insert{k, k*k*k} end') +master.admin("for k = 20, 29 do box.space[42]:upsert({k}, {}, {k}) end") lsn = master.get_lsn(master_id) replica = TarantoolServer(server.ini)