diff --git a/include/errcode.h b/include/errcode.h index 2675b113e67590323243c9fda77cbdea93d5cc4f..1128e8e25e8829443ee9bbcc1295df1128211ea9 100644 --- a/include/errcode.h +++ b/include/errcode.h @@ -77,7 +77,7 @@ enum { TNT_ERRMSG_MAX = 512 }; /* 23 */_(ER_RESERVED23, 0, "Reserved23") \ /* end of silverproxy error codes */ \ /* 24 */_(ER_UNUSED24, 2, "Unused24") \ - /* 25 */_(ER_TUPLE_IS_EMPTY, 2, "UPDATE error: the new tuple has no fields") \ + /* 25 */_(ER_UNUSED25, 2, "Unused25") \ /* 26 */_(ER_FIBER_STACK, 2, "Can not create a new fiber: recursion limit reached") \ /* 27 */_(ER_UNUSED27, 2, "Unused27") \ /* 28 */_(ER_UNUSED28, 2, "Unused28") \ diff --git a/include/pickle.h b/include/pickle.h index 9fc087c1861fbbd3b8463840b373794c3a0b19e0..ac3fea07539ac7bd64308aa6142a861e06ddd71e 100644 --- a/include/pickle.h +++ b/include/pickle.h @@ -228,4 +228,17 @@ pack_lstr(char *buf, const void *str, uint32_t len) return (char *) memcpy(pack_varint32(buf, len), str, len) + len; } +#define pack_u(bits) \ +static inline char * \ +pack_u##bits(char *buf, uint##bits##_t val) \ +{ \ + *(uint##bits##_t *) buf = val; \ + return buf + sizeof(uint##bits##_t); \ +} + +pack_u(8) +pack_u(16) +pack_u(32) +pack_u(64) + #endif /* TARANTOOL_PICKLE_H_INCLUDED */ diff --git a/src/box/bitset_index.cc b/src/box/bitset_index.cc index 1b9070e3a5af62bba724b9e0a412e55d5d36ba62..49d9ac00aed18f6e08a3f722796d361e77918132 100644 --- a/src/box/bitset_index.cc +++ b/src/box/bitset_index.cc @@ -93,7 +93,7 @@ bitset_index_iterator_next(struct iterator *iterator) BitsetIndex::BitsetIndex(struct key_def *key_def, struct space *space) : Index(key_def, space) { - assert (!key_def->is_unique); + assert(!key_def->is_unique); if (bitset_index_create(&index, realloc) != 0) panic_syserror("bitset_index_create"); @@ -126,7 +126,7 @@ BitsetIndex::endBuild() void BitsetIndex::build(Index *pk) { - assert (!key_def->is_unique); + assert(!key_def->is_unique); struct iterator *it = pk->position(); struct tuple *tuple; @@ -213,7 +213,7 @@ BitsetIndex::replace(struct tuple *old_tuple, struct tuple *new_tuple, if (bitset_index_contains_value(&index, value)) { ret = old_tuple; - assert (old_tuple != new_tuple); + assert(old_tuple != new_tuple); bitset_index_remove_value(&index, value); } } diff --git a/src/box/box_lua.cc b/src/box/box_lua.cc index 808b1d015cf5c9d41fb0252afba37e90d5f20927..ce6e1711411dd148e463b252dafefe7302ad54e7 100644 --- a/src/box/box_lua.cc +++ b/src/box/box_lua.cc @@ -32,6 +32,7 @@ #include "box/box.h" #include "request.h" #include "txn.h" +#include "tuple_update.h" extern "C" { #include <lua.h> @@ -182,63 +183,6 @@ lbox_tuple_slice(struct lua_State *L) return end - start; } - -/** - * Given a tuple, range of fields to remove (start and end field - * numbers), and a list of fields to paste, calculate the size of - * the resulting tuple. - * - * @param L lua stack, contains a list of arguments to paste - * @param start offset in the lua stack where paste arguments start - * @param tuple old tuple - * @param offset cut field offset - * @param len how many fields to cut - * @param[out] sizes of the left and right part - * - * @return size of the new tuple -*/ -static size_t -transform_calculate(struct lua_State *L, struct tuple *tuple, - int start, int argc, int offset, int len, - size_t lr[2]) -{ - /* calculate size of the new tuple */ - const char *tuple_end = tuple->data + tuple->bsize; - const char *tuple_field = tuple->data; - - lr[0] = tuple_range_size(&tuple_field, tuple_end, offset); - - /* calculate sizes of supplied fields */ - size_t mid = 0; - for (int i = start ; i <= argc ; i++) { - switch (lua_type(L, i)) { - case LUA_TNUMBER: - mid += varint32_sizeof(sizeof(u32)) + sizeof(u32); - break; - case LUA_TCDATA: - mid += varint32_sizeof(sizeof(u64)) + sizeof(u64); - break; - case LUA_TSTRING: { - size_t field_size = lua_objlen(L, i); - mid += varint32_sizeof(field_size) + field_size; - break; - } - default: - luaL_error(L, "tuple.transform(): unsupported field type '%s'", - lua_typename(L, lua_type(L, i))); - break; - } - } - - /* calculate size of the removed fields */ - tuple_range_size(&tuple_field, tuple_end, len); - - /* calculate last part of the tuple fields */ - lr[1] = tuple_end - tuple_field; - - return lr[0] + mid + lr[1]; -} - /** * Tuple transforming function. * @@ -256,8 +200,8 @@ lbox_tuple_transform(struct lua_State *L) int argc = lua_gettop(L); if (argc < 3) luaL_error(L, "tuple.transform(): bad arguments"); - int offset = lua_tointeger(L, 2); - int len = lua_tointeger(L, 3); + lua_Integer offset = lua_tointeger(L, 2); /* Can be negative and can be > INT_MAX */ + lua_Integer len = lua_tointeger(L, 3); /* validate offset and len */ if (offset < 0) { @@ -272,45 +216,100 @@ lbox_tuple_transform(struct lua_State *L) if (len > tuple->field_count - offset) len = tuple->field_count - offset; - /* calculate size of the new tuple */ - size_t lr[2]; /* left and right part sizes */ - size_t size = transform_calculate(L, tuple, 4, argc, offset, len, lr); + assert(offset + len <= tuple->field_count); - /* allocate new tuple */ - struct tuple *dest = tuple_alloc(size); - dest->field_count = (tuple->field_count - len) + (argc - 3); + /* + * Calculate the number of operations and length of UPDATE expression + */ + uint32_t op_cnt = 0; + size_t expr_len = 0; + expr_len += sizeof(uint32_t); /* op_count */ + if (offset < tuple->field_count) { + /* Add an UPDATE operation for each removed field. */ + op_cnt += len; + expr_len += len * sizeof(uint32_t); /* Field */ + expr_len += len * sizeof(uint8_t); /* UPDATE_OP_DELETE */ + expr_len += len * varint32_sizeof(0); /* Unused */ + } - /* construct tuple */ - memcpy(dest->data, tuple->data, lr[0]); - char *ptr = dest->data + lr[0]; for (int i = 4; i <= argc; i++) { + uint32_t field_len = 0; switch (lua_type(L, i)) { - case LUA_TNUMBER: { - u32 v = lua_tonumber(L, i); - ptr = pack_lstr(ptr, &v, sizeof(v)); + case LUA_TNUMBER: + field_len = sizeof(uint32_t); break; - } - case LUA_TCDATA: { - u64 v = tarantool_lua_tointeger64(L, i); - ptr = pack_lstr(ptr, &v, sizeof(v)); + case LUA_TCDATA: + field_len = sizeof(uint64_t); break; - } - case LUA_TSTRING: { - size_t field_size = 0; - const char *v = luaL_checklstring(L, i, &field_size); - ptr = pack_lstr(ptr, v, field_size); + case LUA_TSTRING: + field_len = lua_objlen(L, i); + break; + default: + luaL_error(L, "tuple.transform(): unsupported field type '%s'", + lua_typename(L, lua_type(L, i))); break; } + + /* Insert one field */ + op_cnt++; + expr_len += sizeof(uint32_t); /* Field Number */ + expr_len += sizeof(uint8_t); /* UPDATE_OP_SET */ + expr_len += varint32_sizeof(field_len) + field_len; /* Field */ + } + if (op_cnt == 0) { + /* tuple_upate() does not accept an empty operation list. */ + lbox_pushtuple(L, tuple); + return 1; + } + + /* + * Prepare UPDATE expression + */ + char *expr = (char *) palloc(fiber->gc_pool, expr_len); + char *pos = expr; + pos = pack_u32(pos, op_cnt); + for (uint32_t i = 0; i < (uint32_t) len; i++) { + pos = pack_u32(pos, offset); + pos = pack_u8(pos, UPDATE_OP_DELETE); + pos = pack_varint32(pos, 0); + } + + for (int i = argc ; i >= 4; i--) { + uint32_t field_u32; + uint64_t field_u64; + const char *field = NULL; + size_t field_len = 0; + switch (lua_type(L, i)) { + case LUA_TNUMBER: + field_u32 = lua_tonumber(L, i); + field = (const char *) &field_u32; + field_len = sizeof(uint32_t); + break; + case LUA_TCDATA: + field_u64 = tarantool_lua_tointeger64(L, i); + field = (const char *) &field_u64; + field_len = sizeof(uint64_t); + break; + case LUA_TSTRING: + field = luaL_checklstring(L, i, &field_len); + break; default: - /* default type check is done in transform_calculate() - * function */ + assert(false); break; } + + assert(field_len <= UINT32_MAX); + /* Insert the field */ + pos = pack_u32(pos, offset); /* Field Number */ + pos = pack_u8(pos, UPDATE_OP_INSERT); /* Operation */ + pos = pack_lstr(pos, field, field_len); /* Field Value */ } - memcpy(ptr, tuple_field_old(tuple, offset + len), lr[1]); + assert(pos == expr + expr_len); - lbox_pushtuple(L, dest); + /* Execute tuple_update */ + struct tuple *new_tuple = tuple_update(tuple, expr, expr + expr_len); + lbox_pushtuple(L, new_tuple); return 1; } diff --git a/src/box/space.cc b/src/box/space.cc index 52f6d24de887cec9c24e3f18a662295a48be3078..dc9eb92017f1d9a74ecb3514e291fd0f9e264f9d 100644 --- a/src/box/space.cc +++ b/src/box/space.cc @@ -383,7 +383,7 @@ space_config() enum index_type type = STR2ENUM(index_type, cfg_index->type); struct key_def *key_def = &space->key_defs[j]; Index *index = Index::factory(type, key_def, space); - assert (index != NULL); + assert(index != NULL); space->index[j] = index; } diff --git a/src/box/tree_index.cc b/src/box/tree_index.cc index 7ba2fabd215c63ec58366f5b17feacf3a1432a3f..a09c4052694f349a8f66d40b109a43820931d780 100644 --- a/src/box/tree_index.cc +++ b/src/box/tree_index.cc @@ -304,7 +304,7 @@ key_is_linear(struct key_def *key_def) static void fold_with_sparse_parts(struct key_def *key_def, struct tuple *tuple, union sparse_part* parts) { - assert (tuple->field_count >= key_def->max_fieldno); + assert(tuple->field_count >= key_def->max_fieldno); const char *part_data = tuple->data; diff --git a/src/box/tuple.cc b/src/box/tuple.cc index f2a95912127d1719e84ae5a106bb4f360960875d..8ddcbefa86ec3a91d8dd163d585045fca78f1875 100644 --- a/src/box/tuple.cc +++ b/src/box/tuple.cc @@ -188,7 +188,7 @@ tuple_print(struct tbuf *buf, const struct tuple *tuple) if (likely(++field_no < tuple->field_count)) tbuf_printf(buf, ", "); } - assert (field_no == tuple->field_count); + assert(field_no == tuple->field_count); tbuf_printf(buf, "}"); } diff --git a/src/box/tuple_update.cc b/src/box/tuple_update.cc index 2e5d7f46402922fe99238e2d5dcd07f04ef1498c..837b5339c413b82aef3401e2a0b3b9e18b2cdbd6 100644 --- a/src/box/tuple_update.cc +++ b/src/box/tuple_update.cc @@ -35,7 +35,7 @@ #include <exception.h> #include <pickle.h> -/** {{{ UPDATE request implementation. +/** UPDATE request implementation. * UPDATE request is represented by a sequence of operations, each * working with a single field. There also are operations which * add or remove fields. More than one operation on the same field @@ -528,9 +528,6 @@ update_calc_new_tuple_length(struct tuple_update *update) if (update->new_tuple_size > UINT32_MAX) tnt_raise(ClientError, ER_TUPLE_IS_TOO_LONG, update->new_tuple_size); - - if (update->new_tuple_size == 0) - tnt_raise(ClientError, ER_TUPLE_IS_EMPTY); } static void @@ -582,7 +579,7 @@ do_update_ops(struct tuple_update *update, char *new_data) new_field = (char *) update->alloc( update->alloc_ctx, op->new_field_len); } - assert (op->meta != NULL); + assert(op->meta != NULL); op->meta->do_op(&op->arg, old_field, new_field); /* Next op uses previous op output as its input. */ old_field = new_field; @@ -603,7 +600,7 @@ do_update_ops(struct tuple_update *update, char *new_data) total_field_count += field_count; } - assert (update->new_tuple_fcount == total_field_count); + assert(update->new_tuple_fcount == total_field_count); } static void @@ -651,7 +648,7 @@ tuple_update_prepare(region_alloc_func alloc, void *alloc_ctx, struct tuple_update *update = (struct tuple_update *) alloc(alloc_ctx, sizeof(*update)); - assert (update != NULL); + assert(update != NULL); memset(update, 0, sizeof(*update)); update->alloc = alloc; update->alloc_ctx = alloc_ctx; diff --git a/src/lib/bitset/expr.c b/src/lib/bitset/expr.c index 8c004c56073fe6ec894ecad2e0923424deb3727d..dee7d6c6c4f3fff8ddbd4973fba87f92600b62a6 100644 --- a/src/lib/bitset/expr.c +++ b/src/lib/bitset/expr.c @@ -160,7 +160,7 @@ int bitset_expr_add_param(struct bitset_expr *expr, size_t bitset_id, bool pre_not) { - assert (expr->size > 0); + assert(expr->size > 0); struct bitset_expr_conj *conj = &expr->conjs[expr->size - 1]; if (bitset_expr_conj_reserve(expr, conj, conj->size + 1) != 0) diff --git a/src/lib/bitset/index.c b/src/lib/bitset/index.c index 7250dcb5208a72295a3227d26b42a07f6ebc65e8..541ac65a92ac9b5bdd4f60a30565bd11309fc286 100644 --- a/src/lib/bitset/index.c +++ b/src/lib/bitset/index.c @@ -43,7 +43,7 @@ int bitset_index_create(struct bitset_index *index, void *(*realloc)(void *ptr, size_t size)) { - assert (index != NULL); + assert(index != NULL); memset(index, 0, sizeof(*index)); index->realloc = realloc; if (bitset_index_reserve(index, 1) != 0) @@ -55,8 +55,8 @@ bitset_index_create(struct bitset_index *index, void bitset_index_destroy(struct bitset_index *index) { - assert (index != NULL); - assert (index->capacity > 0); + assert(index != NULL); + assert(index->capacity > 0); for (size_t b = 0; b < index->capacity; b++) { if (index->bitsets[b] == NULL) @@ -138,9 +138,9 @@ int bitset_index_insert(struct bitset_index *index, const void *key, size_t key_size, size_t value) { - assert (index != NULL); - assert (key != NULL); - assert (index->capacity > 0); + assert(index != NULL); + assert(key != NULL); + assert(index->capacity > 0); /* * Step 0: allocate enough number of bitsets @@ -367,8 +367,8 @@ int bitset_index_init_iterator(struct bitset_index *index, struct bitset_iterator *it, struct bitset_expr *expr) { - assert (index != NULL); - assert (it != NULL); + assert(index != NULL); + assert(it != NULL); /* Check that we have all required bitsets */ size_t max = 0; diff --git a/src/lib/bitset/page.h b/src/lib/bitset/page.h index 4f519b30c8183db31a7c2f156f885b92f7400c6e..2f77925e239d72f87fdeed0666b8f0abe0f4ec13 100644 --- a/src/lib/bitset/page.h +++ b/src/lib/bitset/page.h @@ -151,7 +151,7 @@ bitset_page_and(struct bitset_page *dst, struct bitset_page *src) bitset_word_t *d = (bitset_word_t *) bitset_page_data(dst); bitset_word_t *s = (bitset_word_t *) bitset_page_data(src); - assert (BITSET_PAGE_DATA_SIZE % sizeof(bitset_word_t) == 0); + assert(BITSET_PAGE_DATA_SIZE % sizeof(bitset_word_t) == 0); int cnt = BITSET_PAGE_DATA_SIZE / sizeof(bitset_word_t); for (int i = 0; i < cnt; i++) { *d++ &= *s++; @@ -164,7 +164,7 @@ bitset_page_nand(struct bitset_page *dst, struct bitset_page *src) bitset_word_t *d = (bitset_word_t *) bitset_page_data(dst); bitset_word_t *s = (bitset_word_t *) bitset_page_data(src); - assert (BITSET_PAGE_DATA_SIZE % sizeof(bitset_word_t) == 0); + assert(BITSET_PAGE_DATA_SIZE % sizeof(bitset_word_t) == 0); int cnt = BITSET_PAGE_DATA_SIZE / sizeof(bitset_word_t); for (int i = 0; i < cnt; i++) { *d++ &= ~*s++; @@ -177,7 +177,7 @@ bitset_page_or(struct bitset_page *dst, struct bitset_page *src) bitset_word_t *d = (bitset_word_t *) bitset_page_data(dst); bitset_word_t *s = (bitset_word_t *) bitset_page_data(src); - assert (BITSET_PAGE_DATA_SIZE % sizeof(bitset_word_t) == 0); + assert(BITSET_PAGE_DATA_SIZE % sizeof(bitset_word_t) == 0); int cnt = BITSET_PAGE_DATA_SIZE / sizeof(bitset_word_t); for (int i = 0; i < cnt; i++) { *d++ |= *s++; diff --git a/src/memcached.cc b/src/memcached.cc index a4b0567db196764ad5c7d9479efaeb0966478d14..1fa36b8f37fcc2d8b3afb9fe0db944d4c48c0abe 100644 --- a/src/memcached.cc +++ b/src/memcached.cc @@ -182,7 +182,7 @@ memcached_meta(struct tuple *tuple) { uint32_t len; const char *field = tuple_field(tuple, 1, &len); - assert (sizeof(struct meta) <= len); + assert(sizeof(struct meta) <= len); return (struct meta *) field; } diff --git a/test/box/lua.result b/test/box/lua.result index f5d95e967cc70c33fbd8ab96171a5b290241946a..bcaca2c74ff89aea4f052734a0fbb7ca58666b3d 100644 --- a/test/box/lua.result +++ b/test/box/lua.result @@ -1186,7 +1186,7 @@ Found 1 tuple: [896755060] lua box.update(0, 'tes5', '#p', 0, '') --- -error: 'UPDATE error: the new tuple has no fields' +error: 'Illegal parameters, tuple must have all indexed fields' ... lua box.space[0]:truncate() --- diff --git a/test/box/lua_misc.result b/test/box/lua_misc.result index 31495fe9f81dff39e6722736d0b62c40769479e5..d931d1f6be428a68e8d13331a967c9874d199d82 100644 --- a/test/box/lua_misc.result +++ b/test/box/lua_misc.result @@ -106,25 +106,24 @@ box.error.ER_PROC_RET: 12290 box.error.ER_TUPLE_IS_TOO_LONG: 11010 box.error.ER_EXACT_MATCH: 11522 box.error.ER_SECONDARY: 770 -box.error.ER_SPACE_DISABLED: 13314 box.error.ER_OK: 0 -box.error.ER_TUPLE_FOUND: 14082 +box.error.ER_NO_SUCH_INDEX: 13570 box.error.ER_TUPLE_NOT_FOUND: 12546 box.error.ER_FIBER_STACK: 6658 -box.error.ER_SPLICE: 10754 -box.error.ER_NO_SUCH_FIELD: 13826 +box.error.ER_UNKNOWN_UPDATE_OP: 11266 +box.error.ER_TUPLE_FOUND: 14082 box.error.ER_UNSUPPORTED: 2562 box.error.ER_INJECTION: 2306 -box.error.ER_NO_SUCH_INDEX: 13570 +box.error.ER_NO_SUCH_FIELD: 13826 box.error.ER_TUPLE_IS_RO: 1025 box.error.ER_ARG_TYPE: 10498 box.error.ER_NO_SUCH_SPACE: 14594 -box.error.ER_TUPLE_IS_EMPTY: 6402 +box.error.ER_SPACE_DISABLED: 13314 box.error.ER_PROC_LUA: 13058 box.error.ER_NO_SUCH_PROC: 12802 -box.error.ER_UNKNOWN_UPDATE_OP: 11266 +box.error.ER_SPLICE: 10754 box.error.ER_KEY_PART_COUNT: 12034 -box.error.ER_FIELD_TYPE: 10242 box.error.ER_WAL_IO: 9986 +box.error.ER_FIELD_TYPE: 10242 box.error.ER_MEMORY_ISSUE: 1793 ... diff --git a/test/lib/sql_ast.py b/test/lib/sql_ast.py index 1d828a732079bb7d6a40e848cc20b4d788a92016..6bf6e9f59938b4976efa0c2bca1ac35df8afc305 100644 --- a/test/lib/sql_ast.py +++ b/test/lib/sql_ast.py @@ -50,7 +50,7 @@ ER = { 22: "ER_RESERVED22" , 23: "ER_RESERVED23" , 24: "ER_UNUSED24" , - 25: "ER_TUPLE_IS_EMPTY" , + 25: "ER_UNUSED25" , 26: "ER_FIBER_STACK" , 27: "ER_UNUSED27" , 28: "ER_UNUSED28" ,