Skip to content

Commit

Permalink
boost/expr_test: add vector expression tests
Browse files Browse the repository at this point in the history
I've added/adjusted tests using vector_constructor and collection_constructor with list_or_vector style before preparation.

Implemented utilities used in expr_test similar to those added in 8f6309b
  • Loading branch information
QuerthDP committed Dec 7, 2024
1 parent 887ce31 commit 8271f54
Show file tree
Hide file tree
Showing 3 changed files with 177 additions and 9 deletions.
127 changes: 118 additions & 9 deletions test/boost/expr_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "types/types.hh"
#include "types/list.hh"
#include "types/map.hh"
#include "types/vector.hh"
#include <boost/test/tools/old/interface.hpp>
#define BOOST_TEST_MODULE core

Expand Down Expand Up @@ -276,6 +277,17 @@ BOOST_AUTO_TEST_CASE(expr_printer_tuple_test) {
BOOST_REQUIRE_EQUAL(value_print(int_int_tuple_const, int_int_tuple), "(456, 789)");
}

BOOST_AUTO_TEST_CASE(expr_printer_vector_test) {
vector_constructor int_vector {
.elements = {make_int_const(13), make_int_const(45), make_int_const(90)},
.type = vector_type_impl::get_instance(int32_type, 3)
};
BOOST_REQUIRE_EQUAL(expr_print(int_vector), "[13, 45, 90]");

cql3::raw_value int_vector_const = evaluate(int_vector, query_options::DEFAULT);
BOOST_REQUIRE_EQUAL(value_print(int_vector_const, int_vector), "[13, 45, 90]");
}

BOOST_AUTO_TEST_CASE(expr_printer_usertype_test) {
column_identifier field_a("a", true);
column_identifier field_b("b", true);
Expand Down Expand Up @@ -741,6 +753,41 @@ BOOST_AUTO_TEST_CASE(evaluate_tuple_constructor_with_prefix_fields) {
BOOST_REQUIRE_EQUAL(evaluate(tuple, evaluation_inputs{}), make_tuple_raw({make_int_raw(1), make_text_raw("12")}));
}

// I think this situation could only occur when using vector constructor explicitly in code.
// Otherwise we do not allow to create an empty vector.
// Not sure if this is a valid test.
BOOST_AUTO_TEST_CASE(evaluate_vector_constructor_empty) {
expression empty_vector = make_vector_constructor({}, int32_type, 3);
BOOST_REQUIRE_EQUAL(evaluate(empty_vector, evaluation_inputs{}), make_int_vector_raw({}));
}

BOOST_AUTO_TEST_CASE(evaluate_vector_constructor) {
expression int_vector = make_vector_constructor({make_int_const(1), make_int_const(2), make_int_const(3)}, int32_type, 3);
BOOST_REQUIRE_EQUAL(evaluate(int_vector, evaluation_inputs{}), make_int_vector_raw({1, 2, 3}));
}

BOOST_AUTO_TEST_CASE(evaluate_vector_constructor_does_not_sort) {
expression int_vector =
make_vector_constructor({make_int_const(3), make_int_const(1), make_int_const(3), make_int_const(1)}, int32_type, 3);
BOOST_REQUIRE_EQUAL(evaluate(int_vector, evaluation_inputs{}), make_int_vector_raw({3, 1, 3, 1}));
}

BOOST_AUTO_TEST_CASE(evaluate_vector_constructor_with_null) {
expression vector_with_null =
make_vector_constructor({make_int_const(1), constant::make_null(int32_type), make_int_const(3)}, int32_type, 3);
BOOST_REQUIRE_THROW(evaluate(vector_with_null, evaluation_inputs{}), exceptions::invalid_request_exception);
}

// I think this situation could only occur when using vector constructor explicitly in code.
// Otherwise we do not allow to create a vector with empty elements.
// Not sure if this is a valid test.
BOOST_AUTO_TEST_CASE(evaluate_vector_constructor_with_empty) {
expression vector_with_empty =
make_vector_constructor({make_int_const(1), make_empty_const(int32_type), make_int_const(3)}, int32_type, 3);
BOOST_REQUIRE_EQUAL(evaluate(vector_with_empty, evaluation_inputs{}),
make_vector_raw({make_int_raw(1), make_empty_raw(), make_int_raw(3)}));
}

BOOST_AUTO_TEST_CASE(evaluate_usertype_constructor_empty) {
expression empty_usertype = make_usertype_constructor({});
BOOST_REQUIRE_EQUAL(evaluate(empty_usertype, evaluation_inputs{}), make_tuple_raw({}));
Expand Down Expand Up @@ -1736,7 +1783,7 @@ BOOST_AUTO_TEST_CASE(prepare_tuple_constructor_of_columns) {
BOOST_REQUIRE_EQUAL(prepared, expected);
}

BOOST_AUTO_TEST_CASE(prepare_list_collection_constructor) {
BOOST_AUTO_TEST_CASE(prepare_list_or_vector_collection_constructor) {
schema_ptr table_schema = make_simple_test_schema();
auto [db, db_data] = make_data_dictionary_database(table_schema);

Expand All @@ -1752,11 +1799,19 @@ BOOST_AUTO_TEST_CASE(prepare_list_collection_constructor) {

data_type list_type = list_type_impl::get_instance(long_type, true);

expression prepared = prepare_expression(constructor, db, "test_ks", table_schema.get(), make_receiver(list_type));
expression expected =
expression prepared_list = prepare_expression(constructor, db, "test_ks", table_schema.get(), make_receiver(list_type));
expression expected_list =
make_list_const({make_bigint_const(123), make_bigint_const(456), make_bigint_const(789)}, long_type);

BOOST_REQUIRE_EQUAL(prepared, expected);
BOOST_REQUIRE_EQUAL(prepared_list, expected_list);

data_type vector_type = vector_type_impl::get_instance(long_type, 3);

expression prepared_vector = prepare_expression(constructor, db, "test_ks", table_schema.get(), make_receiver(vector_type));
expression expected_vector =
make_vector_const({make_bigint_const(123), make_bigint_const(456), make_bigint_const(789)}, long_type);

BOOST_REQUIRE_EQUAL(prepared_vector, expected_vector);
}

// preparing empty nonfrozen collections results in null
Expand All @@ -1773,9 +1828,11 @@ BOOST_AUTO_TEST_CASE(prepare_list_collection_constructor_empty_nonfrozen) {
expression expected = constant::make_null(list_type);

BOOST_REQUIRE_EQUAL(prepared, expected);

// Vector type is not tested here because it is always frozen.
}

BOOST_AUTO_TEST_CASE(prepare_list_collection_constructor_empty_frozen) {
BOOST_AUTO_TEST_CASE(prepare_list_or_vector_collection_constructor_empty_frozen) {
schema_ptr table_schema = make_simple_test_schema();
auto [db, db_data] = make_data_dictionary_database(table_schema);

Expand All @@ -1788,9 +1845,15 @@ BOOST_AUTO_TEST_CASE(prepare_list_collection_constructor_empty_frozen) {
expression expected = constant(make_list_raw({}), list_type);

BOOST_REQUIRE_EQUAL(prepared, expected);

data_type vector_type = vector_type_impl::get_instance(long_type, 0);

// Should throw because we can't prepare an empty vector.
BOOST_REQUIRE_THROW(prepare_expression(constructor, db, "test_ks", table_schema.get(), make_receiver(vector_type)),
exceptions::invalid_request_exception);
}

BOOST_AUTO_TEST_CASE(prepare_list_collection_constructor_no_receiver) {
BOOST_AUTO_TEST_CASE(prepare_list_or_vector_collection_constructor_no_receiver) {
schema_ptr table_schema = make_simple_test_schema();
auto [db, db_data] = make_data_dictionary_database(table_schema);

Expand All @@ -1804,8 +1867,6 @@ BOOST_AUTO_TEST_CASE(prepare_list_collection_constructor_no_receiver) {
},
.type = nullptr};

data_type list_type = list_type_impl::get_instance(long_type, true);

BOOST_REQUIRE_THROW(prepare_expression(constructor, db, "test_ks", table_schema.get(), nullptr),
exceptions::invalid_request_exception);
}
Expand Down Expand Up @@ -1850,7 +1911,48 @@ BOOST_AUTO_TEST_CASE(prepare_list_collection_constructor_with_bind_var) {
BOOST_REQUIRE_EQUAL(prepared, expected);
}

BOOST_AUTO_TEST_CASE(prepare_list_collection_constructor_with_null) {
// As vector_constructor is not allowed in prepare_expression,
// collection_constructor with list_or_vector style is used instead.
BOOST_AUTO_TEST_CASE(prepare_vector_collection_constructor_with_bind_var) {
schema_ptr table_schema = make_simple_test_schema();
auto [db, db_data] = make_data_dictionary_database(table_schema);

expression constructor = collection_constructor{
.style = collection_constructor::style_type::list_or_vector,
.elements =
{
make_int_untyped("123"),
bind_variable{.bind_index = 1, .receiver = nullptr},
make_int_untyped("789"),
},
.type = nullptr};

data_type vector_type = vector_type_impl::get_instance(long_type, 3);

expression prepared = prepare_expression(constructor, db, "test_ks", table_schema.get(), make_receiver(vector_type));

// prepared bind_variable contains a receiver which we need to extract
// in order to prepare an equal expected value.
vector_constructor* prepared_constructor = as_if<vector_constructor>(&prepared);
BOOST_REQUIRE(prepared_constructor != nullptr);
BOOST_REQUIRE_EQUAL(prepared_constructor->elements.size(), 3);

bind_variable* prepared_bind_var = as_if<bind_variable>(&prepared_constructor->elements[1]);
BOOST_REQUIRE(prepared_bind_var != nullptr);

::lw_shared_ptr<column_specification> bind_var_receiver = prepared_bind_var->receiver;
BOOST_REQUIRE(bind_var_receiver.get() != nullptr);
BOOST_REQUIRE(bind_var_receiver->type == long_type);

expression expected = vector_constructor{
.elements = {make_bigint_const(123), bind_variable{.bind_index = 1, .receiver = bind_var_receiver},
make_bigint_const(789)},
.type = vector_type};

BOOST_REQUIRE_EQUAL(prepared, expected);
}

BOOST_AUTO_TEST_CASE(prepare_list_or_vector_collection_constructor_with_null) {
schema_ptr table_schema = make_simple_test_schema();
auto [db, db_data] = make_data_dictionary_database(table_schema);

Expand All @@ -1865,6 +1967,12 @@ BOOST_AUTO_TEST_CASE(prepare_list_collection_constructor_with_null) {

BOOST_REQUIRE_EQUAL(prepare_expression(constructor, db, "test_ks", table_schema.get(), make_receiver(list_type)),
make_int_list_const({123, 456, std::nullopt}));

data_type vector_type = vector_type_impl::get_instance(int32_type, 3);

// Should throw because we can't prepare a vector with null element.
BOOST_REQUIRE_THROW(prepare_expression(constructor, db, "test_ks", table_schema.get(), make_receiver(vector_type)),
exceptions::invalid_request_exception);
}

BOOST_AUTO_TEST_CASE(prepare_set_collection_constructor) {
Expand Down Expand Up @@ -3597,6 +3705,7 @@ void test_prepare_good_binary_operator(expression good_binop_unprepared,
tuple_type_impl::get_instance({boolean_type}),
tuple_type_impl::get_instance({boolean_type, float_type}),
tuple_type_impl::get_instance({utf8_type, float_type}),
vector_type_impl::get_instance(boolean_type, 1),
user_type_impl::get_instance("test_ks", "test_ut", {"field1", "field2"}, {boolean_type, float_type}, false),
user_type_impl::get_instance("test_ks", "test_ut", {"field1", "field2"}, {boolean_type, float_type}, true)};

Expand Down
44 changes: 44 additions & 0 deletions test/lib/expr_test_utils.cc
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,27 @@ raw_value make_tuple_raw(const std::vector<raw_value>& values) {
return raw_value::make_value(std::move(ret));
}

// This function implements custom serialization of vectors.
// Some tests require the vector to contain an empty value,
// which is impossible to express using the existing code.
// It only supports vectors of fixed-length elements.
raw_value make_vector_raw(const std::vector<raw_value>& values) {
size_t serialized_len = 0;
for (const raw_value& val : values) {
if (val.is_value()) {
serialized_len += val.view().size_bytes();
}
}
managed_bytes ret(managed_bytes::initialized_later(), serialized_len);
managed_bytes_mutable_view out(ret);
for (const raw_value& val : values) {
val.view().with_value([&](const FragmentedView auto& bytes_view) {
write_fragmented(out, bytes_view);
});
}
return raw_value::make_value(std::move(ret));
}

template <class T>
raw_value to_raw_value(const T& t) {
if constexpr (std::same_as<T, raw_value>) {
Expand Down Expand Up @@ -293,6 +314,16 @@ constant make_tuple_const(const std::vector<constant>& vals, const std::vector<d
return test_utils::make_tuple_const(to_raw_values(vals), element_types);
}

constant make_vector_const(const std::vector<raw_value>& vals, data_type elements_type) {
raw_value raw_vector = make_vector_raw(vals);
data_type vector_type = vector_type_impl::get_instance(elements_type, vals.size());
return constant(std::move(raw_vector), std::move(vector_type));
}

constant make_vector_const(const std::vector<constant>& vals, data_type elements_type) {
return make_vector_const(to_raw_values(vals), elements_type);
}

raw_value make_int_list_raw(const std::vector<std::optional<int32_t>>& values) {
return make_list_raw(to_raw_values(values));
}
Expand All @@ -305,6 +336,10 @@ raw_value make_int_int_map_raw(const std::vector<std::pair<int32_t, int32_t>>& v
return make_map_raw(to_raw_value_pairs(values));
}

raw_value make_int_vector_raw(const std::vector<int32_t>& values) {
return make_vector_raw(to_raw_values(values));
}

constant make_int_list_const(const std::vector<std::optional<int32_t>>& values) {
return constant(make_int_list_raw(values), list_type_impl::get_instance(int32_type, true));
}
Expand All @@ -317,6 +352,10 @@ constant make_int_int_map_const(const std::vector<std::pair<int32_t, int32_t>>&
return constant(make_int_int_map_raw(values), map_type_impl::get_instance(int32_type, int32_type, true));
}

constant make_int_vector_const(const std::vector<int32_t>& values) {
return constant(make_int_vector_raw(values), vector_type_impl::get_instance(int32_type, values.size()));
}

collection_constructor make_list_constructor(std::vector<expression> elements, data_type elements_type) {
return collection_constructor{.style = collection_constructor::style_type::list_or_vector,
.elements = std::move(elements),
Expand Down Expand Up @@ -353,6 +392,11 @@ tuple_constructor make_tuple_constructor(std::vector<expression> elements, std::
.type = tuple_type_impl::get_instance(std::move(element_types))};
}

vector_constructor make_vector_constructor(std::vector<expression> elements, data_type elements_type, size_t dimension) {
return vector_constructor{.elements = std::move(elements),
.type = vector_type_impl::get_instance(elements_type, dimension)};
}

usertype_constructor make_usertype_constructor(std::vector<std::pair<sstring_view, constant>> field_values) {
usertype_constructor::elements_map_type elements_map;
std::vector<bytes> field_names;
Expand Down
15 changes: 15 additions & 0 deletions test/lib/expr_test_utils.hh
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "types/list.hh"
#include "types/map.hh"
#include "types/set.hh"
#include "types/vector.hh"

namespace cql3 {
namespace expr {
Expand Down Expand Up @@ -67,6 +68,12 @@ raw_value make_map_raw(const std::vector<std::pair<raw_value, raw_value>>& value
// which is impossible to express using the existing code.
raw_value make_tuple_raw(const std::vector<raw_value>& values);

// This function implements custom serialization of vectors.
// Some tests require the vector to contain unset_value or an empty value,
// which is impossible to express using the existing code.
// It only supports vectors of fixed-length elements.
raw_value make_vector_raw(const std::vector<raw_value>& values);

constant make_list_const(const std::vector<raw_value>& vals, data_type elements_type);
constant make_list_const(const std::vector<constant>& vals, data_type elements_type);

Expand All @@ -84,15 +91,22 @@ constant make_map_const(const std::vector<std::pair<constant, constant>>& vals,
constant make_tuple_const(const std::vector<raw_value>& vals, const std::vector<data_type>& element_types);
constant make_tuple_const(const std::vector<constant>& vals, const std::vector<data_type>& element_types);

constant make_vector_const(const std::vector<raw_value>& vals, data_type elements_type);
constant make_vector_const(const std::vector<constant>& vals, data_type elements_type);

raw_value make_int_list_raw(const std::vector<std::optional<int32_t>>& values);
raw_value make_int_set_raw(const std::vector<int32_t>& values);

raw_value make_int_int_map_raw(const std::vector<std::pair<int32_t, int32_t>>& values);

raw_value make_int_vector_raw(const std::vector<int32_t>& values);

constant make_int_list_const(const std::vector<std::optional<int32_t>>& values);
constant make_int_set_const(const std::vector<int32_t>& values);
constant make_int_int_map_const(const std::vector<std::pair<int32_t, int32_t>>& values);

constant make_int_vector_const(const std::vector<int32_t>& values);

collection_constructor make_list_constructor(std::vector<expression> elements, data_type elements_type);
collection_constructor make_set_constructor(std::vector<expression> elements, data_type elements_type);
collection_constructor make_map_constructor(const std::vector<expression> elements,
Expand All @@ -102,6 +116,7 @@ collection_constructor make_map_constructor(const std::vector<std::pair<expressi
data_type key_type,
data_type element_type);
tuple_constructor make_tuple_constructor(std::vector<expression> elements, std::vector<data_type> element_types);
vector_constructor make_vector_constructor(std::vector<expression> elements, data_type elements_type, size_t dimension);
usertype_constructor make_usertype_constructor(std::vector<std::pair<sstring_view, constant>> field_values);

::lw_shared_ptr<column_specification> make_receiver(data_type receiver_type, sstring name = "receiver_name");
Expand Down

0 comments on commit 8271f54

Please sign in to comment.