Files
scylladb/tests/compound_test.cc
Tomasz Grabiec 9d923a61e1 Merge "Fixes to sstable files for non-compound schemas" from Duarte
This series mainly fixes issues with the serialization of promoted
index entries for non-compound schemas and with the serialization of
range tombstones, also for non-compound schemas.

We lift the correct cell name writing code into its own function,
and direct all users to it. We also ensure backward compatibility with
incorrectly generated promoted indexes and range tombstones.

Fixes #2995
Fixes #2986
Fixes #2979
Fixes #2992
Fixes #2993

* git@github.com:duarten/scylla.git  promoted-index-serialization/v3:
  sstables/sstables: Unify column name writers
  sstables/sstables: Don't write index entry for a missing row maker
  sstables/sstables: Reuse write_range_tombstone() for row tombstones
  sstables/sstables: Lift index writing for row tombstones
  sstables/sstables: Leverage index code upon range tombstone consume
  sstables/sstables: Move out tombstone check in write_range_tombstone()
  sstables/sstables: A schema with static columns is always compound
  sstables/sstables: Lift column name writing logic
  sstables/sstables: Use schema-aware write_column_name() for
    collections
  sstables/sstables: Use schema-aware write_column_name() for row marker
  sstables/sstables: Use schema-aware write_column_name() for static row
  sstables/sstables: Writing promoted index entry leverages
    column_name_writer
  sstables/sstables: Add supported feature list to sstables
  sstables/sstables: Don't use incorrectly serialized promoted index
  cql3/single_column_primary_key_restrictions: Implement is_inclusive()
  cql3/delete_statement: Constrain range deletions for non-compound
    schemas
  tests/cql_query_test: Verify range deletion constraints
  sstables/sstables: Correctly deserialize range tombstones
  service/storage_service: Add feature for correct non-compound RTs
  tests/sstable_*: Start the storage service for some cases
  sstables/sstable_writer: Prepare to control range tombstone
    serialization
  sstables/sstables: Correctly serialize range tombstones
  tests/sstable_assertions: Fix monotonicity check for promoted indexes
  tests/sstable_assertions: Assert a promoted index is empty
  tests/sstable_mutation_test: Verify promoted index serializes
    correctly
  tests/sstable_mutation_test: Verify promoted index repeats tombstones
  tests/sstable_mutation_test: Ensure range tombstone serializes
    correctly
  tests/sstable_datafile_test: Add test for incorrect promoted index
  tests/sstable_datafile_test: Verify reading of incorrect range
    tombstones
  sstables/sstable: Rename schema-oblivious write_column_name() function
  sstables/sstables: No promoted index without clustering keys
  tests/sstable_mutation_test: Verify promoted index is not generated
  sstables/sstables: Optimize column name writing and indexing
  compound_compat: Don't assume compoundness

(cherry picked from commit bd1efbc25c)

Also added sstables::make_sstable() to preserve source compatibility in tests.
2017-11-30 16:21:13 +02:00

339 lines
13 KiB
C++

/*
* Copyright (C) 2015 ScyllaDB
*/
/*
* This file is part of Scylla.
*
* Scylla is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Scylla is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Scylla. If not, see <http://www.gnu.org/licenses/>.
*/
#define BOOST_TEST_MODULE core
#include <boost/test/unit_test.hpp>
#include "compound.hh"
#include "compound_compat.hh"
#include "tests/range_assert.hh"
#include "schema_builder.hh"
#include "disk-error-handler.hh"
thread_local disk_error_signal_type commit_error;
thread_local disk_error_signal_type general_disk_error;
static std::vector<bytes> to_bytes_vec(std::vector<sstring> values) {
std::vector<bytes> result;
for (auto&& v : values) {
result.emplace_back(to_bytes(v));
}
return result;
}
template <typename Compound>
static
range_assert<typename Compound::iterator>
assert_that_components(Compound& t, bytes packed) {
return assert_that_range(t.begin(packed), t.end(packed));
}
template <typename Compound>
static void test_sequence(Compound& t, std::vector<sstring> strings) {
auto packed = t.serialize_value(to_bytes_vec(strings));
assert_that_components(t, packed).equals(to_bytes_vec(strings));
};
BOOST_AUTO_TEST_CASE(test_iteration_over_non_prefixable_tuple) {
compound_type<allow_prefixes::no> t({bytes_type, bytes_type, bytes_type});
test_sequence(t, {"el1", "el2", "el3"});
test_sequence(t, {"el1", "el2", ""});
test_sequence(t, {"", "el2", "el3"});
test_sequence(t, {"el1", "", ""});
test_sequence(t, {"", "", "el3"});
test_sequence(t, {"el1", "", "el3"});
test_sequence(t, {"", "", ""});
}
BOOST_AUTO_TEST_CASE(test_iteration_over_prefixable_tuple) {
compound_type<allow_prefixes::yes> t({bytes_type, bytes_type, bytes_type});
test_sequence(t, {"el1", "el2", "el3"});
test_sequence(t, {"el1", "el2", ""});
test_sequence(t, {"", "el2", "el3"});
test_sequence(t, {"el1", "", ""});
test_sequence(t, {"", "", "el3"});
test_sequence(t, {"el1", "", "el3"});
test_sequence(t, {"", "", ""});
test_sequence(t, {"el1", "el2", ""});
test_sequence(t, {"el1", "el2"});
test_sequence(t, {"el1", ""});
test_sequence(t, {"el1"});
test_sequence(t, {""});
test_sequence(t, {});
}
BOOST_AUTO_TEST_CASE(test_iteration_over_non_prefixable_singular_tuple) {
compound_type<allow_prefixes::no> t({bytes_type});
test_sequence(t, {"el1"});
test_sequence(t, {""});
}
BOOST_AUTO_TEST_CASE(test_iteration_over_prefixable_singular_tuple) {
compound_type<allow_prefixes::yes> t({bytes_type});
test_sequence(t, {"elem1"});
test_sequence(t, {""});
test_sequence(t, {});
}
template <allow_prefixes AllowPrefixes>
void do_test_conversion_methods_for_singular_compound() {
compound_type<AllowPrefixes> t({bytes_type});
{
assert_that_components(t, t.serialize_value(to_bytes_vec({"asd"}))) // r-value version
.equals(to_bytes_vec({"asd"}));
}
{
auto vec = to_bytes_vec({"asd"});
assert_that_components(t, t.serialize_value(vec)) // l-value version
.equals(to_bytes_vec({"asd"}));
}
{
std::vector<bytes_opt> vec = { bytes_opt("asd") };
assert_that_components(t, t.serialize_optionals(vec))
.equals(to_bytes_vec({"asd"}));
}
{
std::vector<bytes_opt> vec = { bytes_opt("asd") };
assert_that_components(t, t.serialize_optionals(std::move(vec))) // r-value
.equals(to_bytes_vec({"asd"}));
}
{
assert_that_components(t, t.serialize_single(bytes("asd")))
.equals(to_bytes_vec({"asd"}));
}
}
BOOST_AUTO_TEST_CASE(test_conversion_methods_for_singular_compound) {
do_test_conversion_methods_for_singular_compound<allow_prefixes::yes>();
do_test_conversion_methods_for_singular_compound<allow_prefixes::no>();
}
template <allow_prefixes AllowPrefixes>
void do_test_conversion_methods_for_non_singular_compound() {
compound_type<AllowPrefixes> t({bytes_type, bytes_type, bytes_type});
{
assert_that_components(t, t.serialize_value(to_bytes_vec({"el1", "el2", "el2"}))) // r-value version
.equals(to_bytes_vec({"el1", "el2", "el2"}));
}
{
auto vec = to_bytes_vec({"el1", "el2", "el3"});
assert_that_components(t, t.serialize_value(vec)) // l-value version
.equals(to_bytes_vec({"el1", "el2", "el3"}));
}
{
std::vector<bytes_opt> vec = { bytes_opt("el1"), bytes_opt("el2"), bytes_opt("el3") };
assert_that_components(t, t.serialize_optionals(vec))
.equals(to_bytes_vec({"el1", "el2", "el3"}));
}
{
std::vector<bytes_opt> vec = { bytes_opt("el1"), bytes_opt("el2"), bytes_opt("el3") };
assert_that_components(t, t.serialize_optionals(std::move(vec))) // r-value
.equals(to_bytes_vec({"el1", "el2", "el3"}));
}
}
BOOST_AUTO_TEST_CASE(test_conversion_methods_for_non_singular_compound) {
do_test_conversion_methods_for_non_singular_compound<allow_prefixes::yes>();
do_test_conversion_methods_for_non_singular_compound<allow_prefixes::no>();
}
BOOST_AUTO_TEST_CASE(test_component_iterator_post_incrementation) {
compound_type<allow_prefixes::no> t({bytes_type, bytes_type, bytes_type});
auto packed = t.serialize_value(to_bytes_vec({"el1", "el2", "el3"}));
auto i = t.begin(packed);
auto end = t.end(packed);
BOOST_REQUIRE_EQUAL(to_bytes("el1"), *i++);
BOOST_REQUIRE_EQUAL(to_bytes("el2"), *i++);
BOOST_REQUIRE_EQUAL(to_bytes("el3"), *i++);
BOOST_REQUIRE(i == end);
}
BOOST_AUTO_TEST_CASE(test_conversion_to_legacy_form) {
compound_type<allow_prefixes::no> singular({bytes_type});
BOOST_REQUIRE_EQUAL(to_legacy(singular, singular.serialize_single(to_bytes("asd"))), bytes("asd"));
BOOST_REQUIRE_EQUAL(to_legacy(singular, singular.serialize_single(to_bytes(""))), bytes(""));
compound_type<allow_prefixes::no> two_components({bytes_type, bytes_type});
BOOST_REQUIRE_EQUAL(to_legacy(two_components, two_components.serialize_value(to_bytes_vec({"el1", "elem2"}))),
bytes({'\x00', '\x03', 'e', 'l', '1', '\x00', '\x00', '\x05', 'e', 'l', 'e', 'm', '2', '\x00'}));
BOOST_REQUIRE_EQUAL(to_legacy(two_components, two_components.serialize_value(to_bytes_vec({"el1", ""}))),
bytes({'\x00', '\x03', 'e', 'l', '1', '\x00', '\x00', '\x00', '\x00'}));
}
BOOST_AUTO_TEST_CASE(test_legacy_ordering_of_singular) {
compound_type<allow_prefixes::no> t({bytes_type});
auto make = [&t] (sstring value) -> bytes {
return t.serialize_single(to_bytes(value));
};
legacy_compound_view<decltype(t)>::tri_comparator cmp(t);
BOOST_REQUIRE(cmp(make("A"), make("B")) < 0);
BOOST_REQUIRE(cmp(make("AA"), make("B")) < 0);
BOOST_REQUIRE(cmp(make("B"), make("AB")) > 0);
BOOST_REQUIRE(cmp(make("B"), make("A")) > 0);
BOOST_REQUIRE(cmp(make("A"), make("A")) == 0);
}
BOOST_AUTO_TEST_CASE(test_legacy_ordering_of_composites) {
compound_type<allow_prefixes::no> t({bytes_type, bytes_type});
auto make = [&t] (sstring v1, sstring v2) -> bytes {
return t.serialize_value(std::vector<bytes>{to_bytes(v1), to_bytes(v2)});
};
legacy_compound_view<decltype(t)>::tri_comparator cmp(t);
BOOST_REQUIRE(cmp(make("A", "B"), make("A", "B")) == 0);
BOOST_REQUIRE(cmp(make("A", "B"), make("A", "C")) < 0);
BOOST_REQUIRE(cmp(make("A", "B"), make("B", "B")) < 0);
BOOST_REQUIRE(cmp(make("A", "C"), make("B", "B")) < 0);
BOOST_REQUIRE(cmp(make("B", "A"), make("A", "A")) > 0);
BOOST_REQUIRE(cmp(make("AA", "B"), make("B", "B")) > 0);
BOOST_REQUIRE(cmp(make("A", "AA"), make("A", "A")) > 0);
BOOST_REQUIRE(cmp(make("", "A"), make("A", "A")) < 0);
BOOST_REQUIRE(cmp(make("A", ""), make("A", "A")) < 0);
}
BOOST_AUTO_TEST_CASE(test_enconding_of_legacy_composites) {
using components = std::vector<composite::component>;
BOOST_REQUIRE_EQUAL(composite(bytes({'\x00', '\x03', 'e', 'l', '1', '\x00'})).components(),
components({std::make_pair(bytes("el1"), composite::eoc::none)}));
BOOST_REQUIRE_EQUAL(composite(bytes({'\x00', '\x00', '\x01'})).components(),
components({std::make_pair(bytes(""), composite::eoc::end)}));
BOOST_REQUIRE_EQUAL(composite(bytes({'\x00', '\x05', 'e', 'l', 'e', 'm', '1', '\xff'})).components(),
components({std::make_pair(bytes("elem1"), composite::eoc::start)}));
BOOST_REQUIRE_EQUAL(composite(bytes({'\x00', '\x03', 'e', 'l', '1', '\x05'})).components(),
components({std::make_pair(bytes("el1"), composite::eoc::end)}));
BOOST_REQUIRE_EQUAL(composite(bytes({'\x00', '\x03', 'e', 'l', '1', '\x00', '\x00', '\x05', 'e', 'l', 'e', 'm', '2', '\x01'})).components(),
components({std::make_pair(bytes("el1"), composite::eoc::none),
std::make_pair(bytes("elem2"), composite::eoc::end)}));
BOOST_REQUIRE_EQUAL(composite(bytes({'\x00', '\x03', 'e', 'l', '1', '\xff', '\x00', '\x00', '\x01'})).components(),
components({std::make_pair(bytes("el1"), composite::eoc::start),
std::make_pair(bytes(""), composite::eoc::end)}));
}
BOOST_AUTO_TEST_CASE(test_enconding_of_singular_composite) {
using components = std::vector<composite::component>;
BOOST_REQUIRE_EQUAL(composite(bytes({'e', 'l', '1'}), false).components(),
components({std::make_pair(bytes("el1"), composite::eoc::none)}));
BOOST_REQUIRE_EQUAL(composite::serialize_value(std::vector<bytes>({bytes({'e', 'l', '1'})}), false).components(),
components({std::make_pair(bytes("el1"), composite::eoc::none)}));
}
BOOST_AUTO_TEST_CASE(test_enconding_of_static_composite) {
using components = std::vector<composite::component>;
auto s = schema_builder("ks", "cf")
.with_column("pk", bytes_type, column_kind::partition_key)
.with_column("ck", bytes_type, column_kind::clustering_key)
.with_column("v", bytes_type, column_kind::regular_column)
.build();
auto c = composite::static_prefix(*s);
BOOST_REQUIRE(c.is_static());
components cs;
for (auto&& p : c.components()) {
cs.push_back(std::make_pair(to_bytes(p.first), p.second));
}
BOOST_REQUIRE_EQUAL(cs, components({std::make_pair(bytes(""), composite::eoc::none)}));
}
BOOST_AUTO_TEST_CASE(test_composite_serialize_value) {
BOOST_REQUIRE_EQUAL(composite::serialize_value(std::vector<bytes>({bytes({'e', 'l', '1'})})).release_bytes(),
bytes({'\x00', '\x03', 'e', 'l', '1', '\x00'}));
}
BOOST_AUTO_TEST_CASE(test_composite_from_exploded) {
using components = std::vector<composite::component>;
BOOST_REQUIRE_EQUAL(composite::from_exploded({bytes_view(bytes({'e', 'l', '1'}))}, true, composite::eoc::start).components(),
components({std::make_pair(bytes("el1"), composite::eoc::start)}));
}
BOOST_AUTO_TEST_CASE(test_composite_view_explode) {
auto to_owning_vector = [] (std::vector<bytes_view> bvs) {
return boost::copy_range<std::vector<bytes>>(bvs | boost::adaptors::transformed([] (auto bv) {
return bytes(bv.begin(), bv.end());
}));
};
{
BOOST_REQUIRE_EQUAL(to_owning_vector(composite_view(composite(bytes({'\x00', '\x03', 'e', 'l', '1', '\x00'}))).explode()),
std::vector<bytes>({bytes({'e', 'l', '1'})}));
}
{
BOOST_REQUIRE_EQUAL(to_owning_vector(composite_view(composite(bytes({'e', 'l', '1'}), false)).explode()),
std::vector<bytes>({bytes({'e', 'l', '1'})}));
}
}
BOOST_AUTO_TEST_CASE(test_composite_validity) {
auto is_valid = [] (bytes b) {
auto v = composite_view(b);
try {
size_t s = 0;
for (auto& c : v.components()) { s += c.first.size() + sizeof(composite::size_type) + sizeof(composite::eoc_type); }
return s == b.size();
} catch (marshal_exception&) {
return false;
}
};
BOOST_REQUIRE_EQUAL(is_valid({'\x00', '\x01', 'a', '\x00'}), true);
BOOST_REQUIRE_EQUAL(is_valid({'\x00', '\x02', 'a', 'a', '\x00'}), true);
BOOST_REQUIRE_EQUAL(is_valid({'\x00', '\x02', 'a', 'a', '\x00', '\x00', '\x01', 'a', '\x00'}), true);
BOOST_REQUIRE_EQUAL(is_valid({'\x00', '\x02', 'a', '\x00'}), false);
BOOST_REQUIRE_EQUAL(is_valid({'\x01', 'a', '\x00'}), false);
BOOST_REQUIRE_EQUAL(is_valid({'\x00', '\x01', 'a', '\x00', '\x00'}), false);
BOOST_REQUIRE_EQUAL(is_valid({'\x00', '\x01', 'a', '\x00', '\x00', '\x00'}), false);
BOOST_REQUIRE_EQUAL(is_valid({'\x00', '\x01', 'a', '\x00', '\x00', '\x01'}), false);
BOOST_REQUIRE_EQUAL(is_valid({'\x00', '\x01', 'a'}), false);
BOOST_REQUIRE_EQUAL(is_valid({'\x00', '\x02', 'a'}), false);
}