diff --git a/src/box/CMakeLists.txt b/src/box/CMakeLists.txt index d3b1b038356c6c4fcdae6b001c4ade94534ce935..4fd2250d9b61acbb1dd2e1400fa19c5fd5a9d4be 100644 --- a/src/box/CMakeLists.txt +++ b/src/box/CMakeLists.txt @@ -26,6 +26,7 @@ add_library(box STATIC error.cc xrow.cc xlog.cc + tuple_format.cc tuple.cc tuple_convert.cc tuple_update.cc diff --git a/src/box/tuple.cc b/src/box/tuple.cc index d19468dbdcc5f970c7b46b4fab335e9d464e8db8..af75518b813ac376d27460ee0ac87ba2935d2075 100644 --- a/src/box/tuple.cc +++ b/src/box/tuple.cc @@ -35,13 +35,6 @@ #include "fiber.h" -/** Global table of tuple formats */ -struct tuple_format **tuple_formats; -struct tuple_format *tuple_format_ber; -static intptr_t recycled_format_ids = FORMAT_ID_NIL; - -static uint32_t formats_size, formats_capacity; - uint32_t snapshot_version; struct quota memtx_quota; @@ -67,156 +60,6 @@ static struct mempool tuple_iterator_pool; */ struct tuple *box_tuple_last; -/** Extract all available type info from keys. */ -static void -field_type_create(struct tuple_format *format, struct rlist *key_list) -{ - /* There may be fields between indexed fields (gaps). */ - for (uint32_t i = 0; i < format->field_count; i++) - format->fields[i].type = UNKNOWN; - - struct key_def *key_def; - /* extract field type info */ - rlist_foreach_entry(key_def, key_list, link) { - for (uint32_t i = 0; i < key_def->part_count; i++) { - assert(key_def->parts[i].fieldno < format->field_count); - enum field_type set_type = key_def->parts[i].type; - enum field_type *fmt_type = - &format->fields[key_def->parts[i].fieldno].type; - if (*fmt_type != UNKNOWN && *fmt_type != set_type) { - tnt_raise(ClientError, - ER_FIELD_TYPE_MISMATCH, - key_def->name, - i + INDEX_OFFSET, - field_type_strs[set_type], - field_type_strs[*fmt_type]); - } - *fmt_type = set_type; - } - } -} - -static void -tuple_format_register(struct tuple_format *format) -{ - if (recycled_format_ids != FORMAT_ID_NIL) { - - format->id = (uint16_t) recycled_format_ids; - recycled_format_ids = (intptr_t) tuple_formats[recycled_format_ids]; - } else { - if (formats_size == formats_capacity) { - uint32_t new_capacity = formats_capacity ? - formats_capacity * 2 : 16; - struct tuple_format **formats; - formats = (struct tuple_format **) - realloc(tuple_formats, new_capacity * - sizeof(tuple_formats[0])); - if (formats == NULL) - tnt_raise(OutOfMemory, - sizeof(struct tuple_format), - "malloc", "tuple_formats"); - - formats_capacity = new_capacity; - tuple_formats = formats; - } - if (formats_size == FORMAT_ID_MAX + 1) { - tnt_raise(LoggedError, ER_TUPLE_FORMAT_LIMIT, - (unsigned) formats_capacity); - } - format->id = formats_size++; - } - tuple_formats[format->id] = format; -} - -static void -tuple_format_deregister(struct tuple_format *format) -{ - if (format->id == FORMAT_ID_NIL) - return; - tuple_formats[format->id] = (struct tuple_format *) recycled_format_ids; - recycled_format_ids = format->id; - format->id = FORMAT_ID_NIL; -} - -static struct tuple_format * -tuple_format_alloc(struct rlist *key_list) -{ - struct key_def *key_def; - uint32_t max_fieldno = 0; - uint32_t key_count = 0; - - /* find max max field no */ - rlist_foreach_entry(key_def, key_list, link) { - struct key_part *part = key_def->parts; - struct key_part *pend = part + key_def->part_count; - key_count++; - for (; part < pend; part++) - max_fieldno = MAX(max_fieldno, part->fieldno); - } - uint32_t field_count = key_count > 0 ? max_fieldno + 1 : 0; - - uint32_t total = sizeof(struct tuple_format) + - field_count * sizeof(struct tuple_field_format); - - struct tuple_format *format = (struct tuple_format *) malloc(total); - - if (format == NULL) - tnt_raise(OutOfMemory, sizeof(struct tuple_format), - "malloc", "tuple format"); - - format->refs = 0; - format->id = FORMAT_ID_NIL; - format->field_count = field_count; - return format; -} - -void -tuple_format_delete(struct tuple_format *format) -{ - tuple_format_deregister(format); - free(format); -} - -struct tuple_format * -tuple_format_new(struct rlist *key_list) -{ - struct tuple_format *format = tuple_format_alloc(key_list); - - try { - tuple_format_register(format); - field_type_create(format, key_list); - } catch (Exception *e) { - tuple_format_delete(format); - throw; - } - - /* Set up offset slots */ - if (format->field_count == 0) { - /* Nothing to store */ - format->field_map_size = 0; - return format; - } - /** - * First field is always simply accessible, - * so we don't store offset for it - */ - format->fields[0].offset_slot = INT32_MAX; - - int current_slot = 0; - for (uint32_t i = 1; i < format->field_count; i++) { - /* - * In the tuple, store only offsets necessary to - * quickly access indexed fields. - */ - if (format->fields[i].type == UNKNOWN) - format->fields[i].offset_slot = INT32_MAX; - else - format->fields[i].offset_slot = --current_slot; - } - format->field_map_size = -current_slot * sizeof(uint32_t); - return format; -} - /* * Validate a new tuple format and initialize tuple-local * format data. @@ -658,10 +501,7 @@ void tuple_init(float tuple_arena_max_size, uint32_t objsize_min, uint32_t objsize_max, float alloc_factor) { - RLIST_HEAD(empty_list); - tuple_format_ber = tuple_format_new(&empty_list); - /* Make sure this one stays around. */ - tuple_format_ref(tuple_format_ber, 1); + tuple_format_init(); /* Apply lowest allowed objsize bounds */ if (objsize_min < OBJSIZE_MIN) @@ -711,19 +551,6 @@ tuple_free() } mempool_destroy(&tuple_iterator_pool); - - /* Clear recycled ids. */ - while (recycled_format_ids != FORMAT_ID_NIL) { - - uint16_t id = (uint16_t) recycled_format_ids; - recycled_format_ids = (intptr_t) tuple_formats[id]; - tuple_formats[id] = NULL; - } - for (struct tuple_format **format = tuple_formats; - format < tuple_formats + formats_size; - format++) - free(*format); /* ignore the reference count. */ - free(tuple_formats); } void diff --git a/src/box/tuple.h b/src/box/tuple.h index b537ed045625257a4e00ee683620b3b4113c28ff..8ed32d6ed940e9a814ae1ec53f4868a97eee2961 100644 --- a/src/box/tuple.h +++ b/src/box/tuple.h @@ -32,6 +32,8 @@ */ #include "trivia/util.h" +#include "tuple_format.h" + #if defined(__cplusplus) extern "C" { #endif /* defined(__cplusplus) */ @@ -257,14 +259,7 @@ box_tuple_upsert(const box_tuple_t *tuple, const char *expr, const #include "tuple_update.h" #include "errinj.h" -enum { FORMAT_ID_MAX = UINT16_MAX - 1, FORMAT_ID_NIL = UINT16_MAX }; -enum { FORMAT_REF_MAX = INT32_MAX, TUPLE_REF_MAX = UINT16_MAX }; -/* - * We don't pass INDEX_OFFSET around dynamically all the time, - * at least hard code it so that in most cases it's a nice error - * message - */ -enum { INDEX_OFFSET = 1 }; +enum { TUPLE_REF_MAX = UINT16_MAX }; /** Common quota for tuples and indexes */ extern struct quota memtx_quota; @@ -273,102 +268,6 @@ extern struct small_alloc memtx_alloc; /** Tuple slab arena */ extern struct slab_arena memtx_arena; -/** - * @brief In-memory tuple format - */ - -/** - * @brief Tuple field format - * Support structure for struct tuple_format. - * Contains information of one field. - */ -struct tuple_field_format { - /** - * Field type of an indexed field. - * If a field participates in at least one of space indexes - * then its type is stored in this member. - * If a field does not participate in an index - * then UNKNOWN is stored for it. - */ - enum field_type type; - /** - * Offset slot in field map in tuple. - * Normally tuple stores field map - offsets of all fields - * participating in indexes. This allows quick access to most - * used fields without parsing entire mspack. - * This member stores position in the field map of tuple - * for current field. - * If the field does not participate in indexes then it has - * no offset in field map and INT_MAX is stored in this member. - * Due to specific field map in tuple (it is stored before tuple), - * the positions in field map is negative. - * Thus if this member is negative, smth like - * tuple->data[((uint32_t *)tuple)[format->offset_slot[fieldno]]] - * gives the start of the field - */ - int32_t offset_slot; -}; - -/** - * @brief Tuple format - * Tuple format describes how tuple is stored and information about its fields - */ -struct tuple_format { - uint16_t id; - /* Format objects are reference counted. */ - int refs; - /* Length of 'fields' array. */ - uint32_t field_count; - /** - * Size of field map of tuple in bytes. - * See tuple_field_format::ofset for details// - */ - uint32_t field_map_size; - - /* Formats of the fields */ - struct tuple_field_format fields[]; -}; - -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 or raise an exception on error - */ -struct tuple_format * -tuple_format_new(struct rlist *key_list); - -/** Delete a format with zero ref count. */ -void -tuple_format_delete(struct tuple_format *format); - -static inline void -tuple_format_ref(struct tuple_format *format, int count) -{ - assert(format->refs + count >= 0); - assert((uint64_t)format->refs + count <= FORMAT_REF_MAX); - - format->refs += count; - if (format->refs == 0) - tuple_format_delete(format); - -}; - /** * An atom of Tarantool storage. Represents MsgPack Array. */ @@ -509,7 +408,7 @@ struct TupleRefNil { static inline struct tuple_format * tuple_format(const struct tuple *tuple) { - struct tuple_format *format = tuple_formats[tuple->format_id]; + struct tuple_format *format = tuple_format_by_id(tuple->format_id); assert(tuple_format_id(format) == tuple->format_id); return format; } diff --git a/src/box/tuple_format.cc b/src/box/tuple_format.cc new file mode 100644 index 0000000000000000000000000000000000000000..b2fefc25aa1577afc5a8dafbaf60643e3bdea495 --- /dev/null +++ b/src/box/tuple_format.cc @@ -0,0 +1,214 @@ +/* + * Copyright 2010-2016, Tarantool AUTHORS, please see AUTHORS file. + * + * 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 "tuple_format.h" + +/** Global table of tuple formats */ +struct tuple_format **tuple_formats; +struct tuple_format *tuple_format_ber; +static intptr_t recycled_format_ids = FORMAT_ID_NIL; + +static uint32_t formats_size = 0, formats_capacity = 0; + +/** Extract all available type info from keys. */ +static void +field_type_create(struct tuple_format *format, struct rlist *key_list) +{ + /* There may be fields between indexed fields (gaps). */ + for (uint32_t i = 0; i < format->field_count; i++) + format->fields[i].type = UNKNOWN; + + struct key_def *key_def; + /* extract field type info */ + rlist_foreach_entry(key_def, key_list, link) { + for (uint32_t i = 0; i < key_def->part_count; i++) { + assert(key_def->parts[i].fieldno < format->field_count); + enum field_type set_type = key_def->parts[i].type; + enum field_type *fmt_type = + &format->fields[key_def->parts[i].fieldno].type; + if (*fmt_type != UNKNOWN && *fmt_type != set_type) { + tnt_raise(ClientError, + ER_FIELD_TYPE_MISMATCH, + key_def->name, + i + INDEX_OFFSET, + field_type_strs[set_type], + field_type_strs[*fmt_type]); + } + *fmt_type = set_type; + } + } +} + +static void +tuple_format_register(struct tuple_format *format) +{ + if (recycled_format_ids != FORMAT_ID_NIL) { + + format->id = (uint16_t) recycled_format_ids; + recycled_format_ids = (intptr_t) tuple_formats[recycled_format_ids]; + } else { + if (formats_size == formats_capacity) { + uint32_t new_capacity = formats_capacity ? + formats_capacity * 2 : 16; + struct tuple_format **formats; + formats = (struct tuple_format **) + realloc(tuple_formats, new_capacity * + sizeof(tuple_formats[0])); + if (formats == NULL) + tnt_raise(OutOfMemory, + sizeof(struct tuple_format), + "malloc", "tuple_formats"); + + formats_capacity = new_capacity; + tuple_formats = formats; + } + if (formats_size == FORMAT_ID_MAX + 1) { + tnt_raise(LoggedError, ER_TUPLE_FORMAT_LIMIT, + (unsigned) formats_capacity); + } + format->id = formats_size++; + } + tuple_formats[format->id] = format; +} + +static void +tuple_format_deregister(struct tuple_format *format) +{ + if (format->id == FORMAT_ID_NIL) + return; + tuple_formats[format->id] = (struct tuple_format *) recycled_format_ids; + recycled_format_ids = format->id; + format->id = FORMAT_ID_NIL; +} + +static struct tuple_format * +tuple_format_alloc(struct rlist *key_list) +{ + struct key_def *key_def; + uint32_t max_fieldno = 0; + uint32_t key_count = 0; + + /* find max max field no */ + rlist_foreach_entry(key_def, key_list, link) { + struct key_part *part = key_def->parts; + struct key_part *pend = part + key_def->part_count; + key_count++; + for (; part < pend; part++) + max_fieldno = MAX(max_fieldno, part->fieldno); + } + uint32_t field_count = key_count > 0 ? max_fieldno + 1 : 0; + + uint32_t total = sizeof(struct tuple_format) + + field_count * sizeof(struct tuple_field_format); + + struct tuple_format *format = (struct tuple_format *) malloc(total); + + if (format == NULL) + tnt_raise(OutOfMemory, sizeof(struct tuple_format), + "malloc", "tuple format"); + + format->refs = 0; + format->id = FORMAT_ID_NIL; + format->field_count = field_count; + return format; +} + +void +tuple_format_delete(struct tuple_format *format) +{ + tuple_format_deregister(format); + free(format); +} + +struct tuple_format * +tuple_format_new(struct rlist *key_list) +{ + struct tuple_format *format = tuple_format_alloc(key_list); + + try { + tuple_format_register(format); + field_type_create(format, key_list); + } catch (Exception *e) { + tuple_format_delete(format); + throw; + } + + /* Set up offset slots */ + if (format->field_count == 0) { + /* Nothing to store */ + format->field_map_size = 0; + return format; + } + /** + * First field is always simply accessible, + * so we don't store offset for it + */ + format->fields[0].offset_slot = INT32_MAX; + + int current_slot = 0; + for (uint32_t i = 1; i < format->field_count; i++) { + /* + * In the tuple, store only offsets necessary to + * quickly access indexed fields. + */ + if (format->fields[i].type == UNKNOWN) + format->fields[i].offset_slot = INT32_MAX; + else + format->fields[i].offset_slot = --current_slot; + } + format->field_map_size = -current_slot * sizeof(uint32_t); + return format; +} + +void +tuple_format_init() +{ + RLIST_HEAD(empty_list); + tuple_format_ber = tuple_format_new(&empty_list); + /* Make sure this one stays around. */ + tuple_format_ref(tuple_format_ber, 1); +} + +/** Destroy tuple format subsystem and free resourses */ +void +tuple_format_free() +{ + /* Clear recycled ids. */ + while (recycled_format_ids != FORMAT_ID_NIL) { + uint16_t id = (uint16_t) recycled_format_ids; + recycled_format_ids = (intptr_t) tuple_formats[id]; + tuple_formats[id] = NULL; + } + for (struct tuple_format **format = tuple_formats; + format < tuple_formats + formats_size; + format++) + free(*format); /* ignore the reference count. */ + free(tuple_formats); +} diff --git a/src/box/tuple_format.h b/src/box/tuple_format.h new file mode 100644 index 0000000000000000000000000000000000000000..884be395d820eef2d15273723b53da162f3eec9f --- /dev/null +++ b/src/box/tuple_format.h @@ -0,0 +1,153 @@ +#ifndef TARANTOOL_BOX_TUPLE_FORMAT_H_INCLUDED +#define TARANTOOL_BOX_TUPLE_FORMAT_H_INCLUDED +/* + * Copyright 2010-2016, Tarantool AUTHORS, please see AUTHORS file. + * + * 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" /* for enum field_type */ + +enum { FORMAT_ID_MAX = UINT16_MAX - 1, FORMAT_ID_NIL = UINT16_MAX }; +enum { FORMAT_REF_MAX = INT32_MAX}; + +/* + * We don't pass INDEX_OFFSET around dynamically all the time, + * at least hard code it so that in most cases it's a nice error + * message + */ +enum { INDEX_OFFSET = 1 }; + + +/** + * @brief Tuple field format + * Support structure for struct tuple_format. + * Contains information of one field. + */ +struct tuple_field_format { + /** + * Field type of an indexed field. + * If a field participates in at least one of space indexes + * then its type is stored in this member. + * If a field does not participate in an index + * then UNKNOWN is stored for it. + */ + enum field_type type; + /** + * Offset slot in field map in tuple. + * Normally tuple stores field map - offsets of all fields + * participating in indexes. This allows quick access to most + * used fields without parsing entire mspack. + * This member stores position in the field map of tuple + * for current field. + * If the field does not participate in indexes then it has + * no offset in field map and INT_MAX is stored in this member. + * Due to specific field map in tuple (it is stored before tuple), + * the positions in field map is negative. + * Thus if this member is negative, smth like + * tuple->data[((uint32_t *)tuple)[format->offset_slot[fieldno]]] + * gives the start of the field + */ + int32_t offset_slot; +}; + +/** + * @brief Tuple format + * Tuple format describes how tuple is stored and information about its fields + */ +struct tuple_format { + uint16_t id; + /* Format objects are reference counted. */ + int refs; + /* Length of 'fields' array. */ + uint32_t field_count; + /** + * Size of field map of tuple in bytes. + * See tuple_field_format::ofset for details// + */ + uint32_t field_map_size; + + /* Formats of the fields */ + struct tuple_field_format fields[]; +}; + +/** + * Default format for a tuple which does not belong + * to any space and is stored in memory. + */ +extern struct tuple_format *tuple_format_ber; + +extern struct tuple_format **tuple_formats; + +static inline uint32_t +tuple_format_id(struct tuple_format *format) +{ + assert(tuple_formats[format->id] == format); + return format->id; +} + +static inline struct tuple_format * +tuple_format_by_id(uint32_t tuple_format_id) +{ + return tuple_formats[tuple_format_id]; +} + +/** + * @brief Allocate, construct and register a new in-memory tuple + * format. + * @param space description + * + * @return tuple format or raise an exception on error + */ +struct tuple_format * +tuple_format_new(struct rlist *key_list); + +/** Delete a format with zero ref count. */ +void +tuple_format_delete(struct tuple_format *format); + +static inline void +tuple_format_ref(struct tuple_format *format, int count) +{ + assert(format->refs + count >= 0); + assert((uint64_t)format->refs + count <= FORMAT_REF_MAX); + + format->refs += count; + if (format->refs == 0) + tuple_format_delete(format); + +}; + +void +tuple_format_init(); + +/** Destroy tuple format subsystem and free resourses */ +void +tuple_format_free(); + +#endif /* #ifndef TARANTOOL_BOX_TUPLE_FORMAT_H_INCLUDED */