cql3: expr: avoid redoing prepare work when evaluating field_selection

prepare_expression() already validates the types and computes
the index of the field; no need to redo that work when
evaluating the expression.

The tests are adjusted to also prepare the expression.

Closes #14562
This commit is contained in:
Avi Kivity
2023-07-07 11:32:07 +03:00
committed by Nadav Har'El
parent a93fd2b162
commit 4fc870a31a
2 changed files with 22 additions and 26 deletions

View File

@@ -1717,19 +1717,6 @@ cql3::raw_value do_evaluate(const conjunction& conj, const evaluation_inputs& in
static
cql3::raw_value do_evaluate(const field_selection& field_select, const evaluation_inputs& inputs) {
const user_type_impl* udt_type = dynamic_cast<const user_type_impl*>(&type_of(field_select.structure)->without_reversed());
if (udt_type == nullptr) {
on_internal_error(expr_logger, "evaluate(field_selection): type is not a user defined type");
}
const sstring& selected_field_name = field_select.field->text();
std::optional<std::size_t> selected_field_idx = udt_type->idx_of_field(to_bytes_view(selected_field_name));
if (!selected_field_idx.has_value()) {
throw exceptions::invalid_request_exception(
format("Unknown field '{}' in expression {}, user type {} doesn't have a field with this name.",
selected_field_name, field_select, udt_type->get_name_as_string()));
}
cql3::raw_value udt_value = evaluate(field_select.structure, inputs);
if (udt_value.is_null()) {
// `<null>.field` should evaluate to NULL.
@@ -1739,7 +1726,7 @@ cql3::raw_value do_evaluate(const field_selection& field_select, const evaluatio
cql3::raw_value field_value = udt_value.view().with_value(
[&](const FragmentedView auto& udt_serialized_bytes) -> cql3::raw_value {
// std::optional<FragmentedView> read_field
auto read_field = read_nth_user_type_field(udt_serialized_bytes, *selected_field_idx);
auto read_field = read_nth_user_type_field(udt_serialized_bytes, field_select.field_idx);
if (read_field.has_value()) {
return cql3::raw_value::make_value(managed_bytes(*read_field));

View File

@@ -3248,6 +3248,9 @@ BOOST_AUTO_TEST_CASE(evaluate_conjunction_of_conjunctions_with_invalid) {
}
BOOST_AUTO_TEST_CASE(evaluate_field_selection) {
auto schema = make_simple_test_schema();
auto [db, db_data] = make_data_dictionary_database(schema);
// The user defined type has 5 fields:
// CREATE TYPE test_ks.my_type (
// int_field int,
@@ -3278,19 +3281,25 @@ BOOST_AUTO_TEST_CASE(evaluate_field_selection) {
.structure = value, .field = make_shared<column_identifier_raw>(selected_field, true), .type = field_type};
};
auto prepare_and_evaluate = [&,db = db] (const expression& e, const evaluation_inputs& inputs) {
auto prepared = prepare_expression(e, db, "", schema.get(), nullptr);
return evaluate(prepared, inputs);
};
// Evaluate the fields, check that field values are correct
BOOST_REQUIRE_EQUAL(evaluate(make_field_selection(udt_value, "int_field", int32_type), evaluation_inputs{}), make_int_raw(123));
BOOST_REQUIRE_EQUAL(evaluate(make_field_selection(udt_value, "float_field", float_type), evaluation_inputs{}),
BOOST_REQUIRE_EQUAL(prepare_and_evaluate(make_field_selection(udt_value, "int_field", int32_type), evaluation_inputs{}), make_int_raw(123));
BOOST_REQUIRE_EQUAL(prepare_and_evaluate(make_field_selection(udt_value, "float_field", float_type), evaluation_inputs{}),
cql3::raw_value::make_null());
BOOST_REQUIRE_EQUAL(evaluate(make_field_selection(udt_value, "text_field", utf8_type), evaluation_inputs{}),
BOOST_REQUIRE_EQUAL(prepare_and_evaluate(make_field_selection(udt_value, "text_field", utf8_type), evaluation_inputs{}),
make_text_raw("abcdef"));
BOOST_REQUIRE_EQUAL(evaluate(make_field_selection(udt_value, "bigint_field", long_type), evaluation_inputs{}),
BOOST_REQUIRE_EQUAL(prepare_and_evaluate(make_field_selection(udt_value, "bigint_field", long_type), evaluation_inputs{}),
make_empty_raw());
BOOST_REQUIRE_EQUAL(evaluate(make_field_selection(udt_value, "int_field2", int32_type), evaluation_inputs{}),
BOOST_REQUIRE_EQUAL(prepare_and_evaluate(make_field_selection(udt_value, "int_field2", int32_type), evaluation_inputs{}),
make_int_raw(1337));
// Evaluate a nonexistent field, should throw an exception
BOOST_REQUIRE_THROW(evaluate(make_field_selection(udt_value, "field_testing", int32_type), evaluation_inputs{}),
BOOST_REQUIRE_THROW(prepare_and_evaluate(make_field_selection(udt_value, "field_testing", int32_type), evaluation_inputs{}),
exceptions::invalid_request_exception);
// Create a UDT value with values for the first 3 fields.
@@ -3302,21 +3311,21 @@ BOOST_AUTO_TEST_CASE(evaluate_field_selection) {
constant(make_tuple_raw({make_int_raw(123), cql3::raw_value::make_null(), make_text_raw("")}), udt_type);
// Evaluate the first 3 fields, check that the value is correct
BOOST_REQUIRE_EQUAL(evaluate(make_field_selection(short_udt_value, "int_field", int32_type), evaluation_inputs{}),
BOOST_REQUIRE_EQUAL(prepare_and_evaluate(make_field_selection(short_udt_value, "int_field", int32_type), evaluation_inputs{}),
make_int_raw(123));
BOOST_REQUIRE_EQUAL(evaluate(make_field_selection(short_udt_value, "float_field", float_type), evaluation_inputs{}),
BOOST_REQUIRE_EQUAL(prepare_and_evaluate(make_field_selection(short_udt_value, "float_field", float_type), evaluation_inputs{}),
cql3::raw_value::make_null());
BOOST_REQUIRE_EQUAL(evaluate(make_field_selection(short_udt_value, "text_field", utf8_type), evaluation_inputs{}),
BOOST_REQUIRE_EQUAL(prepare_and_evaluate(make_field_selection(short_udt_value, "text_field", utf8_type), evaluation_inputs{}),
make_text_raw(""));
// The serialized value doesn't contain any data for the 4th or 5th field, so they should be NULL.
BOOST_REQUIRE_EQUAL(evaluate(make_field_selection(short_udt_value, "bigint_field", long_type), evaluation_inputs{}),
BOOST_REQUIRE_EQUAL(prepare_and_evaluate(make_field_selection(short_udt_value, "bigint_field", long_type), evaluation_inputs{}),
cql3::raw_value::make_null());
BOOST_REQUIRE_EQUAL(evaluate(make_field_selection(short_udt_value, "int_field2", int32_type), evaluation_inputs{}),
BOOST_REQUIRE_EQUAL(prepare_and_evaluate(make_field_selection(short_udt_value, "int_field2", int32_type), evaluation_inputs{}),
cql3::raw_value::make_null());
// Evaluate a nonexistent field, should throw an exception
BOOST_REQUIRE_THROW(evaluate(make_field_selection(short_udt_value, "field_testing", int32_type), evaluation_inputs{}),
BOOST_REQUIRE_THROW(prepare_and_evaluate(make_field_selection(short_udt_value, "field_testing", int32_type), evaluation_inputs{}),
exceptions::invalid_request_exception);
}