diff --git a/src/box/CMakeLists.txt b/src/box/CMakeLists.txt index d033cdb268c3e3b8a2a73eeb88d18c917d172dd6..bdbbbb075ac7003baf8dabae4419a37106e9e2cb 100644 --- a/src/box/CMakeLists.txt +++ b/src/box/CMakeLists.txt @@ -83,6 +83,7 @@ add_library(box STATIC vy_upsert.c vy_read_set.c vy_scheduler.c + request.c space.c space_def.c sequence.c diff --git a/src/box/request.c b/src/box/request.c new file mode 100644 index 0000000000000000000000000000000000000000..9118dcc7da87a0a45160611b14d8e5a0aa8494da --- /dev/null +++ b/src/box/request.c @@ -0,0 +1,153 @@ +/* + * Copyright 2010-2018, 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 "request.h" + +#include <stddef.h> +#include <stdint.h> +#include <string.h> +#include <msgpuck.h> +#include <small/region.h> + +#include "fiber.h" +#include "space.h" +#include "index.h" +#include "sequence.h" +#include "key_def.h" +#include "tuple.h" +#include "xrow.h" +#include "iproto_constants.h" + +void +request_rebind_to_primary_key(struct request *request, struct space *space, + struct tuple *found_tuple) +{ + struct index *pk = space_index(space, 0); + assert(pk != NULL); + uint32_t key_len; + char *key = tuple_extract_key(found_tuple, pk->def->key_def, &key_len); + assert(key != NULL); + request->key = key; + request->key_end = key + key_len; + request->index_id = 0; + /* Clear the *body* to ensure it's rebuilt at commit. */ + request->header = NULL; +} + +int +request_handle_sequence(struct request *request, struct space *space) +{ + struct sequence *seq = space->sequence; + + assert(seq != NULL); + assert(request->type == IPROTO_INSERT || + request->type == IPROTO_REPLACE); + + /* + * An automatically generated sequence inherits + * privileges of the space it is used with. + */ + if (!seq->is_generated && + access_check_sequence(seq) != 0) + return -1; + + struct index *pk = space_index(space, 0); + if (unlikely(pk == NULL)) + return 0; + + /* + * Look up the first field of the primary key. + */ + const char *data = request->tuple; + const char *data_end = request->tuple_end; + int len = mp_decode_array(&data); + int fieldno = pk->def->key_def->parts[0].fieldno; + if (unlikely(len < fieldno + 1)) + return 0; + + const char *key = data; + if (unlikely(fieldno > 0)) { + do { + mp_next(&key); + } while (--fieldno > 0); + } + + int64_t value; + if (mp_typeof(*key) == MP_NIL) { + /* + * If the first field of the primary key is nil, + * this is an auto increment request and we need + * to replace the nil with the next value generated + * by the space sequence. + */ + if (unlikely(sequence_next(seq, &value) != 0)) + return -1; + + const char *key_end = key; + mp_decode_nil(&key_end); + + size_t buf_size = (request->tuple_end - request->tuple) + + mp_sizeof_uint(UINT64_MAX); + char *tuple = region_alloc(&fiber()->gc, buf_size); + if (tuple == NULL) + return -1; + char *tuple_end = mp_encode_array(tuple, len); + + if (unlikely(key != data)) { + memcpy(tuple_end, data, key - data); + tuple_end += key - data; + } + + if (value >= 0) + tuple_end = mp_encode_uint(tuple_end, value); + else + tuple_end = mp_encode_int(tuple_end, value); + + memcpy(tuple_end, key_end, data_end - key_end); + tuple_end += data_end - key_end; + + assert(tuple_end <= tuple + buf_size); + + request->tuple = tuple; + request->tuple_end = tuple_end; + } else { + /* + * If the first field is not nil, update the space + * sequence with its value, to make sure that an + * auto increment request never tries to insert a + * value that is already in the space. Note, this + * code is also invoked on final recovery to restore + * the sequence value from WAL. + */ + if (likely(mp_read_int64(&key, &value) == 0)) + return sequence_update(seq, value); + } + return 0; +} diff --git a/src/box/request.h b/src/box/request.h new file mode 100644 index 0000000000000000000000000000000000000000..ff1d97e05626bbd3bddacd2cbd7737b844d433ff --- /dev/null +++ b/src/box/request.h @@ -0,0 +1,69 @@ +#ifndef TARANTOOL_BOX_REQUEST_H_INCLUDED +#define TARANTOOL_BOX_REQUEST_H_INCLUDED +/* + * Copyright 2010-2018, 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. + */ + +#if defined(__cplusplus) +extern "C" { +#endif /* defined(__cplusplus) */ + +struct request; +struct space; +struct tuple; + +/** + * Convert a request accessing a secondary key to a primary + * key undo record, given it found a tuple. + * Flush iproto header of the request to be reconstructed in + * txn_add_redo(). + * + * @param request - request to fix + * @param space - space corresponding to request + * @param found_tuple - tuple found by secondary key + */ +void +request_rebind_to_primary_key(struct request *request, struct space *space, + struct tuple *found_tuple); + +/** + * Handle INSERT/REPLACE in a space with a sequence attached. + * + * @param request - request to fix + * @param space - space corresponding to request + */ +int +request_handle_sequence(struct request *request, struct space *space); + +#if defined(__cplusplus) +} /* extern "C" */ +#endif /* defined(__cplusplus) */ + +#endif /* TARANTOOL_BOX_REQUEST_H_INCLUDED */ diff --git a/src/box/space.c b/src/box/space.c index aeebf10afd758f68996dc7a7df1bb1490d5a9504..d3692094668a21e16cbd558968cc54f1fbe2ae46 100644 --- a/src/box/space.c +++ b/src/box/space.c @@ -31,14 +31,13 @@ #include "space.h" #include <stdlib.h> #include <string.h> -#include "tuple.h" -#include "tuple_compare.h" +#include "tuple_format.h" #include "trigger.h" #include "user.h" #include "session.h" +#include "request.h" #include "xrow.h" #include "iproto_constants.h" -#include "sequence.h" int access_check_space(struct space *space, user_access_t access) @@ -309,125 +308,6 @@ space_def_check_compatibility(const struct space_def *old_def, return 0; } -/** - * Convert a request accessing a secondary key to a primary key undo - * record, given it found a tuple. - * Flush iproto header of the request to be reconstructed in txn_add_redo(). - * - * @param request - request to fix - * @param space - space corresponding to request - * @param found_tuple - tuple found by secondary key - */ -static void -request_rebind_to_primary_key(struct request *request, struct space *space, - struct tuple *found_tuple) -{ - struct index *pk = space_index(space, 0); - assert(pk != NULL); - uint32_t key_len; - char *key = tuple_extract_key(found_tuple, pk->def->key_def, &key_len); - assert(key != NULL); - request->key = key; - request->key_end = key + key_len; - request->index_id = 0; - /* Clear the *body* to ensure it's rebuilt at commit. */ - request->header = NULL; -} - -/** - * Handle INSERT/REPLACE in a space with a sequence attached. - */ -static int -request_handle_sequence(struct request *request, struct space *space) -{ - struct sequence *seq = space->sequence; - - assert(seq != NULL); - assert(request->type == IPROTO_INSERT || - request->type == IPROTO_REPLACE); - - /* - * An automatically generated sequence inherits - * privileges of the space it is used with. - */ - if (!seq->is_generated && - access_check_sequence(seq) != 0) - return -1; - - struct index *pk = space_index(space, 0); - if (unlikely(pk == NULL)) - return 0; - - /* - * Look up the first field of the primary key. - */ - const char *data = request->tuple; - const char *data_end = request->tuple_end; - int len = mp_decode_array(&data); - int fieldno = pk->def->key_def->parts[0].fieldno; - if (unlikely(len < fieldno + 1)) - return 0; - - const char *key = data; - if (unlikely(fieldno > 0)) { - do { - mp_next(&key); - } while (--fieldno > 0); - } - - int64_t value; - if (mp_typeof(*key) == MP_NIL) { - /* - * If the first field of the primary key is nil, - * this is an auto increment request and we need - * to replace the nil with the next value generated - * by the space sequence. - */ - if (unlikely(sequence_next(seq, &value) != 0)) - return -1; - - const char *key_end = key; - mp_decode_nil(&key_end); - - size_t buf_size = (request->tuple_end - request->tuple) + - mp_sizeof_uint(UINT64_MAX); - char *tuple = region_alloc(&fiber()->gc, buf_size); - if (tuple == NULL) - return -1; - char *tuple_end = mp_encode_array(tuple, len); - - if (unlikely(key != data)) { - memcpy(tuple_end, data, key - data); - tuple_end += key - data; - } - - if (value >= 0) - tuple_end = mp_encode_uint(tuple_end, value); - else - tuple_end = mp_encode_int(tuple_end, value); - - memcpy(tuple_end, key_end, data_end - key_end); - tuple_end += data_end - key_end; - - assert(tuple_end <= tuple + buf_size); - - request->tuple = tuple; - request->tuple_end = tuple_end; - } else { - /* - * If the first field is not nil, update the space - * sequence with its value, to make sure that an - * auto increment request never tries to insert a - * value that is already in the space. Note, this - * code is also invoked on final recovery to restore - * the sequence value from WAL. - */ - if (likely(mp_read_int64(&key, &value) == 0)) - return sequence_update(seq, value); - } - return 0; -} - int space_execute_dml(struct space *space, struct txn *txn, struct request *request, struct tuple **result)