diff --git a/client/tarantool_checksum/tc_space.c b/client/tarantool_checksum/tc_space.c index 4d76a771ffe4829ac1faa57a7e32912d76cf878f..37a4e8ceb94708e86dce8d29865b32023df74a15 100644 --- a/client/tarantool_checksum/tc_space.c +++ b/client/tarantool_checksum/tc_space.c @@ -51,8 +51,9 @@ int tc_space_init(struct tc_spaces *s) { void tc_space_free(struct tc_spaces *s) { - mh_int_t i; - mh_foreach(s->t, i) { + while (mh_size(s->t) > 0) { + mh_int_t i = mh_first(s->t); + struct tc_space *space = mh_u32ptr_node(s->t, i)->val; mh_u32ptr_del(s->t, i, NULL); diff --git a/include/box/box.h b/include/box/box.h index 08cd9cb2c3e22065c032a5c47542fc134cf2b444..2070dd9bca43080584f26d524c31a38c00e94e35 100644 --- a/include/box/box.h +++ b/include/box/box.h @@ -49,7 +49,7 @@ struct tarantool_cfg; struct lua_State; /** To be called at program start. */ -void box_init(void); +void box_init(bool init_storage); /** To be called at program end. */ void box_free(void); diff --git a/include/errcode.h b/include/errcode.h index 5d9248d41b75d400218f8806872d97391130cfd9..3f834717c61c91bd4f1e36962a6c207cfbc8e993 100644 --- a/include/errcode.h +++ b/include/errcode.h @@ -55,8 +55,8 @@ enum { TNT_ERRMSG_MAX = 512 }; /* 2 */_(ER_ILLEGAL_PARAMS, 2, "Illegal parameters, %s") \ /* 3 */_(ER_SECONDARY, 2, "Can't modify data upon a request on the secondary port.") \ /* 4 */_(ER_TUPLE_IS_RO, 1, "Tuple is marked as read-only") \ - /* 5 */_(ER_UNUSED5, 2, "Unused5") \ - /* 6 */_(ER_UNUSED6, 2, "Unused6") \ + /* 5 */_(ER_INDEX_TYPE, 2, "Unsupported index type: %s") \ + /* 6 */_(ER_SPACE_EXISTS, 2, "Space %u already exists") \ /* 7 */_(ER_MEMORY_ISSUE, 1, "Failed to allocate %u bytes in %s for %s") \ /* 8 */_(ER_UNUSED8, 2, "Unused8") \ /* 9 */_(ER_INJECTION, 2, "Error injection '%s'") \ diff --git a/include/lua/init.h b/include/lua/init.h index 382516efc3490aca83809e71cba52705a6335b07..0088d33046c828cb7c29ce8a197dfada94116ed3 100644 --- a/include/lua/init.h +++ b/include/lua/init.h @@ -28,6 +28,7 @@ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ +#include <stddef.h> #include <inttypes.h> struct lua_State; @@ -119,5 +120,13 @@ int luaL_pushnumber64(struct lua_State *L, uint64_t val); struct tbuf; void show_plugins_stat(struct tbuf *out); +/** + * @brief A palloc-like wrapper to allocate memory using lua_newuserdata + * @param ctx lua_State + * @param size a number of bytes to allocate + * @return a pointer to the allocated memory + */ +void * +lua_region_alloc(void *ctx, size_t size); #endif /* INCLUDES_TARANTOOL_LUA_H */ diff --git a/include/palloc.h b/include/palloc.h index d1f89569e73a1091a77fa8bd8c3255318f516d1a..c108a42950dd7e8f2151c968ae1b13b5d5b99339 100644 --- a/include/palloc.h +++ b/include/palloc.h @@ -61,6 +61,12 @@ size_t palloc_allocated(struct palloc_pool *); void palloc_stat(struct tbuf *buf); +static inline void * +palloc_region_alloc(void *ctx, size_t size) +{ + return palloc((struct palloc_pool *) ctx, size); +} + #if defined(__cplusplus) } /* extern "C" */ #endif /* defined(__cplusplus) */ diff --git a/include/tarantool.h b/include/tarantool.h index 017ae428814d05b6e5f71b3a52ad5fcb092b0b7c..a32850acf2e7ec8c319df578f1fe377079e217f7 100644 --- a/include/tarantool.h +++ b/include/tarantool.h @@ -42,7 +42,7 @@ extern int snapshot_pid; extern struct tarantool_cfg cfg; extern const char *cfg_filename; extern char *cfg_filename_fullpath; -extern bool init_storage, booting; +extern bool booting; extern char *binary_filename; extern char *custom_proc_title; int reload_cfg(struct tbuf *out); diff --git a/src/box/CMakeLists.txt b/src/box/CMakeLists.txt index 9929d5be5da4db8974cc4bbc9ab34fc7c6ea7641..3df5f3144828eb5c131872b6036b960112d847e3 100644 --- a/src/box/CMakeLists.txt +++ b/src/box/CMakeLists.txt @@ -19,6 +19,7 @@ tarantool_module("box" tuple.cc tuple_convert.cc tuple_update.cc + key_def.cc index.cc hash_index.cc tree_index.cc diff --git a/src/box/box.cc b/src/box/box.cc index 019ee1bc99ecd4e487dffe331a8f407ebd38907a..00bf4ed46d158cc59e1379a0abb9e8167569851f 100644 --- a/src/box/box.cc +++ b/src/box/box.cc @@ -119,21 +119,16 @@ process_ro(struct port *port, uint32_t op, const char *reqdata, uint32_t reqlen) static void recover_snap_row(const void *data) { - assert(primary_indexes_enabled == false); - const struct box_snap_row *row = (const struct box_snap_row *) data; struct space *space = space_find(row->space); Index *index = space_index(space, 0); - /* Check to see if the tuple has a sufficient number of fields. */ - if (unlikely(row->tuple_size < space->max_fieldno)) { - tnt_raise(IllegalParams, - "tuple must have all indexed fields"); - } + struct tuple *tuple; try { const char *tuple_data = row->data; - tuple = tuple_new(row->tuple_size, &tuple_data, + tuple = tuple_new(space->format, + row->tuple_size, &tuple_data, tuple_data + row->data_size); } catch (const ClientError &e) { say_error("\n" @@ -323,7 +318,7 @@ box_free(void) } void -box_init(void) +box_init(bool init_storage) { title("loading"); atexit(box_free); @@ -400,10 +395,6 @@ snapshot_space(struct space *sp, void *udata) void box_snapshot(struct log_io *l, struct fio_batch *batch) { - /* --init-storage switch */ - if (primary_indexes_enabled == false) - return; - struct snapshot_space_param ud = { l, batch }; space_foreach(snapshot_space, &ud); diff --git a/src/box/box_lua.cc b/src/box/box_lua.cc index 21503c9928c71d8d945b497609712265f5000660..1cc76df1ec279220fa87627ca2b26c0c6302c6d0 100644 --- a/src/box/box_lua.cc +++ b/src/box/box_lua.cc @@ -202,7 +202,7 @@ struct lua_field { uint32_t u32; uint64_t u64; }; - enum field_data_type type; + enum field_type type; }; /** @@ -353,7 +353,11 @@ lbox_tuple_transform(struct lua_State *L) const char *expr = lua_tolstring(L, -1, &expr_len); /* Execute tuple_update */ - struct tuple *new_tuple = tuple_update(tuple, expr, expr + expr_len); + struct tuple *new_tuple = tuple_update(tuple_format_ber, + lua_region_alloc, L, + tuple, expr, expr + expr_len); + /* Cleanup memory allocated by lua_region_alloc */ + lua_settop(L, 0); lbox_pushtuple(L, new_tuple); return 1; } @@ -1031,7 +1035,7 @@ lua_table_to_tuple(struct lua_State *L, int index) tuple_len += field.len + varint32_sizeof(field.len); lua_pop(L, 1); } - struct tuple *tuple = tuple_alloc(tuple_len); + struct tuple *tuple = tuple_alloc(tuple_format_ber, tuple_len); /* * Important: from here and on if there is an exception, * the tuple is leaked. @@ -1058,7 +1062,8 @@ lua_totuple(struct lua_State *L, int index) struct lua_field field; lua_tofield(L, index, &field); if (field.type != UNKNOWN) { - tuple = tuple_alloc(field.len + varint32_sizeof(field.len)); + tuple = tuple_alloc(tuple_format_ber, + field.len + varint32_sizeof(field.len)); tuple->field_count = 1; pack_lstr(tuple->data, field.data, field.len); return tuple; @@ -1587,7 +1592,8 @@ box_unpack_response(struct lua_State *L, const char *s, const char *end) if (tend > end) tnt_raise(IllegalParams, "incorrect packet length"); - struct tuple *tuple = tuple_new(field_count, &s, tend); + struct tuple *tuple = tuple_new(tuple_format_ber, + field_count, &s, tend); lbox_pushtuple(L, tuple); } return s; diff --git a/src/box/index.cc b/src/box/index.cc index 9a7c212c5c6a254df8237dc12ce37e803ce5f976..323e200cd9334cb9ad86f4be18a54abcde2f5153 100644 --- a/src/box/index.cc +++ b/src/box/index.cc @@ -35,8 +35,6 @@ #include "exception.h" #include "space.h" -const char *field_data_type_strs[] = {"UNKNOWN", "NUM", "NUM64", "STR", "\0"}; -STRS(index_type, INDEX_TYPE); STRS(iterator_type, ITERATOR_TYPE); /* {{{ Utilities. **********************************************/ @@ -48,7 +46,7 @@ key_validate_parts(struct key_def *key_def, for (uint32_t part = 0; part < part_count; part++) { uint32_t part_size = load_varint32(&key); - enum field_data_type part_type = key_def->parts[part].type; + enum field_type part_type = key_def->parts[part].type; if (part_type == NUM && part_size != sizeof(uint32_t)) tnt_raise(ClientError, ER_KEY_FIELD_TYPE, "u32"); diff --git a/src/box/index.h b/src/box/index.h index bc60a10399d5ff59f16b6aa29a7dd55b49539116..646562b84fec215097c3133d372369cf5fe2f964 100644 --- a/src/box/index.h +++ b/src/box/index.h @@ -32,26 +32,11 @@ #include "tarantool/util.h" #include "object.h" +#include "key_def.h" struct tuple; struct space; -/* - * Possible field data types. Can't use STRS/ENUM macros for them, - * since there is a mismatch between enum name (STRING) and type - * name literal ("STR"). STR is already used as Objective C type. - */ -enum field_data_type { UNKNOWN = 0, NUM, NUM64, STRING, field_data_type_MAX }; -extern const char *field_data_type_strs[]; - -#define INDEX_TYPE(_) \ - _(HASH, 0) /* HASH Index */ \ - _(TREE, 1) /* TREE Index */ \ - _(BITSET, 2) /* BITSET Index */ \ - -ENUM(index_type, INDEX_TYPE); -extern const char *index_type_strs[]; - /** * @abstract Iterator type * Controls how to iterate over tuples in an index. @@ -99,37 +84,6 @@ struct iterator { void (*free)(struct iterator *); }; -/** Descriptor of a single part in a multipart key. */ -struct key_part { - uint32_t fieldno; - enum field_data_type type; -}; - -/* Descriptor of a multipart key. */ -struct key_def { - /* Description of parts of a multipart index. */ - struct key_part *parts; - /* - * An array holding field positions in 'parts' array. - * Imagine there is index[1] = { key_field[0].fieldno=5, - * key_field[1].fieldno=3 }. - * 'parts' array for such index contains data from - * key_field[0] and key_field[1] respectively. - * max_fieldno is 5, and cmp_order array holds offsets of - * field 3 and 5 in 'parts' array: -1, -1, 0, -1, 1. - */ - uint32_t *cmp_order; - /* The size of the 'parts' array. */ - uint32_t part_count; - /* - * The size of 'cmp_order' array (= max fieldno in 'parts' - * array). - */ - uint32_t max_fieldno; - bool is_unique; - enum index_type type; -}; - /** * Check that the key has correct part count and correct part size * for use in an index iterator. @@ -148,7 +102,7 @@ key_validate(struct key_def *key_def, enum iterator_type type, const char *key, * index (i.e. the key must be fully specified). */ void -primary_key_validate(struct key_def *key_deff, const char *key, +primary_key_validate(struct key_def *key_def, const char *key, uint32_t part_count); @@ -175,7 +129,6 @@ enum dup_replace_mode { DUP_REPLACE }; - class Index: public Object { public: diff --git a/src/box/key_def.cc b/src/box/key_def.cc new file mode 100644 index 0000000000000000000000000000000000000000..29c51c59443a58b79e15ec74e976fab385cff3e7 --- /dev/null +++ b/src/box/key_def.cc @@ -0,0 +1,99 @@ +/* + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include "key_def.h" +extern "C" { +#include <cfg/tarantool_box_cfg.h> +} /* extern "C" */ +#include "exception.h" +#include <stddef.h> + +const char *field_type_strs[] = {"UNKNOWN", "NUM", "NUM64", "STR", "\0"}; +STRS(index_type, INDEX_TYPE); + +void +key_def_create(struct key_def *def, struct tarantool_cfg_space_index *cfg_index) +{ + def->max_fieldno = 0; + def->part_count = 0; + + def->type = STR2ENUM(index_type, cfg_index->type); + if (def->type == index_type_MAX) + tnt_raise(LoggedError, ER_INDEX_TYPE, cfg_index->type); + + /* Calculate key part count and maximal field number. */ + for (uint32_t k = 0; cfg_index->key_field[k] != NULL; ++k) { + auto cfg_key = cfg_index->key_field[k]; + + if (cfg_key->fieldno == -1) { + /* last filled key reached */ + break; + } + + def->max_fieldno = MAX(def->max_fieldno, cfg_key->fieldno); + def->part_count++; + } + + /* init def array */ + def->parts = (struct key_part *) malloc(sizeof(struct key_part) * + def->part_count); + + uint32_t cmp_order_size = (def->max_fieldno + 1) * sizeof(uint32_t); + /* init compare order array */ + def->cmp_order = (uint32_t *) malloc(cmp_order_size); + + for (uint32_t fieldno = 0; fieldno <= def->max_fieldno; fieldno++) + def->cmp_order[fieldno] = UINT32_MAX; + + /* fill fields and compare order */ + for (uint32_t k = 0; cfg_index->key_field[k] != NULL; ++k) { + auto cfg_key = cfg_index->key_field[k]; + + if (cfg_key->fieldno == -1) { + /* last filled key reached */ + break; + } + + /* fill keys */ + def->parts[k].fieldno = cfg_key->fieldno; + def->parts[k].type = STR2ENUM(field_type, cfg_key->type); + /* fill compare order */ + if (def->cmp_order[cfg_key->fieldno] == UINT32_MAX) + def->cmp_order[cfg_key->fieldno] = k; + } + def->is_unique = cfg_index->unique; +} + +/** Free a key definition. */ +void +key_def_destroy(struct key_def *key_def) +{ + free(key_def->parts); + free(key_def->cmp_order); +} + diff --git a/src/box/key_def.h b/src/box/key_def.h new file mode 100644 index 0000000000000000000000000000000000000000..8cc234082736c727c7afdc99b55e3fb07703162d --- /dev/null +++ b/src/box/key_def.h @@ -0,0 +1,97 @@ +#ifndef TARANTOOL_BOX_KEY_DEF_H_INCLUDED +#define TARANTOOL_BOX_KEY_DEF_H_INCLUDED +/* + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include "tarantool/util.h" +/* + * Possible field data types. Can't use STRS/ENUM macros for them, + * since there is a mismatch between enum name (STRING) and type + * name literal ("STR"). STR is already used as Objective C type. + */ +enum field_type { UNKNOWN = 0, NUM, NUM64, STRING, field_type_MAX }; +extern const char *field_type_strs[]; + + +static inline uint32_t +field_type_maxlen(enum field_type type) +{ + static const uint32_t maxlen[] = + { UINT32_MAX, 4, 8, UINT32_MAX, UINT32_MAX }; + return maxlen[type]; +} + +#define INDEX_TYPE(_) \ + _(HASH, 0) /* HASH Index */ \ + _(TREE, 1) /* TREE Index */ \ + _(BITSET, 2) /* BITSET Index */ \ + +ENUM(index_type, INDEX_TYPE); +extern const char *index_type_strs[]; + +/** Descriptor of a single part in a multipart key. */ +struct key_part { + uint32_t fieldno; + enum field_type type; +}; + +/* Descriptor of a multipart key. */ +struct key_def { + /* Description of parts of a multipart index. */ + struct key_part *parts; + /* + * An array holding field positions in 'parts' array. + * Imagine there is index[1] = { key_field[0].fieldno=5, + * key_field[1].fieldno=3 }. + * 'parts' array for such index contains data from + * key_field[0] and key_field[1] respectively. + * max_fieldno is 5, and cmp_order array holds offsets of + * field 3 and 5 in 'parts' array: -1, -1, -1, 0, -1, 1. + */ + uint32_t *cmp_order; + /* The size of the 'parts' array. */ + uint32_t part_count; + /* + * Max fieldno in 'parts' array. Defines the size of + * cmp_order array (which is max_fieldno + 1). + */ + uint32_t max_fieldno; + bool is_unique; + enum index_type type; +}; + +struct tarantool_cfg_space_index; + +void +key_def_create(struct key_def *def, + struct tarantool_cfg_space_index *cfg_index); + +void +key_def_destroy(struct key_def *def); + +#endif /* TARANTOOL_BOX_KEY_DEF_H_INCLUDED */ diff --git a/src/box/request.cc b/src/box/request.cc index 4a476811f8e2bb1d3a821ab387ef6b3713cad969..cf71febf40db507cd032593fa8639ec2b942a6ff 100644 --- a/src/box/request.cc +++ b/src/box/request.cc @@ -72,16 +72,17 @@ execute_replace(struct request *request, struct txn *txn) txn_add_redo(txn, request->type, request->data, request->len); const char **reqpos = &request->data; const char *reqend = request->data + request->len; - struct space *sp = read_space(reqpos, reqend); + struct space *space = read_space(reqpos, reqend); request->flags |= (pick_u32(reqpos, reqend) & BOX_ALLOWED_REQUEST_FLAGS); uint32_t field_count = pick_u32(reqpos, reqend); - struct tuple *new_tuple = tuple_new(field_count, reqpos, reqend); + struct tuple *new_tuple = tuple_new(space->format, field_count, + reqpos, reqend); try { - space_validate_tuple(sp, new_tuple); + space_validate_tuple(space, new_tuple); enum dup_replace_mode mode = dup_replace_mode(request->flags); - txn_replace(txn, sp, NULL, new_tuple, mode); + txn_replace(txn, space, NULL, new_tuple, mode); } catch (const Exception& e) { tuple_free(new_tuple); @@ -96,7 +97,7 @@ execute_update(struct request *request, struct txn *txn) txn_add_redo(txn, request->type, request->data, request->len); const char **reqpos = &request->data; const char *reqend = request->data + request->len; - struct space *sp = read_space(reqpos, reqend); + struct space *space = read_space(reqpos, reqend); request->flags |= (pick_u32(reqpos, reqend) & BOX_ALLOWED_REQUEST_FLAGS); /* Parse UPDATE request. */ @@ -104,7 +105,7 @@ execute_update(struct request *request, struct txn *txn) uint32_t key_part_count; const char *key = read_key(reqpos, reqend, &key_part_count); - Index *pk = space_index(sp, 0); + Index *pk = space_index(space, 0); /* Try to find the tuple by primary key. */ primary_key_validate(pk->key_def, key, key_part_count); struct tuple *old_tuple = pk->findByKey(key, key_part_count); @@ -113,10 +114,13 @@ execute_update(struct request *request, struct txn *txn) return; /* Update the tuple. */ - struct tuple *new_tuple = tuple_update(old_tuple, *reqpos, reqend); + struct tuple *new_tuple = tuple_update(space->format, + palloc_region_alloc, + fiber->gc_pool, + old_tuple, *reqpos, reqend); try { - space_validate_tuple(sp, new_tuple); - txn_replace(txn, sp, old_tuple, new_tuple, DUP_INSERT); + space_validate_tuple(space, new_tuple); + txn_replace(txn, space, old_tuple, new_tuple, DUP_INSERT); } catch (const Exception& e) { tuple_free(new_tuple); throw; @@ -130,9 +134,9 @@ execute_select(struct request *request, struct port *port) { const char **reqpos = &request->data; const char *reqend = request->data + request->len; - struct space *sp = read_space(reqpos, reqend); + struct space *space = read_space(reqpos, reqend); uint32_t index_no = pick_u32(reqpos, reqend); - Index *index = index_find(sp, index_no); + Index *index = index_find(space, index_no); uint32_t offset = pick_u32(reqpos, reqend); uint32_t limit = pick_u32(reqpos, reqend); uint32_t count = pick_u32(reqpos, reqend); @@ -181,7 +185,7 @@ execute_delete(struct request *request, struct txn *txn) txn_add_redo(txn, type, request->data, request->len); const char **reqpos = &request->data; const char *reqend = request->data + request->len; - struct space *sp = read_space(reqpos, reqend); + struct space *space = read_space(reqpos, reqend); if (type == DELETE) { request->flags |= pick_u32(reqpos, reqend) & BOX_ALLOWED_REQUEST_FLAGS; @@ -190,14 +194,14 @@ execute_delete(struct request *request, struct txn *txn) uint32_t key_part_count; const char *key = read_key(reqpos, reqend, &key_part_count); /* Try to find tuple by primary key */ - Index *pk = space_index(sp, 0); + Index *pk = space_index(space, 0); primary_key_validate(pk->key_def, key, key_part_count); struct tuple *old_tuple = pk->findByKey(key, key_part_count); if (old_tuple == NULL) return; - txn_replace(txn, sp, old_tuple, NULL, DUP_REPLACE_OR_INSERT); + txn_replace(txn, space, old_tuple, NULL, DUP_REPLACE_OR_INSERT); } /** To collects stats, we need a valid request type. diff --git a/src/box/space.cc b/src/box/space.cc index 19b739d220fd4c7f3496aca6406ba7783b872cc5..d9c255010411abbeb9b31634804c152533b49948 100644 --- a/src/box/space.cc +++ b/src/box/space.cc @@ -44,29 +44,80 @@ extern "C" { static struct mh_i32ptr_t *spaces; -bool secondary_indexes_enabled = false; -bool primary_indexes_enabled = false; +/** + * Secondary indexes are built in bulk after all data is + * recovered. This flag indicates that the indexes are + * already built and ready for use. + */ +static bool secondary_indexes_enabled = false; +/** + * Primary indexes are enabled only after reading the snapshot. + */ +static bool primary_indexes_enabled = false; -struct space * -space_create(uint32_t space_no, struct key_def *key_defs, uint32_t key_count, uint32_t arity) + +static void +space_create(struct space *space, uint32_t space_no, + struct key_def *key_defs, uint32_t key_count, + uint32_t arity) +{ + memset(space, 0, sizeof(struct space)); + space->no = space_no; + space->arity = arity; + space->key_defs = key_defs; + space->key_count = key_count; + space->format = tuple_format_new(key_defs, key_count); + /* fill space indexes */ + for (uint32_t j = 0; j < key_count; ++j) { + struct key_def *key_def = &space->key_defs[j]; + Index *index = Index::factory(key_def->type, key_def, space); + if (index == NULL) { + tnt_raise(LoggedError, ER_MEMORY_ISSUE, + "class Index", "malloc"); + } + space->index[j] = index; + } +} + +static void +space_destroy(struct space *space) { + for (uint32_t j = 0 ; j < space->key_count; j++) { + Index *index = space->index[j]; + delete index; + key_def_destroy(&space->key_defs[j]); + } + free(space->key_defs); +} +struct space * +space_new(uint32_t space_no, struct key_def *key_defs, + uint32_t key_count, uint32_t arity) +{ struct space *space = space_by_n(space_no); if (space) - panic("Space %d is already exists", space_no); - space = (struct space *) calloc(sizeof(struct space), 1); - space->no = space_no; + tnt_raise(LoggedError, ER_SPACE_EXISTS, space_no); + + space = (struct space *) malloc(sizeof(struct space)); + + space_create(space, space_no, key_defs, key_count, arity); const struct mh_i32ptr_node_t node = { space->no, space }; mh_i32ptr_put(spaces, &node, NULL, NULL); - space->arity = arity; - space->key_defs = key_defs; - space->key_count = key_count; - return space; } +static void +space_delete(struct space *space) +{ + const struct mh_i32ptr_node_t node = { space->no, NULL }; + mh_int_t k = mh_i32ptr_get(spaces, &node, NULL); + assert(k != mh_end(spaces)); + mh_i32ptr_del(spaces, k, NULL); + space_destroy(space); + free(space); +} /* return space by its number */ struct space * @@ -109,22 +160,6 @@ space_foreach(void (*func)(struct space *sp, void *udata), void *udata) { } } -/** Set index by index no */ -void -space_set_index(struct space *sp, uint32_t index_no, Index *idx) -{ - assert(index_no < BOX_INDEX_MAX); - sp->index[index_no] = idx; -} - -/** Free a key definition. */ -static void -key_free(struct key_def *key_def) -{ - free(key_def->parts); - free(key_def->cmp_order); -} - struct tuple * space_replace(struct space *sp, struct tuple *old_tuple, struct tuple *new_tuple, enum dup_replace_mode mode) @@ -165,169 +200,25 @@ space_replace(struct space *sp, struct tuple *old_tuple, void space_validate_tuple(struct space *sp, struct tuple *new_tuple) { - /* Check to see if the tuple has a sufficient number of fields. */ - if (new_tuple->field_count < sp->max_fieldno) - tnt_raise(IllegalParams, - "tuple must have all indexed fields"); - if (sp->arity > 0 && sp->arity != new_tuple->field_count) tnt_raise(IllegalParams, "tuple field count must match space cardinality"); - /* Sweep through the tuple and check the field sizes. */ - struct tuple_iterator it; - tuple_rewind(&it, new_tuple); - const char *field; - uint32_t len; - uint32_t fieldno = 0; - while ((field = tuple_next(&it, &len))) { - if (fieldno == sp->max_fieldno) - break; - /* - * Check fixed size fields (NUM and NUM64) and - * skip undefined size fields (STRING and UNKNOWN). - */ - if (sp->field_types[fieldno] == NUM) { - if (len != sizeof(uint32_t)) - tnt_raise(ClientError, ER_KEY_FIELD_TYPE, - "NUM"); - } else if (sp->field_types[fieldno] == NUM64) { - if (len != sizeof(uint64_t)) - tnt_raise(ClientError, ER_KEY_FIELD_TYPE, - "NUM64"); - } - fieldno++; - } } void space_free(void) { - mh_int_t i; + while (mh_size(spaces) > 0) { + mh_int_t i = mh_first(spaces); - mh_foreach(spaces, i) { struct space *space = (struct space *) mh_i32ptr_node(spaces, i)->val; - mh_i32ptr_del(spaces, i, NULL); - - for (uint32_t j = 0 ; j < space->key_count; j++) { - Index *index = space->index[j]; - delete index; - key_free(&space->key_defs[j]); - } - - free(space->key_defs); - free(space->field_types); - free(space); + space_delete(space); } - + tuple_free(); } -static void -key_init(struct key_def *def, struct tarantool_cfg_space_index *cfg_index) -{ - def->max_fieldno = 0; - def->part_count = 0; - - def->type = STR2ENUM(index_type, cfg_index->type); - if (def->type == index_type_MAX) - panic("Wrong index type: %s", cfg_index->type); - - /* Calculate key part count and maximal field number. */ - for (uint32_t k = 0; cfg_index->key_field[k] != NULL; ++k) { - auto cfg_key = cfg_index->key_field[k]; - - if (cfg_key->fieldno == -1) { - /* last filled key reached */ - break; - } - - def->max_fieldno = MAX(def->max_fieldno, cfg_key->fieldno); - def->part_count++; - } - - /* init def array */ - def->parts = (struct key_part *) malloc(sizeof(struct key_part) * - def->part_count); - if (def->parts == NULL) { - panic("can't allocate def parts array for index"); - } - - /* init compare order array */ - def->max_fieldno++; - def->cmp_order = (uint32_t *) malloc(def->max_fieldno * sizeof(uint32_t)); - if (def->cmp_order == NULL) { - panic("can't allocate def cmp_order array for index"); - } - for (uint32_t fieldno = 0; fieldno < def->max_fieldno; fieldno++) { - def->cmp_order[fieldno] = BOX_FIELD_MAX; - } - - /* fill fields and compare order */ - for (uint32_t k = 0; cfg_index->key_field[k] != NULL; ++k) { - auto cfg_key = cfg_index->key_field[k]; - - if (cfg_key->fieldno == -1) { - /* last filled key reached */ - break; - } - - /* fill keys */ - def->parts[k].fieldno = cfg_key->fieldno; - def->parts[k].type = STR2ENUM(field_data_type, cfg_key->type); - /* fill compare order */ - if (def->cmp_order[cfg_key->fieldno] == BOX_FIELD_MAX) - def->cmp_order[cfg_key->fieldno] = k; - } - def->is_unique = cfg_index->unique; -} - -/** - * Extract all available field info from keys - * - * @param space space to extract field info for - * @param key_count the number of keys - * @param key_defs key description array - */ -static void -space_init_field_types(struct space *space) -{ - uint32_t i, max_fieldno; - uint32_t key_count = space->key_count; - struct key_def *key_defs = space->key_defs; - - /* find max max field no */ - max_fieldno = 0; - for (i = 0; i < key_count; i++) { - max_fieldno= MAX(max_fieldno, key_defs[i].max_fieldno); - } - - /* alloc & init field type info */ - space->max_fieldno = max_fieldno; - space->field_types = (enum field_data_type *) - calloc(max_fieldno, sizeof(enum field_data_type)); - - /* extract field type info */ - for (i = 0; i < key_count; i++) { - struct key_def *def = &key_defs[i]; - for (uint32_t pi = 0; pi < def->part_count; pi++) { - struct key_part *part = &def->parts[pi]; - assert(part->fieldno < max_fieldno); - space->field_types[part->fieldno] = part->type; - } - } - -#ifndef NDEBUG - /* validate field type info */ - for (i = 0; i < key_count; i++) { - struct key_def *def = &key_defs[i]; - for (uint32_t pi = 0; pi < def->part_count; pi++) { - struct key_part *part = &def->parts[pi]; - assert(space->field_types[part->fieldno] == part->type); - } - } -#endif -} static void space_config() @@ -346,50 +237,26 @@ space_config() assert(cfg.memcached_port == 0 || i != cfg.memcached_space); - struct space *space = space_by_n(i); - if (space) - panic("space %u is already exists", i); - - space = (struct space *) calloc(sizeof(struct space), 1); - space->no = i; - - space->arity = (cfg_space->cardinality != -1) ? - cfg_space->cardinality : 0; + uint32_t arity = (cfg_space->cardinality != -1 ? + cfg_space->cardinality : 0); /* * Collect key/field info. We need aggregate * information on all keys before we can create * indexes. */ - space->key_count = 0; - for (uint32_t j = 0; cfg_space->index[j] != NULL; ++j) { - ++space->key_count; - } + uint32_t key_count = 0; + while (cfg_space->index[key_count] != NULL) + key_count++; + struct key_def *key_defs = (struct key_def *) + malloc(key_count * sizeof(struct key_def)); - space->key_defs = (struct key_def *) malloc(space->key_count * - sizeof(struct key_def)); - if (space->key_defs == NULL) { - panic("can't allocate key def array"); - } - for (uint32_t j = 0; cfg_space->index[j] != NULL; ++j) { - auto cfg_index = cfg_space->index[j]; - key_init(&space->key_defs[j], cfg_index); - } - space_init_field_types(space); - - /* fill space indexes */ for (uint32_t j = 0; cfg_space->index[j] != NULL; ++j) { auto cfg_index = cfg_space->index[j]; - 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); - space->index[j] = index; + key_def_create(&key_defs[j], cfg_index); } + (void) space_new(i, key_defs, key_count, arity); - const struct mh_i32ptr_node_t node = - { space->no, space }; - mh_i32ptr_put(spaces, &node, NULL, NULL); say_info("space %i successfully configured", i); } } @@ -398,6 +265,7 @@ void space_init(void) { spaces = mh_i32ptr_new(); + tuple_init(); /* configure regular spaces */ space_config(); @@ -548,7 +416,7 @@ check_spaces(struct tarantool_cfg *conf) } /* key must has valid type */ - if (STR2ENUM(field_data_type, key->type) == field_data_type_MAX) { + if (STR2ENUM(field_type, key->type) == field_type_MAX) { out_warning(CNF_OK, "(space = %zu index = %zu) " "unknown field data type: `%s'", i, j, key->type); return -1; @@ -628,8 +496,8 @@ check_spaces(struct tarantool_cfg *conf) break; uint32_t f = key->fieldno; - enum field_data_type t = STR2ENUM(field_data_type, key->type); - assert(t != field_data_type_MAX); + enum field_type t = STR2ENUM(field_type, key->type); + assert(t != field_type_MAX); if (types[f] != t) { if (types[f] == UNKNOWN) { types[f] = t; diff --git a/src/box/space.h b/src/box/space.h index ec996baa95d21b8baa2a671e834b33d085d1a53e..8bd6aff5f5a649c09413275b133ce8c67f2b9639 100644 --- a/src/box/space.h +++ b/src/box/space.h @@ -29,6 +29,7 @@ * SUCH DAMAGE. */ #include "index.h" +#include "key_def.h" #include <exception.h> #include <box/box.h> @@ -38,8 +39,8 @@ struct tarantool_cfg; struct space { Index *index[BOX_INDEX_MAX]; /** If not set (is 0), any tuple in the - * space can have any number of fields (but - * @sa max_fieldno). If set, Each tuple + * space can have any number of fields. + * If set, each tuple * must have exactly this many fields. */ uint32_t arity; @@ -57,25 +58,11 @@ struct space { */ struct key_def *key_defs; - /** - * Field types of indexed fields. This is an array of size - * field_count. If there are gaps, i.e. fields that do not - * participate in any index and thus we cannot infer their - * type, then respective array members have value UNKNOWN. - * XXX: right now UNKNOWN is also set for fields which types - * in two indexes contradict each other. - */ - enum field_data_type *field_types; - - /** - * Max field no which participates in any of the space indexes. - * Each tuple in this space must have, therefore, at least - * field_count fields. - */ - uint32_t max_fieldno; - /** Space number. */ uint32_t no; + + /** Default tuple format used by this space */ + struct tuple_format *format; }; @@ -190,10 +177,6 @@ space_index(struct space *sp, uint32_t index_no) return NULL; } -/** Set index by index no. */ -void -space_set_index(struct space *sp, uint32_t index_no, Index *idx); - /** * Call a visitor function on every enabled space. */ @@ -217,7 +200,6 @@ space_find(uint32_t space_no) tnt_raise(ClientError, ER_NO_SUCH_SPACE, space_no); } - /** Get key_def ordinal number. */ static inline uint32_t key_def_n(struct space *sp, struct key_def *kp) @@ -226,21 +208,9 @@ key_def_n(struct space *sp, struct key_def *kp) return kp - sp->key_defs; } -static inline uint32_t -space_max_fieldno(struct space *sp) -{ - return sp->max_fieldno; -} - -static inline enum field_data_type -space_field_type(struct space *sp, uint32_t no) -{ - return sp->field_types[no]; -} - - struct space * -space_create(uint32_t space_no, struct key_def *key_defs, uint32_t key_count, uint32_t arity); +space_new(uint32_t space_no, struct key_def *key_defs, + uint32_t key_count, uint32_t arity); /** Get index ordinal number in space. */ @@ -257,17 +227,6 @@ index_is_primary(Index *index) return index_n(index) == 0; } -/** - * Secondary indexes are built in bulk after all data is - * recovered. This flag indicates that the indexes are - * already built and ready for use. - */ -extern bool secondary_indexes_enabled; -/** - * Primary indexes are enabled only after reading the snapshot. - */ -extern bool primary_indexes_enabled; - void space_init(void); void space_free(void); int @@ -277,7 +236,6 @@ void begin_build_primary_indexes(void); void end_build_primary_indexes(void); void build_secondary_indexes(void); - static inline Index * index_find(struct space *sp, uint32_t index_no) { diff --git a/src/box/tuple.cc b/src/box/tuple.cc index 76d1e5dc79bfb5bda2e80dc9d10e36e61285665e..14ba42296847a250ec7c293ba4def6a409a1dce5 100644 --- a/src/box/tuple.cc +++ b/src/box/tuple.cc @@ -31,22 +31,189 @@ #include <salloc.h> #include "tbuf.h" -#include "index.h" +#include "key_def.h" #include "tuple_update.h" #include <exception.h> #include <palloc.h> #include <fiber.h> #include "scoped_guard.h" +#include <stdio.h> + +/** Global table of tuple formats */ +struct tuple_format **tuple_formats; +struct tuple_format *tuple_format_ber; + +static uint32_t formats_size, formats_capacity; + +/** Extract all available type info from keys. */ +void +field_type_create(enum field_type *types, uint32_t field_count, + struct key_def *key_def, uint32_t key_count) +{ + /* There may be fields between indexed fields (gaps). */ + memset(types, 0, sizeof(*types) * field_count); + + struct key_def *end = key_def + key_count; + /* extract field type info */ + for (; key_def < end; key_def++) { + struct key_part *part = key_def->parts; + struct key_part *pend = part + key_def->part_count; + for (; part < pend; part++) { + assert(part->fieldno < field_count); + types[part->fieldno] = part->type; + } + } +} + +static struct tuple_format * +tuple_format_alloc_and_register(struct key_def *key_def, + uint32_t key_count) +{ + uint32_t total; + struct tuple_format *format; + struct key_def *end = key_def + key_count; + uint32_t max_fieldno = 0; + uint32_t field_count; + + /* find max max field no */ + for (; key_def < end; key_def++) + max_fieldno= MAX(max_fieldno, key_def->max_fieldno); + + if (formats_size == formats_capacity) { + uint32_t new_capacity = formats_capacity ? + formats_capacity * 2 : 16; + struct tuple_format **formats; + if (new_capacity >= UINT16_MAX) + goto error; + formats = (struct tuple_format **) realloc(tuple_formats, + new_capacity * sizeof(tuple_formats[0])); + if (formats == NULL) + goto error; + + formats_capacity = new_capacity; + tuple_formats = formats; + } + field_count = key_count > 0 ? max_fieldno + 1 : 0; + + total = sizeof(struct tuple_format) + + field_count * sizeof(int32_t) + + field_count * sizeof(enum field_type); + + format = (struct tuple_format *) malloc(total); + + if (format == NULL) + goto error; + + format->id = formats_size++; + format->max_fieldno = max_fieldno; + format->field_count = field_count; + format->types = (enum field_type *) + ((char *) format + sizeof(*format) + + field_count * sizeof(int32_t)); + tuple_formats[format->id] = format; + return format; +error: + tnt_raise(LoggedError, ER_MEMORY_ISSUE, + sizeof(struct tuple_format), "tuple format", "malloc"); + return NULL; +} + +struct tuple_format * +tuple_format_new(struct key_def *key_def, uint32_t key_count) +{ + struct tuple_format *format = + tuple_format_alloc_and_register(key_def, key_count); + + field_type_create(format->types, format->field_count, + key_def, key_count); + + int32_t i = 0; + uint32_t prev_offset = 0; + /* + * In the format, store all offsets available, + * they may be useful. + */ + for (; i < format->max_fieldno; i++) { + uint32_t maxlen = field_type_maxlen(format->types[i]); + if (maxlen == UINT32_MAX) + break; + format->offset[i] = (varint32_sizeof(maxlen) + maxlen + + prev_offset); + assert(format->offset[i] > 0); + prev_offset = format->offset[i]; + } + int j = 0; + for (; i < format->max_fieldno; i++) { + /* + * In the tuple, store only offsets necessary to + * quickly access indexed fields. Start from + * field 1, not field 0, field 0 offset is 0. + */ + if (format->types[i + 1] == UNKNOWN) + format->offset[i] = INT32_MIN; + else + format->offset[i] = --j; + } + if (format->field_count > 0) { + /* + * The last offset is always there and is unused, + * to simplify the loop in tuple_init_field_map() + */ + format->offset[format->field_count - 1] = INT32_MIN; + } + format->field_map_size = -j * sizeof(uint32_t); + return format; +} + +/* + * Validate a new tuple format and initialize tuple-local + * format data. + */ +static inline void +tuple_init_field_map(struct tuple *tuple, struct tuple_format *format) +{ + /* Check to see if the tuple has a sufficient number of fields. */ + if (tuple->field_count < format->field_count) + tnt_raise(IllegalParams, + "tuple must have all indexed fields"); + + int32_t *offset = format->offset; + enum field_type *type = format->types; + enum field_type *end = format->types + format->field_count; + const char *pos = tuple->data; + uint32_t *field_map = (uint32_t *) tuple; + + for (; type < end; offset++, type++) { + if (pos >= tuple->data + tuple->bsize) + tnt_raise(IllegalParams, + "incorrect tuple format"); + uint32_t len = load_varint32(&pos); + uint32_t type_maxlen = field_type_maxlen(*type); + /* + * For fixed offsets, validate fields have + * correct lengths. + */ + if (type_maxlen != UINT32_MAX && len != type_maxlen) { + tnt_raise(ClientError, ER_KEY_FIELD_TYPE, + field_type_strs[*type]); + } + pos += len; + if (*offset < 0 && *offset != INT32_MIN) + field_map[*offset] = pos - tuple->data; + } +} /** Allocate a tuple */ struct tuple * -tuple_alloc(size_t size) +tuple_alloc(struct tuple_format *format, size_t size) { - size_t total = sizeof(struct tuple) + size; - struct tuple *tuple = (struct tuple *) salloc(total, "tuple"); + size_t total = sizeof(struct tuple) + size + format->field_map_size; + char *ptr = (char *) salloc(total, "tuple"); + struct tuple *tuple = (struct tuple *)(ptr + format->field_map_size); - tuple->flags = tuple->refs = 0; + tuple->refs = 0; tuple->bsize = size; + tuple->format_id = tuple_format_id(format); say_debug("tuple_alloc(%zu) = %p", size, tuple); return tuple; @@ -61,7 +228,8 @@ tuple_free(struct tuple *tuple) { say_debug("tuple_free(%p)", tuple); assert(tuple->refs == 0); - sfree(tuple); + char *ptr = (char *) tuple - tuple_format(tuple)->field_map_size; + sfree(ptr); } /** @@ -87,36 +255,39 @@ tuple_ref(struct tuple *tuple, int count) * @returns field data if field exists or NULL */ const char * -tuple_field_old(struct tuple *tuple, uint32_t i) +tuple_field_old(const struct tuple_format *format, + const struct tuple *tuple, uint32_t i) { const char *field = tuple->data; - const char *tuple_end = tuple->data + tuple->bsize; + + if (i == 0) + return field; + i--; + if (i < format->max_fieldno) { + if (format->offset[i] > 0) + return field + format->offset[i]; + if (format->offset[i] != INT32_MIN) { + uint32_t *field_map = (uint32_t *) tuple; + int32_t idx = format->offset[i]; + return field + field_map[idx]; + } + } + const char *tuple_end = field + tuple->bsize; while (field < tuple_end) { - if (i == 0) - return field; uint32_t len = load_varint32(&field); field += len; + if (i == 0) + return field; i--; } return tuple_end; } const char * -tuple_field(const struct tuple *tuple, uint32_t field_no, uint32_t *len) +tuple_seek(struct tuple_iterator *it, uint32_t i, uint32_t *len) { - const char *field = tuple_field_old((struct tuple *) tuple, field_no); - if (field < tuple->data + tuple->bsize) { - *len = load_varint32(&field); - return field; - } - return NULL; -} - -const char * -tuple_seek(struct tuple_iterator *it, uint32_t field_no, uint32_t *len) -{ - it->pos = tuple_field_old((struct tuple *) it->tuple, field_no); + it->pos = tuple_field_old(tuple_format(it->tuple), it->tuple, i); return tuple_next(it, len); } @@ -195,31 +366,28 @@ tuple_print(struct tbuf *buf, const struct tuple *tuple) tbuf_printf(buf, "}"); } -static void * -palloc_region_alloc(void *ctx, size_t size) -{ - return palloc((struct palloc_pool *) ctx, size); -} - struct tuple * -tuple_update(const struct tuple *old_tuple, const char *expr, +tuple_update(struct tuple_format *format, + void *(*region_alloc)(void *, size_t), void *alloc_ctx, + const struct tuple *old_tuple, const char *expr, const char *expr_end) { uint32_t new_size = 0; uint32_t new_field_count = 0; struct tuple_update *update = - tuple_update_prepare(palloc_region_alloc, fiber->gc_pool, + tuple_update_prepare(region_alloc, alloc_ctx, expr, expr_end, old_tuple->data, old_tuple->data + old_tuple->bsize, old_tuple->field_count, &new_size, &new_field_count); /* Allocate a new tuple. */ - struct tuple *new_tuple = tuple_alloc(new_size); + struct tuple *new_tuple = tuple_alloc(format, new_size); new_tuple->field_count = new_field_count; try { tuple_update_execute(update, new_tuple->data); + tuple_init_field_map(new_tuple, format); } catch (const Exception&) { tuple_free(new_tuple); throw; @@ -228,90 +396,76 @@ tuple_update(const struct tuple *old_tuple, const char *expr, } struct tuple * -tuple_new(uint32_t field_count, const char **data, const char *end) +tuple_new(struct tuple_format *format, uint32_t field_count, + const char **data, const char *end) { size_t tuple_len = end - *data; if (tuple_len != tuple_range_size(data, end, field_count)) tnt_raise(IllegalParams, "tuple_new(): incorrect tuple format"); - struct tuple *new_tuple = tuple_alloc(tuple_len); + struct tuple *new_tuple = tuple_alloc(format, tuple_len); new_tuple->field_count = field_count; memcpy(new_tuple->data, end - tuple_len, tuple_len); + try { + tuple_init_field_map(new_tuple, format); + } catch (...) { + tuple_free(new_tuple); + throw; + } return new_tuple; } +/* + * Compare two tuple fields. + * Separate version exists since compare is a very + * often used operation, so any performance speed up + * in it can have dramatic impact on the overall + * server performance. + */ static inline int -tuple_compare_field(const char *field_a, uint32_t size_a, - const char *field_b, uint32_t size_b, - enum field_data_type type) +tuple_compare_field(const char *field_a, const char *field_b, + enum field_type type) { - /* - * field_a is always a tuple field. - * field_b can be either a tuple field or a key part. - * All tuple fields were validated before by space_validate_tuple(). - * All key parts were validated before by key_validate(). - */ - switch (type) { - case NUM: - { - assert(size_a == sizeof(uint32_t)); - assert(size_b == sizeof(uint32_t)); - uint32_t a = *(uint32_t *) field_a; - uint32_t b = *(uint32_t *) field_b; - return a < b ? -1 : (a > b); - } - case NUM64: - { - assert(size_a == sizeof(uint64_t)); - uint64_t a = *(uint64_t *) field_a; - uint64_t b; - /* Allow search in NUM64 indexes using NUM keys. */ - if (size_b == sizeof(uint32_t)) { - b = *(uint32_t *) field_b; - } else { - assert(size_b == sizeof(uint64_t)); - b = *(uint64_t *) field_b; - } - return a < b ? -1 : (a > b); - } - case STRING: - { - int cmp = memcmp(field_a, field_b, MIN(size_a, size_b)); - if (cmp != 0) - return cmp; - - if (size_a > size_b) { - return 1; - } else if (size_a < size_b){ - return -1; - } else { - return 0; - } - } - default: - assert(false); + if (type != STRING) { + assert(field_a[0] == field_b[0]); + /* + * Little-endian unsigned int is memcmp + * compatible. + */ + return memcmp(field_a + 1, field_b + 1, field_a[0]); } + uint32_t size_a = load_varint32(&field_a); + uint32_t size_b = load_varint32(&field_b); + int r = memcmp(field_a, field_b, MIN(size_a, size_b)); + if (r == 0) + r = size_a < size_b ? -1 : size_a > size_b; + return r; } int tuple_compare(const struct tuple *tuple_a, const struct tuple *tuple_b, const struct key_def *key_def) { - for (uint32_t part = 0; part < key_def->part_count; part++) { - uint32_t field_no = key_def->parts[part].fieldno; - uint32_t size_a, size_b; - const char *field_a = tuple_field(tuple_a, field_no, &size_a); - const char *field_b = tuple_field(tuple_b, field_no, &size_b); - - int r = tuple_compare_field(field_a, size_a, field_b, size_b, - key_def->parts[part].type); - if (r != 0) { - return r; - } + if (key_def->part_count == 1 && key_def->parts[0].fieldno == 0) + return tuple_compare_field(tuple_a->data, tuple_b->data, + key_def->parts[0].type); + + struct key_part *part = key_def->parts; + struct key_part *end = part + key_def->part_count; + struct tuple_format *format_a = tuple_format(tuple_a); + struct tuple_format *format_b = tuple_format(tuple_b); + const char *field_a; + const char *field_b; + int r; + + for (; part < end; part++) { + field_a = tuple_field_old(format_a, tuple_a, part->fieldno); + field_b = tuple_field_old(format_b, tuple_b, part->fieldno); + if ((r = tuple_compare_field(field_a, field_b, part->type))) + break; } - - return 0; + return r; } int @@ -319,33 +473,67 @@ tuple_compare_dup(const struct tuple *tuple_a, const struct tuple *tuple_b, const struct key_def *key_def) { int r = tuple_compare(tuple_a, tuple_b, key_def); - if (r != 0) { - return r; - } + if (r == 0) + r = tuple_a < tuple_b ? -1 : tuple_a > tuple_b; - return tuple_a < tuple_b ? -1 : (tuple_a > tuple_b); + return r; } int -tuple_compare_with_key(const struct tuple *tuple_a, const char *key, +tuple_compare_with_key(const struct tuple *tuple, const char *key, uint32_t part_count, const struct key_def *key_def) { - part_count = MIN(part_count, key_def->part_count); - for (uint32_t part = 0; part < part_count; part++) { - uint32_t field_no = key_def->parts[part].fieldno; - - uint32_t size_a; - const char *field_a = tuple_field(tuple_a, field_no, &size_a); - - uint32_t key_size = load_varint32(&key); - int r = tuple_compare_field(field_a, size_a, key, key_size, - key_def->parts[part].type); - if (r != 0) { - return r; + struct key_part *part = key_def->parts; + struct key_part *end = part + MIN(part_count, key_def->part_count); + struct tuple_format *format = tuple_format(tuple); + const char *field; + uint32_t field_size; + uint32_t key_size; + int r = 0; /* Part count can be 0 in wildcard searches. */ + for (; part < end; part++, key += key_size) { + field = tuple_field_old(format, tuple, part->fieldno); + field_size = load_varint32(&field); + key_size = load_varint32(&key); + switch (part->type) { + case NUM: + r = memcmp(field, key, sizeof(uint32_t)); + break; + case NUM64: + if (key_size == sizeof(uint32_t)) { + /* + * Allow search in NUM64 indexes + * using NUM keys. + */ + uint64_t b = *(uint32_t *) key; + r = memcmp(field, &b, sizeof(uint64_t)); + } else { + r = memcmp(field, key, sizeof(uint64_t)); + } + break; + default: + r = memcmp(field, key, MIN(field_size, key_size)); + if (r == 0) + r = field_size < key_size ? -1 : field_size > key_size; + break; } - - key += key_size; + if (r != 0) + break; } + return r; +} - return 0; +void +tuple_init() +{ + tuple_format_ber = tuple_format_new(NULL, 0); +} + +void +tuple_free() +{ + for (struct tuple_format **format = tuple_formats; + format < tuple_formats + formats_size; + format++) + free(*format); + free(tuple_formats); } diff --git a/src/box/tuple.h b/src/box/tuple.h index 401b21a26f67ca318e194091b456de5d474e2445..8ba49b1946c8ac3317b3c7cac1dc0eb343e4cc13 100644 --- a/src/box/tuple.h +++ b/src/box/tuple.h @@ -29,10 +29,85 @@ * SUCH DAMAGE. */ #include "tarantool/util.h" +#include "key_def.h" #include <pickle.h> struct tbuf; -struct key_def; + +/** + * @brief In-memory tuple format + */ +struct tuple_format { + uint16_t id; + /** + * Max field no which participates in any of the space + * indexes. Each tuple of this format must have, + * therefore, at least max_fieldno fields. + * + */ + uint32_t max_fieldno; + /* Length of 'types' and 'offset' arrays. */ + uint32_t field_count; + /** + * Field types of indexed fields. This is an array of size + * field_count. If there are gaps, i.e. fields that do not + * participate in any index and thus we cannot infer their + * type, then respective array members have value UNKNOWN. + */ + enum field_type *types; + /** + * Each tuple has an area with field offsets. This area + * is located in front of the tuple. It is used to quickly + * find field start inside tuple data. This area only + * stores offsets of fields preceded with fields of + * dynamic length. If preceding fields have a fixed + * length, field offset can be calculated once for all + * tuples and thus is stored directly in the format object. + * The variable below stores the size of field map in the + * tuple, *in bytes*. + */ + uint32_t field_map_size; + /** + * For each field participating in an index, the format + * may either store the fixed offset of the field + * (identical in all tuples with this format), or an + * offset in the dynamic offset map (field_map), which, + * in turn, stores the offset of the field (such offset is + * varying between different tuples of the same format). + * If an offset is fixed, it's positive, so that + * tuple->data[format->offset[fieldno] gives the + * start of the field. + * If it is varying, it's negative, so that + * tuple->data[((uint32_t *) * tuple)[format->offset[fieldno]]] + * gives the start of the field. + */ + int32_t offset[0]; +}; + +extern struct tuple_format **tuple_formats; +/** + * Default format for a tuple which does not belong + * to any space and is stored in memory. + */ +extern struct tuple_format *tuple_format_ber; + + +static inline uint32_t +tuple_format_id(struct tuple_format *format) +{ + assert(tuple_formats[format->id] == format); + return format->id; +} + +/** + * @brief Allocate, construct and register a new in-memory tuple + * format. + * @param space description + * + * @return tuple format + */ +struct tuple_format * +tuple_format_new(struct key_def *key_def, uint32_t key_count); /** * An atom of Tarantool/Box storage. Consists of a list of fields. @@ -42,8 +117,8 @@ struct tuple { /** reference counter */ uint16_t refs; - /* see enum tuple_flags */ - uint16_t flags; + /** format identifier */ + uint16_t format_id; /** length of the variable part of the tuple */ uint32_t bsize; /** number of fields in the variable part. */ @@ -62,7 +137,7 @@ struct tuple * @post tuple->refs = 1 */ struct tuple * -tuple_alloc(size_t size); +tuple_alloc(struct tuple_format *format, size_t size); /** * Create a new tuple from a sequence of BER-len encoded fields. @@ -73,7 +148,8 @@ tuple_alloc(size_t size); * Throws an exception if tuple format is incorrect. */ struct tuple * -tuple_new(uint32_t field_count, const char **data, const char *end); +tuple_new(struct tuple_format *format, uint32_t field_count, + const char **data, const char *end); /** * Change tuple reference counter. If it has reached zero, free the tuple. @@ -83,24 +159,46 @@ tuple_new(uint32_t field_count, const char **data, const char *end); void tuple_ref(struct tuple *tuple, int count); +/** +* @brief Return a tuple format instance +* @param tuple tuple +* @return tuple format instance +*/ +static inline struct tuple_format * +tuple_format(const struct tuple *tuple) +{ + struct tuple_format *format = tuple_formats[tuple->format_id]; + assert(tuple_format_id(format) == tuple->format_id); + return format; +} + /** * Get a field from tuple by index. + * Returns a pointer to BER-length prefixed field. * - * @returns field data if the field exists, or NULL */ const char * -tuple_field_old(struct tuple *tuple, uint32_t i); +tuple_field_old(const struct tuple_format *format, + const struct tuple *tuple, uint32_t i); /** * @brief Return field data of the field * @param tuple tuple * @param field_no field number - * @param field pointer where the start of field data will be stored + * @param field pointer where the start of field data will be stored, + * or NULL if field is out of range * @param len pointer where the len of the field will be stored - * @throws IllegalParams if \a field_no is out of range */ -const char * -tuple_field(const struct tuple *tuple, uint32_t field_no, uint32_t *len); +static inline const char * +tuple_field(const struct tuple *tuple, uint32_t i, uint32_t *len) +{ + const char *field = tuple_field_old(tuple_format(tuple), tuple, i); + if (field < tuple->data + tuple->bsize) { + *len = load_varint32(&field); + return field; + } + return NULL; +} /** * @brief Tuple Interator @@ -166,8 +264,10 @@ void tuple_print(struct tbuf *buf, const struct tuple *tuple); struct tuple * -tuple_update(const struct tuple *old_tuple, const char *expr, - const char *expr_end); +tuple_update(struct tuple_format *new_format, + void *(*region_alloc)(void *, size_t), void *alloc_ctx, + const struct tuple *old_tuple, + const char *expr, const char *expr_end); /** Tuple length when adding to iov. */ static inline size_t tuple_len(struct tuple *tuple) @@ -240,5 +340,12 @@ tuple_to_obuf(struct tuple *tuple, struct obuf *buf); void tuple_to_luabuf(struct tuple *tuple, struct luaL_Buffer *b); +/** Initialize tuple library */ +void +tuple_init(); + +/** Cleanup tuple library */ +void +tuple_free(); #endif /* TARANTOOL_BOX_TUPLE_H_INCLUDED */ diff --git a/src/lua/init.cc b/src/lua/init.cc index 6892af4b491e76bb23c406f38d5b899c100f08b8..78e82997f961b20d5f59eb77d560f9326381acea 100644 --- a/src/lua/init.cc +++ b/src/lua/init.cc @@ -1381,7 +1381,9 @@ tarantool_lua_dofile(struct lua_State *L, const char *filename) { lua_getglobal(L, "dofile"); lua_pushstring(L, filename); - return lua_pcall(L, 1, 1, 0); + lbox_pcall(L); + bool result = lua_toboolean(L, 1); + return result ? 0 : 1; } void @@ -1503,6 +1505,9 @@ load_init_script(va_list ap) /* Execute the init file. */ if (tarantool_lua_dofile(L, path)) panic("%s", lua_tostring(L, -1)); + + /* clear the stack from return values. */ + lua_settop(L, 0); } /* * The file doesn't exist. It's OK, tarantool may @@ -1555,3 +1560,10 @@ tarantool_lua_load_init_script(struct lua_State *L) */ tarantool_lua_sandbox(tarantool_L); } + +void * +lua_region_alloc(void *ctx, size_t size) +{ + struct lua_State *L = (struct lua_State *) ctx; + return lua_newuserdata(L, size); +} diff --git a/src/memcached.cc b/src/memcached.cc index 5494b5862b87f3790199249b82fada07641c0c6e..7e99cf54a610db095008801dab29bae87d7b5a1d 100644 --- a/src/memcached.cc +++ b/src/memcached.cc @@ -501,7 +501,6 @@ memcached_space_init() if (cfg.memcached_port == 0) return; - /* Configure memcached index key. */ struct key_def *key_def = (struct key_def *) malloc(sizeof(struct key_def)); key_def->part_count = 1; @@ -511,21 +510,13 @@ memcached_space_init() key_def->parts = (struct key_part *) malloc(sizeof(struct key_part)); key_def->cmp_order = (uint32_t *) malloc(sizeof(uint32_t)); - if (key_def->parts == NULL || key_def->cmp_order == NULL) - panic("out of memory when configuring memcached_space"); - key_def->parts[0].fieldno = 0; key_def->parts[0].type = STRING; key_def->max_fieldno = 1; key_def->cmp_order[0] = 0; - - struct space *memc_s = - space_create(cfg.memcached_space, key_def, 1, 4); - - Index *memc_index = Index::factory(HASH, key_def, memc_s); - space_set_index(memc_s, 0, memc_index); + (void) space_new(cfg.memcached_space, key_def, 1, 4); } /** Delete a bunch of expired keys. */ diff --git a/src/tarantool.cc b/src/tarantool.cc index 22c8766928cd01244c674fa691ae51b7ded2d1d8..f467159a42a997c69d27282cb045a2948def4908 100644 --- a/src/tarantool.cc +++ b/src/tarantool.cc @@ -84,7 +84,7 @@ struct tarantool_cfg cfg; static ev_signal *sigs = NULL; int snapshot_pid = 0; /* snapshot processes pid */ -bool init_storage, booting = true; +bool booting = true; extern const void *opt_def; static int @@ -578,7 +578,7 @@ background() } void -tarantool_free(void) +tarantool_lua_free() { /* * Got to be done prior to anything else, since GC @@ -586,7 +586,13 @@ tarantool_free(void) */ if (tarantool_L) tarantool_lua_close(tarantool_L); + tarantool_L = NULL; +} + +void +tarantool_free(void) +{ recovery_free(); stat_free(); @@ -622,6 +628,13 @@ initialize_minimal() coeio_init(); } +/** Callback of snapshot_save() when doing --init-storage */ +void +init_storage(struct log_io * /* l */, struct fio_batch * /* batch */) +{ + /* Nothing. */ +} + int main(int argc, char **argv) { @@ -790,11 +803,10 @@ main(int argc, char **argv) } if (gopt(opt, 'I')) { - init_storage = true; initialize_minimal(); - box_init(); + box_init(true); set_lsn(recovery_state, 1); - snapshot_save(recovery_state, box_snapshot); + snapshot_save(recovery_state, init_storage); exit(EXIT_SUCCESS); } @@ -838,7 +850,8 @@ main(int argc, char **argv) try { tarantool_L = tarantool_lua_init(); - box_init(); + box_init(false); + atexit(tarantool_lua_free); memcached_init(cfg.bind_ipaddr, cfg.memcached_port); tarantool_lua_load_cfg(tarantool_L, &cfg); /* diff --git a/test/box/lua_misc.result b/test/box/lua_misc.result index d931d1f6be428a68e8d13331a967c9874d199d82..e4a93593306388d349eb4c4dd1fdc46491e72695 100644 --- a/test/box/lua_misc.result +++ b/test/box/lua_misc.result @@ -105,25 +105,27 @@ box.error.ER_NONMASTER: 258 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_FIELD_TYPE: 10242 +box.error.ER_PROC_LUA: 13058 +box.error.ER_TUPLE_FOUND: 14082 box.error.ER_OK: 0 -box.error.ER_NO_SUCH_INDEX: 13570 +box.error.ER_NO_SUCH_FIELD: 13826 box.error.ER_TUPLE_NOT_FOUND: 12546 box.error.ER_FIBER_STACK: 6658 -box.error.ER_UNKNOWN_UPDATE_OP: 11266 -box.error.ER_TUPLE_FOUND: 14082 +box.error.ER_SPLICE: 10754 +box.error.ER_NO_SUCH_INDEX: 13570 box.error.ER_UNSUPPORTED: 2562 box.error.ER_INJECTION: 2306 -box.error.ER_NO_SUCH_FIELD: 13826 -box.error.ER_TUPLE_IS_RO: 1025 +box.error.ER_SPACE_DISABLED: 13314 +box.error.ER_INDEX_TYPE: 1282 box.error.ER_ARG_TYPE: 10498 box.error.ER_NO_SUCH_SPACE: 14594 -box.error.ER_SPACE_DISABLED: 13314 -box.error.ER_PROC_LUA: 13058 +box.error.ER_UNKNOWN_UPDATE_OP: 11266 +box.error.ER_SPACE_EXISTS: 1538 box.error.ER_NO_SUCH_PROC: 12802 -box.error.ER_SPLICE: 10754 -box.error.ER_KEY_PART_COUNT: 12034 box.error.ER_WAL_IO: 9986 -box.error.ER_FIELD_TYPE: 10242 +box.error.ER_KEY_PART_COUNT: 12034 +box.error.ER_TUPLE_IS_RO: 1025 +box.error.ER_SECONDARY: 770 box.error.ER_MEMORY_ISSUE: 1793 ... diff --git a/test/lib/server.py b/test/lib/server.py index 8f292b14cb33b00a38aadc3ae36d96b467f1d8ca..20be099eb85ac228423d67a608e09cb501057f00 100644 --- a/test/lib/server.py +++ b/test/lib/server.py @@ -226,7 +226,9 @@ class Server(object): return # kill process - os.kill(self.read_pidfile(), signal.SIGTERM) + pid = self.read_pidfile(); + if pid != -1: + os.kill(pid, signal.SIGTERM) #self.process.kill(signal.SIGTERM) if self.gdb or self.valgrind: self.process.expect(pexpect.EOF, timeout = 1 << 30) diff --git a/test/lib/sql_ast.py b/test/lib/sql_ast.py index 6bf6e9f59938b4976efa0c2bca1ac35df8afc305..26b6d07e435dc3bd1bb1be0e508603902c5b3755 100644 --- a/test/lib/sql_ast.py +++ b/test/lib/sql_ast.py @@ -30,8 +30,8 @@ ER = { 2: "ER_ILLEGAL_PARAMS" , 3: "ER_SECONDARY" , 4: "ER_TUPLE_IS_RO" , - 5: "ER_UNUSED5" , - 6: "ER_UNUSED6" , + 5: "ER_INDEX_TYPE" , + 6: "ER_SPACE_EXISTS" , 7: "ER_MEMORY_ISSUE" , 8: "ER_UNUSED8" , 9: "ER_INJECTION" , diff --git a/test/replication/memcached.test b/test/replication/memcached.test index 0152df2e3cd20200b7f89813956dfe3cd8c5e15f..f7c3d45fbffbbb231d6fd78430a0143348434096 100644 --- a/test/replication/memcached.test +++ b/test/replication/memcached.test @@ -22,39 +22,21 @@ replica.deploy("replication/cfg/replica.cfg", replica_memcached = replica.memcached ################################### -def wait_for_lsn(lsn, serv): - serv_admin = serv.admin - while True: - if get_lsn(serv) == lsn: - return lsn - time.sleep(0.01) - -def wait_for_next_lsn(lsn, serv): - serv_admin = serv.admin - while True: - if get_lsn(serv) != lsn: - return lsn - time.sleep(0.01) - def get_lsn(serv): serv_admin = serv.admin resp = exec serv_admin silent "lua box.info.lsn" return yaml.load(resp)[0] -def wait(next = False, serv_master = master, serv_replica = replica): - if next: - lsn = get_lsn(serv_replica) - return wait_for_next_lsn(lsn, serv_replica) - else: - lsn = get_lsn(serv_master) - return wait_for_lsn(lsn, serv_replica) +def wait(serv_master = master, serv_replica = replica): + lsn = get_lsn(serv_master) + serv_replica.wait_lsn(lsn) + return lsn def get_memcached_len(serv): serv_admin = serv.admin resp = exec serv_admin silent "lua box.space[box.cfg.memcached_space]:len()" return yaml.load(resp)[0] - def wait_for_empty_space(serv): serv_admin = serv.admin while True: @@ -102,20 +84,21 @@ exec replica_memcached "get 10\r\n" print """# check that expiration is working properly on replica""" exec master_memcached silent "set 1 0 1 %d\r\n%s\r\n" % (len(sonet[0]), sonet[0]) -wait() +lsn = wait() exec replica_memcached "get 1\r\n" -wait(True) +replica.wait_lsn(lsn + 1) exec replica_memcached "get 1\r\n" print """# check that expiration is working properly, when replica becomes master""" exec master_memcached silent "set 1 0 1 %d\r\n%s\r\n" % (len(sonet[0]), sonet[0]) +lsn = wait() replica.reconfigure("replication/cfg/replica_to_master.cfg") exec replica_memcached "get 1\r\n" -wait(True) +replica.wait_lsn(lsn + 1) exec replica_memcached "get 1\r\n" -# resore default suite config +# restore default suite config replica.stop() replica.cleanup(True) master.stop() diff --git a/test/unit/base64.c b/test/unit/base64.c index dca11da881db7b082e8e25aa444514984cf65bdc..89077f69283252e159273eaa4cb8ff9ac63e1b99 100644 --- a/test/unit/base64.c +++ b/test/unit/base64.c @@ -9,7 +9,7 @@ base64_test(const char *str) header(); int len = strlen(str); - int base64_buflen = len * 4/3 + 4; + int base64_buflen = base64_bufsize(len); char *base64_buf = malloc(base64_buflen); char *strbuf = malloc(len + 1);