diff --git a/mod/box/CMakeLists.txt b/mod/box/CMakeLists.txt index 259ef3e4107b3c01460538c25cf5d1b2e5c9e5d8..790e960cafb5c5e5851d27cd1f86961f4aa0e916 100644 --- a/mod/box/CMakeLists.txt +++ b/mod/box/CMakeLists.txt @@ -34,5 +34,5 @@ set_source_files_properties(memcached-grammar.m set_source_files_properties(memcached.m PROPERTIES COMPILE_FLAGS "-Wno-uninitialized") -tarantool_module("box" tuple.m index.m tree.m box.m box.lua.c box_lua.m - memcached.m memcached-grammar.m) +tarantool_module("box" tuple.m index.m tree.m space.m box.m box.lua.c + box_lua.m memcached.m memcached-grammar.m) diff --git a/mod/box/box.h b/mod/box/box.h index bf94f4c8e728034784b62979bde3cd9954938431..a1838244c0b25d8fe9e81d5e9787432b2f2e1cdd 100644 --- a/mod/box/box.h +++ b/mod/box/box.h @@ -25,8 +25,6 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ - -#include <mod/box/index.h> #include "exception.h" #include "iproto.h" #include <tbuf.h> @@ -35,58 +33,11 @@ struct tarantool_cfg; struct box_tuple; -enum -{ - BOX_INDEX_MAX = 10, - BOX_SPACE_MAX = 256, +enum { /** A limit on how many operations a single UPDATE can have. */ BOX_UPDATE_OP_CNT_MAX = 128, }; -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 - * must have exactly this many fields. - */ - int arity; - - /** - * The number of indexes in the space. - * - * It is equal to the number of non-nil members of the index - * array and defines the key_defs array size as well. - */ - int key_count; - - /** - * The descriptors for all indexes that belong to the 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. - */ - int max_fieldno; - - bool enabled; -}; - -extern struct space *space; - struct box_out { void (*add_u32)(u32 *u32); void (*dup_u32)(u32 u32); @@ -94,6 +45,8 @@ struct box_out { }; extern struct box_out box_out_quiet; +@class Index; +struct space; struct box_txn { u16 op; @@ -171,41 +124,6 @@ ENUM(update_op_codes, UPDATE_OP_CODES); extern iproto_callback rw_callback; -/** - * Get space ordinal number. - */ -static inline int -space_n(struct space *sp) -{ - assert(sp >= space && sp < (space + BOX_SPACE_MAX)); - return sp - space; -} - -/** - * Get key_def ordinal number. - */ -static inline int -key_def_n(struct space *sp, struct key_def *kp) -{ - assert(kp >= sp->key_defs && kp < (sp->key_defs + sp->key_count)); - return kp - sp->key_defs; -} - -/** - * Get index ordinal number. - */ -static inline int -index_n(Index *index) -{ - return key_def_n(index->space, index->key_def); -} - -static inline bool -index_is_primary(Index *index) -{ - return index_n(index) == 0; -} - /* These are used to implement memcached 'GET' */ static inline struct box_txn *in_txn() { return fiber->mod_data.txn; } struct box_txn *txn_begin(); diff --git a/mod/box/box.m b/mod/box/box.m index e7abf9b68eb00b5fd6f1809d3190798e7a856a53..7352542a7c13b9cc99ea83000ba646d06f826f33 100644 --- a/mod/box/box.m +++ b/mod/box/box.m @@ -47,6 +47,7 @@ #include <mod/box/tuple.h> #include "memcached.h" #include "box_lua.h" +#include "space.h" extern pid_t logger_pid; @@ -78,11 +79,6 @@ const int BOX_REF_THRESHOLD = 8196; struct space *space = NULL; -/* 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; - struct box_snap_row { u32 space; u32 tuple_size; @@ -91,21 +87,6 @@ struct box_snap_row { } __attribute__((packed)); -static inline int -index_count(struct space *sp) -{ - if (!secondary_indexes_enabled) { - /* If the secondary indexes are not enabled yet - then we can use only the primary index. So - return 1 if there is at least one index (which - must be primary) and return 0 otherwise. */ - return sp->key_count > 0; - } else { - /* Return the actual number of indexes. */ - return sp->key_count; - } -} - static inline struct box_snap_row * box_snap_row(const struct tbuf *t) { @@ -1482,223 +1463,6 @@ xlog_print(struct recovery_state *r __attribute__((unused)), struct tbuf *t) return res; } -/** Free a key definition. */ -static void -key_free(struct key_def *key_def) -{ - free(key_def->parts); - free(key_def->cmp_order); -} - -static void -space_free(void) -{ - int i; - for (i = 0 ; i < BOX_SPACE_MAX ; i++) { - if (!space[i].enabled) - continue; - - int j; - for (j = 0 ; j < space[i].key_count; j++) { - Index *index = space[i].index[j]; - [index free]; - key_free(&space[i].key_defs[j]); - } - - free(space[i].key_defs); - free(space[i].field_types); - } -} - -static void -key_init(struct key_def *def, struct tarantool_cfg_space_index *cfg_index) -{ - def->max_fieldno = 0; - def->part_count = 0; - - /* Calculate key part count and maximal field number. */ - for (int k = 0; cfg_index->key_field[k] != NULL; ++k) { - typeof(cfg_index->key_field[k]) 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 = 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 = malloc(def->max_fieldno * sizeof(u32)); - if (def->cmp_order == NULL) { - panic("can't allocate def cmp_order array for index"); - } - memset(def->cmp_order, -1, def->max_fieldno * sizeof(u32)); - - /* fill fields and compare order */ - for (int k = 0; cfg_index->key_field[k] != NULL; ++k) { - typeof(cfg_index->key_field[k]) 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 */ - 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) -{ - int i, max_fieldno; - int 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 = malloc(max_fieldno * sizeof(enum field_data_type)); - for (i = 0; i < max_fieldno; i++) { - space->field_types[i] = UNKNOWN; - } - - /* extract field type info */ - for (i = 0; i < key_count; i++) { - struct key_def *def = &key_defs[i]; - for (int 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 (int 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(void) -{ - /* exit if no spaces are configured */ - if (cfg.space == NULL) { - return; - } - - /* fill box spaces */ - for (int i = 0; cfg.space[i] != NULL; ++i) { - tarantool_cfg_space *cfg_space = cfg.space[i]; - - if (!CNF_STRUCT_DEFINED(cfg_space) || !cfg_space->enabled) - continue; - - assert(cfg.memcached_port == 0 || i != cfg.memcached_space); - - space[i].enabled = true; - space[i].arity = cfg_space->cardinality; - - /* - * Collect key/field info. We need aggregate - * information on all keys before we can create - * indexes. - */ - space[i].key_count = 0; - for (int j = 0; cfg_space->index[j] != NULL; ++j) { - ++space[i].key_count; - } - - space[i].key_defs = malloc(space[i].key_count * - sizeof(struct key_def)); - if (space[i].key_defs == NULL) { - panic("can't allocate key def array"); - } - for (int j = 0; cfg_space->index[j] != NULL; ++j) { - typeof(cfg_space->index[j]) cfg_index = cfg_space->index[j]; - key_init(&space[i].key_defs[j], cfg_index); - } - space_init_field_types(&space[i]); - - /* fill space indexes */ - for (int j = 0; cfg_space->index[j] != NULL; ++j) { - typeof(cfg_space->index[j]) cfg_index = cfg_space->index[j]; - enum index_type type = STR2ENUM(index_type, cfg_index->type); - struct key_def *key_def = &space[i].key_defs[j]; - Index *index = [Index alloc: type :key_def :&space[i]]; - [index init: key_def :&space[i]]; - space[i].index[j] = index; - } - say_info("space %i successfully configured", i); - } -} - -static void -space_init(void) -{ - /* Allocate and initialize space memory. */ - space = palloc(eter_pool, sizeof(struct space) * BOX_SPACE_MAX); - memset(space, 0, sizeof(struct space) * BOX_SPACE_MAX); - - /* configure regular spaces */ - space_config(); - - /* configure memcached space */ - memcached_space_init(); -} - -static void -build_indexes(void) -{ - for (u32 n = 0; n < BOX_SPACE_MAX; ++n) { - if (space[n].enabled == false) - continue; - if (space[n].key_count <= 1) - continue; /* no secondary keys */ - - say_info("Building secondary keys in space %" PRIu32 "...", n); - - Index *pk = space[n].index[0]; - for (int i = 1; i < space[n].key_count; i++) { - Index *index = space[n].index[i]; - [index build: pk]; - } - - say_info("Space %"PRIu32": done", n); - } -} - static void box_process_rw(u32 op, struct tbuf *request_data) { @@ -1849,179 +1613,6 @@ box_leave_local_standby_mode(void *data __attribute__((unused))) box_enter_master_or_replica_mode(&cfg); } -static i32 -check_spaces(struct tarantool_cfg *conf) -{ - /* exit if no spaces are configured */ - if (conf->space == NULL) { - return 0; - } - - for (size_t i = 0; conf->space[i] != NULL; ++i) { - typeof(conf->space[i]) space = conf->space[i]; - - if (!CNF_STRUCT_DEFINED(space)) { - /* space undefined, skip it */ - continue; - } - - if (!space->enabled) { - /* space disabled, skip it */ - continue; - } - - /* check space bound */ - if (i >= BOX_SPACE_MAX) { - /* maximum space is reached */ - out_warning(0, "(space = %zu) " - "too many spaces (%i maximum)", i, space); - return -1; - } - - if (conf->memcached_port && i == conf->memcached_space) { - out_warning(0, "Space %i is already used as " - "memcached_space.", i); - return -1; - } - - /* at least one index in space must be defined - * */ - if (space->index == NULL) { - out_warning(0, "(space = %zu) " - "at least one index must be defined", i); - return -1; - } - - int max_key_fieldno = -1; - - /* check spaces indexes */ - for (size_t j = 0; space->index[j] != NULL; ++j) { - typeof(space->index[j]) index = space->index[j]; - u32 key_part_count = 0; - enum index_type index_type; - - /* check index bound */ - if (j >= BOX_INDEX_MAX) { - /* maximum index in space reached */ - out_warning(0, "(space = %zu index = %zu) " - "too many indexed (%i maximum)", i, j, BOX_INDEX_MAX); - return -1; - } - - /* at least one key in index must be defined */ - if (index->key_field == NULL) { - out_warning(0, "(space = %zu index = %zu) " - "at least one field must be defined", i, j); - return -1; - } - - /* check unique property */ - if (index->unique == -1) { - /* unique property undefined */ - out_warning(0, "(space = %zu index = %zu) " - "unique property is undefined", i, j); - } - - for (size_t k = 0; index->key_field[k] != NULL; ++k) { - typeof(index->key_field[k]) key = index->key_field[k]; - - if (key->fieldno == -1) { - /* last key reached */ - break; - } - - /* key must has valid type */ - if (STR2ENUM(field_data_type, key->type) == field_data_type_MAX) { - out_warning(0, "(space = %zu index = %zu) " - "unknown field data type: `%s'", i, j, key->type); - return -1; - } - - if (max_key_fieldno < key->fieldno) { - max_key_fieldno = key->fieldno; - } - - ++key_part_count; - } - - /* Check key part count. */ - if (key_part_count == 0) { - out_warning(0, "(space = %zu index = %zu) " - "at least one field must be defined", i, j); - return -1; - } - - index_type = STR2ENUM(index_type, index->type); - - /* check index type */ - if (index_type == index_type_MAX) { - out_warning(0, "(space = %zu index = %zu) " - "unknown index type '%s'", i, j, index->type); - return -1; - } - - /* First index must be unique. */ - if (j == 0 && index->unique == false) { - out_warning(0, "(space = %zu) space first index must be unique", i); - return -1; - } - - switch (index_type) { - case HASH: - /* check hash index */ - /* hash index must has single-field key */ - if (key_part_count != 1) { - out_warning(0, "(space = %zu index = %zu) " - "hash index must has a single-field key", i, j); - return -1; - } - /* hash index must be unique */ - if (!index->unique) { - out_warning(0, "(space = %zu index = %zu) " - "hash index must be unique", i, j); - return -1; - } - break; - case TREE: - /* extra check for tree index not needed */ - break; - default: - assert(false); - } - } - - /* Check for index field type conflicts */ - if (max_key_fieldno >= 0) { - char *types = alloca(max_key_fieldno + 1); - memset(types, UNKNOWN, max_key_fieldno + 1); - for (size_t j = 0; space->index[j] != NULL; ++j) { - typeof(space->index[j]) index = space->index[j]; - for (size_t k = 0; index->key_field[k] != NULL; ++k) { - typeof(index->key_field[k]) key = index->key_field[k]; - int f = key->fieldno; - if (f == -1) { - break; - } - enum field_data_type t = STR2ENUM(field_data_type, key->type); - assert(t != field_data_type_MAX); - if (types[f] != t) { - if (types[f] == UNKNOWN) { - types[f] = t; - } else { - out_warning(0, "(space = %zu fieldno = %zu) " - "index field type mismatch", i, f); - return -1; - } - } - } - - } - } - } - - return 0; -} - i32 mod_check_config(struct tarantool_cfg *conf) { @@ -2134,6 +1725,8 @@ mod_init(void) /* initialization spaces */ space_init(); + /* configure memcached space */ + memcached_space_init(); /* recovery initialization */ recovery_init(cfg.snap_dir, cfg.wal_dir, diff --git a/mod/box/box_lua.m b/mod/box/box_lua.m index c161c3c697daa633f3b9f630a6ecd9acec93d7a2..caa0a047e26e103bc044267f9ebfb295ff53f52b 100644 --- a/mod/box/box_lua.m +++ b/mod/box/box_lua.m @@ -43,6 +43,7 @@ #include "pickle.h" #include "tuple.h" +#include "space.h" /* contents of box.lua */ extern const char box_lua[]; diff --git a/mod/box/index.m b/mod/box/index.m index d9a896d6ca70c11d2030565a7408d21d878be1b1..154cd919294bb15c30037e5fc0c0b6c736d9de88 100644 --- a/mod/box/index.m +++ b/mod/box/index.m @@ -29,7 +29,7 @@ #include "tuple.h" #include "pickle.h" #include "exception.h" -#include "box.h" +#include "space.h" #include "assoc.h" const char *field_data_type_strs[] = {"NUM", "NUM64", "STR", "\0"}; diff --git a/mod/box/memcached.m b/mod/box/memcached.m index a7ad8997b122610334fb8d01d8a583d6db94df60..1641d3241227923687b82500c3454558f2288982 100644 --- a/mod/box/memcached.m +++ b/mod/box/memcached.m @@ -33,6 +33,7 @@ #include "stat.h" #include "salloc.h" #include "pickle.h" +#include "space.h" #define STAT(_) \ _(MEMC_GET, 1) \ diff --git a/mod/box/space.h b/mod/box/space.h new file mode 100644 index 0000000000000000000000000000000000000000..74256b0baec379c7b2b5467a4424da23da591aa9 --- /dev/null +++ b/mod/box/space.h @@ -0,0 +1,142 @@ +#ifndef TARANTOOL_SPACE_H_INCLUDED +#define TARANTOOL_SPACE_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 "mod/box/index.h" + +struct tarantool_cfg; + +enum { + BOX_INDEX_MAX = 10, + BOX_SPACE_MAX = 256, +}; + +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 + * must have exactly this many fields. + */ + int arity; + + /** + * The number of indexes in the space. + * + * It is equal to the number of non-nil members of the index + * array and defines the key_defs array size as well. + */ + int key_count; + + /** + * The descriptors for all indexes that belong to the 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. + */ + int max_fieldno; + + bool enabled; +}; + +extern struct space *space; + +/** Get space ordinal number. */ +static inline int +space_n(struct space *sp) +{ + assert(sp >= space && sp < (space + BOX_SPACE_MAX)); + return sp - space; +} + +/** Get key_def ordinal number. */ +static inline int +key_def_n(struct space *sp, struct key_def *kp) +{ + assert(kp >= sp->key_defs && kp < (sp->key_defs + sp->key_count)); + return kp - sp->key_defs; +} + +/** Get index ordinal number in space. */ +static inline int +index_n(Index *index) +{ + return key_def_n(index->space, index->key_def); +} + +/** Check whether or not an index is primary in space. */ +static inline bool +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; + +static inline int +index_count(struct space *sp) +{ + if (!secondary_indexes_enabled) { + /* If secondary indexes are not enabled yet, + we can use only the primary index. So return + 1 if there is at least one index (which + must be primary) and return 0 otherwise. */ + return sp->key_count > 0; + } else { + /* Return the actual number of indexes. */ + return sp->key_count; + } +} + +void space_init(void); +void space_free(void); +i32 check_spaces(struct tarantool_cfg *conf); +/* Build secondary keys. */ +void build_indexes(void); + +#endif /* TARANTOOL_SPACE_H_INCLUDED */ diff --git a/mod/box/space.m b/mod/box/space.m new file mode 100644 index 0000000000000000000000000000000000000000..9d427dad68420334d3b0fcceb1b7b9b3f3b7206b --- /dev/null +++ b/mod/box/space.m @@ -0,0 +1,423 @@ +/* + * 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 "space.h" +#include <stdlib.h> +#include <cfg/tarantool_box_cfg.h> +#include <cfg/warning.h> +#include <tarantool.h> +#include <exception.h> + +bool secondary_indexes_enabled = false; +/** Free a key definition. */ +static void +key_free(struct key_def *key_def) +{ + free(key_def->parts); + free(key_def->cmp_order); +} + +void +space_free(void) +{ + int i; + for (i = 0 ; i < BOX_SPACE_MAX ; i++) { + if (!space[i].enabled) + continue; + + int j; + for (j = 0 ; j < space[i].key_count; j++) { + Index *index = space[i].index[j]; + [index free]; + key_free(&space[i].key_defs[j]); + } + + free(space[i].key_defs); + free(space[i].field_types); + } +} + +static void +key_init(struct key_def *def, struct tarantool_cfg_space_index *cfg_index) +{ + def->max_fieldno = 0; + def->part_count = 0; + + /* Calculate key part count and maximal field number. */ + for (int k = 0; cfg_index->key_field[k] != NULL; ++k) { + typeof(cfg_index->key_field[k]) 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 = 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 = malloc(def->max_fieldno * sizeof(u32)); + if (def->cmp_order == NULL) { + panic("can't allocate def cmp_order array for index"); + } + memset(def->cmp_order, -1, def->max_fieldno * sizeof(u32)); + + /* fill fields and compare order */ + for (int k = 0; cfg_index->key_field[k] != NULL; ++k) { + typeof(cfg_index->key_field[k]) 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 */ + 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) +{ + int i, max_fieldno; + int 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 = malloc(max_fieldno * sizeof(enum field_data_type)); + for (i = 0; i < max_fieldno; i++) { + space->field_types[i] = UNKNOWN; + } + + /* extract field type info */ + for (i = 0; i < key_count; i++) { + struct key_def *def = &key_defs[i]; + for (int 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 (int 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() +{ + /* exit if no spaces are configured */ + if (cfg.space == NULL) { + return; + } + + /* fill box spaces */ + for (int i = 0; cfg.space[i] != NULL; ++i) { + tarantool_cfg_space *cfg_space = cfg.space[i]; + + if (!CNF_STRUCT_DEFINED(cfg_space) || !cfg_space->enabled) + continue; + + assert(cfg.memcached_port == 0 || i != cfg.memcached_space); + + space[i].enabled = true; + space[i].arity = cfg_space->cardinality; + + /* + * Collect key/field info. We need aggregate + * information on all keys before we can create + * indexes. + */ + space[i].key_count = 0; + for (int j = 0; cfg_space->index[j] != NULL; ++j) { + ++space[i].key_count; + } + + space[i].key_defs = malloc(space[i].key_count * + sizeof(struct key_def)); + if (space[i].key_defs == NULL) { + panic("can't allocate key def array"); + } + for (int j = 0; cfg_space->index[j] != NULL; ++j) { + typeof(cfg_space->index[j]) cfg_index = cfg_space->index[j]; + key_init(&space[i].key_defs[j], cfg_index); + } + space_init_field_types(&space[i]); + + /* fill space indexes */ + for (int j = 0; cfg_space->index[j] != NULL; ++j) { + typeof(cfg_space->index[j]) cfg_index = cfg_space->index[j]; + enum index_type type = STR2ENUM(index_type, cfg_index->type); + struct key_def *key_def = &space[i].key_defs[j]; + Index *index = [Index alloc: type :key_def :&space[i]]; + [index init: key_def :&space[i]]; + space[i].index[j] = index; + } + say_info("space %i successfully configured", i); + } +} + +void +space_init(void) +{ + /* Allocate and initialize space memory. */ + space = palloc(eter_pool, sizeof(struct space) * BOX_SPACE_MAX); + memset(space, 0, sizeof(struct space) * BOX_SPACE_MAX); + + /* configure regular spaces */ + space_config(); +} + +void +build_indexes(void) +{ + for (u32 n = 0; n < BOX_SPACE_MAX; ++n) { + if (space[n].enabled == false) + continue; + if (space[n].key_count <= 1) + continue; /* no secondary keys */ + + say_info("Building secondary keys in space %" PRIu32 "...", n); + + Index *pk = space[n].index[0]; + for (int i = 1; i < space[n].key_count; i++) { + Index *index = space[n].index[i]; + [index build: pk]; + } + + say_info("Space %"PRIu32": done", n); + } +} + +i32 +check_spaces(struct tarantool_cfg *conf) +{ + /* exit if no spaces are configured */ + if (conf->space == NULL) { + return 0; + } + + for (size_t i = 0; conf->space[i] != NULL; ++i) { + typeof(conf->space[i]) space = conf->space[i]; + + if (!CNF_STRUCT_DEFINED(space)) { + /* space undefined, skip it */ + continue; + } + + if (!space->enabled) { + /* space disabled, skip it */ + continue; + } + + /* check space bound */ + if (i >= BOX_SPACE_MAX) { + /* maximum space is reached */ + out_warning(0, "(space = %zu) " + "too many spaces (%i maximum)", i, space); + return -1; + } + + if (conf->memcached_port && i == conf->memcached_space) { + out_warning(0, "Space %i is already used as " + "memcached_space.", i); + return -1; + } + + /* at least one index in space must be defined + * */ + if (space->index == NULL) { + out_warning(0, "(space = %zu) " + "at least one index must be defined", i); + return -1; + } + + int max_key_fieldno = -1; + + /* check spaces indexes */ + for (size_t j = 0; space->index[j] != NULL; ++j) { + typeof(space->index[j]) index = space->index[j]; + u32 key_part_count = 0; + enum index_type index_type; + + /* check index bound */ + if (j >= BOX_INDEX_MAX) { + /* maximum index in space reached */ + out_warning(0, "(space = %zu index = %zu) " + "too many indexed (%i maximum)", i, j, BOX_INDEX_MAX); + return -1; + } + + /* at least one key in index must be defined */ + if (index->key_field == NULL) { + out_warning(0, "(space = %zu index = %zu) " + "at least one field must be defined", i, j); + return -1; + } + + /* check unique property */ + if (index->unique == -1) { + /* unique property undefined */ + out_warning(0, "(space = %zu index = %zu) " + "unique property is undefined", i, j); + } + + for (size_t k = 0; index->key_field[k] != NULL; ++k) { + typeof(index->key_field[k]) key = index->key_field[k]; + + if (key->fieldno == -1) { + /* last key reached */ + break; + } + + /* key must has valid type */ + if (STR2ENUM(field_data_type, key->type) == field_data_type_MAX) { + out_warning(0, "(space = %zu index = %zu) " + "unknown field data type: `%s'", i, j, key->type); + return -1; + } + + if (max_key_fieldno < key->fieldno) { + max_key_fieldno = key->fieldno; + } + + ++key_part_count; + } + + /* Check key part count. */ + if (key_part_count == 0) { + out_warning(0, "(space = %zu index = %zu) " + "at least one field must be defined", i, j); + return -1; + } + + index_type = STR2ENUM(index_type, index->type); + + /* check index type */ + if (index_type == index_type_MAX) { + out_warning(0, "(space = %zu index = %zu) " + "unknown index type '%s'", i, j, index->type); + return -1; + } + + /* First index must be unique. */ + if (j == 0 && index->unique == false) { + out_warning(0, "(space = %zu) space first index must be unique", i); + return -1; + } + + switch (index_type) { + case HASH: + /* check hash index */ + /* hash index must has single-field key */ + if (key_part_count != 1) { + out_warning(0, "(space = %zu index = %zu) " + "hash index must has a single-field key", i, j); + return -1; + } + /* hash index must be unique */ + if (!index->unique) { + out_warning(0, "(space = %zu index = %zu) " + "hash index must be unique", i, j); + return -1; + } + break; + case TREE: + /* extra check for tree index not needed */ + break; + default: + assert(false); + } + } + + /* Check for index field type conflicts */ + if (max_key_fieldno >= 0) { + char *types = alloca(max_key_fieldno + 1); + memset(types, UNKNOWN, max_key_fieldno + 1); + for (size_t j = 0; space->index[j] != NULL; ++j) { + typeof(space->index[j]) index = space->index[j]; + for (size_t k = 0; index->key_field[k] != NULL; ++k) { + typeof(index->key_field[k]) key = index->key_field[k]; + int f = key->fieldno; + if (f == -1) { + break; + } + enum field_data_type t = STR2ENUM(field_data_type, key->type); + assert(t != field_data_type_MAX); + if (types[f] != t) { + if (types[f] == UNKNOWN) { + types[f] = t; + } else { + out_warning(0, "(space = %zu fieldno = %zu) " + "index field type mismatch", i, f); + return -1; + } + } + } + + } + } + } + + return 0; +} + diff --git a/mod/box/tree.m b/mod/box/tree.m index 37b8367850de9bc12606eb1486fcc23a145a867f..1546651ca07304cc7472edf7eceed3be2fe5434d 100644 --- a/mod/box/tree.m +++ b/mod/box/tree.m @@ -24,8 +24,9 @@ */ #include "tree.h" -#include "box.h" #include "tuple.h" +#include "space.h" +#include "exception.h" #include <pickle.h> /* {{{ Utilities. *************************************************/