diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index e14f11bd06f2607c997f402b7f2cdde3b1ddfb9e..8845258aab3979e808c4c7c52183c6ec0be86805 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -143,6 +143,23 @@ set(ITERATOR_TEST_LIBS core tuple xrow unit) add_executable(vy_mem.test vy_mem.c ${ITERATOR_TEST_SOURCES}) target_link_libraries(vy_mem.test ${ITERATOR_TEST_LIBS}) +add_executable(vy_point_iterator.test + vy_point_iterator.c + vy_iterators_helper.c + vy_log_stub.c + ${PROJECT_SOURCE_DIR}/src/box/vy_point_iterator.c + ${PROJECT_SOURCE_DIR}/src/box/vy_write_iterator.c + ${PROJECT_SOURCE_DIR}/src/box/vy_stmt.c + ${PROJECT_SOURCE_DIR}/src/box/vy_mem.c + ${PROJECT_SOURCE_DIR}/src/box/vy_run.c + ${PROJECT_SOURCE_DIR}/src/box/vy_range.c + ${PROJECT_SOURCE_DIR}/src/box/vy_tx.c + ${PROJECT_SOURCE_DIR}/src/box/vy_upsert.c + ${PROJECT_SOURCE_DIR}/src/box/vy_index.c + ${PROJECT_SOURCE_DIR}/src/box/vy_cache.c +) +target_link_libraries(vy_point_iterator.test core tuple xrow xlog unit) + add_executable(column_mask.test column_mask.c) target_link_libraries(column_mask.test tuple unit) diff --git a/test/unit/vy_iterators_helper.c b/test/unit/vy_iterators_helper.c index 170cfbe265efcbf7bf2a6277f37b9b864ee68ffe..1fee0474d2aadefb7d0637273b64a2c5ea8d31a7 100644 --- a/test/unit/vy_iterators_helper.c +++ b/test/unit/vy_iterators_helper.c @@ -88,9 +88,15 @@ vy_new_simple_stmt(struct tuple_format *format, * important. */ struct iovec operations[1]; - char tmp[16]; + char tmp[32]; char *ops = mp_encode_array(tmp, 1); - ops = mp_encode_array(ops, 0); + ops = mp_encode_array(ops, 3); + ops = mp_encode_str(ops, "+", 1); + ops = mp_encode_uint(ops, templ->upsert_field); + if (templ->upsert_value >= 0) + ops = mp_encode_uint(ops, templ->upsert_value); + else + ops = mp_encode_int(ops, templ->upsert_value); operations[0].iov_base = tmp; operations[0].iov_len = ops - tmp; fail_if(templ->optimize_update); diff --git a/test/unit/vy_iterators_helper.h b/test/unit/vy_iterators_helper.h index d69297f67eb1d6232ff232037b70322436475c6e..a89fc4b904ad19a8c88592b7a29dfb002d5adc84 100644 --- a/test/unit/vy_iterators_helper.h +++ b/test/unit/vy_iterators_helper.h @@ -43,10 +43,10 @@ #define vyend 99999999 #define MAX_FIELDS_COUNT 100 #define STMT_TEMPLATE(lsn, type, ...) \ -{ { __VA_ARGS__, vyend }, IPROTO_##type, lsn, false } +{ { __VA_ARGS__, vyend }, IPROTO_##type, lsn, false, 0, 0 } #define STMT_TEMPLATE_OPTIMIZED(lsn, type, ...) \ -{ { __VA_ARGS__, vyend }, IPROTO_##type, lsn, true } +{ { __VA_ARGS__, vyend }, IPROTO_##type, lsn, true, 0, 0 } extern struct tuple_format_vtab vy_tuple_format_vtab; extern struct tuple_format *vy_key_format; @@ -72,7 +72,7 @@ vy_iterator_C_test_finish(); struct vy_stmt_template { /** Array of statement fields, ended with 'vyend'. */ const int fields[MAX_FIELDS_COUNT]; - /** Statement type: REPLACE/UPSERT/DELETE. */ + /** Statement type: REPLACE/UPSERT/DELETE/UPSERT. */ enum iproto_type type; /** Statement lsn. */ int64_t lsn; @@ -81,6 +81,13 @@ struct vy_stmt_template { * to skip it in the write_iterator. */ bool optimize_update; + /* + * In case of upsert it is possible to use only one 'add' operation. + * This is the column number of the operation. + */ + uint32_t upsert_field; + /** And that is the value to add. */ + int32_t upsert_value; }; /** diff --git a/test/unit/vy_log_stub.c b/test/unit/vy_log_stub.c new file mode 100644 index 0000000000000000000000000000000000000000..daabf3f9ea1f94dc429a1551afe221614c45fbdd --- /dev/null +++ b/test/unit/vy_log_stub.c @@ -0,0 +1,61 @@ +/* + * Copyright 2010-2017, 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 AUTHORS ``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 + * AUTHORS 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 "vy_log.h" + +#include <trivia/util.h> + +int64_t +vy_log_next_id(void) +{ + static int64_t id = 0; + return id++; +} + +void +vy_log_tx_begin(void) {} + +int +vy_log_tx_commit(void) +{ + return 0; +} + +void +vy_log_write(const struct vy_log_record *record) {} + +int +vy_recovery_load_index(struct vy_recovery *recovery, + uint32_t space_id, uint32_t index_id, + int64_t index_lsn, bool snapshot_recovery, + vy_recovery_cb cb, void *cb_arg) +{ + unreachable(); +} diff --git a/test/unit/vy_point_iterator.c b/test/unit/vy_point_iterator.c new file mode 100644 index 0000000000000000000000000000000000000000..b5b8d962e0e6dc950feb06edb631dcb95aa5fd0e --- /dev/null +++ b/test/unit/vy_point_iterator.c @@ -0,0 +1,313 @@ +#include "trivia/util.h" +#include "unit.h" +#include "vy_index.h" +#include "vy_cache.h" +#include "vy_run.h" +#include "fiber.h" +#include <small/lsregion.h> +#include <small/slab_cache.h> +#include <bit/bit.h> +#include <crc32.h> +#include <box/vy_point_iterator.h> +#include "vy_iterators_helper.h" +#include "vy_write_iterator.h" + +uint64_t schema_version; + +static void +test_basic() +{ + header(); + plan(15); + + const size_t QUOTA = 100 * 1024 * 1024; + int64_t generation = 0; + struct slab_cache *slab_cache = cord_slab_cache(); + struct lsregion lsregion; + lsregion_create(&lsregion, slab_cache->arena); + + int rc; + struct vy_index_env index_env; + rc = vy_index_env_create(&index_env, ".", &lsregion, &generation, + NULL, NULL); + is(rc, 0, "vy_index_env_create"); + + struct vy_run_env run_env; + vy_run_env_create(&run_env); + + struct vy_cache_env cache_env; + vy_cache_env_create(&cache_env, slab_cache, QUOTA); + + struct vy_cache cache; + uint32_t fields[] = { 0 }; + uint32_t types[] = { FIELD_TYPE_UNSIGNED }; + struct key_def *key_def = box_key_def_new(fields, types, 1); + isnt(key_def, NULL, "key_def is not NULL"); + + vy_cache_create(&cache, &cache_env, key_def); + + struct tuple_format *format = + tuple_format_new(&vy_tuple_format_vtab, &key_def, 1, 0); + isnt(format, NULL, "tuple_format_new is not NULL"); + tuple_format_ref(format); + + struct index_opts index_opts = index_opts_default; + struct index_def *index_def = + index_def_new(512, 0, "primary", sizeof("primary"), TREE, + &index_opts, key_def, NULL); + + struct vy_index *pk = vy_index_new(&index_env, &cache_env, index_def, + format, NULL); + isnt(pk, NULL, "index is not NULL") + + struct vy_range *range = vy_range_new(1, NULL, NULL, pk->cmp_def); + + isnt(pk, NULL, "range is not NULL") + vy_index_add_range(pk, range); + + struct rlist read_views = RLIST_HEAD_INITIALIZER(read_views); + + char dir_tmpl[] = "./vy_point_test.XXXXXX"; + char *dir_name = mkdtemp(dir_tmpl); + isnt(dir_name, NULL, "temp dir name is not NULL") + char path[PATH_MAX]; + strcpy(path, dir_name); + strcat(path, "/0"); + rc = mkdir(path, 0777); + is(rc, 0, "temp dir create (2)"); + strcat(path, "/0"); + rc = mkdir(path, 0777); + is(rc, 0, "temp dir create (3)"); + + /* Filling the index with test data */ + /* Prepare variants */ + const size_t num_of_keys = 100; + bool in_mem1[num_of_keys]; /* UPSERT value += 1, lsn 4 */ + bool in_mem2[num_of_keys]; /* UPSERT value += 2, lsn 3 */ + bool in_run1[num_of_keys]; /* UPSERT value += 4, lsn 2 */ + bool in_run2[num_of_keys]; /* UPSERT value += 8, lsn 1 */ + bool in_cache[num_of_keys]; + uint32_t expect[num_of_keys]; + int64_t expect_lsn[num_of_keys]; + for (size_t i = 0; i < num_of_keys; i++) { + in_mem1[i] = i & 1; + in_mem2[i] = i & 2; + in_run1[i] = i & 4; + in_run2[i] = i & 8; + in_cache[i] = i & 16; + expect[i] = (in_mem1[i] ? 1 : 0) + (in_mem2[i] ? 2 : 0) + + (in_run1[i] ? 4 : 0) + (in_run2[i] ? 8 : 0); + expect_lsn[i] = expect[i] == 0 ? 0 : 5 - bit_ctz_u32(expect[i]); + } + + for (size_t i = 0; i < num_of_keys; i++) { + if (!in_cache[i]) + continue; + if (expect[i] != 0) { + struct vy_stmt_template tmpl_key = + STMT_TEMPLATE(0, SELECT, i); + struct vy_stmt_template tmpl_val = + STMT_TEMPLATE(expect_lsn[i], REPLACE, i, expect[i]); + vy_cache_insert_templates_chain(&cache, format, + &tmpl_val, 1, &tmpl_key, + ITER_EQ); + } + } + + /* create second mem */ + for (size_t i = 0; i < num_of_keys; i++) { + if (!in_mem2[i]) + continue; + struct vy_stmt_template tmpl_val = + STMT_TEMPLATE(3, UPSERT, i, 2); + tmpl_val.upsert_field = 1; + tmpl_val.upsert_value = 2; + vy_mem_insert_template(pk->mem, &tmpl_val); + } + + rc = vy_index_rotate_mem(pk); + is(rc, 0, "vy_index_rotate_mem"); + + /* create first mem */ + for (size_t i = 0; i < num_of_keys; i++) { + if (!in_mem1[i]) + continue; + struct vy_stmt_template tmpl_val = + STMT_TEMPLATE(4, UPSERT, i, 1); + tmpl_val.upsert_field = 1; + tmpl_val.upsert_value = 1; + vy_mem_insert_template(pk->mem, &tmpl_val); + } + + /* create second run */ + struct vy_mem *run_mem = + vy_mem_new(pk->env->allocator, *pk->env->p_generation, + pk->cmp_def, pk->mem_format, + pk->mem_format_with_colmask, + pk->upsert_format, 0); + + for (size_t i = 0; i < num_of_keys; i++) { + if (!in_run2[i]) + continue; + struct vy_stmt_template tmpl_val = + STMT_TEMPLATE(1, UPSERT, i, 8); + tmpl_val.upsert_field = 1; + tmpl_val.upsert_value = 8; + vy_mem_insert_template(run_mem, &tmpl_val); + } + struct vy_stmt_stream *write_stream + = vy_write_iterator_new(pk->cmp_def, pk->disk_format, + pk->upsert_format, pk->id == 0, + true, &read_views); + vy_write_iterator_new_mem(write_stream, run_mem); + struct vy_run *run = vy_run_new(1); + isnt(run, NULL, "vy_run_new"); + + rc = vy_run_write(run, dir_name, 0, pk->id, + write_stream, 4096, pk->cmp_def, pk->key_def, + 100500, 0.1); + is(rc, 0, "vy_run_write"); + + write_stream->iface->close(write_stream); + vy_mem_delete(run_mem); + + vy_index_add_run(pk, run); + struct vy_slice *slice = vy_slice_new(1, run, NULL, NULL, pk->cmp_def); + vy_range_add_slice(range, slice); + vy_run_unref(run); + + /* create first run */ + run_mem = + vy_mem_new(pk->env->allocator, *pk->env->p_generation, + pk->cmp_def, pk->mem_format, + pk->mem_format_with_colmask, + pk->upsert_format, 0); + + for (size_t i = 0; i < num_of_keys; i++) { + if (!in_run1[i]) + continue; + struct vy_stmt_template tmpl_val = + STMT_TEMPLATE(2, UPSERT, i, 4); + tmpl_val.upsert_field = 1; + tmpl_val.upsert_value = 4; + vy_mem_insert_template(run_mem, &tmpl_val); + } + write_stream + = vy_write_iterator_new(pk->cmp_def, pk->disk_format, + pk->upsert_format, pk->id == 0, + true, &read_views); + vy_write_iterator_new_mem(write_stream, run_mem); + run = vy_run_new(2); + isnt(run, NULL, "vy_run_new"); + + rc = vy_run_write(run, dir_name, 0, pk->id, + write_stream, 4096, pk->cmp_def, pk->key_def, + 100500, 0.1); + is(rc, 0, "vy_run_write"); + + write_stream->iface->close(write_stream); + vy_mem_delete(run_mem); + + vy_index_add_run(pk, run); + slice = vy_slice_new(1, run, NULL, NULL, pk->cmp_def); + vy_range_add_slice(range, slice); + vy_run_unref(run); + + /* Compare with expected */ + bool results_ok = true; + bool has_errors = false; + for (int64_t vlsn = 0; vlsn <= 6; vlsn++) { + struct vy_read_view rv; + rv.vlsn = vlsn == 6 ? INT64_MAX : vlsn; + const struct vy_read_view *prv = &rv; + + for (size_t i = 0; i < num_of_keys; i++) { + uint32_t expect = 0; + int64_t expect_lsn = 0; + if (in_run2[i] && vlsn >= 1) { + expect += 8; + expect_lsn = 1; + } + if (in_run1[i] && vlsn >= 2) { + expect += 4; + expect_lsn = 2; + } + if (in_mem2[i] && vlsn >= 3) { + expect += 2; + expect_lsn = 3; + } + if (in_mem1[i] && vlsn >= 4) { + expect += 1; + expect_lsn = 4; + } + + struct vy_stmt_template tmpl_key = + STMT_TEMPLATE(0, SELECT, i); + struct tuple *key = + vy_new_simple_stmt(format, pk->upsert_format, + pk->mem_format_with_colmask, + &tmpl_key); + struct vy_point_iterator itr; + vy_point_iterator_open(&itr, &run_env, pk, + NULL, &prv, key); + struct tuple *res; + rc = vy_point_iterator_get(&itr, &res); + tuple_unref(key); + if (rc != 0) { + has_errors = true; + continue; + } + if (expect == 0) { + /* No value expected. */ + if (res != NULL) + results_ok = false; + continue; + } else { + if (res == NULL) { + results_ok = false; + continue; + } + } + uint32_t got; + tuple_field_u32(res, 1, &got); + if (got != expect && expect_lsn != vy_stmt_lsn(res)) + results_ok = false; + } + } + + is(results_ok, true, "select results"); + is(has_errors, false, "no errors happened"); + + vy_index_unref(pk); + index_def_delete(index_def); + tuple_format_unref(format); + vy_cache_destroy(&cache); + box_key_def_delete(key_def); + vy_cache_env_destroy(&cache_env); + vy_run_env_destroy(&run_env); + vy_index_env_destroy(&index_env); + + lsregion_destroy(&lsregion); + + strcpy(path, "rm -rf "); + strcat(path, dir_name); + system(path); + + check_plan(); + footer(); +} + +int +main() +{ + plan(1); + + vy_iterator_C_test_init(128 * 1024); + crc32_init(); + + test_basic(); + + vy_iterator_C_test_finish(); + + return check_plan(); +} diff --git a/test/unit/vy_point_iterator.result b/test/unit/vy_point_iterator.result new file mode 100644 index 0000000000000000000000000000000000000000..6b8dcdefce01f98da86431c33dc0f6c6b64d2924 --- /dev/null +++ b/test/unit/vy_point_iterator.result @@ -0,0 +1,20 @@ +1..1 + *** test_basic *** + 1..15 + ok 1 - vy_index_env_create + ok 2 - key_def is not NULL + ok 3 - tuple_format_new is not NULL + ok 4 - index is not NULL + ok 5 - range is not NULL + ok 6 - temp dir name is not NULL + ok 7 - temp dir create (2) + ok 8 - temp dir create (3) + ok 9 - vy_index_rotate_mem + ok 10 - vy_run_new + ok 11 - vy_run_write + ok 12 - vy_run_new + ok 13 - vy_run_write + ok 14 - select results + ok 15 - no errors happened +ok 1 - subtests + *** test_basic: done ***