From 9c4051607141ccef88a5c0c6888f804fb0de85a9 Mon Sep 17 00:00:00 2001 From: Jan Ciolek Date: Tue, 28 Sep 2021 18:02:57 +0200 Subject: [PATCH 01/24] cql3: expr: Add receiver to expr::bind_variable bind_variable used to have only the type of bound value. Now this type is replaced with receiver, which describes information about column corresponding to this value. A receiver contains type, column name, etc. Receiver is needed in order to implement fill_prepare_context in the next commit. It's an argument of prepare_context::add_variable_specification. Signed-off-by: Jan Ciolek --- cql3/constants.cc | 2 +- cql3/expr/expression.cc | 19 +++++++++---------- cql3/expr/expression.hh | 7 +++---- cql3/lists.cc | 2 +- cql3/maps.cc | 2 +- cql3/sets.cc | 2 +- cql3/tuples.cc | 4 ++-- cql3/user_types.cc | 2 +- 8 files changed, 19 insertions(+), 21 deletions(-) diff --git a/cql3/constants.cc b/cql3/constants.cc index e23a3b4ef3..d1fb009195 100644 --- a/cql3/constants.cc +++ b/cql3/constants.cc @@ -62,7 +62,7 @@ expr::expression constants::marker::to_expression() { return expr::bind_variable { .shape = expr::bind_variable::shape_type::scalar, .bind_index = _bind_index, - .value_type = _receiver->type + .receiver = _receiver }; } diff --git a/cql3/expr/expression.cc b/cql3/expr/expression.cc index 5cf74b5b90..918d77f52c 100644 --- a/cql3/expr/expression.cc +++ b/cql3/expr/expression.cc @@ -1503,28 +1503,27 @@ static managed_bytes reserialize_value(View value_bytes, } constant evaluate(const bind_variable& bind_var, const query_options& options) { - if (bind_var.value_type.get() == nullptr) { + if (bind_var.receiver.get() == nullptr) { on_internal_error(expr_logger, - "evaluate(bind_variable) called with nullptr type, should be prepared first"); + "evaluate(bind_variable) called with nullptr receiver, should be prepared first"); } cql3::raw_value_view value = options.get_value_at(bind_var.bind_index); if (value.is_null()) { - return constant::make_null(bind_var.value_type); + return constant::make_null(bind_var.receiver->type); } if (value.is_unset_value()) { - return constant::make_unset_value(bind_var.value_type); + return constant::make_unset_value(bind_var.receiver->type); } - const abstract_type& value_type = bind_var.value_type->without_reversed(); + const abstract_type& value_type = bind_var.receiver->type->without_reversed(); try { value.validate(value_type, options.get_cql_serialization_format()); } catch (const marshal_exception& e) { - throw exceptions::invalid_request_exception( - format("Exception while binding value of type {}: {}", - bind_var.value_type->name(), e.what())); + throw exceptions::invalid_request_exception(format("Exception while binding column {:s}: {:s}", + bind_var.receiver->name->to_cql_string(), e.what())); } if (value_type.bound_value_needs_to_be_reserialized(options.get_cql_serialization_format())) { @@ -1532,10 +1531,10 @@ constant evaluate(const bind_variable& bind_var, const query_options& options) { return reserialize_value(value_bytes, value_type, options.get_cql_serialization_format()); }); - return constant(raw_value::make_value(std::move(new_value)), bind_var.value_type); + return constant(raw_value::make_value(std::move(new_value)), bind_var.receiver->type); } - return constant(raw_value::make_value(value), bind_var.value_type); + return constant(raw_value::make_value(value), bind_var.receiver->type); } constant evaluate(const tuple_constructor& tuple, const query_options& options) { diff --git a/cql3/expr/expression.hh b/cql3/expr/expression.hh index 12058b4e71..64ed24a8a6 100644 --- a/cql3/expr/expression.hh +++ b/cql3/expr/expression.hh @@ -263,10 +263,9 @@ struct bind_variable { shape_type shape; int32_t bind_index; - // Type of the bound value. - // Before preparing can be nullptr. - // After preparing always holds a valid type. - data_type value_type; + // Describes where this bound value will be assigned. + // Contains value type and other useful information. + ::lw_shared_ptr receiver; }; // A constant which does not yet have a date type. It is partially typed diff --git a/cql3/lists.cc b/cql3/lists.cc index 84930bef0d..585f21ad05 100644 --- a/cql3/lists.cc +++ b/cql3/lists.cc @@ -196,7 +196,7 @@ expr::expression lists::marker::to_expression() { return expr::bind_variable { .shape = expr::bind_variable::shape_type::scalar, .bind_index = _bind_index, - .value_type = _receiver->type + .receiver = _receiver }; } diff --git a/cql3/maps.cc b/cql3/maps.cc index 889de7d0c6..4c8996efd6 100644 --- a/cql3/maps.cc +++ b/cql3/maps.cc @@ -210,7 +210,7 @@ expr::expression maps::marker::to_expression() { return expr::bind_variable { .shape = expr::bind_variable::shape_type::scalar, .bind_index = _bind_index, - .value_type = _receiver->type + .receiver = _receiver }; } diff --git a/cql3/sets.cc b/cql3/sets.cc index cfabfc129b..134b41959f 100644 --- a/cql3/sets.cc +++ b/cql3/sets.cc @@ -170,7 +170,7 @@ expr::expression sets::marker::to_expression() { return expr::bind_variable { .shape = expr::bind_variable::shape_type::scalar, .bind_index = _bind_index, - .value_type = _receiver->type + .receiver = _receiver }; } diff --git a/cql3/tuples.cc b/cql3/tuples.cc index ad29691463..bcf5089109 100644 --- a/cql3/tuples.cc +++ b/cql3/tuples.cc @@ -84,7 +84,7 @@ expr::expression tuples::marker::to_expression() { return expr::bind_variable { .shape = expr::bind_variable::shape_type::tuple, .bind_index = _bind_index, - .value_type = _receiver->type + .receiver = _receiver }; } @@ -92,7 +92,7 @@ expr::expression tuples::in_marker::to_expression() { return expr::bind_variable { .shape = expr::bind_variable::shape_type::tuple_in, .bind_index = _bind_index, - .value_type = _receiver->type + .receiver = _receiver }; } diff --git a/cql3/user_types.cc b/cql3/user_types.cc index 4b3bcad382..c3b393a5bc 100644 --- a/cql3/user_types.cc +++ b/cql3/user_types.cc @@ -147,7 +147,7 @@ expr::expression user_types::marker::to_expression() { return expr::bind_variable { .shape = expr::bind_variable::shape_type::scalar, .bind_index = _bind_index, - .value_type = _receiver->type + .receiver = _receiver }; } From edaa3b5dc2f0829a1b6381d3b47efde33bef6e97 Mon Sep 17 00:00:00 2001 From: Jan Ciolek Date: Thu, 14 Oct 2021 21:30:59 +0200 Subject: [PATCH 02/24] cql3: expr: add expr::visit that takes a mutable expression Currently expr::visit can only take a const expression as an argument. For cases where we want to visit the expression and modify it a new function is needed. Signed-off-by: Jan Ciolek --- cql3/expr/expression.hh | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/cql3/expr/expression.hh b/cql3/expr/expression.hh index 64ed24a8a6..f82d207f99 100644 --- a/cql3/expr/expression.hh +++ b/cql3/expr/expression.hh @@ -129,6 +129,26 @@ concept invocable_on_expression && std::invocable ; +template +concept invocable_on_expression_ref + = std::invocable + && std::invocable + && std::invocable + && std::invocable + && std::invocable + && std::invocable + && std::invocable + && std::invocable + && std::invocable + && std::invocable + && std::invocable + && std::invocable + && std::invocable + && std::invocable + && std::invocable + && std::invocable + ; + /// A CQL expression -- union of all possible expression types. bool means a Boolean constant. class expression final { // 'impl' holds a variant of all expression types, but since @@ -146,6 +166,7 @@ public: expression& operator=(expression&&) noexcept = default; friend auto visit(invocable_on_expression auto&& visitor, const expression& e); + friend auto visit(invocable_on_expression_ref auto&& visitor, expression& e); template friend bool is(const expression& e); @@ -349,6 +370,10 @@ auto visit(invocable_on_expression auto&& visitor, const expression& e) { return std::visit(visitor, e._v->v); } +auto visit(invocable_on_expression_ref auto&& visitor, expression& e) { + return std::visit(visitor, e._v->v); +} + template bool is(const expression& e) { return std::holds_alternative(e._v->v); From c2eb3a58b89a22ac54ffafe47dd7c1e87bea2236 Mon Sep 17 00:00:00 2001 From: Jan Ciolek Date: Thu, 14 Oct 2021 21:31:27 +0200 Subject: [PATCH 03/24] cql3: expr: Implement fill_prepare_context(expression) Adds a new function - expr::fill_prepare_context. This function has the same functionality as term::fill_prepare_context, which will be removed soon. fill_prepare_context used to take its argument with a const qualifier, but it turns out that the argume> It sets the cache ids of function calls corresponding to partition key restrictions. New function doesn't have const to make this clear and avoid surprises. Added expr::visit that takes an argument without const qualifier. There were some problems with cache_ids in function_call. prepare_context used to collect ::shared_ptr of some function call, and then this allowed it to clear cache ids of all involved functions on demand. To replicate this prepare_context now collects shared pointers to expr::function_call cache ids. It currently collects both, but functions::function_call will be removed soon. Signed-off-by: Jan Ciolek --- cql3/expr/expression.cc | 76 +++++++++++++++++++-- cql3/expr/expression.hh | 25 ++++++- cql3/functions/functions.cc | 2 +- cql3/prepare_context.cc | 31 +++++++-- cql3/prepare_context.hh | 6 +- cql3/restrictions/statement_restrictions.cc | 1 - cql3/statements/modification_statement.cc | 6 +- 7 files changed, 131 insertions(+), 16 deletions(-) diff --git a/cql3/expr/expression.cc b/cql3/expr/expression.cc index 918d77f52c..1268caec64 100644 --- a/cql3/expr/expression.cc +++ b/cql3/expr/expression.cc @@ -50,6 +50,7 @@ #include "cql3/user_types.hh" #include "cql3/functions/scalar_function.hh" #include "cql3/functions/function_call.hh" +#include "cql3/prepare_context.hh" namespace cql3 { namespace expr { @@ -1764,8 +1765,10 @@ constant evaluate(const function_call& fun_call, const query_options& options) { arguments.emplace_back(to_bytes_opt(std::move(arg_val.value))); } - if (fun_call.lwt_cache_id.has_value()) { - computed_function_values::mapped_type* cached_value = options.find_cached_pk_function_call(*fun_call.lwt_cache_id); + bool has_cache_id = fun_call.lwt_cache_id.get() != nullptr && fun_call.lwt_cache_id->has_value(); + if (has_cache_id) { + computed_function_values::mapped_type* cached_value = + options.find_cached_pk_function_call(**fun_call.lwt_cache_id); if (cached_value != nullptr) { return constant(raw_value::make_value(*cached_value), scalar_fun->return_type()); } @@ -1773,8 +1776,8 @@ constant evaluate(const function_call& fun_call, const query_options& options) { bytes_opt result = scalar_fun->execute(cql_serialization_format::internal(), arguments); - if (fun_call.lwt_cache_id.has_value()) { - options.cache_pk_function_call(*fun_call.lwt_cache_id, result); + if (has_cache_id) { + options.cache_pk_function_call(**fun_call.lwt_cache_id, result); } if (!result.has_value()) { @@ -1911,6 +1914,71 @@ utils::chunked_vector> get_list_of_tuples_element return tuples_list; } +void fill_prepare_context(expression& e, prepare_context& ctx) { + expr::visit(overloaded_functor { + [&](bind_variable& bind_var) { + ctx.add_variable_specification(bind_var.bind_index, bind_var.receiver); + }, + [&](collection_constructor& c) { + for (expr::expression& element : c.elements) { + fill_prepare_context(element, ctx); + } + }, + [&](tuple_constructor& t) { + for (expr::expression& element : t.elements) { + fill_prepare_context(element, ctx); + } + }, + [&](usertype_constructor& u) { + for (auto& [field_name, field_val] : u.elements) { + fill_prepare_context(field_val, ctx); + } + }, + [&](function_call& f) { + const shared_ptr& func = std::get>(f.func); + if (ctx.is_processing_pk_restrictions() && !func->is_pure()) { + ctx.add_pk_function_call(f); + } + + for (expr::expression& argument : f.args) { + fill_prepare_context(argument, ctx); + } + }, + [&](binary_operator& binop) { + fill_prepare_context(binop.lhs, ctx); + fill_prepare_context(binop.rhs, ctx); + }, + [&](conjunction& c) { + for (expression& child : c.children) { + fill_prepare_context(child, ctx); + } + }, + [](token&) {}, + [](unresolved_identifier&) {}, + [&](column_mutation_attribute& a) { + fill_prepare_context(a.column, ctx); + }, + [&](cast& c) { + fill_prepare_context(c.arg, ctx); + }, + [&](field_selection& fs) { + fill_prepare_context(fs.structure, ctx); + }, + [&](column_value& cv) { + fill_prepare_context(cv.sub, ctx); + }, + [](untyped_constant&) {}, + [](null&) {}, + [](constant&) {}, + }, e); +} + +void fill_prepare_context(::shared_ptr& t, prepare_context& ctx) { + if (t.get() != nullptr) { + t->fill_prepare_context(ctx); + } +} + expression to_expression(const ::shared_ptr& term_ptr) { if (term_ptr.get() == nullptr) { return constant::make_null(); diff --git a/cql3/expr/expression.hh b/cql3/expr/expression.hh index f82d207f99..239a2b5b9c 100644 --- a/cql3/expr/expression.hh +++ b/cql3/expr/expression.hh @@ -53,6 +53,7 @@ namespace query { namespace cql3 { struct term; +struct prepare_context; class column_identifier_raw; class query_options; @@ -262,7 +263,21 @@ struct function_call { // The query should be executed on a shard that has the pk partition, // but it changes with each uuid() call. // uuid() call result is cached and sent to the proper shard. - std::optional lwt_cache_id; + // + // Cache id is kept in shared_ptr because of how prepare_context works. + // During fill_prepare_context all function cache ids are collected + // inside prepare_context. + // Later when some condition occurs we might decide to clear + // cache ids of all function calls found in prepare_context. + // However by this time these function calls could have been + // copied multiple times. Prepare_context keeps a shared_ptr + // to function_call ids, and then clearing the shared id + // clears it in all possible copies. + // This logic was introduced back when everything was shared_ptr, + // now a better solution might exist. + // + // This field can be nullptr, it means that there is no cache id set. + ::shared_ptr> lwt_cache_id; }; struct cast { @@ -708,6 +723,14 @@ std::vector get_elements(const constant&); // It is useful with IN restrictions like (a, b) IN [(1, 2), (3, 4)]. utils::chunked_vector> get_list_of_tuples_elements(const constant&); +// Retrieves information needed in prepare_context. +// Collects the column specification for the bind variables in this expression. +// Sets lwt_cache_id field in function_calls. +void fill_prepare_context(expression&, cql3::prepare_context&); + +// Needed for now because some fields of expression structs are terms, will be removed soon. +void fill_prepare_context(::shared_ptr&, cql3::prepare_context&); + expression to_expression(const ::shared_ptr&); } // namespace expr diff --git a/cql3/functions/functions.cc b/cql3/functions/functions.cc index 4cdf5f4f41..b0aebe6be6 100644 --- a/cql3/functions/functions.cc +++ b/cql3/functions/functions.cc @@ -512,7 +512,7 @@ expr::expression function_call::to_expression() { return expr::function_call { .func = _fun, .args = std::move(args), - .lwt_cache_id = _id, + .lwt_cache_id = ::make_shared>(_id), }; } diff --git a/cql3/prepare_context.cc b/cql3/prepare_context.cc index f4b644d4e1..444100ffe6 100644 --- a/cql3/prepare_context.cc +++ b/cql3/prepare_context.cc @@ -98,18 +98,41 @@ void prepare_context::set_bound_variables(const std::vector& fun_call : _pk_fn_calls) { + fun_call->set_id(std::nullopt); + } + + for (::shared_ptr>& cache_id : _pk_function_calls_cache_ids) { + if (cache_id.get() != nullptr) { + *cache_id = std::nullopt; + } + } } void prepare_context::add_pk_function_call(::shared_ptr fn) { constexpr auto fn_limit = std::numeric_limits::max(); - if (_pk_fn_calls.size() == fn_limit) { + if (_pk_function_calls_cache_ids.size() == fn_limit) { throw exceptions::invalid_request_exception( format("Too many function calls within one statement. Max supported number is {}", fn_limit)); } - fn->set_id(_pk_fn_calls.size()); + + fn->set_id(_pk_function_calls_cache_ids.size()); + _pk_fn_calls.emplace_back(std::move(fn)); + // Workaround for now, this will be removed later along with this method + _pk_function_calls_cache_ids.push_back({}); +} + +void prepare_context::add_pk_function_call(expr::function_call& fn) { + constexpr auto fn_limit = std::numeric_limits::max(); + if (_pk_function_calls_cache_ids.size() == fn_limit) { + throw exceptions::invalid_request_exception( + format("Too many function calls within one statement. Max supported number is {}", fn_limit)); + } + + fn.lwt_cache_id = ::make_shared>(_pk_function_calls_cache_ids.size()); + _pk_function_calls_cache_ids.emplace_back(fn.lwt_cache_id); } diff --git a/cql3/prepare_context.hh b/cql3/prepare_context.hh index 6ae1eec957..93949a8010 100644 --- a/cql3/prepare_context.hh +++ b/cql3/prepare_context.hh @@ -47,6 +47,7 @@ #include #include #include +#include "cql3/expr/expression.hh" class schema; @@ -70,6 +71,8 @@ private: // participate in partition key ranges computation within an LWT statement. using function_calls_t = std::vector<::shared_ptr>; function_calls_t _pk_fn_calls; + + std::vector<::shared_ptr>> _pk_function_calls_cache_ids; // The flag denoting whether the context is currently in partition key // processing mode (inside query restrictions AST nodes). If set to true, // then every `function_call` instance will be recorded in the context and @@ -93,11 +96,12 @@ public: void set_bound_variables(const std::vector>& prepare_meta); - function_calls_t& pk_function_calls(); + void clear_pk_function_calls_cache(); // Record a new function call, which evaluates a partition key constraint. // Also automatically assigns an id to the AST node for caching purposes. void add_pk_function_call(::shared_ptr fn); + void add_pk_function_call(cql3::expr::function_call& fn); // Inform the context object that it has started or ended processing the // partition key part of statement restrictions. diff --git a/cql3/restrictions/statement_restrictions.cc b/cql3/restrictions/statement_restrictions.cc index 99a8fedd21..073e7091d3 100644 --- a/cql3/restrictions/statement_restrictions.cc +++ b/cql3/restrictions/statement_restrictions.cc @@ -1742,6 +1742,5 @@ bool statement_restrictions::range_or_slice_eq_null(const query_options& options return boost::algorithm::any_of(_partition_range_restrictions, std::bind_front(has_eq_null, options)) || boost::algorithm::any_of(_clustering_prefix_restrictions, std::bind_front(has_eq_null, options)); } - } // namespace restrictions } // namespace cql3 diff --git a/cql3/statements/modification_statement.cc b/cql3/statements/modification_statement.cc index 8b33ccb635..4e85c62294 100644 --- a/cql3/statements/modification_statement.cc +++ b/cql3/statements/modification_statement.cc @@ -499,10 +499,8 @@ modification_statement::prepare(database& db, prepare_context& ctx, cql_stats& s // Since this cache is only meaningful for LWT queries, just clear the ids // if it's not a conditional statement so that the AST nodes don't // participate in the caching mechanism later. - if (!prepared_stmt->has_conditions()) { - for (auto& fn : ctx.pk_function_calls()) { - fn->set_id(std::nullopt); - } + if (!prepared_stmt->has_conditions() && prepared_stmt->_restrictions.has_value()) { + ctx.clear_pk_function_calls_cache(); } return prepared_stmt; } From a82351dc79d5f64fb8a6629e3d0036241cf3e7c0 Mon Sep 17 00:00:00 2001 From: Jan Ciolek Date: Tue, 12 Oct 2021 13:38:59 +0200 Subject: [PATCH 04/24] cql3: expr: Add constant::view() method Add a method that returns raw_value_view to expr::constant. It's added for convenience - without it in many places we would have to write my_value.value.to_view(). Signed-off-by: Jan Ciolek --- cql3/expr/expression.cc | 22 +++++++++++++--------- cql3/expr/expression.hh | 2 ++ cql3/lists.cc | 4 ++-- cql3/maps.cc | 2 +- cql3/sets.cc | 2 +- cql3/user_types.cc | 2 +- 6 files changed, 20 insertions(+), 14 deletions(-) diff --git a/cql3/expr/expression.cc b/cql3/expr/expression.cc index 1268caec64..e9c52698d6 100644 --- a/cql3/expr/expression.cc +++ b/cql3/expr/expression.cc @@ -955,7 +955,7 @@ std::ostream& operator<<(std::ostream& os, const column_value& cv) { std::ostream& operator<<(std::ostream& os, const expression& expr) { expr::visit(overloaded_functor{ - [&] (const constant& v) { os << v.value.to_view(); }, + [&] (const constant& v) { os << v.view(); }, [&] (const conjunction& conj) { fmt::print(os, "({})", fmt::join(conj.children, ") AND (")); }, [&] (const binary_operator& opr) { os << "(" << opr.lhs << ") " << opr.op << ' ' << *opr.rhs; @@ -1283,6 +1283,10 @@ bool constant::is_null_or_unset() const { return is_null() || is_unset_value(); } +cql3::raw_value_view constant::view() const { + return value.to_view(); +} + std::optional get_bool_value(const constant& constant_val) { if (constant_val.type->get_kind() != abstract_type::kind::boolean) { return std::nullopt; @@ -1296,7 +1300,7 @@ std::optional get_bool_value(const constant& constant_val) { return std::nullopt; } - return constant_val.value.to_view().deserialize(*boolean_type); + return constant_val.view().deserialize(*boolean_type); } constant evaluate(term* term_ptr, const query_options& options) { @@ -1657,14 +1661,14 @@ static constant evaluate_map(const collection_constructor& collection, const que return constant::make_unset_value(collection.type); } - if (key.value.to_view().size_bytes() > std::numeric_limits::max()) { + if (key.view().size_bytes() > std::numeric_limits::max()) { // TODO: Behaviour copied from maps::delayed_value::bind(), but this seems incorrect // The original reasoning is: // "We don't support values > 64K because the serialization format encode the length as an unsigned short." // but CQL uses int32_t to encode length of a map key throw exceptions::invalid_request_exception(format("Map key is too long. Map keys are limited to {:d} bytes but {:d} bytes keys provided", std::numeric_limits::max(), - key.value.to_view().size_bytes())); + key.view().size_bytes())); } evaluated_elements.emplace(std::move(key.value).to_managed_bytes(), @@ -1833,7 +1837,7 @@ static void ensure_can_get_value_elements(const constant& val, utils::chunked_vector get_list_elements(const constant& val) { ensure_can_get_value_elements(val, abstract_type::kind::list, "expr::get_list_elements"); - return val.value.to_view().with_value([](const FragmentedView auto& value_bytes) { + return val.view().with_value([](const FragmentedView auto& value_bytes) { return partially_deserialize_listlike(value_bytes, cql_serialization_format::internal()); }); } @@ -1841,7 +1845,7 @@ utils::chunked_vector get_list_elements(const constant& val) { utils::chunked_vector get_set_elements(const constant& val) { ensure_can_get_value_elements(val, abstract_type::kind::set, "expr::get_set_elements"); - return val.value.to_view().with_value([](const FragmentedView auto& value_bytes) { + return val.view().with_value([](const FragmentedView auto& value_bytes) { return partially_deserialize_listlike(value_bytes, cql_serialization_format::internal()); }); } @@ -1849,7 +1853,7 @@ utils::chunked_vector get_set_elements(const constant& val) { std::vector> get_map_elements(const constant& val) { ensure_can_get_value_elements(val, abstract_type::kind::map, "expr::get_map_elements"); - return val.value.to_view().with_value([](const FragmentedView auto& value_bytes) { + return val.view().with_value([](const FragmentedView auto& value_bytes) { return partially_deserialize_map(value_bytes, cql_serialization_format::internal()); }); } @@ -1857,7 +1861,7 @@ std::vector> get_map_elements(const cons std::vector get_tuple_elements(const constant& val) { ensure_can_get_value_elements(val, abstract_type::kind::tuple, "expr::get_tuple_elements"); - return val.value.to_view().with_value([&](const FragmentedView auto& value_bytes) { + return val.view().with_value([&](const FragmentedView auto& value_bytes) { const tuple_type_impl& ttype = static_cast(val.type->without_reversed()); return ttype.split_fragmented(value_bytes); }); @@ -1866,7 +1870,7 @@ std::vector get_tuple_elements(const constant& val) { std::vector get_user_type_elements(const constant& val) { ensure_can_get_value_elements(val, abstract_type::kind::user, "expr::get_user_type_elements"); - return val.value.to_view().with_value([&](const FragmentedView auto& value_bytes) { + return val.view().with_value([&](const FragmentedView auto& value_bytes) { const user_type_impl& utype = static_cast(val.type->without_reversed()); return utype.split_fragmented(value_bytes); }); diff --git a/cql3/expr/expression.hh b/cql3/expr/expression.hh index 239a2b5b9c..8401020649 100644 --- a/cql3/expr/expression.hh +++ b/cql3/expr/expression.hh @@ -330,6 +330,8 @@ struct constant { bool is_unset_value() const; bool is_null_or_unset() const; bool has_empty_value_bytes() const; + + cql3::raw_value_view view() const; }; // Denotes construction of a tuple from its elements, e.g. ('a', ?, some_column) in CQL. diff --git a/cql3/lists.cc b/cql3/lists.cc index 585f21ad05..3d4ddc1704 100644 --- a/cql3/lists.cc +++ b/cql3/lists.cc @@ -355,7 +355,7 @@ lists::do_append(const expr::constant& list_value, if (list_value.is_null()) { m.set_cell(prefix, column, params.make_dead_cell()); } else { - m.set_cell(prefix, column, params.make_cell(*column.type, list_value.value.to_view())); + m.set_cell(prefix, column, params.make_cell(*column.type, list_value.view())); } } } @@ -477,7 +477,7 @@ lists::discarder_by_index::execute(mutation& m, const clustering_key_prefix& pre } auto&& existing_list_opt = params.get_prefetched_list(m.key(), prefix, column); - int32_t idx = index.value.to_view().deserialize(*int32_type); + int32_t idx = index.view().deserialize(*int32_type); if (!existing_list_opt) { throw exceptions::invalid_request_exception("Attempted to delete an element from a list which is null"); diff --git a/cql3/maps.cc b/cql3/maps.cc index 4c8996efd6..a36bb24378 100644 --- a/cql3/maps.cc +++ b/cql3/maps.cc @@ -293,7 +293,7 @@ maps::do_put(mutation& m, const clustering_key_prefix& prefix, const update_para if (map_value.is_null()) { m.set_cell(prefix, column, params.make_dead_cell()); } else { - m.set_cell(prefix, column, params.make_cell(*column.type, map_value.value.to_view())); + m.set_cell(prefix, column, params.make_cell(*column.type, map_value.view())); } } } diff --git a/cql3/sets.cc b/cql3/sets.cc index 134b41959f..1ee69ac019 100644 --- a/cql3/sets.cc +++ b/cql3/sets.cc @@ -229,7 +229,7 @@ sets::adder::do_add(mutation& m, const clustering_key_prefix& row_key, const upd m.set_cell(row_key, column, mut.serialize(set_type)); } else if (!value.is_null()) { // for frozen sets, we're overwriting the whole cell - m.set_cell(row_key, column, params.make_cell(*column.type, value.value.to_view())); + m.set_cell(row_key, column, params.make_cell(*column.type, value.view())); } else { m.set_cell(row_key, column, params.make_dead_cell()); } diff --git a/cql3/user_types.cc b/cql3/user_types.cc index c3b393a5bc..7cf234895e 100644 --- a/cql3/user_types.cc +++ b/cql3/user_types.cc @@ -200,7 +200,7 @@ void user_types::setter::execute(mutation& m, const clustering_key_prefix& row_k m.set_cell(row_key, column, mut.serialize(type)); } else { if (!ut_value.is_null()) { - m.set_cell(row_key, column, params.make_cell(type, ut_value.value.to_view())); + m.set_cell(row_key, column, params.make_cell(type, ut_value.view())); } else { m.set_cell(row_key, column, params.make_dead_cell()); } From 1502abaca14bdfb677c669eaa6149754eb6e7075 Mon Sep 17 00:00:00 2001 From: Jan Ciolek Date: Tue, 28 Sep 2021 13:28:32 +0200 Subject: [PATCH 05/24] cql3: Remove term in cql3/attributes Replace all uses of term with expression in cql3/attributes Signed-off-by: Jan Ciolek --- cql3/attributes.cc | 51 +++++++++++++++++++++++++++++----------------- cql3/attributes.hh | 14 +++++++------ 2 files changed, 40 insertions(+), 25 deletions(-) diff --git a/cql3/attributes.cc b/cql3/attributes.cc index 510e9d93dc..c929b44958 100644 --- a/cql3/attributes.cc +++ b/cql3/attributes.cc @@ -48,7 +48,9 @@ std::unique_ptr attributes::none() { return std::unique_ptr{new attributes{{}, {}, {}}}; } -attributes::attributes(::shared_ptr&& timestamp, ::shared_ptr&& time_to_live, ::shared_ptr&& timeout) +attributes::attributes(std::optional&& timestamp, + std::optional&& time_to_live, + std::optional&& timeout) : _timestamp{std::move(timestamp)} , _time_to_live{std::move(time_to_live)} , _timeout{std::move(timeout)} @@ -67,11 +69,11 @@ bool attributes::is_timeout_set() const { } int64_t attributes::get_timestamp(int64_t now, const query_options& options) { - if (!_timestamp) { + if (!_timestamp.has_value()) { return now; } - auto tval = expr::evaluate_to_raw_view(_timestamp, options); + expr::constant tval = expr::evaluate(*_timestamp, options); if (tval.is_null()) { throw exceptions::invalid_request_exception("Invalid null value of timestamp"); } @@ -79,17 +81,17 @@ int64_t attributes::get_timestamp(int64_t now, const query_options& options) { return now; } try { - return tval.validate_and_deserialize(*long_type, options.get_cql_serialization_format()); + return tval.view().validate_and_deserialize(*long_type, cql_serialization_format::internal()); } catch (marshal_exception& e) { throw exceptions::invalid_request_exception("Invalid timestamp value"); } } int32_t attributes::get_time_to_live(const query_options& options) { - if (!_time_to_live) + if (!_time_to_live.has_value()) return 0; - auto tval = expr::evaluate_to_raw_view(_time_to_live, options); + expr::constant tval = expr::evaluate(*_time_to_live, options); if (tval.is_null()) { throw exceptions::invalid_request_exception("Invalid null value of TTL"); } @@ -99,7 +101,7 @@ int32_t attributes::get_time_to_live(const query_options& options) { int32_t ttl; try { - ttl = tval.validate_and_deserialize(*int32_type, options.get_cql_serialization_format()); + ttl = tval.view().validate_and_deserialize(*int32_type, cql_serialization_format::internal()); } catch (marshal_exception& e) { throw exceptions::invalid_request_exception("Invalid TTL value"); @@ -119,11 +121,11 @@ int32_t attributes::get_time_to_live(const query_options& options) { db::timeout_clock::duration attributes::get_timeout(const query_options& options) const { - auto timeout = expr::evaluate_to_raw_view(_timeout, options); + expr::constant timeout = expr::evaluate(*_timeout, options); if (timeout.is_null() || timeout.is_unset_value()) { throw exceptions::invalid_request_exception("Timeout value cannot be unset/null"); } - cql_duration duration = timeout.deserialize(*duration_type); + cql_duration duration = timeout.view().deserialize(*duration_type); if (duration.months || duration.days) { throw exceptions::invalid_request_exception("Timeout values cannot be expressed in days/months"); } @@ -136,22 +138,33 @@ db::timeout_clock::duration attributes::get_timeout(const query_options& options return std::chrono::duration_cast(std::chrono::nanoseconds(duration.nanoseconds)); } -void attributes::fill_prepare_context(prepare_context& ctx) const { - if (_timestamp) { - _timestamp->fill_prepare_context(ctx); +void attributes::fill_prepare_context(prepare_context& ctx) { + if (_timestamp.has_value()) { + expr::fill_prepare_context(*_timestamp, ctx); } - if (_time_to_live) { - _time_to_live->fill_prepare_context(ctx); + if (_time_to_live.has_value()) { + expr::fill_prepare_context(*_time_to_live, ctx); } - if (_timeout) { - _timeout->fill_prepare_context(ctx); + if (_timeout.has_value()) { + expr::fill_prepare_context(*_timeout, ctx); } } std::unique_ptr attributes::raw::prepare(database& db, const sstring& ks_name, const sstring& cf_name) const { - auto ts = !timestamp ? ::shared_ptr{} : prepare_term(*timestamp, db, ks_name, timestamp_receiver(ks_name, cf_name)); - auto ttl = !time_to_live ? ::shared_ptr{} : prepare_term(*time_to_live, db, ks_name, time_to_live_receiver(ks_name, cf_name)); - auto to = !timeout ? ::shared_ptr{} : prepare_term(*timeout, db, ks_name, timeout_receiver(ks_name, cf_name)); + std::optional ts, ttl, to; + + if (timestamp.has_value()) { + ts = expr::to_expression(prepare_term(*timestamp, db, ks_name, timestamp_receiver(ks_name, cf_name))); + } + + if (time_to_live.has_value()) { + ttl = expr::to_expression(prepare_term(*time_to_live, db, ks_name, time_to_live_receiver(ks_name, cf_name))); + } + + if (timeout.has_value()) { + to = expr::to_expression(prepare_term(*timeout, db, ks_name, timeout_receiver(ks_name, cf_name))); + } + return std::unique_ptr{new attributes{std::move(ts), std::move(ttl), std::move(to)}}; } diff --git a/cql3/attributes.hh b/cql3/attributes.hh index b7a0fb5769..cf37b33cc8 100644 --- a/cql3/attributes.hh +++ b/cql3/attributes.hh @@ -41,8 +41,8 @@ #pragma once -#include "cql3/term.hh" #include "cql3/expr/expression.hh" +#include "db/timeout_clock.hh" namespace cql3 { @@ -55,13 +55,15 @@ class prepare_context; */ class attributes final { private: - const ::shared_ptr _timestamp; - const ::shared_ptr _time_to_live; - const ::shared_ptr _timeout; + std::optional _timestamp; + std::optional _time_to_live; + std::optional _timeout; public: static std::unique_ptr none(); private: - attributes(::shared_ptr&& timestamp, ::shared_ptr&& time_to_live, ::shared_ptr&& timeout); + attributes(std::optional&& timestamp, + std::optional&& time_to_live, + std::optional&& timeout); public: bool is_timestamp_set() const; @@ -75,7 +77,7 @@ public: db::timeout_clock::duration get_timeout(const query_options& options) const; - void fill_prepare_context(prepare_context& ctx) const; + void fill_prepare_context(prepare_context& ctx); class raw final { public: From e5391f1eed1c0aa1965864b177f1772e13a61139 Mon Sep 17 00:00:00 2001 From: Jan Ciolek Date: Tue, 12 Oct 2021 10:45:17 +0200 Subject: [PATCH 06/24] types: Add map_type_impl::serialize(range of ) Adds two functions that take a range over pairs of serialized values and return a serialized map value. There are 2 functions - one operating on bytes and one operating on managed_bytes. The version with managed_bytes is used in expression.cc, used to be a local static function. The bytes version will be used in type_json.cc in the next commit. Signed-off-by: Jan Ciolek --- cql3/expr/expression.cc | 44 ++------------------- types/collection.hh | 1 + types/map.hh | 86 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 90 insertions(+), 41 deletions(-) diff --git a/cql3/expr/expression.cc b/cql3/expr/expression.cc index e9c52698d6..938beed393 100644 --- a/cql3/expr/expression.cc +++ b/cql3/expr/expression.cc @@ -1379,45 +1379,6 @@ constant evaluate(const expression& e, const query_options& options) { }, e); } -// Serializes a map using the internal cql serialization format -// Takes a range of pair -template -requires std::convertible_to, std::pair> -static managed_bytes serialize_map(const Range& map_range) { - size_t serialized_len = 4; - size_t map_size = 0; - for (const std::pair& elem : map_range) { - serialized_len += 4 + elem.first.size() + 4 + elem.second.size(); - map_size += 1; - } - - if (map_size > std::numeric_limits::max()) { - throw exceptions::invalid_request_exception( - fmt::format("Map size too large: {} > {}", map_size, std::numeric_limits::max())); - } - - managed_bytes result(managed_bytes::initialized_later(), serialized_len); - managed_bytes_mutable_view out(result); - - write_collection_size(out, map_size, cql_serialization_format::internal()); - for (const std::pair& elem : map_range) { - if (elem.first.size() > std::numeric_limits::max()) { - throw exceptions::invalid_request_exception( - fmt::format("Map key size too large: {} bytes > {}", map_size, std::numeric_limits::max())); - } - - if (elem.second.size() > std::numeric_limits::max()) { - throw exceptions::invalid_request_exception( - fmt::format("Map value size too large: {} bytes > {}", map_size, std::numeric_limits::max())); - } - - write_collection_value(out, cql_serialization_format::internal(), elem.first); - write_collection_value(out, cql_serialization_format::internal(), elem.second); - } - - return result; -} - // Takes a value and reserializes it where needs_to_be_reserialized() says it's needed template static managed_bytes reserialize_value(View value_bytes, @@ -1486,7 +1447,7 @@ static managed_bytes reserialize_value(View value_bytes, values_map.emplace(std::move(element)); } - return serialize_map(values_map); + return map_type_impl::serialize_to_managed_bytes(values_map); } if (type.is_tuple() || type.is_user_type()) { @@ -1685,7 +1646,8 @@ static constant evaluate_map(const collection_constructor& collection, const que } } - return constant(raw_value::make_value(serialize_map(evaluated_elements)), collection.type); + managed_bytes serialized_map = map_type_impl::serialize_to_managed_bytes(evaluated_elements); + return constant(raw_value::make_value(std::move(serialized_map)), collection.type); } constant evaluate(const collection_constructor& collection, const query_options& options) { diff --git a/types/collection.hh b/types/collection.hh index 306e662367..420b2b3f17 100644 --- a/types/collection.hh +++ b/types/collection.hh @@ -31,6 +31,7 @@ #include "schema_fwd.hh" #include "log.hh" #include "cql_serialization_format.hh" +#include "exceptions/exceptions.hh" namespace cql3 { diff --git a/types/map.hh b/types/map.hh index 8b27ee930b..0b0e4ed0bb 100644 --- a/types/map.hh +++ b/types/map.hh @@ -61,6 +61,92 @@ public: cql_serialization_format sf); static managed_bytes serialize_partially_deserialized_form_fragmented(const std::vector>& v, cql_serialization_format sf); + + // Serializes a map using the internal cql serialization format + // Takes a range of pair + template + requires std::convertible_to, std::pair> + static bytes serialize_to_bytes(const Range& map_range); + + // Serializes a map using the internal cql serialization format + // Takes a range of pair + template + requires std::convertible_to, std::pair> + static managed_bytes serialize_to_managed_bytes(const Range& map_range); }; data_value make_map_value(data_type tuple_type, map_type_impl::native_type value); + +template +requires std::convertible_to, std::pair> +bytes map_type_impl::serialize_to_bytes(const Range& map_range) { + size_t serialized_len = 4; + size_t map_size = 0; + for (const std::pair& elem : map_range) { + serialized_len += 4 + elem.first.size() + 4 + elem.second.size(); + map_size += 1; + } + + if (map_size > std::numeric_limits::max()) { + throw exceptions::invalid_request_exception( + fmt::format("Map size too large: {} > {}", map_size, std::numeric_limits::max())); + } + + bytes result(bytes::initialized_later(), serialized_len); + bytes::iterator out = result.begin(); + + write_collection_size(out, map_size, cql_serialization_format::internal()); + for (const std::pair& elem : map_range) { + if (elem.first.size() > std::numeric_limits::max()) { + throw exceptions::invalid_request_exception( + fmt::format("Map key size too large: {} bytes > {}", map_size, std::numeric_limits::max())); + } + + if (elem.second.size() > std::numeric_limits::max()) { + throw exceptions::invalid_request_exception( + fmt::format("Map value size too large: {} bytes > {}", map_size, std::numeric_limits::max())); + } + + write_collection_value(out, cql_serialization_format::internal(), elem.first); + write_collection_value(out, cql_serialization_format::internal(), elem.second); + } + + return result; +} + +template +requires std::convertible_to, std::pair> +managed_bytes map_type_impl::serialize_to_managed_bytes(const Range& map_range) { + size_t serialized_len = 4; + size_t map_size = 0; + for (const std::pair& elem : map_range) { + serialized_len += 4 + elem.first.size() + 4 + elem.second.size(); + map_size += 1; + } + + if (map_size > std::numeric_limits::max()) { + throw exceptions::invalid_request_exception( + fmt::format("Map size too large: {} > {}", map_size, std::numeric_limits::max())); + } + + managed_bytes result(managed_bytes::initialized_later(), serialized_len); + managed_bytes_mutable_view out(result); + + write_collection_size(out, map_size, cql_serialization_format::internal()); + for (const std::pair& elem : map_range) { + if (elem.first.size() > std::numeric_limits::max()) { + throw exceptions::invalid_request_exception( + fmt::format("Map key size too large: {} bytes > {}", map_size, std::numeric_limits::max())); + } + + if (elem.second.size() > std::numeric_limits::max()) { + throw exceptions::invalid_request_exception( + fmt::format("Map value size too large: {} bytes > {}", map_size, std::numeric_limits::max())); + } + + write_collection_value(out, cql_serialization_format::internal(), elem.first); + write_collection_value(out, cql_serialization_format::internal(), elem.second); + } + + return result; +} From ba202cd8bdcfd8c6f1dbb280f642f5ad31cbca7c Mon Sep 17 00:00:00 2001 From: Jan Ciolek Date: Thu, 28 Oct 2021 20:42:43 +0200 Subject: [PATCH 07/24] cql3: Use internal cql format in insert_prepared_json_statement cache expr::constant is always serialized using the internal cql serialization format, but currently the code keeps values in the cache in other format. As preparation for moving from term to expression change so that values kept in the cache are serialized using the internal format. Signed-off-by: Jan Ciolek --- cql3/statements/update_statement.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cql3/statements/update_statement.cc b/cql3/statements/update_statement.cc index 6e0bc33150..f66d220fae 100644 --- a/cql3/statements/update_statement.cc +++ b/cql3/statements/update_statement.cc @@ -181,7 +181,7 @@ void update_statement::add_update_for_key(mutation& m, const query::clustering_r modification_statement::json_cache_opt insert_prepared_json_statement::maybe_prepare_json_cache(const query_options& options) const { sstring json_string = utf8_type->to_string(to_bytes(expr::evaluate_to_raw_view(_term, options))); - return json_helpers::parse(std::move(json_string), s->all_columns(), options.get_cql_serialization_format()); + return json_helpers::parse(std::move(json_string), s->all_columns(), cql_serialization_format::internal()); } void @@ -212,7 +212,7 @@ insert_prepared_json_statement::execute_set_value(mutation& m, const clustering_ return; } - cql_serialization_format sf = params._options.get_cql_serialization_format(); + cql_serialization_format sf = cql_serialization_format::internal(); visit(*column.type, make_visitor( [&] (const list_type_impl& ltype) { auto val = ::make_shared(lists::value::from_serialized(raw_value_view::make_value(*value), ltype, sf)); From d36847801b836ea118d696d8a8ab9431a1179ab0 Mon Sep 17 00:00:00 2001 From: Jan Ciolek Date: Wed, 29 Sep 2021 15:42:41 +0200 Subject: [PATCH 08/24] cql3: Remove term in update_statement Replace all uses of term with expression in cql3/statements/update_statement There was some trouble with extracting values from json. The original code worked this way on a map example: > There is a json string to parse: {'b': 1, 'a': 2, 'b': 3} > The code parses the json and creates bytes where this map is serialized but without removing duplicates, sorting etc. > Then a maps::delayed_value is created from these bytes. During creation map elements are extracted, sorted and duplicates are removed. This map value is then used in setter Now when maps::delayed_value is changed to expr::constant the step where elements are sorted is lost. Because of this we need to do this earlier, the best place is during original json parsing. Additionally I suspect that removing duplicated elements used to work only on the first level, in case of map of maps it wouldn't work. Now it will work no matter how many layers of maps there are. Signed-off-by: Jan Ciolek --- cql3/statements/update_statement.cc | 24 +++++++++++------------- cql3/statements/update_statement.hh | 6 +++--- cql3/type_json.cc | 16 +++++++--------- 3 files changed, 21 insertions(+), 25 deletions(-) diff --git a/cql3/statements/update_statement.cc b/cql3/statements/update_statement.cc index f66d220fae..b9425425b4 100644 --- a/cql3/statements/update_statement.cc +++ b/cql3/statements/update_statement.cc @@ -180,7 +180,8 @@ void update_statement::add_update_for_key(mutation& m, const query::clustering_r } modification_statement::json_cache_opt insert_prepared_json_statement::maybe_prepare_json_cache(const query_options& options) const { - sstring json_string = utf8_type->to_string(to_bytes(expr::evaluate_to_raw_view(_term, options))); + expr::constant c = expr::evaluate(_value, options); + sstring json_string = utf8_type->to_string(to_bytes(c.view())); return json_helpers::parse(std::move(json_string), s->all_columns(), cql_serialization_format::internal()); } @@ -212,29 +213,26 @@ insert_prepared_json_statement::execute_set_value(mutation& m, const clustering_ return; } - cql_serialization_format sf = cql_serialization_format::internal(); + + expr::constant val(raw_value::make_value(*value), column.type); visit(*column.type, make_visitor( [&] (const list_type_impl& ltype) { - auto val = ::make_shared(lists::value::from_serialized(raw_value_view::make_value(*value), ltype, sf)); - lists::setter::execute(m, prefix, params, column, expr::evaluate(val, query_options::DEFAULT)); + lists::setter::execute(m, prefix, params, column, val); }, [&] (const set_type_impl& stype) { - auto val = ::make_shared(sets::value::from_serialized(raw_value_view::make_value(*value), stype, sf)); - sets::setter::execute(m, prefix, params, column, expr::evaluate(val, query_options::DEFAULT)); + sets::setter::execute(m, prefix, params, column, val); }, [&] (const map_type_impl& mtype) { - auto val = ::make_shared(maps::value::from_serialized(raw_value_view::make_value(*value), mtype, sf)); - maps::setter::execute(m, prefix, params, column, expr::evaluate(val, query_options::DEFAULT)); + maps::setter::execute(m, prefix, params, column, val); }, [&] (const user_type_impl& utype) { - auto val = ::make_shared(user_types::value::from_serialized(raw_value_view::make_value(*value), utype)); - user_types::setter::execute(m, prefix, params, column, expr::evaluate(val, query_options::DEFAULT)); + user_types::setter::execute(m, prefix, params, column, val); }, [&] (const abstract_type& type) { if (type.is_collection()) { throw std::runtime_error(format("insert_prepared_json_statement::execute_set_value: unhandled collection type {}", type.name())); } - constants::setter::execute(m, prefix, params, column, raw_value_view::make_value(*value)); + constants::setter::execute(m, prefix, params, column, val.view()); } )); } @@ -365,8 +363,8 @@ insert_json_statement::prepare_internal(database& db, schema_ptr schema, (void)_if_not_exists; assert(expr::is(_json_value) || expr::is(_json_value)); auto json_column_placeholder = ::make_shared("", true); - auto prepared_json_value = prepare_term(_json_value, db, "", make_lw_shared("", "", json_column_placeholder, utf8_type)); - prepared_json_value->fill_prepare_context(ctx); + auto prepared_json_value = expr::to_expression(prepare_term(_json_value, db, "", make_lw_shared("", "", json_column_placeholder, utf8_type))); + expr::fill_prepare_context(prepared_json_value, ctx); auto stmt = ::make_shared(ctx.bound_variables_size(), schema, std::move(attrs), stats, std::move(prepared_json_value), _default_unset); prepare_conditions(db, *schema, ctx, *stmt); return stmt; diff --git a/cql3/statements/update_statement.hh b/cql3/statements/update_statement.hh index b6d38d3291..edc26cdca3 100644 --- a/cql3/statements/update_statement.hh +++ b/cql3/statements/update_statement.hh @@ -82,7 +82,7 @@ private: * Overridden add_update_for_key uses this parsed JSON to look up values for columns. */ class insert_prepared_json_statement : public update_statement { - ::shared_ptr _term; + expr::expression _value; bool _default_unset; public: insert_prepared_json_statement( @@ -90,9 +90,9 @@ public: schema_ptr s, std::unique_ptr attrs, cql_stats& stats, - ::shared_ptr t, bool default_unset) + expr::expression v, bool default_unset) : update_statement(statement_type::INSERT, bound_terms, s, std::move(attrs), stats) - , _term(t) + , _value(std::move(v)) , _default_unset(default_unset) { _restrictions = restrictions::statement_restrictions(s, false); } diff --git a/cql3/type_json.cc b/cql3/type_json.cc index d9e5797563..0950cb800d 100644 --- a/cql3/type_json.cc +++ b/cql3/type_json.cc @@ -101,31 +101,29 @@ static bytes from_json_object_aux(const map_type_impl& t, const rjson::value& va if (!value.IsObject()) { throw marshal_exception("map_type must be represented as JSON Object"); } - std::vector raw_map; - raw_map.reserve(value.MemberCount()); + std::map raw_map(t.get_keys_type()->as_less_comparator()); for (auto it = value.MemberBegin(); it != value.MemberEnd(); ++it) { + bytes value = from_json_object(*t.get_values_type(), it->value, sf); if (!t.get_keys_type()->is_compatible_with(*utf8_type)) { // Keys in maps can only be strings in JSON, but they can also be a string representation // of another JSON type, which needs to be reparsed. Example - map>, int> // will be represented like this: { "[1, 3, 6]": 3, "[]": 0, "[1, 2]": 2 } rjson::value map_key = rjson::parse(rjson::to_string_view(it->name)); - raw_map.emplace_back(from_json_object(*t.get_keys_type(), map_key, sf)); + raw_map.emplace(from_json_object(*t.get_keys_type(), map_key, sf), std::move(value)); } else { - raw_map.emplace_back(from_json_object(*t.get_keys_type(), it->name, sf)); + raw_map.emplace(from_json_object(*t.get_keys_type(), it->name, sf), std::move(value)); } - raw_map.emplace_back(from_json_object(*t.get_values_type(), it->value, sf)); } - return collection_type_impl::pack(raw_map.begin(), raw_map.end(), raw_map.size() / 2, sf); + return map_type_impl::serialize_to_bytes(raw_map); } static bytes from_json_object_aux(const set_type_impl& t, const rjson::value& value, cql_serialization_format sf) { if (!value.IsArray()) { throw marshal_exception("set_type must be represented as JSON Array"); } - std::vector raw_set; - raw_set.reserve(value.Size()); + std::set raw_set(t.get_elements_type()->as_less_comparator()); for (const rjson::value& v : value.GetArray()) { - raw_set.emplace_back(from_json_object(*t.get_elements_type(), v, sf)); + raw_set.emplace(from_json_object(*t.get_elements_type(), v, sf)); } return collection_type_impl::pack(raw_set.begin(), raw_set.end(), raw_set.size(), sf); } From a24d06c195bd126a1ed21a4439682d7f2fceb96c Mon Sep 17 00:00:00 2001 From: Jan Ciolek Date: Wed, 29 Sep 2021 16:40:23 +0200 Subject: [PATCH 09/24] cql3: Remove term in select_statement Replace all uses of term with expression in cql3/statements/select_statement Signed-off-by: Jan Ciolek --- cql3/statements/raw/select_statement.hh | 4 +-- cql3/statements/select_statement.cc | 44 +++++++++++++------------ cql3/statements/select_statement.hh | 22 ++++++------- 3 files changed, 36 insertions(+), 34 deletions(-) diff --git a/cql3/statements/raw/select_statement.hh b/cql3/statements/raw/select_statement.hh index 77f81d496c..990c9993be 100644 --- a/cql3/statements/raw/select_statement.hh +++ b/cql3/statements/raw/select_statement.hh @@ -133,8 +133,8 @@ private: bool for_view = false, bool allow_filtering = false); - /** Returns a ::shared_ptr for the limit or null if no limit is set */ - ::shared_ptr prepare_limit(database& db, prepare_context& ctx, const std::optional& limit); + /** Returns an expression for the limit or nullopt if no limit is set */ + std::optional prepare_limit(database& db, prepare_context& ctx, const std::optional& limit); static void verify_ordering_is_allowed(const restrictions::statement_restrictions& restrictions); diff --git a/cql3/statements/select_statement.cc b/cql3/statements/select_statement.cc index 4fa7838ea9..eec18ae575 100644 --- a/cql3/statements/select_statement.cc +++ b/cql3/statements/select_statement.cc @@ -140,8 +140,8 @@ select_statement::select_statement(schema_ptr schema, ::shared_ptr> group_by_cell_indices, bool is_reversed, ordering_comparator_type ordering_comparator, - ::shared_ptr limit, - ::shared_ptr per_partition_limit, + std::optional limit, + std::optional per_partition_limit, cql_stats& stats, std::unique_ptr attrs) : cql_statement(select_timeout(*restrictions)) @@ -251,12 +251,14 @@ select_statement::make_partition_slice(const query_options& options) const std::move(static_columns), std::move(regular_columns), _opts, nullptr, options.get_cql_serialization_format(), get_per_partition_limit(options)); } -uint64_t select_statement::do_get_limit(const query_options& options, ::shared_ptr limit, uint64_t default_limit) const { - if (!limit || _selection->is_aggregate()) { +uint64_t select_statement::do_get_limit(const query_options& options, + const std::optional& limit, + uint64_t default_limit) const { + if (!limit.has_value() || _selection->is_aggregate()) { return default_limit; } - auto val = expr::evaluate_to_raw_view(limit, options); + auto val = expr::evaluate(*limit, options); if (val.is_null()) { throw exceptions::invalid_request_exception("Invalid null value of limit"); } @@ -264,7 +266,7 @@ uint64_t select_statement::do_get_limit(const query_options& options, ::shared_p return default_limit; } try { - auto l = val.validate_and_deserialize(*int32_type, options.get_cql_serialization_format()); + auto l = val.view().validate_and_deserialize(*int32_type, cql_serialization_format::internal()); if (l <= 0) { throw exceptions::invalid_request_exception("LIMIT must be strictly positive"); } @@ -820,11 +822,11 @@ primary_key_select_statement::primary_key_select_statement(schema_ptr schema, ui ::shared_ptr> group_by_cell_indices, bool is_reversed, ordering_comparator_type ordering_comparator, - ::shared_ptr limit, - ::shared_ptr per_partition_limit, + std::optional limit, + std::optional per_partition_limit, cql_stats &stats, std::unique_ptr attrs) - : select_statement{schema, bound_terms, parameters, selection, restrictions, group_by_cell_indices, is_reversed, ordering_comparator, limit, per_partition_limit, stats, std::move(attrs)} + : select_statement{schema, bound_terms, parameters, selection, restrictions, group_by_cell_indices, is_reversed, ordering_comparator, std::move(limit), std::move(per_partition_limit), stats, std::move(attrs)} { if (_ks_sel == ks_selector::NONSYSTEM) { if (_restrictions->need_filtering() || @@ -848,8 +850,8 @@ indexed_table_select_statement::prepare(database& db, ::shared_ptr> group_by_cell_indices, bool is_reversed, ordering_comparator_type ordering_comparator, - ::shared_ptr limit, - ::shared_ptr per_partition_limit, + std::optional limit, + std::optional per_partition_limit, cql_stats &stats, std::unique_ptr attrs) { @@ -878,8 +880,8 @@ indexed_table_select_statement::prepare(database& db, std::move(group_by_cell_indices), is_reversed, std::move(ordering_comparator), - limit, - per_partition_limit, + std::move(limit), + std::move(per_partition_limit), stats, *index_opt, std::move(used_index_restrictions), @@ -895,8 +897,8 @@ indexed_table_select_statement::indexed_table_select_statement(schema_ptr schema ::shared_ptr> group_by_cell_indices, bool is_reversed, ordering_comparator_type ordering_comparator, - ::shared_ptr limit, - ::shared_ptr per_partition_limit, + std::optional limit, + std::optional per_partition_limit, cql_stats &stats, const secondary_index::index& index, ::shared_ptr used_index_restrictions, @@ -1476,16 +1478,16 @@ select_statement::prepare_restrictions(database& db, } } -/** Returns a ::shared_ptr for the limit or null if no limit is set */ -::shared_ptr +/** Returns a expr::expression for the limit or nullopt if no limit is set */ +std::optional select_statement::prepare_limit(database& db, prepare_context& ctx, const std::optional& limit) { - if (!limit) { - return {}; + if (!limit.has_value()) { + return std::nullopt; } - auto prep_limit = prepare_term(*limit, db, keyspace(), limit_receiver()); - prep_limit->fill_prepare_context(ctx); + expr::expression prep_limit = expr::to_expression(prepare_term(*limit, db, keyspace(), limit_receiver())); + expr::fill_prepare_context(prep_limit, ctx); return prep_limit; } diff --git a/cql3/statements/select_statement.hh b/cql3/statements/select_statement.hh index 1896a48bbd..ddb4e063fc 100644 --- a/cql3/statements/select_statement.hh +++ b/cql3/statements/select_statement.hh @@ -86,8 +86,8 @@ protected: ::shared_ptr _restrictions; ::shared_ptr> _group_by_cell_indices; ///< Indices in result row of cells holding GROUP BY values. bool _is_reversed; - ::shared_ptr _limit; - ::shared_ptr _per_partition_limit; + std::optional _limit; + std::optional _per_partition_limit; template using compare_fn = raw::select_statement::compare_fn; @@ -118,8 +118,8 @@ public: ::shared_ptr> group_by_cell_indices, bool is_reversed, ordering_comparator_type ordering_comparator, - ::shared_ptr limit, - ::shared_ptr per_partition_limit, + std::optional limit, + std::optional per_partition_limit, cql_stats& stats, std::unique_ptr attrs); @@ -158,7 +158,7 @@ public: db::timeout_clock::duration get_timeout(const service::client_state& state, const query_options& options) const; protected: - uint64_t do_get_limit(const query_options& options, ::shared_ptr limit, uint64_t default_limit) const; + uint64_t do_get_limit(const query_options& options, const std::optional& limit, uint64_t default_limit) const; uint64_t get_limit(const query_options& options) const { return do_get_limit(options, _limit, query::max_rows); } @@ -181,8 +181,8 @@ public: ::shared_ptr> group_by_cell_indices, bool is_reversed, ordering_comparator_type ordering_comparator, - ::shared_ptr limit, - ::shared_ptr per_partition_limit, + std::optional limit, + std::optional per_partition_limit, cql_stats &stats, std::unique_ptr attrs); }; @@ -205,8 +205,8 @@ public: ::shared_ptr> group_by_cell_indices, bool is_reversed, ordering_comparator_type ordering_comparator, - ::shared_ptr limit, - ::shared_ptr per_partition_limit, + std::optional limit, + std::optional per_partition_limit, cql_stats &stats, std::unique_ptr attrs); @@ -218,8 +218,8 @@ public: ::shared_ptr> group_by_cell_indices, bool is_reversed, ordering_comparator_type ordering_comparator, - ::shared_ptr limit, - ::shared_ptr per_partition_limit, + std::optional limit, + std::optional per_partition_limit, cql_stats &stats, const secondary_index::index& index, ::shared_ptr used_index_restrictions, From 805ba145d71de739aa5a49a7dfdfc7655f960912 Mon Sep 17 00:00:00 2001 From: Jan Ciolek Date: Wed, 29 Sep 2021 17:53:40 +0200 Subject: [PATCH 10/24] cql3: Remove term in column_condition Replace all uses of term with expression in cql3/column_condition Signed-off-by: Jan Ciolek --- cql3/column_condition.cc | 58 ++++++++++++++++++++-------------------- cql3/column_condition.hh | 25 +++++++++-------- 2 files changed, 41 insertions(+), 42 deletions(-) diff --git a/cql3/column_condition.cc b/cql3/column_condition.cc index d4cbd28a91..cef3a1b732 100644 --- a/cql3/column_condition.cc +++ b/cql3/column_condition.cc @@ -118,17 +118,15 @@ uint32_t read_and_check_list_index(const cql3::raw_value_view& key) { namespace cql3 { -void column_condition::collect_marker_specificaton(prepare_context& ctx) const { +void column_condition::collect_marker_specificaton(prepare_context& ctx) { if (_collection_element) { - _collection_element->fill_prepare_context(ctx); + expr::fill_prepare_context(*_collection_element, ctx); } - if (!_in_values.empty()) { - for (auto&& value : _in_values) { - value->fill_prepare_context(ctx); - } + for (auto&& value : _in_values) { + expr::fill_prepare_context(value, ctx); } if (_value) { - _value->fill_prepare_context(ctx); + expr::fill_prepare_context(*_value, ctx); } } @@ -150,12 +148,13 @@ bool column_condition::applies_to(const data_value* cell_value, const query_opti // The code below implements these rules in a way compatible with Cassandra. // Use a map/list value instead of entire collection if a key is present in the predicate. - if (_collection_element != nullptr && cell_value != nullptr) { + if (_collection_element.has_value() && cell_value != nullptr) { // Checked in column_condition::raw::prepare() assert(cell_value->type()->is_collection()); const collection_type_impl& cell_type = static_cast(*cell_value->type()); - cql3::raw_value_view key = expr::evaluate_to_raw_view(_collection_element, options); + expr::constant key_constant = expr::evaluate(*_collection_element, options); + cql3::raw_value_view key = key_constant.view(); if (key.is_unset_value()) { throw exceptions::invalid_request_exception( format("Invalid 'unset' value in {} element access", cell_type.cql3_type_name())); @@ -211,7 +210,7 @@ bool column_condition::applies_to(const data_value* cell_value, const query_opti if (is_compare(_op)) { // <, >, >=, <=, != - cql3::raw_value_view param = expr::evaluate_to_raw_view(_value, options); + expr::constant param = expr::evaluate(*_value, options); if (param.is_unset_value()) { throw exceptions::invalid_request_exception("Invalid 'unset' value in condition"); @@ -230,7 +229,7 @@ bool column_condition::applies_to(const data_value* cell_value, const query_opti } // type::validate() is called earlier when creating the value, so it's safe to pass to_bytes() result // directly to compare. - return is_satisfied_by(_op, *cell_value->type(), *column.type, *cell_value, to_bytes(param)); + return is_satisfied_by(_op, *cell_value->type(), *column.type, *cell_value, to_bytes(param.view())); } if (_op == expr::oper_t::LIKE) { @@ -240,14 +239,14 @@ bool column_condition::applies_to(const data_value* cell_value, const query_opti if (_matcher) { return (*_matcher)(bytes_view(cell_value->serialize_nonnull())); } else { - auto param = expr::evaluate_to_raw_view(_value, options); // LIKE pattern + auto param = expr::evaluate(*_value, options); // LIKE pattern if (param.is_unset_value()) { throw exceptions::invalid_request_exception("Invalid 'unset' value in LIKE pattern"); } if (param.is_null()) { throw exceptions::invalid_request_exception("Invalid NULL value in LIKE pattern"); } - like_matcher matcher(to_bytes(param)); + like_matcher matcher(to_bytes(param.view())); return matcher(bytes_view(cell_value->serialize_nonnull())); } } @@ -257,8 +256,8 @@ bool column_condition::applies_to(const data_value* cell_value, const query_opti // FIXME Use managed_bytes_opt std::vector in_values; - if (_value) { - expr::constant lval = expr::evaluate(_value, options); + if (_value.has_value()) { + expr::constant lval = expr::evaluate(*_value, options); if (lval.is_null()) { throw exceptions::invalid_request_exception("Invalid null value for IN condition"); } @@ -271,7 +270,7 @@ bool column_condition::applies_to(const data_value* cell_value, const query_opti } } else { for (auto&& v : _in_values) { - in_values.emplace_back(to_bytes_opt(expr::evaluate_to_raw_view(v, options))); + in_values.emplace_back(to_bytes_opt(expr::evaluate(v, options).view())); } } // If cell value is NULL, IN list must contain NULL or an empty set/list. Otherwise it must contain cell value. @@ -289,7 +288,7 @@ column_condition::raw::prepare(database& db, const sstring& keyspace, const colu if (receiver.type->is_counter()) { throw exceptions::invalid_request_exception("Conditions on counters are not supported"); } - shared_ptr collection_element_term; + std::optional collection_element_expression; lw_shared_ptr value_spec = receiver.column_specification; if (_collection_element) { @@ -315,13 +314,13 @@ column_condition::raw::prepare(database& db, const sstring& keyspace, const colu throw exceptions::invalid_request_exception( format("Unsupported collection type {} in a condition with element access", ctype->cql3_type_name())); } - collection_element_term = prepare_term(*_collection_element, db, keyspace, element_spec); + collection_element_expression = expr::to_expression(prepare_term(*_collection_element, db, keyspace, element_spec)); } if (is_compare(_op)) { validate_operation_on_durations(*receiver.type, _op); - return column_condition::condition(receiver, collection_element_term, - prepare_term(*_value, db, keyspace, value_spec), nullptr, _op); + return column_condition::condition(receiver, std::move(collection_element_expression), + expr::to_expression(prepare_term(*_value, db, keyspace, value_spec)), nullptr, _op); } if (_op == expr::oper_t::LIKE) { @@ -329,15 +328,15 @@ column_condition::raw::prepare(database& db, const sstring& keyspace, const colu if (literal_term) { // Pass matcher object const sstring& pattern = literal_term->raw_text; - return column_condition::condition(receiver, collection_element_term, - prepare_term(*_value, db, keyspace, value_spec), + return column_condition::condition(receiver, std::move(collection_element_expression), + expr::to_expression(prepare_term(*_value, db, keyspace, value_spec)), std::make_unique(bytes_view(reinterpret_cast(pattern.data()), pattern.size())), _op); } else { // Pass through rhs value, matcher object built on execution // TODO: caller should validate parametrized LIKE pattern - return column_condition::condition(receiver, collection_element_term, - prepare_term(*_value, db, keyspace, value_spec), nullptr, _op); + return column_condition::condition(receiver, std::move(collection_element_expression), + expr::to_expression(prepare_term(*_value, db, keyspace, value_spec)), nullptr, _op); } } @@ -347,16 +346,17 @@ column_condition::raw::prepare(database& db, const sstring& keyspace, const colu if (_in_marker) { assert(_in_values.empty()); - shared_ptr multi_item_term = prepare_term(*_in_marker, db, keyspace, value_spec); - return column_condition::in_condition(receiver, collection_element_term, multi_item_term, {}); + expr::expression multi_item_term = expr::to_expression(prepare_term(*_in_marker, db, keyspace, value_spec)); + return column_condition::in_condition(receiver, collection_element_expression, std::move(multi_item_term), {}); } // Both _in_values and in _in_marker can be missing in case of empty IN list: "a IN ()" - std::vector<::shared_ptr> terms; + std::vector terms; terms.reserve(_in_values.size()); for (auto&& value : _in_values) { - terms.push_back(prepare_term(value, db, keyspace, value_spec)); + terms.push_back(expr::to_expression(prepare_term(value, db, keyspace, value_spec))); } - return column_condition::in_condition(receiver, collection_element_term, {}, std::move(terms)); + return column_condition::in_condition(receiver, std::move(collection_element_expression), + std::nullopt, std::move(terms)); } } // end of namespace cql3 diff --git a/cql3/column_condition.hh b/cql3/column_condition.hh index 0db4dc7f66..af2d6cf2f2 100644 --- a/cql3/column_condition.hh +++ b/cql3/column_condition.hh @@ -41,7 +41,6 @@ #pragma once -#include "cql3/term.hh" #include "cql3/abstract_marker.hh" #include "cql3/expr/expression.hh" #include "utils/like_matcher.hh" @@ -60,17 +59,17 @@ public: // object stands for the string cell. See column_condition::raw::prepare() for details. const column_definition& column; private: - // For collection, when testing the equality of a specific element, nullptr otherwise. - ::shared_ptr _collection_element; + // For collection, when testing the equality of a specific element, nullopt otherwise. + std::optional _collection_element; // A literal value for comparison predicates or a multi item terminal for "a IN ?" - ::shared_ptr _value; + std::optional _value; // List of terminals for "a IN (value, value, ...)" - std::vector<::shared_ptr> _in_values; + std::vector _in_values; const std::unique_ptr _matcher; expr::oper_t _op; public: - column_condition(const column_definition& column, ::shared_ptr collection_element, - ::shared_ptr value, std::vector<::shared_ptr> in_values, + column_condition(const column_definition& column, std::optional collection_element, + std::optional value, std::vector in_values, std::unique_ptr matcher, expr::oper_t op) : column(column) , _collection_element(std::move(collection_element)) @@ -89,7 +88,7 @@ public: * @param boundNames the list of column specification where to collect the * bind variables of this term in. */ - void collect_marker_specificaton(prepare_context& ctx) const; + void collect_marker_specificaton(prepare_context& ctx); // Retrieve parameter marker values, if any, find the appropriate collection // element if the cell is a collection and an element access is used in the expression, @@ -102,15 +101,15 @@ public: * "IF col = 'foo'" * "IF col LIKE " */ - static lw_shared_ptr condition(const column_definition& def, ::shared_ptr collection_element, - ::shared_ptr value, std::unique_ptr matcher, expr::oper_t op) { + static lw_shared_ptr condition(const column_definition& def, std::optional collection_element, + expr::expression value, std::unique_ptr matcher, expr::oper_t op) { return make_lw_shared(def, std::move(collection_element), std::move(value), - std::vector<::shared_ptr>{}, std::move(matcher), op); + std::vector{}, std::move(matcher), op); } // Helper constructor wrapper for "IF col IN ... and IF col['key'] IN ... */ - static lw_shared_ptr in_condition(const column_definition& def, ::shared_ptr collection_element, - ::shared_ptr in_marker, std::vector<::shared_ptr> in_values) { + static lw_shared_ptr in_condition(const column_definition& def, std::optional collection_element, + std::optional in_marker, std::vector in_values) { return make_lw_shared(def, std::move(collection_element), std::move(in_marker), std::move(in_values), nullptr, expr::oper_t::IN); } From fd1596171e2adf1d6a1bd2abbc1d76b17b98ea7c Mon Sep 17 00:00:00 2001 From: Jan Ciolek Date: Thu, 30 Sep 2021 13:13:22 +0200 Subject: [PATCH 11/24] cql3: expr: Add evaluate_IN_list(expression, options) evaluate_IN_list was only defined for a term, but now we are removing term so it should be also defined for an expression. The internal code is the same - this function used to convert the term to expression and then did all operations on expression. Signed-off-by: Jan Ciolek --- cql3/expr/expression.cc | 4 ++++ cql3/expr/expression.hh | 1 + 2 files changed, 5 insertions(+) diff --git a/cql3/expr/expression.cc b/cql3/expr/expression.cc index 938beed393..454e161088 100644 --- a/cql3/expr/expression.cc +++ b/cql3/expr/expression.cc @@ -1769,6 +1769,10 @@ constant evaluate_IN_list(term* term_ptr, const query_options& options) { expression e = term_ptr->to_expression(); + return evaluate_IN_list(e, options); +} + +constant evaluate_IN_list(const expression& e, const query_options& options) { if (auto collection = expr::as_if(&e)) { if (collection->style == collection_constructor::style_type::list) { return evaluate_list(*collection, options, true); diff --git a/cql3/expr/expression.hh b/cql3/expr/expression.hh index 8401020649..20d908160a 100644 --- a/cql3/expr/expression.hh +++ b/cql3/expr/expression.hh @@ -698,6 +698,7 @@ constant evaluate(term&, const query_options&); constant evaluate_IN_list(const ::shared_ptr&, const query_options&); constant evaluate_IN_list(term*, const query_options&); constant evaluate_IN_list(term&, const query_options&); +constant evaluate_IN_list(const expression&, const query_options&); // Calls evaluate() on the term and then converts the constant to raw_value_view cql3::raw_value_view evaluate_to_raw_view(const ::shared_ptr&, const query_options&); From e37906ae3451cbc9b5124d339938480d2e184cc3 Mon Sep 17 00:00:00 2001 From: Jan Ciolek Date: Thu, 30 Sep 2021 15:05:21 +0200 Subject: [PATCH 12/24] cql3: expr: Remove term in expression Some struct inside the expression variant still contained term. Replace those terms with expression. Signed-off-by: Jan Ciolek --- cql3/expr/expression.cc | 95 ++++++++++--------- cql3/expr/expression.hh | 24 +++-- cql3/restrictions/multi_column_restriction.hh | 16 ++-- cql3/restrictions/statement_restrictions.cc | 8 +- cql3/single_column_relation.cc | 20 ++-- cql3/single_column_relation.hh | 8 +- cql3/token_relation.cc | 12 +-- test/boost/statement_restrictions_test.cc | 9 +- 8 files changed, 97 insertions(+), 95 deletions(-) diff --git a/cql3/expr/expression.cc b/cql3/expr/expression.cc index 454e161088..20116058cf 100644 --- a/cql3/expr/expression.cc +++ b/cql3/expr/expression.cc @@ -70,7 +70,7 @@ expression::operator=(const expression& o) { return *this; } -binary_operator::binary_operator(expression lhs, oper_t op, ::shared_ptr rhs, comparison_order order) +binary_operator::binary_operator(expression lhs, oper_t op, expression rhs, comparison_order order) : lhs(std::move(lhs)) , op(op) , rhs(std::move(rhs)) @@ -94,7 +94,7 @@ children_t explode_conjunction(expression e) { using cql3::selection::selection; /// Serialized values for all types of cells, plus selection (to find a column's index) and options (for -/// subscript term's value). +/// subscript expression's value). struct row_data_from_partition_slice { const std::vector& partition_key; const std::vector& clustering_key; @@ -104,7 +104,7 @@ struct row_data_from_partition_slice { /// Everything needed to compute column values during restriction evaluation. struct column_value_eval_bag { - const query_options& options; // For evaluating subscript terms. + const query_options& options; // For evaluating subscript expressions. row_data_from_partition_slice row_data; }; @@ -120,9 +120,9 @@ managed_bytes_opt get_value(const column_value& col, const column_value_eval_bag } const auto deserialized = cdef->type->deserialize(managed_bytes_view(*data.other_columns[data.sel.index_of(*cdef)])); const auto& data_map = value_cast(deserialized); - const auto key = evaluate_to_raw_view(col.sub, options); + const auto key = evaluate(*col.sub, options); auto&& key_type = col_type->name_comparator(); - const auto found = key.with_linearized([&] (bytes_view key_bv) { + const auto found = key.view().with_linearized([&] (bytes_view key_bv) { using entry = std::pair; return std::find_if(data_map.cbegin(), data_map.cend(), [&] (const entry& element) { return key_type->compare(element.first.serialize_nonnull(), key_bv) == 0; @@ -167,13 +167,13 @@ bool equal(const managed_bytes_opt& rhs, const column_value& lhs, const column_v return get_value_comparator(lhs)->equal(managed_bytes_view(*value), managed_bytes_view(*rhs)); } -/// Convenience overload for term. -bool equal(term& rhs, const column_value& lhs, const column_value_eval_bag& bag) { - return equal(to_managed_bytes_opt(evaluate_to_raw_view(rhs, bag.options)), lhs, bag); +/// Convenience overload for expression. +bool equal(const expression& rhs, const column_value& lhs, const column_value_eval_bag& bag) { + return equal(evaluate(rhs, bag.options).value.to_managed_bytes_opt(), lhs, bag); } /// True iff columns' values equal t. -bool equal(term& t, const tuple_constructor& columns_tuple, const column_value_eval_bag& bag) { +bool equal(const expression& t, const tuple_constructor& columns_tuple, const column_value_eval_bag& bag) { const constant tup = evaluate(t, bag.options); if (!tup.type->is_tuple()) { throw exceptions::invalid_request_exception("multi-column equality has right-hand side that isn't a tuple"); @@ -212,7 +212,7 @@ bool limits(managed_bytes_view lhs, oper_t op, managed_bytes_view rhs, const abs } /// True iff the column value is limited by rhs in the manner prescribed by op. -bool limits(const column_value& col, oper_t op, term& rhs, const column_value_eval_bag& bag) { +bool limits(const column_value& col, oper_t op, const expression& rhs, const column_value_eval_bag& bag) { if (!is_slice(op)) { // For EQ or NEQ, use equal(). throw std::logic_error("limits() called on non-slice op"); } @@ -220,17 +220,17 @@ bool limits(const column_value& col, oper_t op, term& rhs, const column_value_ev if (!lhs) { return false; } - const auto b = to_managed_bytes_opt(evaluate_to_raw_view(rhs, bag.options)); + const auto b = evaluate(rhs, bag.options).value.to_managed_bytes_opt(); return b ? limits(*lhs, op, *b, *get_value_comparator(col)) : false; } /// True iff the column values are limited by t in the manner prescribed by op. -bool limits(const tuple_constructor& columns_tuple, const oper_t op, term& t, +bool limits(const tuple_constructor& columns_tuple, const oper_t op, const expression& e, const column_value_eval_bag& bag) { if (!is_slice(op)) { // For EQ or NEQ, use equal(). throw std::logic_error("limits() called on non-slice op"); } - const constant tup = evaluate(t, bag.options); + const constant tup = evaluate(e, bag.options); if (!tup.type->is_tuple()) { throw exceptions::invalid_request_exception( "multi-column comparison has right-hand side that isn't a tuple"); @@ -397,13 +397,13 @@ bool like(const column_value& cv, const raw_value_view& pattern, const column_va } /// True iff the column value is in the set defined by rhs. -bool is_one_of(const column_value& col, term& rhs, const column_value_eval_bag& bag) { +bool is_one_of(const column_value& col, const expression& rhs, const column_value_eval_bag& bag) { const constant in_list = evaluate_IN_list(rhs, bag.options); statements::request_validations::check_false( in_list.is_null(), "Invalid null value for column %s", col.col->name_as_text()); if (!in_list.type->without_reversed().is_list()) { - throw std::logic_error("unexpected term type in is_one_of(single column)"); + throw std::logic_error("unexpected expression type in is_one_of(single column)"); } return boost::algorithm::any_of(get_list_elements(in_list), [&] (const managed_bytes_opt& b) { return equal(b, col, bag); @@ -411,10 +411,10 @@ bool is_one_of(const column_value& col, term& rhs, const column_value_eval_bag& } /// True iff the tuple of column values is in the set defined by rhs. -bool is_one_of(const tuple_constructor& tuple, term& rhs, const column_value_eval_bag& bag) { +bool is_one_of(const tuple_constructor& tuple, const expression& rhs, const column_value_eval_bag& bag) { constant in_list = evaluate_IN_list(rhs, bag.options); if (!in_list.type->without_reversed().is_list()) { - throw std::logic_error("unexpected term type in is_one_of(multi-column)"); + throw std::logic_error("unexpected expression type in is_one_of(multi-column)"); } return boost::algorithm::any_of(get_list_of_tuples_elements(in_list), [&] (const std::vector& el) { return boost::equal(tuple.elements, el, [&] (const expression& c, const managed_bytes_opt& b) { @@ -458,30 +458,33 @@ bool is_satisfied_by(const binary_operator& opr, const column_value_eval_bag& ba return expr::visit(overloaded_functor{ [&] (const column_value& col) { if (opr.op == oper_t::EQ) { - return equal(*opr.rhs, col, bag); + return equal(opr.rhs, col, bag); } else if (opr.op == oper_t::NEQ) { - return !equal(*opr.rhs, col, bag); + return !equal(opr.rhs, col, bag); } else if (is_slice(opr.op)) { - return limits(col, opr.op, *opr.rhs, bag); + return limits(col, opr.op, opr.rhs, bag); } else if (opr.op == oper_t::CONTAINS) { - return contains(col, evaluate_to_raw_view(opr.rhs, bag.options), bag); + constant val = evaluate(opr.rhs, bag.options); + return contains(col, val.view(), bag); } else if (opr.op == oper_t::CONTAINS_KEY) { - return contains_key(col, evaluate_to_raw_view(opr.rhs, bag.options), bag); + constant val = evaluate(opr.rhs, bag.options); + return contains_key(col, val.view(), bag); } else if (opr.op == oper_t::LIKE) { - return like(col, evaluate_to_raw_view(opr.rhs, bag.options), bag); + constant val = evaluate(opr.rhs, bag.options); + return like(col, val.view(), bag); } else if (opr.op == oper_t::IN) { - return is_one_of(col, *opr.rhs, bag); + return is_one_of(col, opr.rhs, bag); } else { throw exceptions::unsupported_operation_exception(format("Unhandled binary_operator: {}", opr)); } }, [&] (const tuple_constructor& cvs) { if (opr.op == oper_t::EQ) { - return equal(*opr.rhs, cvs, bag); + return equal(opr.rhs, cvs, bag); } else if (is_slice(opr.op)) { - return limits(cvs, opr.op, *opr.rhs, bag); + return limits(cvs, opr.op, opr.rhs, bag); } else if (opr.op == oper_t::IN) { - return is_one_of(cvs, *opr.rhs, bag); + return is_one_of(cvs, opr.rhs, bag); } else { throw exceptions::unsupported_operation_exception( format("Unhandled multi-column binary_operator: {}", opr)); @@ -607,26 +610,26 @@ const auto deref = boost::adaptors::transformed([] (const managed_bytes_opt& b) /// Returns possible values from t, which must be RHS of IN. value_list get_IN_values( - const ::shared_ptr& t, const query_options& options, const serialized_compare& comparator, + const expression& e, const query_options& options, const serialized_compare& comparator, sstring_view column_name) { - const constant in_list = evaluate_IN_list(t, options); + const constant in_list = evaluate_IN_list(e, options); if (in_list.is_unset_value()) { throw exceptions::invalid_request_exception(format("Invalid unset value for column {}", column_name)); } statements::request_validations::check_false(in_list.is_null(), "Invalid null value for column %s", column_name); if (!in_list.type->without_reversed().is_list()) { - throw std::logic_error(format("get_IN_values(single-column) on invalid term {}", *t)); + throw std::logic_error(format("get_IN_values(single-column) on invalid expression {}", e)); } utils::chunked_vector list_elems = get_list_elements(in_list); return to_sorted_vector(std::move(list_elems) | non_null | deref, comparator); } /// Returns possible values for k-th column from t, which must be RHS of IN. -value_list get_IN_values(const ::shared_ptr& t, size_t k, const query_options& options, +value_list get_IN_values(const expression& e, size_t k, const query_options& options, const serialized_compare& comparator) { - const constant in_list = evaluate_IN_list(t, options); + const constant in_list = evaluate_IN_list(e, options); if (!in_list.type->without_reversed().is_list()) { - throw std::logic_error(format("get_IN_values(multi-column) on invalid term {}", *t)); + throw std::logic_error(format("get_IN_values(multi-column) on invalid expression {}", e)); } const auto split_values = get_list_of_tuples_elements(in_list); // Need lvalue from which to make std::view. const auto result_range = split_values @@ -704,7 +707,7 @@ value_set possible_lhs_values(const column_definition* cdef, const expression& e return unbounded_value_set; } if (is_compare(oper.op)) { - managed_bytes_opt val = to_managed_bytes_opt(evaluate_to_raw_view(oper.rhs, options)); + managed_bytes_opt val = evaluate(oper.rhs, options).value.to_managed_bytes_opt(); if (!val) { return empty_value_set; // All NULL comparisons fail; no column values match. } @@ -727,7 +730,7 @@ value_set possible_lhs_values(const column_definition* cdef, const expression& e const auto column_index_on_lhs = std::distance(tuple.elements.begin(), found); if (is_compare(oper.op)) { // RHS must be a tuple due to upstream checks. - managed_bytes_opt val = get_tuple_elements(evaluate(*oper.rhs, options)).at(column_index_on_lhs); + managed_bytes_opt val = get_tuple_elements(evaluate(oper.rhs, options)).at(column_index_on_lhs); if (!val) { return empty_value_set; // All NULL comparisons fail; no column values match. } @@ -749,7 +752,7 @@ value_set possible_lhs_values(const column_definition* cdef, const expression& e if (cdef) { return unbounded_value_set; } - const auto val = to_managed_bytes_opt(evaluate_to_raw_view(oper.rhs, options)); + const auto val = evaluate(oper.rhs, options).value.to_managed_bytes_opt(); if (!val) { return empty_value_set; // All NULL comparisons fail; no token values match. } @@ -958,7 +961,7 @@ std::ostream& operator<<(std::ostream& os, const expression& expr) { [&] (const constant& v) { os << v.view(); }, [&] (const conjunction& conj) { fmt::print(os, "({})", fmt::join(conj.children, ") AND (")); }, [&] (const binary_operator& opr) { - os << "(" << opr.lhs << ") " << opr.op << ' ' << *opr.rhs; + os << "(" << opr.lhs << ") " << opr.op << ' ' << opr.rhs; }, [&] (const token& t) { os << "TOKEN"; }, [&] (const column_value& col) { @@ -1111,7 +1114,7 @@ expression search_and_replace(const expression& e, }; }, [&] (const binary_operator& oper) -> expression { - return binary_operator(recurse(oper.lhs), oper.op, oper.rhs); + return binary_operator(recurse(oper.lhs), oper.op, recurse(oper.rhs)); }, [&] (const column_mutation_attribute& cma) -> expression { return column_mutation_attribute{cma.kind, recurse(cma.column)}; @@ -1154,6 +1157,12 @@ expression search_and_replace(const expression& e, [&] (const field_selection& fs) -> expression { return field_selection{recurse(fs.structure), fs.field}; }, + [&] (const column_value& cv) -> expression { + if (cv.sub.has_value()) { + return column_value(cv.col, recurse(*cv.sub)); + } + return cv; + }, [&] (LeafExpression auto const& e) -> expression { return e; }, @@ -1935,7 +1944,9 @@ void fill_prepare_context(expression& e, prepare_context& ctx) { fill_prepare_context(fs.structure, ctx); }, [&](column_value& cv) { - fill_prepare_context(cv.sub, ctx); + if (cv.sub.has_value()) { + fill_prepare_context(*cv.sub, ctx); + } }, [](untyped_constant&) {}, [](null&) {}, @@ -1943,12 +1954,6 @@ void fill_prepare_context(expression& e, prepare_context& ctx) { }, e); } -void fill_prepare_context(::shared_ptr& t, prepare_context& ctx) { - if (t.get() != nullptr) { - t->fill_prepare_context(ctx); - } -} - expression to_expression(const ::shared_ptr& term_ptr) { if (term_ptr.get() == nullptr) { return constant::make_null(); diff --git a/cql3/expr/expression.hh b/cql3/expr/expression.hh index 20d908160a..6208743759 100644 --- a/cql3/expr/expression.hh +++ b/cql3/expr/expression.hh @@ -183,7 +183,6 @@ public: template concept LeafExpression = std::same_as - || std::same_as || std::same_as || std::same_as || std::same_as @@ -192,14 +191,14 @@ concept LeafExpression || std::same_as ; -/// A column, optionally subscripted by a term (eg, c1 or c2['abc']). +/// A column, optionally subscripted by a value (eg, c1 or c2['abc']). struct column_value { const column_definition* col; - ::shared_ptr sub; ///< If present, this LHS is col[sub], otherwise just col. + std::optional sub; ///< If present, this LHS is col[sub], otherwise just col. /// For easy creation of vector from vector. column_value(const column_definition* col) : col(col) {} /// The compiler doesn't auto-generate this due to the other constructor's existence. - column_value(const column_definition* col, ::shared_ptr sub) : col(col), sub(sub) {} + column_value(const column_definition* col, expr::expression sub) : col(col), sub(std::move(sub)) {} }; /// Represents token function on LHS of an operator relation. No need to list column definitions @@ -218,10 +217,10 @@ enum class comparison_order : char { struct binary_operator { expression lhs; oper_t op; - ::shared_ptr rhs; + expression rhs; comparison_order order; - binary_operator(expression lhs, oper_t op, ::shared_ptr rhs, comparison_order order = comparison_order::cql); + binary_operator(expression lhs, oper_t op, expression rhs, comparison_order order = comparison_order::cql); }; /// A conjunction of restrictions. @@ -474,7 +473,12 @@ const binary_operator* find_atom(const expression& e, Fn f) { } return nullptr; }, - [] (const column_value&) -> const binary_operator* { return nullptr; }, + [&] (const column_value& cv) -> const binary_operator* { + if (cv.sub.has_value()) { + return find_atom(*cv.sub, f); + } + return nullptr; + }, [] (const token&) -> const binary_operator* { return nullptr; }, [] (const unresolved_identifier&) -> const binary_operator* { return nullptr; }, [] (const column_mutation_attribute&) -> const binary_operator* { return nullptr; }, @@ -644,9 +648,6 @@ extern expression replace_token(const expression&, const column_definition*); // Recursively copies e and returns it. Calls replace_candidate() on all nodes. If it returns nullopt, // continue with the copying. If it returns an expression, that expression replaces the current node. -// -// Note only binary_operator's LHS is searched. The RHS is not an expression, but a term, so it is left -// unmodified. extern expression search_and_replace(const expression& e, const noncopyable_function (const expression& candidate)>& replace_candidate); @@ -731,9 +732,6 @@ utils::chunked_vector> get_list_of_tuples_element // Sets lwt_cache_id field in function_calls. void fill_prepare_context(expression&, cql3::prepare_context&); -// Needed for now because some fields of expression structs are terms, will be removed soon. -void fill_prepare_context(::shared_ptr&, cql3::prepare_context&); - expression to_expression(const ::shared_ptr&); } // namespace expr diff --git a/cql3/restrictions/multi_column_restriction.hh b/cql3/restrictions/multi_column_restriction.hh index 444109986d..da68688c00 100644 --- a/cql3/restrictions/multi_column_restriction.hh +++ b/cql3/restrictions/multi_column_restriction.hh @@ -204,7 +204,7 @@ public: { using namespace expr; expression = binary_operator{ - column_definitions_as_tuple_constructor(_column_defs), oper_t::EQ, _value}; + column_definitions_as_tuple_constructor(_column_defs), oper_t::EQ, expr::to_expression(_value)}; } virtual bool is_supported_by(const secondary_index::index& index) const override { @@ -329,7 +329,7 @@ public: expression = binary_operator{ column_definitions_as_tuple_constructor(_column_defs), oper_t::IN, - ::make_shared(_values, std::move(in_list_type))}; + expr::to_expression(::make_shared(_values, std::move(in_list_type)))}; } }; @@ -348,7 +348,7 @@ public: expression = binary_operator{ column_definitions_as_tuple_constructor(_column_defs), oper_t::IN, - std::move(marker)}; + expr::to_expression(std::move(marker))}; } }; @@ -370,7 +370,7 @@ public: expression = expr::binary_operator{ column_definitions_as_tuple_constructor(defs), expr::pick_operator(bound, inclusive), - std::move(term), + expr::to_expression(std::move(term)), m}; } @@ -472,17 +472,17 @@ private: */ ::shared_ptr make_single_column_restriction(std::optional bound, bool inclusive, std::size_t column_pos, const managed_bytes_opt& value) const { - ::shared_ptr term = - ::make_shared(cql3::raw_value::make_value(value), _column_defs[column_pos]->type); + expr::expression e = expr::to_expression( + ::make_shared(cql3::raw_value::make_value(value), _column_defs[column_pos]->type)); using namespace expr; if (!bound){ auto r = ::make_shared(*_column_defs[column_pos]); - r->expression = binary_operator{column_value{_column_defs[column_pos]}, expr::oper_t::EQ, std::move(term)}; + r->expression = binary_operator{column_value{_column_defs[column_pos]}, expr::oper_t::EQ, std::move(e)}; return r; } else { auto r = ::make_shared(*_column_defs[column_pos]); r->expression = binary_operator{ - column_value(_column_defs[column_pos]), pick_operator(*bound, inclusive), std::move(term)}; + column_value(_column_defs[column_pos]), pick_operator(*bound, inclusive), std::move(e)}; return r; } } diff --git a/cql3/restrictions/statement_restrictions.cc b/cql3/restrictions/statement_restrictions.cc index 073e7091d3..79ae318e19 100644 --- a/cql3/restrictions/statement_restrictions.cc +++ b/cql3/restrictions/statement_restrictions.cc @@ -1617,7 +1617,7 @@ void statement_restrictions::prepare_indexed_global(const schema& idx_tbl_schema oper_t::EQ, // TODO: This should be a unique marker whose value we set at execution time. There is currently no // handy mechanism for doing that in query_options. - ::make_shared(raw_value::make_unset_value(), token_column->type)); + expr::constant::make_unset_value(token_column->type)); } void statement_restrictions::prepare_indexed_local(const schema& idx_tbl_schema) { @@ -1689,8 +1689,8 @@ std::vector statement_restrictions::get_global_index_cl } // WARNING: We must not yield to another fiber from here until the function's end, lest this RHS be // overwritten. - const_cast<::shared_ptr&>(expr::as((*_idx_tbl_ck_prefix)[0]).rhs) = - ::make_shared(raw_value::make_value(*token_bytes), token_column.type); + const_cast(expr::as((*_idx_tbl_ck_prefix)[0]).rhs) = + expr::constant(raw_value::make_value(*token_bytes), token_column.type); // Multi column restrictions are not added to _idx_tbl_ck_prefix, they are handled later by filtering. return get_single_column_clustering_bounds(options, idx_tbl_schema, *_idx_tbl_ck_prefix); @@ -1734,7 +1734,7 @@ sstring statement_restrictions::to_string() const { static bool has_eq_null(const query_options& options, const expression& expr) { return find_atom(expr, [&] (const binary_operator& binop) { - return binop.op == oper_t::EQ && !evaluate_to_raw_view(binop.rhs, options); + return binop.op == oper_t::EQ && evaluate(binop.rhs, options).is_null(); }); } diff --git a/cql3/single_column_relation.cc b/cql3/single_column_relation.cc index c9272eefad..9d356dc0cd 100644 --- a/cql3/single_column_relation.cc +++ b/cql3/single_column_relation.cc @@ -76,13 +76,13 @@ single_column_relation::new_EQ_restriction(database& db, schema_ptr schema, prep } if (!_map_key) { auto r = ::make_shared(column_def); - auto term = to_term(to_receivers(*schema, column_def), *_value, db, schema->ks_name(), ctx); - r->expression = binary_operator{column_value{&column_def}, expr::oper_t::EQ, std::move(term)}; + auto e = expr::to_expression(to_term(to_receivers(*schema, column_def), *_value, db, schema->ks_name(), ctx)); + r->expression = binary_operator{column_value{&column_def}, expr::oper_t::EQ, std::move(e)}; return r; } auto&& receivers = to_receivers(*schema, column_def); - auto&& entry_key = to_term({receivers[0]}, *_map_key, db, schema->ks_name(), ctx); - auto&& entry_value = to_term({receivers[1]}, *_value, db, schema->ks_name(), ctx); + auto&& entry_key = expr::to_expression(to_term({receivers[0]}, *_map_key, db, schema->ks_name(), ctx)); + auto&& entry_value = expr::to_expression(to_term({receivers[1]}, *_value, db, schema->ks_name(), ctx)); auto r = make_shared(column_def); r->expression = binary_operator{ column_value(&column_def, std::move(entry_key)), oper_t::EQ, std::move(entry_value)}; @@ -100,23 +100,23 @@ single_column_relation::new_IN_restriction(database& db, schema_ptr schema, prep auto receivers = to_receivers(*schema, column_def); assert(_in_values.empty() || !_value); if (_value) { - auto term = to_term(receivers, *_value, db, schema->ks_name(), ctx); + auto e = expr::to_expression(to_term(receivers, *_value, db, schema->ks_name(), ctx)); auto r = ::make_shared(column_def); - r->expression = binary_operator{column_value{&column_def}, expr::oper_t::IN, std::move(term)}; + r->expression = binary_operator{column_value{&column_def}, expr::oper_t::IN, std::move(e)}; return r; } auto terms = to_terms(receivers, _in_values, db, schema->ks_name(), ctx); // Convert a single-item IN restriction to an EQ restriction if (terms.size() == 1) { auto r = ::make_shared(column_def); - r->expression = binary_operator{column_value{&column_def}, expr::oper_t::EQ, std::move(terms[0])}; + r->expression = binary_operator{column_value{&column_def}, expr::oper_t::EQ, expr::to_expression(std::move(terms[0]))}; return r; } auto r = ::make_shared(column_def); r->expression = binary_operator{ column_value{&column_def}, expr::oper_t::IN, - ::make_shared(std::move(terms), list_type_impl::get_instance(column_def.type, false))}; + expr::to_expression(::make_shared(std::move(terms), list_type_impl::get_instance(column_def.type, false)))}; return r; } @@ -128,9 +128,9 @@ single_column_relation::new_LIKE_restriction( throw exceptions::invalid_request_exception( format("LIKE is allowed only on string types, which {} is not", column_def.name_as_text())); } - auto term = to_term(to_receivers(*schema, column_def), *_value, db, schema->ks_name(), ctx); + auto e = expr::to_expression(to_term(to_receivers(*schema, column_def), *_value, db, schema->ks_name(), ctx)); auto r = ::make_shared(column_def); - r->expression = binary_operator{column_value{&column_def}, expr::oper_t::LIKE, std::move(term)}; + r->expression = binary_operator{column_value{&column_def}, expr::oper_t::LIKE, std::move(e)}; return r; } diff --git a/cql3/single_column_relation.hh b/cql3/single_column_relation.hh index 86e16a1559..b8c2746593 100644 --- a/cql3/single_column_relation.hh +++ b/cql3/single_column_relation.hh @@ -169,9 +169,9 @@ protected: throw exceptions::invalid_request_exception("Slice restrictions are not supported on duration columns"); } - auto term = to_term(to_receivers(*schema, column_def), *_value, db, schema->ks_name(), ctx); + auto e = expr::to_expression(to_term(to_receivers(*schema, column_def), *_value, db, schema->ks_name(), ctx)); auto r = ::make_shared(column_def); - r->expression = expr::binary_operator{expr::column_value{&column_def}, _relation_type, std::move(term)}; + r->expression = expr::binary_operator{expr::column_value{&column_def}, _relation_type, std::move(e)}; return r; } @@ -179,10 +179,10 @@ protected: prepare_context& ctx, bool is_key) override { auto&& column_def = to_column_definition(*schema, *_entity); - auto term = to_term(to_receivers(*schema, column_def), *_value, db, schema->ks_name(), ctx); + auto e = expr::to_expression(to_term(to_receivers(*schema, column_def), *_value, db, schema->ks_name(), ctx)); auto r = ::make_shared(column_def); r->expression = expr::binary_operator{ - expr::column_value{&column_def}, is_key ? expr::oper_t::CONTAINS_KEY : expr::oper_t::CONTAINS, std::move(term)}; + expr::column_value{&column_def}, is_key ? expr::oper_t::CONTAINS_KEY : expr::oper_t::CONTAINS, std::move(e)}; return r; } diff --git a/cql3/token_relation.cc b/cql3/token_relation.cc index cded9f73bb..0f8ad8662c 100644 --- a/cql3/token_relation.cc +++ b/cql3/token_relation.cc @@ -83,11 +83,11 @@ std::vector> cql3::token_relation::to_ database& db, schema_ptr schema, prepare_context& ctx) { auto column_defs = get_column_definitions(*schema); - auto term = to_term(to_receivers(*schema, column_defs), _value, db, - schema->ks_name(), ctx); + auto e = expr::to_expression(to_term(to_receivers(*schema, column_defs), _value, db, + schema->ks_name(), ctx)); auto r = ::make_shared(column_defs); using namespace expr; - r->expression = binary_operator{token{}, oper_t::EQ, std::move(term)}; + r->expression = binary_operator{token{}, oper_t::EQ, std::move(e)}; return r; } @@ -105,11 +105,11 @@ std::vector> cql3::token_relation::to_ statements::bound bound, bool inclusive) { auto column_defs = get_column_definitions(*schema); - auto term = to_term(to_receivers(*schema, column_defs), _value, db, - schema->ks_name(), ctx); + auto e = expr::to_expression(to_term(to_receivers(*schema, column_defs), _value, db, + schema->ks_name(), ctx)); auto r = ::make_shared(column_defs); using namespace expr; - r->expression = binary_operator{token{}, pick_operator(bound, inclusive), std::move(term)}; + r->expression = binary_operator{token{}, pick_operator(bound, inclusive), std::move(e)}; return r; } diff --git a/test/boost/statement_restrictions_test.cc b/test/boost/statement_restrictions_test.cc index fb0cab4d85..3d571bef9f 100644 --- a/test/boost/statement_restrictions_test.cc +++ b/test/boost/statement_restrictions_test.cc @@ -420,8 +420,7 @@ BOOST_AUTO_TEST_CASE(expression_extract_column_restrictions) { // AND pk1 AND pk2 // AND (pk1, pk2) std::vector big_where; - ::shared_ptr zero_value = - ::make_shared(raw_value::make_value(I(0)), int32_type); + expr::constant zero_value = constant(raw_value::make_value(I(0)), int32_type); expression pk1_restriction(binary_operator(column_value(&col_pk1), oper_t::EQ, zero_value)); expression pk2_restriction(binary_operator(column_value(&col_pk2), oper_t::EQ, zero_value)); @@ -438,10 +437,10 @@ BOOST_AUTO_TEST_CASE(expression_extract_column_restrictions) { std::vector zeros_tuple_elems(columns.size(), managed_bytes_opt(I(0))); data_type tup_type = tuple_type_impl::get_instance(std::vector(columns.size(), int32_type)); - ::shared_ptr zeros_tuple = - ::make_shared(std::move(zeros_tuple_elems), std::move(tup_type)); + managed_bytes tup_bytes = tuple_type_impl::build_value_fragmented(std::move(zeros_tuple_elems)); + constant zeros_tuple(raw_value::make_value(std::move(tup_bytes)), std::move(tup_type)); - return binary_operator(column_tuple, oper, zeros_tuple); + return binary_operator(column_tuple, oper, std::move(zeros_tuple)); }; expression pk1_pk2_restriction = make_multi_column_restriction({&col_pk1, &col_pk2}, oper_t::LT); From a59683b929e6b30d9803453a6167c92c7df06c25 Mon Sep 17 00:00:00 2001 From: Jan Ciolek Date: Thu, 7 Oct 2021 16:20:19 +0200 Subject: [PATCH 13/24] cql3: Remove term in term_slice, rename to bounds_slice term_slice is an interval from one term to the other. [term1, term2] Replaced terms with expressions. Because the name has 'term' in it, it was changed to bounds_slice. Signed-off-by: Jan Ciolek --- .../{term_slice.hh => bounds_slice.hh} | 40 +++++++------------ cql3/restrictions/multi_column_restriction.hh | 6 +-- .../restrictions/single_column_restriction.hh | 2 +- cql3/restrictions/token_restriction.hh | 2 +- 4 files changed, 20 insertions(+), 30 deletions(-) rename cql3/restrictions/{term_slice.hh => bounds_slice.hh} (82%) diff --git a/cql3/restrictions/term_slice.hh b/cql3/restrictions/bounds_slice.hh similarity index 82% rename from cql3/restrictions/term_slice.hh rename to cql3/restrictions/bounds_slice.hh index 81b4f34c03..7260efc36d 100644 --- a/cql3/restrictions/term_slice.hh +++ b/cql3/restrictions/bounds_slice.hh @@ -42,7 +42,6 @@ #pragma once #include "cql3/restrictions/restriction.hh" -#include "cql3/term.hh" #include #include "to_string.hh" #include "exceptions/exceptions.hh" @@ -52,36 +51,27 @@ namespace cql3 { namespace restrictions { -class term_slice final { +class bounds_slice final { private: struct bound { bool inclusive; - ::shared_ptr t; + std::optional value; }; bound _bounds[2]; private: - term_slice(::shared_ptr start, bool include_start, ::shared_ptr end, bool include_end) + bounds_slice(std::optional start, bool include_start, + std::optional end, bool include_end) : _bounds{{include_start, std::move(start)}, {include_end, std::move(end)}} { } public: - static term_slice new_instance(statements::bound bound, bool include, ::shared_ptr term) { + static bounds_slice new_instance(statements::bound bound, bool include, std::optional value) { if (is_start(bound)) { - return term_slice(std::move(term), include, {}, false); + return bounds_slice(std::move(value), include, std::nullopt, false); } else { - return term_slice({}, false, std::move(term), include); + return bounds_slice(std::nullopt, false, std::move(value), include); } } - /** - * Returns the boundary value. - * - * @param bound the boundary type - * @return the boundary value - */ - ::shared_ptr bound(statements::bound b) const { - return _bounds[get_idx(b)].t; - } - /** * Checks if this slice has a boundary for the specified type. * @@ -89,7 +79,7 @@ public: * @return true if this slice has a boundary for the specified type, false otherwise. */ bool has_bound(statements::bound b) const { - return bool(_bounds[get_idx(b)].t); + return _bounds[get_idx(b)].value.has_value(); } /** @@ -100,7 +90,7 @@ public: * false otherwise. */ bool is_inclusive(statements::bound b) const { - return !_bounds[get_idx(b)].t || _bounds[get_idx(b)].inclusive; + return !_bounds[get_idx(b)].value.has_value() || _bounds[get_idx(b)].inclusive; } /** @@ -108,7 +98,7 @@ public: * * @param other the slice to merge with */ - void merge(const term_slice& other) { + void merge(const bounds_slice& other) { if (has_bound(statements::bound::START)) { assert(!other.has_bound(statements::bound::START)); _bounds[get_idx(statements::bound::END)] = other._bounds[get_idx(statements::bound::END)]; @@ -134,15 +124,15 @@ public: } sstring to_string() const { - static auto print_term = [] (::shared_ptr t) -> sstring { - return t ? t->to_string() : "null"; + static auto print_value = [] (const std::optional& v) -> sstring { + return v.has_value() ? expr::to_string(*v) : "none"; }; return format("({} {}, {} {})", - _bounds[0].inclusive ? ">=" : ">", print_term(_bounds[0].t), - _bounds[1].inclusive ? "<=" : "<", print_term(_bounds[1].t)); + _bounds[0].inclusive ? ">=" : ">", print_value(_bounds[0].value), + _bounds[1].inclusive ? "<=" : "<", print_value(_bounds[1].value)); } - friend std::ostream& operator<<(std::ostream& out, const term_slice& slice) { + friend std::ostream& operator<<(std::ostream& out, const bounds_slice& slice) { return out << slice.to_string(); } diff --git a/cql3/restrictions/multi_column_restriction.hh b/cql3/restrictions/multi_column_restriction.hh index da68688c00..b38dad6bcd 100644 --- a/cql3/restrictions/multi_column_restriction.hh +++ b/cql3/restrictions/multi_column_restriction.hh @@ -355,17 +355,17 @@ public: class multi_column_restriction::slice final : public multi_column_restriction { using restriction_shared_ptr = ::shared_ptr; using mode = expr::comparison_order; - term_slice _slice; + bounds_slice _slice; mode _mode; - slice(schema_ptr schema, std::vector defs, term_slice slice, mode m) + slice(schema_ptr schema, std::vector defs, bounds_slice slice, mode m) : multi_column_restriction(schema, std::move(defs)) , _slice(slice) , _mode(m) { } public: slice(schema_ptr schema, std::vector defs, statements::bound bound, bool inclusive, shared_ptr term, mode m = mode::cql) - : slice(schema, defs, term_slice::new_instance(bound, inclusive, term), m) + : slice(schema, defs, bounds_slice::new_instance(bound, inclusive, term.get() != nullptr ? std::make_optional(expr::to_expression(term)) : std::nullopt), m) { expression = expr::binary_operator{ column_definitions_as_tuple_constructor(defs), diff --git a/cql3/restrictions/single_column_restriction.hh b/cql3/restrictions/single_column_restriction.hh index fd7bcb871c..70ef921dc9 100644 --- a/cql3/restrictions/single_column_restriction.hh +++ b/cql3/restrictions/single_column_restriction.hh @@ -44,7 +44,7 @@ #include #include "cql3/restrictions/restriction.hh" -#include "cql3/restrictions/term_slice.hh" +#include "cql3/restrictions/bounds_slice.hh" #include "cql3/term.hh" #include "cql3/abstract_marker.hh" #include diff --git a/cql3/restrictions/token_restriction.hh b/cql3/restrictions/token_restriction.hh index d5a5fd8954..f8b9a4a2ac 100644 --- a/cql3/restrictions/token_restriction.hh +++ b/cql3/restrictions/token_restriction.hh @@ -43,7 +43,7 @@ #include "restriction.hh" #include "primary_key_restrictions.hh" #include "exceptions/exceptions.hh" -#include "term_slice.hh" +#include "bounds_slice.hh" #include "keys.hh" class column_definition; From 7f2ecf1aa25180816b4ab3815982d6ce313b38a7 Mon Sep 17 00:00:00 2001 From: Jan Ciolek Date: Thu, 7 Oct 2021 17:06:06 +0200 Subject: [PATCH 14/24] cql3: Remove term in multi_column_restrictions Replace all uses of term with expression in cql3/multi_column_restrictions Signed-off-by: Jan Ciolek --- cql3/multi_column_relation.hh | 10 +++++--- cql3/restrictions/multi_column_restriction.hh | 23 +++++++++++-------- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/cql3/multi_column_relation.hh b/cql3/multi_column_relation.hh index 4883b6ac11..8a6d9e5f65 100644 --- a/cql3/multi_column_relation.hh +++ b/cql3/multi_column_relation.hh @@ -160,7 +160,7 @@ protected: return cs->column_specification; }); auto t = to_term(col_specs, get_value(), db, schema->ks_name(), ctx); - return ::make_shared(schema, rs, t); + return ::make_shared(schema, rs, expr::to_expression(t)); } virtual shared_ptr new_IN_restriction(database& db, schema_ptr schema, @@ -180,9 +180,13 @@ protected: auto ts = to_terms(col_specs, raws, db, schema->ks_name(), ctx); // Convert a single-item IN restriction to an EQ restriction if (ts.size() == 1) { - return ::make_shared(schema, rs, std::move(ts[0])); + return ::make_shared(schema, rs, expr::to_expression(ts[0])); } - return ::make_shared(schema, rs, ts); + std::vector es; + for (const ::shared_ptr& t : ts) { + es.push_back(expr::to_expression(t)); + } + return ::make_shared(schema, rs, std::move(es)); } } diff --git a/cql3/restrictions/multi_column_restriction.hh b/cql3/restrictions/multi_column_restriction.hh index b38dad6bcd..9205291689 100644 --- a/cql3/restrictions/multi_column_restriction.hh +++ b/cql3/restrictions/multi_column_restriction.hh @@ -196,15 +196,15 @@ public: class multi_column_restriction::EQ final : public multi_column_restriction { private: - ::shared_ptr _value; + expr::expression _value; public: - EQ(schema_ptr schema, std::vector defs, ::shared_ptr value) + EQ(schema_ptr schema, std::vector defs, expr::expression value) : multi_column_restriction(schema, std::move(defs)) , _value(std::move(value)) { using namespace expr; expression = binary_operator{ - column_definitions_as_tuple_constructor(_column_defs), oper_t::EQ, expr::to_expression(_value)}; + column_definitions_as_tuple_constructor(_column_defs), oper_t::EQ, _value}; } virtual bool is_supported_by(const secondary_index::index& index) const override { @@ -309,11 +309,11 @@ public: */ class multi_column_restriction::IN_with_values final : public multi_column_restriction::IN { private: - std::vector<::shared_ptr> _values; + std::vector _value; public: - IN_with_values(schema_ptr schema, std::vector defs, std::vector<::shared_ptr> value) + IN_with_values(schema_ptr schema, std::vector defs, std::vector value) : multi_column_restriction::IN(schema, std::move(defs)) - , _values(std::move(value)) + , _value(std::move(value)) { std::vector column_types; column_types.reserve(defs.size()); @@ -325,11 +325,17 @@ public: data_type list_elements_type = tuple_type_impl::get_instance(std::move(column_types)); data_type in_list_type = list_type_impl::get_instance(std::move(list_elements_type), false); + expr::collection_constructor values_list { + .style = expr::collection_constructor::style_type::list, + .elements = _value, + .type = std::move(in_list_type) + }; + using namespace expr; expression = binary_operator{ column_definitions_as_tuple_constructor(_column_defs), oper_t::IN, - expr::to_expression(::make_shared(_values, std::move(in_list_type)))}; + std::move(values_list)}; } }; @@ -472,8 +478,7 @@ private: */ ::shared_ptr make_single_column_restriction(std::optional bound, bool inclusive, std::size_t column_pos, const managed_bytes_opt& value) const { - expr::expression e = expr::to_expression( - ::make_shared(cql3::raw_value::make_value(value), _column_defs[column_pos]->type)); + expr::expression e = expr::constant(cql3::raw_value::make_value(value), _column_defs[column_pos]->type); using namespace expr; if (!bound){ auto r = ::make_shared(*_column_defs[column_pos]); From 3b4dc39eb84df2f30b1642770599338655846644 Mon Sep 17 00:00:00 2001 From: Jan Ciolek Date: Thu, 7 Oct 2021 18:49:42 +0200 Subject: [PATCH 15/24] cql3: Remove term in relations Replace uses of term with expression in cql3/*relation.hh Signed-off-by: Jan Ciolek --- cql3/multi_column_relation.hh | 36 +++++++-------- cql3/relation.hh | 44 +++++++++---------- cql3/restrictions/multi_column_restriction.hh | 12 ++--- cql3/single_column_relation.cc | 41 +++++++++-------- cql3/single_column_relation.hh | 10 ++--- cql3/token_relation.cc | 16 +++---- cql3/token_relation.hh | 10 ++--- 7 files changed, 85 insertions(+), 84 deletions(-) diff --git a/cql3/multi_column_relation.hh b/cql3/multi_column_relation.hh index 8a6d9e5f65..1c691ad520 100644 --- a/cql3/multi_column_relation.hh +++ b/cql3/multi_column_relation.hh @@ -159,8 +159,8 @@ protected: std::transform(rs.begin(), rs.end(), col_specs.begin(), [] (auto cs) { return cs->column_specification; }); - auto t = to_term(col_specs, get_value(), db, schema->ks_name(), ctx); - return ::make_shared(schema, rs, expr::to_expression(t)); + auto e = to_expression(col_specs, get_value(), db, schema->ks_name(), ctx); + return ::make_shared(schema, rs, std::move(e)); } virtual shared_ptr new_IN_restriction(database& db, schema_ptr schema, @@ -171,20 +171,16 @@ protected: return cs->column_specification; }); if (_in_marker) { - auto t = to_term(col_specs, get_value(), db, schema->ks_name(), ctx); - auto as_abstract_marker = static_pointer_cast(t); - return ::make_shared(schema, rs, as_abstract_marker); + auto e = to_expression(col_specs, get_value(), db, schema->ks_name(), ctx); + auto bound_value_marker = expr::as(e); + return ::make_shared(schema, rs, std::move(bound_value_marker)); } else { std::vector raws(_in_values.size()); std::copy(_in_values.begin(), _in_values.end(), raws.begin()); - auto ts = to_terms(col_specs, raws, db, schema->ks_name(), ctx); + auto es = to_expressions(col_specs, raws, db, schema->ks_name(), ctx); // Convert a single-item IN restriction to an EQ restriction - if (ts.size() == 1) { - return ::make_shared(schema, rs, expr::to_expression(ts[0])); - } - std::vector es; - for (const ::shared_ptr& t : ts) { - es.push_back(expr::to_expression(t)); + if (es.size() == 1) { + return ::make_shared(schema, rs, std::move(es[0])); } return ::make_shared(schema, rs, std::move(es)); } @@ -198,8 +194,8 @@ protected: std::transform(rs.begin(), rs.end(), col_specs.begin(), [] (auto cs) { return cs->column_specification; }); - auto t = to_term(col_specs, get_value(), db, schema->ks_name(), ctx); - return ::make_shared(schema, rs, bound, inclusive, t, _mode); + auto e = to_expression(col_specs, get_value(), db, schema->ks_name(), ctx); + return ::make_shared(schema, rs, bound, inclusive, std::move(e), _mode); } virtual shared_ptr new_contains_restriction(database& db, schema_ptr schema, @@ -219,12 +215,12 @@ protected: return create_multi_column_relation(std::move(new_entities), _relation_type, _values_or_marker, _in_values, _in_marker); } - virtual shared_ptr to_term(const std::vector>& receivers, - const expr::expression& raw, database& db, const sstring& keyspace, - prepare_context& ctx) const override { - auto t = prepare_term_multi_column(raw, db, keyspace, receivers); - t->fill_prepare_context(ctx); - return t; + virtual expr::expression to_expression(const std::vector>& receivers, + const expr::expression& raw, database& db, const sstring& keyspace, + prepare_context& ctx) const override { + auto e = expr::to_expression(prepare_term_multi_column(raw, db, keyspace, receivers)); + expr::fill_prepare_context(e, ctx); + return e; } std::vector receivers(database& db, const schema& schema) { diff --git a/cql3/relation.hh b/cql3/relation.hh index cbc7b22eeb..3a3861c268 100644 --- a/cql3/relation.hh +++ b/cql3/relation.hh @@ -46,7 +46,6 @@ #include "prepare_context.hh" #include "restrictions/restriction.hh" #include "statements/bound.hh" -#include "term.hh" #include "expr/expression.hh" namespace cql3 { @@ -240,41 +239,42 @@ public: protected: /** - * Converts the specified Raw into a Term. + * Converts the specified Raw into an Expression. * @param receivers the columns to which the values must be associated at - * @param raw the raw term to convert + * @param raw the raw expression to convert * @param keyspace the keyspace name * @param boundNames the variables specification where to collect the bind variables * - * @return the Term corresponding to the specified Raw - * @throws InvalidRequestException if the Raw term is not valid + * @return the Expression corresponding to the specified Raw + * @throws InvalidRequestException if the Raw expression is not valid */ - virtual ::shared_ptr to_term(const std::vector>& receivers, - const expr::expression& raw, - database& db, - const sstring& keyspace, - prepare_context& ctx) const = 0; + virtual expr::expression to_expression(const std::vector>& receivers, + const expr::expression& raw, + database& db, + const sstring& keyspace, + prepare_context& ctx) const = 0; /** - * Converts the specified Raw terms into a Terms. + * Converts the specified Raw expressions into expressionss. * @param receivers the columns to which the values must be associated at - * @param raws the raw terms to convert + * @param raws the raw expressions to convert * @param keyspace the keyspace name * @param boundNames the variables specification where to collect the bind variables * - * @return the Terms corresponding to the specified Raw terms - * @throws InvalidRequestException if the Raw terms are not valid + * @return the Expressions corresponding to the specified Raw expressions + * @throws InvalidRequestException if the Raw expressions are not valid */ - std::vector<::shared_ptr> to_terms(const std::vector>& receivers, - const std::vector& raws, - database& db, - const sstring& keyspace, - prepare_context& ctx) const { - std::vector<::shared_ptr> terms; + std::vector to_expressions(const std::vector>& receivers, + const std::vector& raws, + database& db, + const sstring& keyspace, + prepare_context& ctx) const { + std::vector expressions; + expressions.reserve(raws.size()); for (const auto& r : raws) { - terms.emplace_back(to_term(receivers, r, db, keyspace, ctx)); + expressions.emplace_back(to_expression(receivers, r, db, keyspace, ctx)); } - return terms; + return expressions; } /** diff --git a/cql3/restrictions/multi_column_restriction.hh b/cql3/restrictions/multi_column_restriction.hh index 9205291689..288b3662a2 100644 --- a/cql3/restrictions/multi_column_restriction.hh +++ b/cql3/restrictions/multi_column_restriction.hh @@ -346,15 +346,15 @@ public: */ class multi_column_restriction::IN_with_marker final : public multi_column_restriction::IN { private: - shared_ptr _marker; + expr::bind_variable _marker; public: - IN_with_marker(schema_ptr schema, std::vector defs, shared_ptr marker) + IN_with_marker(schema_ptr schema, std::vector defs, expr::bind_variable marker) : IN(schema, std::move(defs)), _marker(marker) { using namespace expr; expression = binary_operator{ column_definitions_as_tuple_constructor(_column_defs), oper_t::IN, - expr::to_expression(std::move(marker))}; + expr::expression(std::move(marker))}; } }; @@ -370,13 +370,13 @@ class multi_column_restriction::slice final : public multi_column_restriction { , _mode(m) { } public: - slice(schema_ptr schema, std::vector defs, statements::bound bound, bool inclusive, shared_ptr term, mode m = mode::cql) - : slice(schema, defs, bounds_slice::new_instance(bound, inclusive, term.get() != nullptr ? std::make_optional(expr::to_expression(term)) : std::nullopt), m) + slice(schema_ptr schema, std::vector defs, statements::bound bound, bool inclusive, expr::expression e, mode m = mode::cql) + : slice(schema, defs, bounds_slice::new_instance(bound, inclusive, e), m) { expression = expr::binary_operator{ column_definitions_as_tuple_constructor(defs), expr::pick_operator(bound, inclusive), - expr::to_expression(std::move(term)), + std::move(e), m}; } diff --git a/cql3/single_column_relation.cc b/cql3/single_column_relation.cc index 9d356dc0cd..ddc5aa1112 100644 --- a/cql3/single_column_relation.cc +++ b/cql3/single_column_relation.cc @@ -54,17 +54,17 @@ using namespace cql3::expr; namespace cql3 { -::shared_ptr -single_column_relation::to_term(const std::vector>& receivers, - const expr::expression& raw, - database& db, - const sstring& keyspace, - prepare_context& ctx) const { +expression +single_column_relation::to_expression(const std::vector>& receivers, + const expr::expression& raw, + database& db, + const sstring& keyspace, + prepare_context& ctx) const { // TODO: optimize vector away, accept single column_specification assert(receivers.size() == 1); - auto term = prepare_term(raw, db, keyspace, receivers[0]); - term->fill_prepare_context(ctx); - return term; + auto expr = expr::to_expression(prepare_term(raw, db, keyspace, receivers[0])); + expr::fill_prepare_context(expr, ctx); + return expr; } ::shared_ptr @@ -76,13 +76,13 @@ single_column_relation::new_EQ_restriction(database& db, schema_ptr schema, prep } if (!_map_key) { auto r = ::make_shared(column_def); - auto e = expr::to_expression(to_term(to_receivers(*schema, column_def), *_value, db, schema->ks_name(), ctx)); + auto e = to_expression(to_receivers(*schema, column_def), *_value, db, schema->ks_name(), ctx); r->expression = binary_operator{column_value{&column_def}, expr::oper_t::EQ, std::move(e)}; return r; } auto&& receivers = to_receivers(*schema, column_def); - auto&& entry_key = expr::to_expression(to_term({receivers[0]}, *_map_key, db, schema->ks_name(), ctx)); - auto&& entry_value = expr::to_expression(to_term({receivers[1]}, *_value, db, schema->ks_name(), ctx)); + auto&& entry_key = to_expression({receivers[0]}, *_map_key, db, schema->ks_name(), ctx); + auto&& entry_value = to_expression({receivers[1]}, *_value, db, schema->ks_name(), ctx); auto r = make_shared(column_def); r->expression = binary_operator{ column_value(&column_def, std::move(entry_key)), oper_t::EQ, std::move(entry_value)}; @@ -100,23 +100,28 @@ single_column_relation::new_IN_restriction(database& db, schema_ptr schema, prep auto receivers = to_receivers(*schema, column_def); assert(_in_values.empty() || !_value); if (_value) { - auto e = expr::to_expression(to_term(receivers, *_value, db, schema->ks_name(), ctx)); + auto e = to_expression(receivers, *_value, db, schema->ks_name(), ctx); auto r = ::make_shared(column_def); r->expression = binary_operator{column_value{&column_def}, expr::oper_t::IN, std::move(e)}; return r; } - auto terms = to_terms(receivers, _in_values, db, schema->ks_name(), ctx); + auto expressions = to_expressions(receivers, _in_values, db, schema->ks_name(), ctx); // Convert a single-item IN restriction to an EQ restriction - if (terms.size() == 1) { + if (expressions.size() == 1) { auto r = ::make_shared(column_def); - r->expression = binary_operator{column_value{&column_def}, expr::oper_t::EQ, expr::to_expression(std::move(terms[0]))}; + r->expression = binary_operator{column_value{&column_def}, expr::oper_t::EQ, std::move(expressions[0])}; return r; } auto r = ::make_shared(column_def); + collection_constructor list_value { + .style = collection_constructor::style_type::list, + .elements = std::move(expressions), + .type = list_type_impl::get_instance(column_def.type, false), + }; r->expression = binary_operator{ column_value{&column_def}, expr::oper_t::IN, - expr::to_expression(::make_shared(std::move(terms), list_type_impl::get_instance(column_def.type, false)))}; + std::move(list_value)}; return r; } @@ -128,7 +133,7 @@ single_column_relation::new_LIKE_restriction( throw exceptions::invalid_request_exception( format("LIKE is allowed only on string types, which {} is not", column_def.name_as_text())); } - auto e = expr::to_expression(to_term(to_receivers(*schema, column_def), *_value, db, schema->ks_name(), ctx)); + auto e = to_expression(to_receivers(*schema, column_def), *_value, db, schema->ks_name(), ctx); auto r = ::make_shared(column_def); r->expression = binary_operator{column_value{&column_def}, expr::oper_t::LIKE, std::move(e)}; return r; diff --git a/cql3/single_column_relation.hh b/cql3/single_column_relation.hh index b8c2746593..6fecda2e5e 100644 --- a/cql3/single_column_relation.hh +++ b/cql3/single_column_relation.hh @@ -115,9 +115,9 @@ public: } protected: - virtual ::shared_ptr to_term(const std::vector>& receivers, - const expr::expression& raw, database& db, const sstring& keyspace, - prepare_context& ctx) const override; + virtual expr::expression to_expression(const std::vector>& receivers, + const expr::expression& raw, database& db, const sstring& keyspace, + prepare_context& ctx) const override; #if 0 public SingleColumnRelation withNonStrictOperator() @@ -169,7 +169,7 @@ protected: throw exceptions::invalid_request_exception("Slice restrictions are not supported on duration columns"); } - auto e = expr::to_expression(to_term(to_receivers(*schema, column_def), *_value, db, schema->ks_name(), ctx)); + auto e = to_expression(to_receivers(*schema, column_def), *_value, db, schema->ks_name(), ctx); auto r = ::make_shared(column_def); r->expression = expr::binary_operator{expr::column_value{&column_def}, _relation_type, std::move(e)}; return r; @@ -179,7 +179,7 @@ protected: prepare_context& ctx, bool is_key) override { auto&& column_def = to_column_definition(*schema, *_entity); - auto e = expr::to_expression(to_term(to_receivers(*schema, column_def), *_value, db, schema->ks_name(), ctx)); + auto e = to_expression(to_receivers(*schema, column_def), *_value, db, schema->ks_name(), ctx); auto r = ::make_shared(column_def); r->expression = expr::binary_operator{ expr::column_value{&column_def}, is_key ? expr::oper_t::CONTAINS_KEY : expr::oper_t::CONTAINS, std::move(e)}; diff --git a/cql3/token_relation.cc b/cql3/token_relation.cc index 0f8ad8662c..284ee400ff 100644 --- a/cql3/token_relation.cc +++ b/cql3/token_relation.cc @@ -83,8 +83,8 @@ std::vector> cql3::token_relation::to_ database& db, schema_ptr schema, prepare_context& ctx) { auto column_defs = get_column_definitions(*schema); - auto e = expr::to_expression(to_term(to_receivers(*schema, column_defs), _value, db, - schema->ks_name(), ctx)); + auto e = to_expression(to_receivers(*schema, column_defs), _value, db, + schema->ks_name(), ctx); auto r = ::make_shared(column_defs); using namespace expr; r->expression = binary_operator{token{}, oper_t::EQ, std::move(e)}; @@ -105,8 +105,8 @@ std::vector> cql3::token_relation::to_ statements::bound bound, bool inclusive) { auto column_defs = get_column_definitions(*schema); - auto e = expr::to_expression(to_term(to_receivers(*schema, column_defs), _value, db, - schema->ks_name(), ctx)); + auto e = to_expression(to_receivers(*schema, column_defs), _value, db, + schema->ks_name(), ctx); auto r = ::make_shared(column_defs); using namespace expr; r->expression = binary_operator{token{}, pick_operator(bound, inclusive), std::move(e)}; @@ -130,13 +130,13 @@ sstring cql3::token_relation::to_string() const { return format("token({}) {} {}", join(", ", _entities), get_operator(), _value); } -::shared_ptr cql3::token_relation::to_term( +cql3::expr::expression cql3::token_relation::to_expression( const std::vector>& receivers, const expr::expression& raw, database& db, const sstring& keyspace, prepare_context& ctx) const { - auto term = expr::prepare_term(raw, db, keyspace, receivers.front()); - term->fill_prepare_context(ctx); - return term; + auto e = expr::to_expression(expr::prepare_term(raw, db, keyspace, receivers.front())); + expr::fill_prepare_context(e, ctx); + return e; } ::shared_ptr cql3::token_relation::maybe_rename_identifier(const cql3::column_identifier::raw& from, cql3::column_identifier::raw to) { diff --git a/cql3/token_relation.hh b/cql3/token_relation.hh index 8812a46faf..ba720832c7 100644 --- a/cql3/token_relation.hh +++ b/cql3/token_relation.hh @@ -123,11 +123,11 @@ public: sstring to_string() const override; protected: - ::shared_ptr to_term(const std::vector>& receivers, - const expr::expression& raw, - database& db, - const sstring& keyspace, - prepare_context& ctx) const override; + expr::expression to_expression(const std::vector>& receivers, + const expr::expression& raw, + database& db, + const sstring& keyspace, + prepare_context& ctx) const override; }; } From 890c8f4026fb5657cc774dcb869d5487d413f0d3 Mon Sep 17 00:00:00 2001 From: Jan Ciolek Date: Thu, 7 Oct 2021 19:59:04 +0200 Subject: [PATCH 16/24] cql3: Remove term in operations Replace term with expression in cql3/operation and its children. Signed-off-by: Jan Ciolek --- cql3/constants.hh | 14 +++---- cql3/lists.cc | 42 +++++++++++-------- cql3/lists.hh | 22 +++++----- cql3/maps.cc | 22 +++++----- cql3/maps.hh | 18 ++++---- cql3/operation.cc | 64 ++++++++++++++--------------- cql3/operation.hh | 14 +++---- cql3/sets.cc | 8 ++-- cql3/sets.hh | 16 ++++---- cql3/statements/update_statement.cc | 2 +- cql3/user_types.cc | 8 ++-- cql3/user_types.hh | 6 +-- 12 files changed, 122 insertions(+), 114 deletions(-) diff --git a/cql3/constants.hh b/cql3/constants.hh index 2b769bd1ce..2491de487b 100644 --- a/cql3/constants.hh +++ b/cql3/constants.hh @@ -122,8 +122,8 @@ public: using operation::operation; virtual void execute(mutation& m, const clustering_key_prefix& prefix, const update_parameters& params) override { - auto value = expr::evaluate_to_raw_view(_t, params._options); - execute(m, prefix, params, column, std::move(value)); + auto value = expr::evaluate(*_e, params._options); + execute(m, prefix, params, column, value.view()); } static void execute(mutation& m, const clustering_key_prefix& prefix, const update_parameters& params, const column_definition& column, cql3::raw_value_view value) { @@ -139,13 +139,13 @@ public: using operation::operation; virtual void execute(mutation& m, const clustering_key_prefix& prefix, const update_parameters& params) override { - auto value = expr::evaluate_to_raw_view(_t, params._options); + auto value = expr::evaluate(*_e, params._options); if (value.is_null()) { throw exceptions::invalid_request_exception("Invalid null value for counter increment"); } else if (value.is_unset_value()) { return; } - auto increment = value.deserialize(*long_type); + auto increment = value.view().deserialize(*long_type); m.set_cell(prefix, column, params.make_counter_update_cell(increment)); } }; @@ -154,13 +154,13 @@ public: using operation::operation; virtual void execute(mutation& m, const clustering_key_prefix& prefix, const update_parameters& params) override { - auto value = expr::evaluate_to_raw_view(_t, params._options); + auto value = expr::evaluate(*_e, params._options); if (value.is_null()) { throw exceptions::invalid_request_exception("Invalid null value for counter increment"); } else if (value.is_unset_value()) { return; } - auto increment = value.deserialize(*long_type); + auto increment = value.view().deserialize(*long_type); if (increment == std::numeric_limits::min()) { throw exceptions::invalid_request_exception(format("The negation of {:d} overflows supported counter precision (signed 8 bytes integer)", increment)); } @@ -171,7 +171,7 @@ public: class deleter : public operation { public: deleter(const column_definition& column) - : operation(column, {}) + : operation(column, std::nullopt) { } virtual void execute(mutation& m, const clustering_key_prefix& prefix, const update_parameters& params) override; diff --git a/cql3/lists.cc b/cql3/lists.cc index 3d4ddc1704..eb6f75aa85 100644 --- a/cql3/lists.cc +++ b/cql3/lists.cc @@ -202,7 +202,7 @@ expr::expression lists::marker::to_expression() { void lists::setter::execute(mutation& m, const clustering_key_prefix& prefix, const update_parameters& params) { - auto value = expr::evaluate(_t, params._options); + auto value = expr::evaluate(*_e, params._options); execute(m, prefix, params, column, std::move(value)); } @@ -227,9 +227,9 @@ lists::setter_by_index::requires_read() const { } void -lists::setter_by_index::fill_prepare_context(prepare_context& ctx) const { +lists::setter_by_index::fill_prepare_context(prepare_context& ctx) { operation::fill_prepare_context(ctx); - _idx->fill_prepare_context(ctx); + expr::fill_prepare_context(_idx, ctx); } void @@ -237,19 +237,19 @@ lists::setter_by_index::execute(mutation& m, const clustering_key_prefix& prefix // we should not get here for frozen lists assert(column.type->is_multi_cell()); // "Attempted to set an individual element on a frozen list"; - auto index = expr::evaluate_to_raw_view(_idx, params._options); + auto index = expr::evaluate(_idx, params._options); if (index.is_null()) { throw exceptions::invalid_request_exception("Invalid null value for list index"); } if (index.is_unset_value()) { throw exceptions::invalid_request_exception("Invalid unset value for list index"); } - auto value = expr::evaluate_to_raw_view(_t, params._options); + auto value = expr::evaluate(*_e, params._options); if (value.is_unset_value()) { return; } - auto idx = index.deserialize(*int32_type); + auto idx = index.view().deserialize(*int32_type); auto&& existing_list_opt = params.get_prefetched_list(m.key(), prefix, column); if (!existing_list_opt) { throw exceptions::invalid_request_exception("Attempted to set an element on a list which is null"); @@ -266,11 +266,11 @@ lists::setter_by_index::execute(mutation& m, const clustering_key_prefix& prefix bytes eidx = eidx_dv.type()->decompose(eidx_dv); collection_mutation_description mut; mut.cells.reserve(1); - if (!value) { + if (value.is_null()) { mut.cells.emplace_back(std::move(eidx), params.make_dead_cell()); } else { mut.cells.emplace_back(std::move(eidx), - params.make_cell(*ltype->value_comparator(), value, atomic_cell::collection_member::yes)); + params.make_cell(*ltype->value_comparator(), value.view(), atomic_cell::collection_member::yes)); } m.set_cell(prefix, column, mut.serialize(*ltype)); @@ -286,22 +286,28 @@ lists::setter_by_uuid::execute(mutation& m, const clustering_key_prefix& prefix, // we should not get here for frozen lists assert(column.type->is_multi_cell()); // "Attempted to set an individual element on a frozen list"; - auto index = expr::evaluate_to_raw_view(_idx, params._options); - auto value = expr::evaluate_to_raw_view(_t, params._options); + auto index = expr::evaluate(_idx, params._options); + auto value = expr::evaluate(*_e, params._options); - if (!index) { + if (index.is_null()) { throw exceptions::invalid_request_exception("Invalid null value for list index"); } + if (index.is_unset_value()) { + throw exceptions::invalid_request_exception("Invalid unset value for list index"); + } + auto ltype = static_cast(column.type.get()); collection_mutation_description mut; mut.cells.reserve(1); - if (!value) { - mut.cells.emplace_back(to_bytes(index), params.make_dead_cell()); + if (value.is_null()) { + mut.cells.emplace_back(std::move(index.value).to_bytes(), params.make_dead_cell()); } else { - mut.cells.emplace_back(to_bytes(index), params.make_cell(*ltype->value_comparator(), value, atomic_cell::collection_member::yes)); + mut.cells.emplace_back( + std::move(index.value).to_bytes(), + params.make_cell(*ltype->value_comparator(), value.view(), atomic_cell::collection_member::yes)); } m.set_cell(prefix, column, mut.serialize(*ltype)); @@ -309,7 +315,7 @@ lists::setter_by_uuid::execute(mutation& m, const clustering_key_prefix& prefix, void lists::appender::execute(mutation& m, const clustering_key_prefix& prefix, const update_parameters& params) { - const expr::constant value = expr::evaluate(_t, params._options); + const expr::constant value = expr::evaluate(*_e, params._options); if (value.is_unset_value()) { return; } @@ -363,7 +369,7 @@ lists::do_append(const expr::constant& list_value, void lists::prepender::execute(mutation& m, const clustering_key_prefix& prefix, const update_parameters& params) { assert(column.type->is_multi_cell()); // "Attempted to prepend to a frozen list"; - expr::constant lvalue = expr::evaluate(_t, params._options); + expr::constant lvalue = expr::evaluate(*_e, params._options); if (lvalue.is_null_or_unset()) { return; } @@ -419,7 +425,7 @@ lists::discarder::execute(mutation& m, const clustering_key_prefix& prefix, cons auto&& existing_list = params.get_prefetched_list(m.key(), prefix, column); // We want to call bind before possibly returning to reject queries where the value provided is not a list. - expr::constant lvalue = expr::evaluate(_t, params._options); + expr::constant lvalue = expr::evaluate(*_e, params._options); if (!existing_list) { return; @@ -468,7 +474,7 @@ lists::discarder_by_index::requires_read() const { void lists::discarder_by_index::execute(mutation& m, const clustering_key_prefix& prefix, const update_parameters& params) { assert(column.type->is_multi_cell()); // "Attempted to delete an item by index from a frozen list"; - expr::constant index = expr::evaluate(_t, params._options); + expr::constant index = expr::evaluate(*_e, params._options); if (index.is_null()) { throw exceptions::invalid_request_exception("Invalid null value for list index"); } diff --git a/cql3/lists.hh b/cql3/lists.hh index 3f67492fdb..63711ebbfe 100644 --- a/cql3/lists.hh +++ b/cql3/lists.hh @@ -118,8 +118,8 @@ public: public: class setter : public operation { public: - setter(const column_definition& column, shared_ptr t) - : operation(column, std::move(t)) { + setter(const column_definition& column, expr::expression e) + : operation(column, std::move(e)) { } virtual void execute(mutation& m, const clustering_key_prefix& prefix, const update_parameters& params) override; static void execute(mutation& m, const clustering_key_prefix& prefix, const update_parameters& params, const column_definition& column, const expr::constant& value); @@ -127,20 +127,20 @@ public: class setter_by_index : public operation { protected: - shared_ptr _idx; + expr::expression _idx; public: - setter_by_index(const column_definition& column, shared_ptr idx, shared_ptr t) - : operation(column, std::move(t)), _idx(std::move(idx)) { + setter_by_index(const column_definition& column, expr::expression idx, expr::expression e) + : operation(column, std::move(e)), _idx(std::move(idx)) { } virtual bool requires_read() const override; - virtual void fill_prepare_context(prepare_context& ctx) const override; + virtual void fill_prepare_context(prepare_context& ctx) override; virtual void execute(mutation& m, const clustering_key_prefix& prefix, const update_parameters& params) override; }; class setter_by_uuid : public setter_by_index { public: - setter_by_uuid(const column_definition& column, shared_ptr idx, shared_ptr t) - : setter_by_index(column, std::move(idx), std::move(t)) { + setter_by_uuid(const column_definition& column, expr::expression idx, expr::expression e) + : setter_by_index(column, std::move(idx), std::move(e)) { } virtual bool requires_read() const override; virtual void execute(mutation& m, const clustering_key_prefix& prefix, const update_parameters& params) override; @@ -166,8 +166,8 @@ public: class discarder : public operation { public: - discarder(const column_definition& column, shared_ptr t) - : operation(column, std::move(t)) { + discarder(const column_definition& column, expr::expression e) + : operation(column, std::move(e)) { } virtual bool requires_read() const override; virtual void execute(mutation& m, const clustering_key_prefix& prefix, const update_parameters& params) override; @@ -175,7 +175,7 @@ public: class discarder_by_index : public operation { public: - discarder_by_index(const column_definition& column, shared_ptr idx) + discarder_by_index(const column_definition& column, expr::expression idx) : operation(column, std::move(idx)) { } virtual bool requires_read() const override; diff --git a/cql3/maps.cc b/cql3/maps.cc index a36bb24378..71f501065c 100644 --- a/cql3/maps.cc +++ b/cql3/maps.cc @@ -216,7 +216,7 @@ expr::expression maps::marker::to_expression() { void maps::setter::execute(mutation& m, const clustering_key_prefix& row_key, const update_parameters& params) { - expr::constant value = expr::evaluate(_t, params._options); + expr::constant value = expr::evaluate(*_e, params._options); execute(m, row_key, params, column, value); } @@ -235,30 +235,32 @@ maps::setter::execute(mutation& m, const clustering_key_prefix& row_key, const u } void -maps::setter_by_key::fill_prepare_context(prepare_context& ctx) const { +maps::setter_by_key::fill_prepare_context(prepare_context& ctx) { operation::fill_prepare_context(ctx); - _k->fill_prepare_context(ctx); + expr::fill_prepare_context(_k, ctx); } void maps::setter_by_key::execute(mutation& m, const clustering_key_prefix& prefix, const update_parameters& params) { using exceptions::invalid_request_exception; assert(column.type->is_multi_cell()); // "Attempted to set a value for a single key on a frozen map"m - auto key = expr::evaluate_to_raw_view(_k, params._options); - auto value = expr::evaluate_to_raw_view(_t, params._options); + auto key = expr::evaluate(_k, params._options); + auto value = expr::evaluate(*_e, params._options); if (value.is_unset_value()) { return; } if (key.is_unset_value()) { throw invalid_request_exception("Invalid unset map key"); } - if (!key) { + if (key.is_null()) { throw invalid_request_exception("Invalid null map key"); } auto ctype = static_cast(column.type.get()); - auto avalue = value ? params.make_cell(*ctype->get_values_type(), value, atomic_cell::collection_member::yes) : params.make_dead_cell(); + auto avalue = !value.is_null() ? + params.make_cell(*ctype->get_values_type(), value.view(), atomic_cell::collection_member::yes) + : params.make_dead_cell(); collection_mutation_description update; - update.cells.emplace_back(to_bytes(key), std::move(avalue)); + update.cells.emplace_back(std::move(key.value).to_bytes(), std::move(avalue)); m.set_cell(prefix, column, update.serialize(*ctype)); } @@ -266,7 +268,7 @@ maps::setter_by_key::execute(mutation& m, const clustering_key_prefix& prefix, c void maps::putter::execute(mutation& m, const clustering_key_prefix& prefix, const update_parameters& params) { assert(column.type->is_multi_cell()); // "Attempted to add items to a frozen map"; - expr::constant value = expr::evaluate(_t, params._options); + expr::constant value = expr::evaluate(*_e, params._options); if (!value.is_unset_value()) { do_put(m, prefix, params, value, column); } @@ -301,7 +303,7 @@ maps::do_put(mutation& m, const clustering_key_prefix& prefix, const update_para void maps::discarder_by_key::execute(mutation& m, const clustering_key_prefix& prefix, const update_parameters& params) { assert(column.type->is_multi_cell()); // "Attempted to delete a single key in a frozen map"; - expr::constant key = expr::evaluate(_t, params._options); + expr::constant key = expr::evaluate(*_e, params._options); if (key.is_null()) { throw exceptions::invalid_request_exception("Invalid null map key"); } diff --git a/cql3/maps.hh b/cql3/maps.hh index eddd4da434..63c92b8c51 100644 --- a/cql3/maps.hh +++ b/cql3/maps.hh @@ -99,8 +99,8 @@ public: class setter : public operation { public: - setter(const column_definition& column, shared_ptr t) - : operation(column, std::move(t)) { + setter(const column_definition& column, expr::expression e) + : operation(column, std::move(e)) { } virtual void execute(mutation& m, const clustering_key_prefix& row_key, const update_parameters& params) override; @@ -108,19 +108,19 @@ public: }; class setter_by_key : public operation { - const shared_ptr _k; + expr::expression _k; public: - setter_by_key(const column_definition& column, shared_ptr k, shared_ptr t) - : operation(column, std::move(t)), _k(std::move(k)) { + setter_by_key(const column_definition& column, expr::expression k, expr::expression e) + : operation(column, std::move(e)), _k(std::move(k)) { } - virtual void fill_prepare_context(prepare_context& ctx) const override; + virtual void fill_prepare_context(prepare_context& ctx) override; virtual void execute(mutation& m, const clustering_key_prefix& prefix, const update_parameters& params) override; }; class putter : public operation { public: - putter(const column_definition& column, shared_ptr t) - : operation(column, std::move(t)) { + putter(const column_definition& column, expr::expression e) + : operation(column, std::move(e)) { } virtual void execute(mutation& m, const clustering_key_prefix& prefix, const update_parameters& params) override; }; @@ -130,7 +130,7 @@ public: class discarder_by_key : public operation { public: - discarder_by_key(const column_definition& column, shared_ptr k) + discarder_by_key(const column_definition& column, expr::expression k) : operation(column, std::move(k)) { } virtual void execute(mutation& m, const clustering_key_prefix& prefix, const update_parameters& params) override; diff --git a/cql3/operation.cc b/cql3/operation.cc index 4fbc3058c4..7ffb57a9f0 100644 --- a/cql3/operation.cc +++ b/cql3/operation.cc @@ -68,20 +68,20 @@ operation::set_element::prepare(database& db, const sstring& keyspace, const col } if (rtype->get_kind() == abstract_type::kind::list) { - auto&& lval = prepare_term(_value, db, keyspace, lists::value_spec_of(*receiver.column_specification)); + auto&& lval = expr::to_expression(prepare_term(_value, db, keyspace, lists::value_spec_of(*receiver.column_specification))); if (_by_uuid) { - auto&& idx = prepare_term(_selector, db, keyspace, lists::uuid_index_spec_of(*receiver.column_specification)); - return make_shared(receiver, idx, lval); + auto&& idx = expr::to_expression(prepare_term(_selector, db, keyspace, lists::uuid_index_spec_of(*receiver.column_specification))); + return make_shared(receiver, std::move(idx), std::move(lval)); } else { - auto&& idx = prepare_term(_selector, db, keyspace, lists::index_spec_of(*receiver.column_specification)); - return make_shared(receiver, idx, lval); + auto&& idx = expr::to_expression(prepare_term(_selector, db, keyspace, lists::index_spec_of(*receiver.column_specification))); + return make_shared(receiver, std::move(idx), std::move(lval)); } } else if (rtype->get_kind() == abstract_type::kind::set) { throw invalid_request_exception(format("Invalid operation ({}) for set column {}", to_string(receiver), receiver.name())); } else if (rtype->get_kind() == abstract_type::kind::map) { - auto key = prepare_term(_selector, db, keyspace, maps::key_spec_of(*receiver.column_specification)); - auto mval = prepare_term(_value, db, keyspace, maps::value_spec_of(*receiver.column_specification)); - return make_shared(receiver, key, mval); + auto key = expr::to_expression(prepare_term(_selector, db, keyspace, maps::key_spec_of(*receiver.column_specification))); + auto mval = expr::to_expression(prepare_term(_value, db, keyspace, maps::value_spec_of(*receiver.column_specification))); + return make_shared(receiver, std::move(key), std::move(mval)); } abort(); } @@ -115,7 +115,7 @@ operation::set_field::prepare(database& db, const sstring& keyspace, const colum format("UDT column {} does not have a field named {}", receiver.name_as_text(), *_field)); } - auto val = prepare_term(_value, db, keyspace, user_types::field_spec_of(*receiver.column_specification, *idx)); + auto val = expr::to_expression(prepare_term(_value, db, keyspace, user_types::field_spec_of(*receiver.column_specification, *idx))); return make_shared(receiver, *idx, std::move(val)); } @@ -161,24 +161,24 @@ operation::addition::to_string(const column_definition& receiver) const { shared_ptr operation::addition::prepare(database& db, const sstring& keyspace, const column_definition& receiver) const { - auto v = prepare_term(_value, db, keyspace, receiver.column_specification); + auto v = expr::to_expression(prepare_term(_value, db, keyspace, receiver.column_specification)); auto ctype = dynamic_pointer_cast(receiver.type); if (!ctype) { if (!receiver.is_counter()) { throw exceptions::invalid_request_exception(format("Invalid operation ({}) for non counter column {}", to_string(receiver), receiver.name())); } - return make_shared(receiver, v); + return make_shared(receiver, std::move(v)); } else if (!ctype->is_multi_cell()) { throw exceptions::invalid_request_exception(format("Invalid operation ({}) for frozen collection column {}", to_string(receiver), receiver.name())); } if (ctype->get_kind() == abstract_type::kind::list) { - return make_shared(receiver, v); + return make_shared(receiver, std::move(v)); } else if (ctype->get_kind() == abstract_type::kind::set) { - return make_shared(receiver, v); + return make_shared(receiver, std::move(v)); } else if (ctype->get_kind() == abstract_type::kind::map) { - return make_shared(receiver, v); + return make_shared(receiver, std::move(v)); } else { abort(); } @@ -201,8 +201,8 @@ operation::subtraction::prepare(database& db, const sstring& keyspace, const col if (!receiver.is_counter()) { throw exceptions::invalid_request_exception(format("Invalid operation ({}) for non counter column {}", to_string(receiver), receiver.name())); } - auto v = prepare_term(_value, db, keyspace, receiver.column_specification); - return make_shared(receiver, v); + auto v = expr::to_expression(prepare_term(_value, db, keyspace, receiver.column_specification)); + return make_shared(receiver, std::move(v)); } if (!ctype->is_multi_cell()) { throw exceptions::invalid_request_exception( @@ -210,9 +210,9 @@ operation::subtraction::prepare(database& db, const sstring& keyspace, const col } if (ctype->get_kind() == abstract_type::kind::list) { - return make_shared(receiver, prepare_term(_value, db, keyspace, receiver.column_specification)); + return make_shared(receiver, expr::to_expression(prepare_term(_value, db, keyspace, receiver.column_specification))); } else if (ctype->get_kind() == abstract_type::kind::set) { - return make_shared(receiver, prepare_term(_value, db, keyspace, receiver.column_specification)); + return make_shared(receiver, expr::to_expression(prepare_term(_value, db, keyspace, receiver.column_specification))); } else if (ctype->get_kind() == abstract_type::kind::map) { auto&& mtype = dynamic_pointer_cast(ctype); // The value for a map subtraction is actually a set @@ -221,7 +221,7 @@ operation::subtraction::prepare(database& db, const sstring& keyspace, const col receiver.column_specification->cf_name, receiver.column_specification->name, set_type_impl::get_instance(mtype->get_keys_type(), false)); - return ::make_shared(receiver, prepare_term(_value, db, keyspace, std::move(vr))); + return ::make_shared(receiver, expr::to_expression(prepare_term(_value, db, keyspace, std::move(vr)))); } abort(); } @@ -238,7 +238,7 @@ operation::prepend::to_string(const column_definition& receiver) const { shared_ptr operation::prepend::prepare(database& db, const sstring& keyspace, const column_definition& receiver) const { - auto v = prepare_term(_value, db, keyspace, receiver.column_specification); + auto v = expr::to_expression(prepare_term(_value, db, keyspace, receiver.column_specification)); if (!dynamic_cast(receiver.type.get())) { throw exceptions::invalid_request_exception(format("Invalid operation ({}) for non list column {}", to_string(receiver), receiver.name())); @@ -257,7 +257,7 @@ operation::prepend::is_compatible_with(const std::unique_ptr& other) ::shared_ptr operation::set_value::prepare(database& db, const sstring& keyspace, const column_definition& receiver) const { - auto v = prepare_term(_value, db, keyspace, receiver.column_specification); + auto v = expr::to_expression(prepare_term(_value, db, keyspace, receiver.column_specification)); if (receiver.type->is_counter()) { throw exceptions::invalid_request_exception(format("Cannot set the value of counter column {} (counters can only be incremented/decremented, not set)", receiver.name_as_text())); @@ -266,21 +266,21 @@ operation::set_value::prepare(database& db, const sstring& keyspace, const colum if (receiver.type->is_collection()) { auto k = receiver.type->get_kind(); if (k == abstract_type::kind::list) { - return make_shared(receiver, v); + return make_shared(receiver, std::move(v)); } else if (k == abstract_type::kind::set) { - return make_shared(receiver, v); + return make_shared(receiver, std::move(v)); } else if (k == abstract_type::kind::map) { - return make_shared(receiver, v); + return make_shared(receiver, std::move(v)); } else { abort(); } } if (receiver.type->is_user_type()) { - return make_shared(receiver, v); + return make_shared(receiver, std::move(v)); } - return ::make_shared(receiver, v); + return ::make_shared(receiver, std::move(v)); } ::shared_ptr @@ -295,7 +295,7 @@ operation::set_counter_value_from_tuple_list::prepare(database& db, const sstrin // We need to fake a column of list> to prepare the value term auto & os = receiver.column_specification; auto spec = make_lw_shared(os->ks_name, os->cf_name, os->name, counter_tuple_list_type); - auto v = prepare_term(_value, db, keyspace, spec); + auto v = expr::to_expression(prepare_term(_value, db, keyspace, spec)); // Will not be used elsewhere, so make it local. class counter_setter : public operation { @@ -306,7 +306,7 @@ operation::set_counter_value_from_tuple_list::prepare(database& db, const sstrin return true; } void execute(mutation& m, const clustering_key_prefix& prefix, const update_parameters& params) override { - expr::constant list_value = expr::evaluate(_t, params._options); + expr::constant list_value = expr::evaluate(*_e, params._options); if (list_value.is_null()) { throw std::invalid_argument("Invalid input data to counter set"); } @@ -356,7 +356,7 @@ operation::set_counter_value_from_tuple_list::prepare(database& db, const sstrin } }; - return make_shared(receiver, v); + return make_shared(receiver, std::move(v)); }; bool @@ -380,13 +380,13 @@ operation::element_deletion::prepare(database& db, const sstring& keyspace, cons } auto ctype = static_pointer_cast(receiver.type); if (ctype->get_kind() == abstract_type::kind::list) { - auto&& idx = prepare_term(_element, db, keyspace, lists::index_spec_of(*receiver.column_specification)); + auto&& idx = expr::to_expression(prepare_term(_element, db, keyspace, lists::index_spec_of(*receiver.column_specification))); return make_shared(receiver, std::move(idx)); } else if (ctype->get_kind() == abstract_type::kind::set) { - auto&& elt = prepare_term(_element, db, keyspace, sets::value_spec_of(*receiver.column_specification)); + auto&& elt = expr::to_expression(prepare_term(_element, db, keyspace, sets::value_spec_of(*receiver.column_specification))); return make_shared(receiver, std::move(elt)); } else if (ctype->get_kind() == abstract_type::kind::map) { - auto&& key = prepare_term(_element, db, keyspace, maps::key_spec_of(*receiver.column_specification)); + auto&& key = expr::to_expression(prepare_term(_element, db, keyspace, maps::key_spec_of(*receiver.column_specification))); return make_shared(receiver, std::move(key)); } abort(); diff --git a/cql3/operation.hh b/cql3/operation.hh index 772f2aaa4b..9dd9eb26b6 100644 --- a/cql3/operation.hh +++ b/cql3/operation.hh @@ -77,14 +77,14 @@ public: const column_definition& column; protected: - // Term involved in the operation. In theory this should not be here since some operation + // Value involved in the operation. In theory this should not be here since some operation // may require none of more than one term, but most need 1 so it simplify things a bit. - const ::shared_ptr _t; + std::optional _e; public: - operation(const column_definition& column_, ::shared_ptr t) + operation(const column_definition& column_, std::optional e) : column{column_} - , _t{t} + , _e(std::move(e)) { } virtual ~operation() {} @@ -107,9 +107,9 @@ public: * @param meta the list of column specification where to collect the * bind variables of this term in. */ - virtual void fill_prepare_context(prepare_context& ctx) const { - if (_t) { - _t->fill_prepare_context(ctx); + virtual void fill_prepare_context(prepare_context& ctx) { + if (_e.has_value()) { + expr::fill_prepare_context(*_e, ctx); } } diff --git a/cql3/sets.cc b/cql3/sets.cc index 1ee69ac019..989809e10c 100644 --- a/cql3/sets.cc +++ b/cql3/sets.cc @@ -176,7 +176,7 @@ expr::expression sets::marker::to_expression() { void sets::setter::execute(mutation& m, const clustering_key_prefix& row_key, const update_parameters& params) { - expr::constant value = expr::evaluate(_t, params._options); + expr::constant value = expr::evaluate(*_e, params._options); execute(m, row_key, params, column, std::move(value)); } @@ -196,7 +196,7 @@ sets::setter::execute(mutation& m, const clustering_key_prefix& row_key, const u void sets::adder::execute(mutation& m, const clustering_key_prefix& row_key, const update_parameters& params) { - const expr::constant value = expr::evaluate(_t, params._options); + const expr::constant value = expr::evaluate(*_e, params._options); if (value.is_unset_value()) { return; } @@ -239,7 +239,7 @@ void sets::discarder::execute(mutation& m, const clustering_key_prefix& row_key, const update_parameters& params) { assert(column.type->is_multi_cell()); // "Attempted to remove items from a frozen set"; - expr::constant svalue = expr::evaluate(_t, params._options); + expr::constant svalue = expr::evaluate(*_e, params._options); if (svalue.is_null_or_unset()) { return; } @@ -257,7 +257,7 @@ sets::discarder::execute(mutation& m, const clustering_key_prefix& row_key, cons void sets::element_discarder::execute(mutation& m, const clustering_key_prefix& row_key, const update_parameters& params) { assert(column.type->is_multi_cell() && "Attempted to remove items from a frozen set"); - expr::constant elt = expr::evaluate(_t, params._options); + expr::constant elt = expr::evaluate(*_e, params._options); if (elt.is_null()) { throw exceptions::invalid_request_exception("Invalid null set element"); } diff --git a/cql3/sets.hh b/cql3/sets.hh index 74eed3d1ba..10d05fab7b 100644 --- a/cql3/sets.hh +++ b/cql3/sets.hh @@ -96,8 +96,8 @@ public: class setter : public operation { public: - setter(const column_definition& column, shared_ptr t) - : operation(column, std::move(t)) { + setter(const column_definition& column, expr::expression e) + : operation(column, std::move(e)) { } virtual void execute(mutation& m, const clustering_key_prefix& row_key, const update_parameters& params) override; static void execute(mutation& m, const clustering_key_prefix& row_key, const update_parameters& params, const column_definition& column, const expr::constant& value); @@ -105,8 +105,8 @@ public: class adder : public operation { public: - adder(const column_definition& column, shared_ptr t) - : operation(column, std::move(t)) { + adder(const column_definition& column, expr::expression e) + : operation(column, std::move(e)) { } virtual void execute(mutation& m, const clustering_key_prefix& row_key, const update_parameters& params) override; static void do_add(mutation& m, const clustering_key_prefix& row_key, const update_parameters& params, @@ -116,16 +116,16 @@ public: // Note that this is reused for Map subtraction too (we subtract a set from a map) class discarder : public operation { public: - discarder(const column_definition& column, shared_ptr t) - : operation(column, std::move(t)) { + discarder(const column_definition& column, expr::expression e) + : operation(column, std::move(e)) { } virtual void execute(mutation& m, const clustering_key_prefix& row_key, const update_parameters& params) override; }; class element_discarder : public operation { public: - element_discarder(const column_definition& column, shared_ptr t) - : operation(column, std::move(t)) { } + element_discarder(const column_definition& column, expr::expression e) + : operation(column, std::move(e)) { } virtual void execute(mutation& m, const clustering_key_prefix& row_key, const update_parameters& params) override; }; }; diff --git a/cql3/statements/update_statement.cc b/cql3/statements/update_statement.cc index b9425425b4..42060ff8d6 100644 --- a/cql3/statements/update_statement.cc +++ b/cql3/statements/update_statement.cc @@ -140,7 +140,7 @@ void update_statement::add_update_for_key(mutation& m, const query::clustering_r if (rb->name().empty() || rb->type == empty_type) { // There is no column outside the PK. So no operation could have passed through validation assert(_column_operations.empty()); - constants::setter(*s->regular_begin(), make_shared(cql3::raw_value::make_value(bytes()), empty_type)).execute(m, prefix, params); + constants::setter(*s->regular_begin(), expr::constant(cql3::raw_value::make_value(bytes()), empty_type)).execute(m, prefix, params); } else { // dense means we don't have a row marker, so don't accept to set only the PK. See CASSANDRA-5648. if (_column_operations.empty()) { diff --git a/cql3/user_types.cc b/cql3/user_types.cc index 7cf234895e..f2ee7b5b23 100644 --- a/cql3/user_types.cc +++ b/cql3/user_types.cc @@ -152,7 +152,7 @@ expr::expression user_types::marker::to_expression() { } void user_types::setter::execute(mutation& m, const clustering_key_prefix& row_key, const update_parameters& params) { - const expr::constant value = expr::evaluate(_t, params._options); + const expr::constant value = expr::evaluate(*_e, params._options); execute(m, row_key, params, column, value); } @@ -210,7 +210,7 @@ void user_types::setter::execute(mutation& m, const clustering_key_prefix& row_k void user_types::setter_by_field::execute(mutation& m, const clustering_key_prefix& row_key, const update_parameters& params) { assert(column.type->is_user_type() && column.type->is_multi_cell()); - auto value = expr::evaluate_to_raw_view(_t, params._options); + auto value = expr::evaluate(*_e, params._options); if (value.is_unset_value()) { return; } @@ -218,8 +218,8 @@ void user_types::setter_by_field::execute(mutation& m, const clustering_key_pref auto& type = static_cast(*column.type); collection_mutation_description mut; - mut.cells.emplace_back(serialize_field_index(_field_idx), value - ? params.make_cell(*type.type(_field_idx), value, atomic_cell::collection_member::yes) + mut.cells.emplace_back(serialize_field_index(_field_idx), !value.is_null() + ? params.make_cell(*type.type(_field_idx), value.view(), atomic_cell::collection_member::yes) : params.make_dead_cell()); m.set_cell(row_key, column, mut.serialize(type)); diff --git a/cql3/user_types.hh b/cql3/user_types.hh index 3ad4b2b85f..dcb9c5081c 100644 --- a/cql3/user_types.hh +++ b/cql3/user_types.hh @@ -107,8 +107,8 @@ public: class setter_by_field : public operation { size_t _field_idx; public: - setter_by_field(const column_definition& column, size_t field_idx, shared_ptr t) - : operation(column, std::move(t)), _field_idx(field_idx) { + setter_by_field(const column_definition& column, size_t field_idx, expr::expression e) + : operation(column, std::move(e)), _field_idx(field_idx) { } virtual void execute(mutation& m, const clustering_key_prefix& row_key, const update_parameters& params) override; @@ -118,7 +118,7 @@ public: size_t _field_idx; public: deleter_by_field(const column_definition& column, size_t field_idx) - : operation(column, nullptr), _field_idx(field_idx) { + : operation(column, std::nullopt), _field_idx(field_idx) { } virtual void execute(mutation& m, const clustering_key_prefix& row_key, const update_parameters& params) override; From 7cabed9ebfe4f22a70f01ba2653108c0ad10a7d1 Mon Sep 17 00:00:00 2001 From: Jan Ciolek Date: Thu, 28 Oct 2021 19:09:56 +0200 Subject: [PATCH 17/24] cql3: expr: Add find_in_expression find_in_expression is a function that looks into the expression and finds the given expression variant for which the predicate function returns true. If nothing is found returns nullptr. For example: find_in_expression(e, [](const binary_operator&) {return true;}) Will return the first binary operator found in the expression. It is now used in find_atom, and soon will be used in other similar functions. Signed-off-by: Jan Ciolek --- cql3/expr/expression.hh | 86 +++++++++++++++++++++++++---------------- 1 file changed, 52 insertions(+), 34 deletions(-) diff --git a/cql3/expr/expression.hh b/cql3/expr/expression.hh index 6208743759..734b08b500 100644 --- a/cql3/expr/expression.hh +++ b/cql3/expr/expression.hh @@ -458,80 +458,98 @@ extern std::ostream& operator<<(std::ostream&, const column_value&); extern std::ostream& operator<<(std::ostream&, const expression&); -/// If there is a binary_operator atom b for which f(b) is true, returns it. Otherwise returns null. -template -requires std::regular_invocable -const binary_operator* find_atom(const expression& e, Fn f) { +// Looks into the expression and finds the given expression variant +// for which the predicate function returns true. +// If nothing is found returns nullptr. +// For example: +// find_in_expression(e, [](const binary_operator&) {return true;}) +// Will return the first binary operator found in the expression +template +requires std::invocable + && std::same_as, bool> +const ExprElem* find_in_expression(const expression& e, Fn predicate_fun) { + if (auto expr_elem = as_if(&e)) { + if (predicate_fun(*expr_elem)) { + return expr_elem; + } + } + return expr::visit(overloaded_functor{ - [&] (const binary_operator& op) { return f(op) ? &op : nullptr; }, - [] (const constant&) -> const binary_operator* { return nullptr; }, - [&] (const conjunction& conj) -> const binary_operator* { + [&] (const binary_operator& op) -> const ExprElem* { + if (auto found = find_in_expression(op.lhs, predicate_fun)) { + return found; + } + return find_in_expression(op.rhs, predicate_fun); + }, + [&] (const conjunction& conj) -> const ExprElem* { for (auto& child : conj.children) { - if (auto found = find_atom(child, f)) { + if (auto found = find_in_expression(child, predicate_fun)) { return found; } } return nullptr; }, - [&] (const column_value& cv) -> const binary_operator* { + [&] (const column_value& cv) -> const ExprElem* { if (cv.sub.has_value()) { - return find_atom(*cv.sub, f); + return find_in_expression(*cv.sub, predicate_fun); } return nullptr; }, - [] (const token&) -> const binary_operator* { return nullptr; }, - [] (const unresolved_identifier&) -> const binary_operator* { return nullptr; }, - [] (const column_mutation_attribute&) -> const binary_operator* { return nullptr; }, - [&] (const function_call& fc) -> const binary_operator* { + [&] (const column_mutation_attribute& a) -> const ExprElem* { + return find_in_expression(a.column, predicate_fun); + }, + [&] (const function_call& fc) -> const ExprElem* { for (auto& arg : fc.args) { - if (auto found = find_atom(arg, f)) { + if (auto found = find_in_expression(arg, predicate_fun)) { return found; } } return nullptr; }, - [&] (const cast& c) -> const binary_operator* { - return find_atom(c.arg, f); + [&] (const cast& c) -> const ExprElem* { + return find_in_expression(c.arg, predicate_fun); }, - [&] (const field_selection& fs) -> const binary_operator* { - return find_atom(fs.structure, f); + [&] (const field_selection& fs) -> const ExprElem* { + return find_in_expression(fs.structure, predicate_fun); }, - [&] (const null&) -> const binary_operator* { - return nullptr; - }, - [&] (const bind_variable&) -> const binary_operator* { - return nullptr; - }, - [&] (const untyped_constant&) -> const binary_operator* { - return nullptr; - }, - [&] (const tuple_constructor& t) -> const binary_operator* { + [&] (const tuple_constructor& t) -> const ExprElem* { for (auto& e : t.elements) { - if (auto found = find_atom(e, f)) { + if (auto found = find_in_expression(e, predicate_fun)) { return found; } } return nullptr; }, - [&] (const collection_constructor& c) -> const binary_operator* { + [&] (const collection_constructor& c) -> const ExprElem* { for (auto& e : c.elements) { - if (auto found = find_atom(e, f)) { + if (auto found = find_in_expression(e, predicate_fun)) { return found; } } return nullptr; }, - [&] (const usertype_constructor& c) -> const binary_operator* { + [&] (const usertype_constructor& c) -> const ExprElem* { for (auto& [k, v] : c.elements) { - if (auto found = find_atom(v, f)) { + if (auto found = find_in_expression(v, predicate_fun)) { return found; } } return nullptr; }, + [](LeafExpression auto const&) -> const ExprElem* { + return nullptr; + } }, e); } +/// If there is a binary_operator atom b for which f(b) is true, returns it. Otherwise returns null. +template +requires std::invocable + && std::same_as, bool> +const binary_operator* find_atom(const expression& e, Fn predicate_fun) { + return find_in_expression(e, predicate_fun); +} + /// Counts binary_operator atoms b for which f(b) is true. template requires std::regular_invocable From 080286cb96cdba15630037dd793513c22893f2ac Mon Sep 17 00:00:00 2001 From: Jan Ciolek Date: Thu, 28 Oct 2021 19:15:24 +0200 Subject: [PATCH 18/24] cql3: expr: Rename find_atom to find_binop Soon there will be other functions that also search in expression, find_atom would be confusing then. find_binop is a more descriptive name. Signed-off-by: Jan Ciolek --- cql3/expr/expression.hh | 14 +++++++------- .../single_column_primary_key_restrictions.hh | 2 +- cql3/restrictions/statement_restrictions.cc | 16 ++++++++-------- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/cql3/expr/expression.hh b/cql3/expr/expression.hh index 734b08b500..2758449715 100644 --- a/cql3/expr/expression.hh +++ b/cql3/expr/expression.hh @@ -546,7 +546,7 @@ const ExprElem* find_in_expression(const expression& e, Fn predicate_fun) { template requires std::invocable && std::same_as, bool> -const binary_operator* find_atom(const expression& e, Fn predicate_fun) { +const binary_operator* find_binop(const expression& e, Fn predicate_fun) { return find_in_expression(e, predicate_fun); } @@ -599,7 +599,7 @@ size_t count_if(const expression& e, Fn f) { } inline const binary_operator* find(const expression& e, oper_t op) { - return find_atom(e, [&] (const binary_operator& o) { return o.op == op; }); + return find_binop(e, [&] (const binary_operator& o) { return o.op == op; }); } inline bool needs_filtering(oper_t op) { @@ -608,7 +608,7 @@ inline bool needs_filtering(oper_t op) { } inline auto find_needs_filtering(const expression& e) { - return find_atom(e, [] (const binary_operator& bo) { return needs_filtering(bo.op); }); + return find_binop(e, [] (const binary_operator& bo) { return needs_filtering(bo.op); }); } inline bool is_slice(oper_t op) { @@ -616,7 +616,7 @@ inline bool is_slice(oper_t op) { } inline bool has_slice(const expression& e) { - return find_atom(e, [] (const binary_operator& bo) { return is_slice(bo.op); }); + return find_binop(e, [] (const binary_operator& bo) { return is_slice(bo.op); }); } inline bool is_compare(oper_t op) { @@ -638,11 +638,11 @@ inline bool is_multi_column(const binary_operator& op) { } inline bool has_token(const expression& e) { - return find_atom(e, [] (const binary_operator& o) { return expr::is(o.lhs); }); + return find_binop(e, [] (const binary_operator& o) { return expr::is(o.lhs); }); } inline bool has_slice_or_needs_filtering(const expression& e) { - return find_atom(e, [] (const binary_operator& o) { return is_slice(o.op) || needs_filtering(o.op); }); + return find_binop(e, [] (const binary_operator& o) { return is_slice(o.op) || needs_filtering(o.op); }); } inline bool is_clustering_order(const binary_operator& op) { @@ -650,7 +650,7 @@ inline bool is_clustering_order(const binary_operator& op) { } inline auto find_clustering_order(const expression& e) { - return find_atom(e, is_clustering_order); + return find_binop(e, is_clustering_order); } /// True iff binary_operator involves a collection. diff --git a/cql3/restrictions/single_column_primary_key_restrictions.hh b/cql3/restrictions/single_column_primary_key_restrictions.hh index 5ee7acd174..6817176d5c 100644 --- a/cql3/restrictions/single_column_primary_key_restrictions.hh +++ b/cql3/restrictions/single_column_primary_key_restrictions.hh @@ -149,7 +149,7 @@ public: } virtual void merge_with(::shared_ptr restriction) override { - if (find_atom(restriction->expression, [] (const expr::binary_operator& b) { + if (find_binop(restriction->expression, [] (const expr::binary_operator& b) { return expr::is(b.lhs); })) { throw exceptions::invalid_request_exception( diff --git a/cql3/restrictions/statement_restrictions.cc b/cql3/restrictions/statement_restrictions.cc index 79ae318e19..b67ecdbac6 100644 --- a/cql3/restrictions/statement_restrictions.cc +++ b/cql3/restrictions/statement_restrictions.cc @@ -452,7 +452,7 @@ statement_restrictions::statement_restrictions(database& db, const expr::allow_local_index allow_local( !_partition_key_restrictions->has_unrestricted_components(*_schema) && _partition_key_restrictions->is_all_eq()); - _has_multi_column = find_atom(_clustering_columns_restrictions->expression, expr::is_multi_column); + _has_multi_column = find_binop(_clustering_columns_restrictions->expression, expr::is_multi_column); _has_queriable_ck_index = _clustering_columns_restrictions->has_supporting_index(sim, allow_local) && !type.is_delete(); _has_queriable_pk_index = _partition_key_restrictions->has_supporting_index(sim, allow_local) @@ -499,7 +499,7 @@ statement_restrictions::statement_restrictions(database& db, if (_uses_secondary_indexing || _clustering_columns_restrictions->needs_filtering(*_schema)) { _index_restrictions.push_back(_clustering_columns_restrictions); - } else if (find_atom(_clustering_columns_restrictions->expression, expr::is_on_collection)) { + } else if (find_binop(_clustering_columns_restrictions->expression, expr::is_on_collection)) { fail(unimplemented::cause::INDEXES); #if 0 _index_restrictions.push_back(new Forwardingprimary_key_restrictions() { @@ -689,7 +689,7 @@ void statement_restrictions::process_clustering_columns_restrictions(bool for_vi return; } - if (find_atom(_clustering_columns_restrictions->expression, expr::is_on_collection) + if (find_binop(_clustering_columns_restrictions->expression, expr::is_on_collection) && !_has_queriable_ck_index && !allow_filtering) { throw exceptions::invalid_request_exception( "Cannot restrict clustering columns by a CONTAINS relation without a secondary index or filtering"); @@ -764,7 +764,7 @@ dht::partition_range_vector partition_ranges_from_singles( std::vector> column_values(schema.partition_key_size()); size_t product_size = 1; for (const auto& e : expressions) { - if (const auto arbitrary_binop = find_atom(e, [] (const binary_operator&) { return true; })) { + if (const auto arbitrary_binop = find_binop(e, [] (const binary_operator&) { return true; })) { if (auto cv = expr::as_if(&arbitrary_binop->lhs)) { const value_set vals = possible_lhs_values(cv->col, e, options); if (auto lst = std::get_if(&vals)) { @@ -1428,7 +1428,7 @@ std::vector statement_restrictions::get_clustering_boun if (_clustering_prefix_restrictions.empty()) { return {query::clustering_range::make_open_ended_both_sides()}; } - if (find_atom(_clustering_prefix_restrictions[0], expr::is_multi_column)) { + if (find_binop(_clustering_prefix_restrictions[0], expr::is_multi_column)) { bool all_natural = true, all_reverse = true; ///< Whether column types are reversed or natural. for (auto& r : _clustering_prefix_restrictions) { // TODO: move to constructor, do only once. using namespace expr; @@ -1647,11 +1647,11 @@ void statement_restrictions::prepare_indexed_local(const schema& idx_tbl_schema) void statement_restrictions::add_clustering_restrictions_to_idx_ck_prefix(const schema& idx_tbl_schema) { for (const auto& e : _clustering_prefix_restrictions) { - if (find_atom(_clustering_prefix_restrictions[0], expr::is_multi_column)) { + if (find_binop(_clustering_prefix_restrictions[0], expr::is_multi_column)) { // TODO: We could handle single-element tuples, eg. `(c)>=(123)`. break; } - const auto any_binop = find_atom(e, [] (auto&&) { return true; }); + const auto any_binop = find_binop(e, [] (auto&&) { return true; }); if (!any_binop) { break; } @@ -1733,7 +1733,7 @@ sstring statement_restrictions::to_string() const { } static bool has_eq_null(const query_options& options, const expression& expr) { - return find_atom(expr, [&] (const binary_operator& binop) { + return find_binop(expr, [&] (const binary_operator& binop) { return binop.op == oper_t::EQ && evaluate(binop.rhs, options).is_null(); }); } From 7bc65868eb39ec0297e00aa17380c5aaba1a950a Mon Sep 17 00:00:00 2001 From: Jan Ciolek Date: Fri, 8 Oct 2021 17:27:30 +0200 Subject: [PATCH 19/24] cql3: expr: Add expr::contains_bind_marker Add a function that checks whether there is a bind marker somewhere inside an expression. It's important to note, that even when there are no bind markers, there can be other things that prevent immediate evaluation of an expression. For example an expression can contain calls to nonpure functions. Signed-off-by: Jan Ciolek --- cql3/expr/expression.cc | 5 +++++ cql3/expr/expression.hh | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/cql3/expr/expression.cc b/cql3/expr/expression.cc index 20116058cf..a7a68430f6 100644 --- a/cql3/expr/expression.cc +++ b/cql3/expr/expression.cc @@ -1954,6 +1954,11 @@ void fill_prepare_context(expression& e, prepare_context& ctx) { }, e); } +bool contains_bind_marker(const expression& e) { + const bind_variable* search_res = find_in_expression(e, [](const bind_variable&) { return true; }); + return search_res != nullptr; +} + expression to_expression(const ::shared_ptr& term_ptr) { if (term_ptr.get() == nullptr) { return constant::make_null(); diff --git a/cql3/expr/expression.hh b/cql3/expr/expression.hh index 2758449715..2fced19eca 100644 --- a/cql3/expr/expression.hh +++ b/cql3/expr/expression.hh @@ -750,6 +750,12 @@ utils::chunked_vector> get_list_of_tuples_element // Sets lwt_cache_id field in function_calls. void fill_prepare_context(expression&, cql3::prepare_context&); +// Checks whether there is a bind_variable inside this expression +// It's important to note, that even when there are no bind markers, +// there can be other things that prevent immediate evaluation of an expression. +// For example an expression can contain calls to nonpure functions. +bool contains_bind_marker(const expression& e); + expression to_expression(const ::shared_ptr&); } // namespace expr From c84e941df99b64f17e27a2bbf8172d805023c352 Mon Sep 17 00:00:00 2001 From: Jan Ciolek Date: Fri, 8 Oct 2021 21:43:45 +0200 Subject: [PATCH 20/24] cql3: expr: Add size check to evaluate_set In old code sets::delayed_value::bind() contained a check that each serialized value is less than certain size. I missed this when implementing evaluate(), so it's brought back to ensure identical behaviour. Signed-off-by: Jan Ciolek --- cql3/expr/expression.cc | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/cql3/expr/expression.cc b/cql3/expr/expression.cc index a7a68430f6..4daa2b76bf 100644 --- a/cql3/expr/expression.cc +++ b/cql3/expr/expression.cc @@ -1603,6 +1603,16 @@ static constant evaluate_set(const collection_constructor& collection, const que return constant::make_unset_value(collection.type); } + if (evaluated_element.view().size_bytes() > std::numeric_limits::max()) { + // TODO: Behaviour copied from sets::delayed_value::bind(), but this seems incorrect + // The original reasoning is: + // "We don't support values > 64K because the serialization format encode the length as an unsigned short." + // but CQL uses int32_t to encode length of a set value length + throw exceptions::invalid_request_exception(format("Set value is too long. Set values are limited to {:d} bytes but {:d} bytes value provided", + std::numeric_limits::max(), + evaluated_element.view().size_bytes())); + } + evaluated_elements.emplace(std::move(evaluated_element).value.to_managed_bytes()); } From 219f1a43593042f0924f8fe16cb9c27482170876 Mon Sep 17 00:00:00 2001 From: Jan Ciolek Date: Fri, 8 Oct 2021 21:44:14 +0200 Subject: [PATCH 21/24] cql3: Make prepare_term return an expression instead of term prepare_term is now the only function that uses terms. Change it so that it returns expression instead of term and remove all occurences of expr::to_expression(prepare_term(...)) Signed-off-by: Jan Ciolek --- cql3/attributes.cc | 6 +- cql3/column_condition.cc | 12 +- cql3/expr/expression.hh | 4 +- cql3/expr/term_expr.cc | 244 ++++++++++-------- cql3/functions/function_call.hh | 2 +- cql3/functions/functions.cc | 34 +-- cql3/multi_column_relation.hh | 2 +- cql3/operation.cc | 34 +-- cql3/single_column_relation.cc | 2 +- cql3/statements/create_aggregate_statement.cc | 4 +- cql3/statements/select_statement.cc | 2 +- cql3/statements/update_statement.cc | 2 +- cql3/token_relation.cc | 2 +- 13 files changed, 186 insertions(+), 164 deletions(-) diff --git a/cql3/attributes.cc b/cql3/attributes.cc index c929b44958..37ad2c9f37 100644 --- a/cql3/attributes.cc +++ b/cql3/attributes.cc @@ -154,15 +154,15 @@ std::unique_ptr attributes::raw::prepare(database& db, const sstring std::optional ts, ttl, to; if (timestamp.has_value()) { - ts = expr::to_expression(prepare_term(*timestamp, db, ks_name, timestamp_receiver(ks_name, cf_name))); + ts = prepare_term(*timestamp, db, ks_name, timestamp_receiver(ks_name, cf_name)); } if (time_to_live.has_value()) { - ttl = expr::to_expression(prepare_term(*time_to_live, db, ks_name, time_to_live_receiver(ks_name, cf_name))); + ttl = prepare_term(*time_to_live, db, ks_name, time_to_live_receiver(ks_name, cf_name)); } if (timeout.has_value()) { - to = expr::to_expression(prepare_term(*timeout, db, ks_name, timeout_receiver(ks_name, cf_name))); + to = prepare_term(*timeout, db, ks_name, timeout_receiver(ks_name, cf_name)); } return std::unique_ptr{new attributes{std::move(ts), std::move(ttl), std::move(to)}}; diff --git a/cql3/column_condition.cc b/cql3/column_condition.cc index cef3a1b732..28b2296cf8 100644 --- a/cql3/column_condition.cc +++ b/cql3/column_condition.cc @@ -314,13 +314,13 @@ column_condition::raw::prepare(database& db, const sstring& keyspace, const colu throw exceptions::invalid_request_exception( format("Unsupported collection type {} in a condition with element access", ctype->cql3_type_name())); } - collection_element_expression = expr::to_expression(prepare_term(*_collection_element, db, keyspace, element_spec)); + collection_element_expression = prepare_term(*_collection_element, db, keyspace, element_spec); } if (is_compare(_op)) { validate_operation_on_durations(*receiver.type, _op); return column_condition::condition(receiver, std::move(collection_element_expression), - expr::to_expression(prepare_term(*_value, db, keyspace, value_spec)), nullptr, _op); + prepare_term(*_value, db, keyspace, value_spec), nullptr, _op); } if (_op == expr::oper_t::LIKE) { @@ -329,14 +329,14 @@ column_condition::raw::prepare(database& db, const sstring& keyspace, const colu // Pass matcher object const sstring& pattern = literal_term->raw_text; return column_condition::condition(receiver, std::move(collection_element_expression), - expr::to_expression(prepare_term(*_value, db, keyspace, value_spec)), + prepare_term(*_value, db, keyspace, value_spec), std::make_unique(bytes_view(reinterpret_cast(pattern.data()), pattern.size())), _op); } else { // Pass through rhs value, matcher object built on execution // TODO: caller should validate parametrized LIKE pattern return column_condition::condition(receiver, std::move(collection_element_expression), - expr::to_expression(prepare_term(*_value, db, keyspace, value_spec)), nullptr, _op); + prepare_term(*_value, db, keyspace, value_spec), nullptr, _op); } } @@ -346,14 +346,14 @@ column_condition::raw::prepare(database& db, const sstring& keyspace, const colu if (_in_marker) { assert(_in_values.empty()); - expr::expression multi_item_term = expr::to_expression(prepare_term(*_in_marker, db, keyspace, value_spec)); + expr::expression multi_item_term = prepare_term(*_in_marker, db, keyspace, value_spec); return column_condition::in_condition(receiver, collection_element_expression, std::move(multi_item_term), {}); } // Both _in_values and in _in_marker can be missing in case of empty IN list: "a IN ()" std::vector terms; terms.reserve(_in_values.size()); for (auto&& value : _in_values) { - terms.push_back(expr::to_expression(prepare_term(value, db, keyspace, value_spec))); + terms.push_back(prepare_term(value, db, keyspace, value_spec)); } return column_condition::in_condition(receiver, std::move(collection_element_expression), std::nullopt, std::move(terms)); diff --git a/cql3/expr/expression.hh b/cql3/expr/expression.hh index 2fced19eca..e0e6ec49a3 100644 --- a/cql3/expr/expression.hh +++ b/cql3/expr/expression.hh @@ -669,8 +669,8 @@ extern expression replace_token(const expression&, const column_definition*); extern expression search_and_replace(const expression& e, const noncopyable_function (const expression& candidate)>& replace_candidate); -extern ::shared_ptr prepare_term(const expression& expr, database& db, const sstring& keyspace, lw_shared_ptr receiver); -extern ::shared_ptr prepare_term_multi_column(const expression& expr, database& db, const sstring& keyspace, const std::vector>& receivers); +extern expression prepare_term(const expression& expr, database& db, const sstring& keyspace, lw_shared_ptr receiver); +extern expression prepare_term_multi_column(const expression& expr, database& db, const sstring& keyspace, const std::vector>& receivers); /** diff --git a/cql3/expr/term_expr.cc b/cql3/expr/term_expr.cc index 635ac6b739..bab07a812f 100644 --- a/cql3/expr/term_expr.cc +++ b/cql3/expr/term_expr.cc @@ -83,13 +83,13 @@ usertype_constructor_test_assignment(const usertype_constructor& u, database& db } static -shared_ptr +expression usertype_constructor_prepare_term(const usertype_constructor& u, database& db, const sstring& keyspace, lw_shared_ptr receiver) { usertype_constructor_validate_assignable_to(u, db, keyspace, *receiver); auto&& ut = static_pointer_cast(receiver->type); bool all_terminal = true; - std::vector> values; - values.reserve(u.elements.size()); + + usertype_constructor::elements_map_type prepared_elements; size_t found_values = 0; for (size_t i = 0; i < ut->size(); ++i) { auto&& field = column_identifier(to_bytes(ut->field_name(i)), utf8_type); @@ -101,13 +101,13 @@ usertype_constructor_prepare_term(const usertype_constructor& u, database& db, c raw = iraw->second; ++found_values; } - auto&& value = prepare_term(raw, db, keyspace, usertype_field_spec_of(*receiver, i)); + expression value = prepare_term(raw, db, keyspace, usertype_field_spec_of(*receiver, i)); - if (dynamic_cast(value.get())) { + if (!is(value)) { all_terminal = false; } - values.push_back(std::move(value)); + prepared_elements.emplace(std::move(field), std::move(value)); } if (found_values != u.elements.size()) { // We had some field that are not part of the type @@ -119,11 +119,15 @@ usertype_constructor_prepare_term(const usertype_constructor& u, database& db, c } } - user_types::delayed_value value(ut, values); + usertype_constructor value { + .elements = std::move(prepared_elements), + .type = ut + }; + if (all_terminal) { - return value.bind(query_options::DEFAULT); + return evaluate(value, query_options::DEFAULT); } else { - return make_shared(std::move(value)); + return value; } } @@ -197,13 +201,13 @@ map_test_assignment(const collection_constructor& c, database& db, const sstring } static -::shared_ptr +expression map_prepare_term(const collection_constructor& c, database& db, const sstring& keyspace, lw_shared_ptr receiver) { map_validate_assignable_to(c, db, keyspace, *receiver); auto key_spec = maps::key_spec_of(*receiver); auto value_spec = maps::value_spec_of(*receiver); - std::unordered_map, shared_ptr> values; + std::vector values; values.reserve(c.elements.size()); bool all_terminal = true; for (auto&& entry : c.elements) { @@ -211,24 +215,33 @@ map_prepare_term(const collection_constructor& c, database& db, const sstring& k if (entry_tuple.elements.size() != 2) { on_internal_error(expr_logger, "map element is not a tuple of arity 2"); } - auto k = prepare_term(entry_tuple.elements[0], db, keyspace, key_spec); - auto v = prepare_term(entry_tuple.elements[1], db, keyspace, value_spec); + expression k = prepare_term(entry_tuple.elements[0], db, keyspace, key_spec); + expression v = prepare_term(entry_tuple.elements[1], db, keyspace, value_spec); - if (k->contains_bind_marker() || v->contains_bind_marker()) { + if (contains_bind_marker(k) || contains_bind_marker(k)) { throw exceptions::invalid_request_exception(format("Invalid map literal for {}: bind variables are not supported inside collection literals", *receiver->name)); } - if (dynamic_pointer_cast(k) || dynamic_pointer_cast(v)) { + // Even when there are no bind markers, the value can still contain a nonpure function + if (!is(k) || !is(v)) { all_terminal = false; } - values.emplace(k, v); + values.emplace_back(tuple_constructor { + .elements = {std::move(k), std::move(v)}, + .type = entry_tuple.type + }); } - maps::delayed_value value(values, receiver->type); + + collection_constructor map_value { + .style = collection_constructor::style_type::map, + .elements = std::move(values), + .type = receiver->type + }; if (all_terminal) { - return value.bind(query_options::DEFAULT); + return evaluate(map_value, query_options::DEFAULT); } else { - return make_shared(std::move(value)); + return map_value; } } @@ -283,7 +296,7 @@ set_test_assignment(const collection_constructor& c, database& db, const sstring } static -shared_ptr +expression set_prepare_term(const collection_constructor& c, database& db, const sstring& keyspace, lw_shared_ptr receiver) { set_validate_assignable_to(c, db, keyspace, *receiver); @@ -294,40 +307,48 @@ set_prepare_term(const collection_constructor& c, database& db, const sstring& k // away to simplify predicate evaluation. See also // https://issues.apache.org/jira/browse/CASSANDRA-5141 if (receiver->type->is_multi_cell()) { - return cql3::constants::NULL_VALUE; + return constant::make_null(receiver->type); } // We've parsed empty maps as a set literal to break the ambiguity so // handle that case now. This branch works for frozen sets/maps only. const map_type_impl* maybe_map_type = dynamic_cast(receiver->type.get()); if (maybe_map_type != nullptr) { - serialized_compare comparator = maybe_map_type->get_keys_type()->as_less_comparator(); - std::map m(empty_type->as_less_comparator()); - return ::make_shared(std::move(m), receiver->type); + collection_constructor map_value { + .style = collection_constructor::style_type::map, + .elements = {}, + .type = receiver->type + }; + return expr::evaluate(map_value, query_options::DEFAULT); } } auto value_spec = set_value_spec_of(*receiver); - std::vector> values; + std::vector values; values.reserve(c.elements.size()); bool all_terminal = true; for (auto& e : c.elements) { - auto t = prepare_term(e, db, keyspace, value_spec); + expression elem = prepare_term(e, db, keyspace, value_spec); - if (t->contains_bind_marker()) { + if (contains_bind_marker(elem)) { throw exceptions::invalid_request_exception(format("Invalid set literal for {}: bind variables are not supported inside collection literals", *receiver->name)); } - if (dynamic_pointer_cast(t)) { + if (!is(elem)) { all_terminal = false; } - values.push_back(std::move(t)); + values.push_back(std::move(elem)); } - auto value = ::make_shared(std::move(values), receiver->type); + collection_constructor value { + .style = collection_constructor::style_type::set, + .elements = std::move(values), + .type = receiver->type + }; + if (all_terminal) { - return value->bind(query_options::DEFAULT); + return evaluate(value, query_options::DEFAULT); } else { return value; } @@ -375,7 +396,7 @@ list_test_assignment(const collection_constructor& c, database& db, const sstrin static -shared_ptr +expression list_prepare_term(const collection_constructor& c, database& db, const sstring& keyspace, lw_shared_ptr receiver) { list_validate_assignable_to(c, db, keyspace, *receiver); @@ -384,29 +405,33 @@ list_prepare_term(const collection_constructor& c, database& db, const sstring& // away to simplify predicate evaluation. See also // https://issues.apache.org/jira/browse/CASSANDRA-5141 if (receiver->type->is_multi_cell() && c.elements.empty()) { - return cql3::constants::NULL_VALUE; + return constant::make_null(receiver->type); } auto&& value_spec = list_value_spec_of(*receiver); - std::vector> values; + std::vector values; values.reserve(c.elements.size()); bool all_terminal = true; for (auto& e : c.elements) { - auto&& t = prepare_term(e, db, keyspace, value_spec); + expression elem = prepare_term(e, db, keyspace, value_spec); - if (t->contains_bind_marker()) { + if (contains_bind_marker(elem)) { throw exceptions::invalid_request_exception(format("Invalid list literal for {}: bind variables are not supported inside collection literals", *receiver->name)); } - if (dynamic_pointer_cast(t)) { + if (!is(elem)) { all_terminal = false; } - values.push_back(std::move(t)); + values.push_back(std::move(elem)); } - lists::delayed_value value(values, receiver->type); + collection_constructor value { + .style = collection_constructor::style_type::list, + .elements = std::move(values), + .type = receiver->type + }; if (all_terminal) { - return value.bind(query_options::DEFAULT); + return evaluate(value, query_options::DEFAULT); } else { - return make_shared(std::move(value)); + return value; } } @@ -453,49 +478,55 @@ tuple_constructor_test_assignment(const tuple_constructor& tc, database& db, con } static -shared_ptr +expression tuple_constructor_prepare_nontuple(const tuple_constructor& tc, database& db, const sstring& keyspace, lw_shared_ptr receiver) { tuple_constructor_validate_assignable_to(tc, db, keyspace, *receiver); - std::vector> values; + std::vector values; bool all_terminal = true; for (size_t i = 0; i < tc.elements.size(); ++i) { - auto&& value = prepare_term(tc.elements[i], db, keyspace, component_spec_of(*receiver, i)); - if (dynamic_pointer_cast(value)) { + expression value = prepare_term(tc.elements[i], db, keyspace, component_spec_of(*receiver, i)); + if (!is(value)) { all_terminal = false; } values.push_back(std::move(value)); } - tuples::delayed_value value(static_pointer_cast(receiver->type), values); + tuple_constructor value { + .elements = std::move(values), + .type = static_pointer_cast(receiver->type) + }; if (all_terminal) { - return value.bind(query_options::DEFAULT); + return evaluate(value, query_options::DEFAULT); } else { - return make_shared(std::move(value)); + return value; } } static -shared_ptr +expression tuple_constructor_prepare_tuple(const tuple_constructor& tc, database& db, const sstring& keyspace, const std::vector>& receivers) { if (tc.elements.size() != receivers.size()) { throw exceptions::invalid_request_exception(format("Expected {:d} elements in value tuple, but got {:d}: {}", receivers.size(), tc.elements.size(), tc)); } - std::vector> values; + std::vector values; std::vector types; bool all_terminal = true; for (size_t i = 0; i < tc.elements.size(); ++i) { - auto&& t = prepare_term(tc.elements[i], db, keyspace, receivers[i]); - if (dynamic_pointer_cast(t)) { + expression elem = prepare_term(tc.elements[i], db, keyspace, receivers[i]); + if (!is(elem)) { all_terminal = false; } - values.push_back(t); + values.push_back(std::move(elem)); types.push_back(receivers[i]->type); } - tuples::delayed_value value(tuple_type_impl::get_instance(std::move(types)), std::move(values)); + tuple_constructor value { + .elements = std::move(values), + .type = tuple_type_impl::get_instance(std::move(types)) + }; if (all_terminal) { - return value.bind(query_options::DEFAULT); + return evaluate(value, query_options::DEFAULT); } else { - return make_shared(std::move(value)); + return value; } } @@ -609,7 +640,7 @@ untyped_constant_test_assignment(const untyped_constant& uc, database& db, const } static -::shared_ptr +constant untyped_constant_prepare_term(const untyped_constant& uc, database& db, const sstring& keyspace, lw_shared_ptr receiver) { if (!is_assignable(untyped_constant_test_assignment(uc, db, keyspace, *receiver))) { @@ -617,7 +648,7 @@ untyped_constant_prepare_term(const untyped_constant& uc, database& db, const ss uc.partial_type, uc.raw_text, *receiver->name, receiver->type->as_cql3_type().to_string())); } raw_value raw_val = cql3::raw_value::make_value(untyped_constant_parsed_value(uc, receiver->type)); - return ::make_shared(std::move(raw_val), receiver->type); + return constant(std::move(raw_val), receiver->type); } static @@ -627,25 +658,14 @@ bind_variable_test_assignment(const bind_variable& bv, database& db, const sstri } static -::shared_ptr +bind_variable bind_variable_scalar_prepare_term(const bind_variable& bv, database& db, const sstring& keyspace, lw_shared_ptr receiver) -{ - if (receiver->type->is_collection()) { - if (receiver->type->without_reversed().is_list()) { - return ::make_shared(bv.bind_index, receiver); - } else if (receiver->type->without_reversed().is_set()) { - return ::make_shared(bv.bind_index, receiver); - } else if (receiver->type->without_reversed().is_map()) { - return ::make_shared(bv.bind_index, receiver); - } - assert(0); - } - - if (receiver->type->is_user_type()) { - return ::make_shared(bv.bind_index, receiver); - } - - return ::make_shared(bv.bind_index, receiver); +{ + return bind_variable { + .shape = bind_variable::shape_type::scalar, + .bind_index = bv.bind_index, + .receiver = receiver + }; } static @@ -656,9 +676,13 @@ bind_variable_scalar_in_make_receiver(const column_specification& receiver) { } static -::shared_ptr +bind_variable bind_variable_scalar_in_prepare_term(const bind_variable& bv, database& db, const sstring& keyspace, lw_shared_ptr receiver) { - return ::make_shared(bv.bind_index, bind_variable_scalar_in_make_receiver(*receiver)); + return bind_variable { + .shape = bind_variable::shape_type::scalar, + .bind_index = bv.bind_index, + .receiver = bind_variable_scalar_in_make_receiver(*receiver) + }; } static @@ -682,9 +706,13 @@ bind_variable_tuple_make_receiver(const std::vector +bind_variable bind_variable_tuple_prepare_term(const bind_variable& bv, database& db, const sstring& keyspace, const std::vector>& receivers) { - return make_shared(bv.bind_index, bind_variable_tuple_make_receiver(receivers)); + return bind_variable { + .shape = bind_variable::shape_type::tuple, + .bind_index = bv.bind_index, + .receiver = bind_variable_tuple_make_receiver(receivers) + }; } static @@ -713,9 +741,13 @@ bind_variable_tuple_in_make_receiver(const std::vector +bind_variable bind_variable_tuple_in_prepare_term(const bind_variable& bv, database& db, const sstring& keyspace, const std::vector>& receivers) { - return make_shared(bv.bind_index, bind_variable_tuple_in_make_receiver(receivers)); + return bind_variable { + .shape = bind_variable::shape_type::tuple_in, + .bind_index = bv.bind_index, + .receiver = bind_variable_tuple_in_make_receiver(receivers) + }; } static @@ -729,12 +761,12 @@ null_test_assignment(database& db, } static -::shared_ptr +constant null_prepare_term(database& db, const sstring& keyspace, lw_shared_ptr receiver) { if (!is_assignable(null_test_assignment(db, keyspace, *receiver))) { throw exceptions::invalid_request_exception("Invalid null value for counter increment/decrement"); } - return constants::NULL_VALUE; + return constant::make_null(receiver->type); } static @@ -770,7 +802,7 @@ cast_test_assignment(const cast& c, database& db, const sstring& keyspace, const } static -shared_ptr +expression cast_prepare_term(const cast& c, database& db, const sstring& keyspace, lw_shared_ptr receiver) { auto type = std::get>(c.type); if (!is_assignable(test_assignment(c.arg, db, keyspace, *casted_spec_of(c, db, keyspace, *receiver)))) { @@ -782,43 +814,43 @@ cast_prepare_term(const cast& c, database& db, const sstring& keyspace, lw_share return prepare_term(c.arg, db, keyspace, receiver); } -::shared_ptr +expression prepare_term(const expression& expr, database& db, const sstring& keyspace, lw_shared_ptr receiver) { return expr::visit(overloaded_functor{ - [] (const constant&) -> ::shared_ptr { + [] (const constant&) -> expression { on_internal_error(expr_logger, "Can't prepare constant_value, it should not appear in parser output"); }, - [&] (const binary_operator&) -> ::shared_ptr { + [&] (const binary_operator&) -> expression { on_internal_error(expr_logger, "binary_operators are not yet reachable via term_raw_expr::prepare()"); }, - [&] (const conjunction&) -> ::shared_ptr { + [&] (const conjunction&) -> expression { on_internal_error(expr_logger, "conjunctions are not yet reachable via term_raw_expr::prepare()"); }, - [&] (const column_value&) -> ::shared_ptr { + [&] (const column_value&) -> expression { on_internal_error(expr_logger, "column_values are not yet reachable via term_raw_expr::prepare()"); }, - [&] (const token&) -> ::shared_ptr { + [&] (const token&) -> expression { on_internal_error(expr_logger, "tokens are not yet reachable via term_raw_expr::prepare()"); }, - [&] (const unresolved_identifier&) -> ::shared_ptr { + [&] (const unresolved_identifier&) -> expression { on_internal_error(expr_logger, "unresolved_identifiers are not yet reachable via term_raw_expr::prepare()"); }, - [&] (const column_mutation_attribute&) -> ::shared_ptr { + [&] (const column_mutation_attribute&) -> expression { on_internal_error(expr_logger, "column_mutation_attributes are not yet reachable via term_raw_expr::prepare()"); }, - [&] (const function_call& fc) -> ::shared_ptr { + [&] (const function_call& fc) -> expression { return functions::prepare_function_call(fc, db, keyspace, std::move(receiver)); }, - [&] (const cast& c) -> ::shared_ptr { + [&] (const cast& c) -> expression { return cast_prepare_term(c, db, keyspace, receiver); }, - [&] (const field_selection&) -> ::shared_ptr { + [&] (const field_selection&) -> expression { on_internal_error(expr_logger, "field_selections are not yet reachable via term_raw_expr::prepare()"); }, - [&] (const null&) -> ::shared_ptr { + [&] (const null&) -> expression { return null_prepare_term(db, keyspace, receiver); }, - [&] (const bind_variable& bv) -> ::shared_ptr { + [&] (const bind_variable& bv) -> expression { switch (bv.shape) { case expr::bind_variable::shape_type::scalar: return bind_variable_scalar_prepare_term(bv, db, keyspace, receiver); case expr::bind_variable::shape_type::scalar_in: return bind_variable_scalar_in_prepare_term(bv, db, keyspace, receiver); @@ -827,13 +859,13 @@ prepare_term(const expression& expr, database& db, const sstring& keyspace, lw_s } on_internal_error(expr_logger, "unexpected shape in bind_variable"); }, - [&] (const untyped_constant& uc) -> ::shared_ptr { + [&] (const untyped_constant& uc) -> expression { return untyped_constant_prepare_term(uc, db, keyspace, receiver); }, - [&] (const tuple_constructor& tc) -> ::shared_ptr { + [&] (const tuple_constructor& tc) -> expression { return tuple_constructor_prepare_nontuple(tc, db, keyspace, receiver); }, - [&] (const collection_constructor& c) -> ::shared_ptr { + [&] (const collection_constructor& c) -> expression { switch (c.style) { case collection_constructor::style_type::list: return list_prepare_term(c, db, keyspace, receiver); case collection_constructor::style_type::set: return set_prepare_term(c, db, keyspace, receiver); @@ -841,16 +873,16 @@ prepare_term(const expression& expr, database& db, const sstring& keyspace, lw_s } on_internal_error(expr_logger, fmt::format("unexpected collection_constructor style {}", static_cast(c.style))); }, - [&] (const usertype_constructor& uc) -> ::shared_ptr { + [&] (const usertype_constructor& uc) -> expression { return usertype_constructor_prepare_term(uc, db, keyspace, receiver); }, }, expr); } -::shared_ptr +expression prepare_term_multi_column(const expression& expr, database& db, const sstring& keyspace, const std::vector>& receivers) { return expr::visit(overloaded_functor{ - [&] (const bind_variable& bv) -> ::shared_ptr { + [&] (const bind_variable& bv) -> expression { switch (bv.shape) { case expr::bind_variable::shape_type::scalar: on_internal_error(expr_logger, "prepare_term_multi_column(bind_variable(scalar))"); case expr::bind_variable::shape_type::scalar_in: on_internal_error(expr_logger, "prepare_term_multi_column(bind_variable(scalar_in))"); @@ -859,10 +891,10 @@ prepare_term_multi_column(const expression& expr, database& db, const sstring& k } on_internal_error(expr_logger, "unexpected shape in bind_variable"); }, - [&] (const tuple_constructor& tc) -> ::shared_ptr { + [&] (const tuple_constructor& tc) -> expression { return tuple_constructor_prepare_tuple(tc, db, keyspace, receivers); }, - [] (const auto& default_case) -> ::shared_ptr { + [] (const auto& default_case) -> expression { on_internal_error(expr_logger, fmt::format("prepare_term_multi_column({})", typeid(default_case).name())); }, }, expr); diff --git a/cql3/functions/function_call.hh b/cql3/functions/function_call.hh index 1d39540e6a..b570e1c7b3 100644 --- a/cql3/functions/function_call.hh +++ b/cql3/functions/function_call.hh @@ -84,7 +84,7 @@ private: cql3::raw_value_view bind_and_get_internal(const query_options& options); }; -::shared_ptr prepare_function_call(const expr::function_call& fc, database& db, const sstring& keyspace, lw_shared_ptr receiver); +expr::expression prepare_function_call(const expr::function_call& fc, database& db, const sstring& keyspace, lw_shared_ptr receiver); assignment_testable::test_result test_assignment_function_call(const cql3::expr::function_call& fc, database& db, const sstring& keyspace, const column_specification& receiver); diff --git a/cql3/functions/functions.cc b/cql3/functions/functions.cc index b0aebe6be6..25c22efd19 100644 --- a/cql3/functions/functions.cc +++ b/cql3/functions/functions.cc @@ -67,7 +67,6 @@ bool as_json_function::requires_thread() const { return false; } thread_local std::unordered_multimap> functions::_declared = init(); static bytes_opt execute_internal(cql_serialization_format sf, scalar_function& fun, std::vector params); -static bytes_opt execute(scalar_function& fun, std::vector> parameters); static shared_ptr make_terminal(shared_ptr fun, cql3::raw_value result, cql_serialization_format sf); void functions::clear_functions() noexcept { @@ -570,7 +569,7 @@ make_terminal(shared_ptr fun, cql3::raw_value result, cql_serializatio )); } -::shared_ptr +expr::expression prepare_function_call(const expr::function_call& fc, database& db, const sstring& keyspace, lw_shared_ptr receiver) { auto&& fun = std::visit(overloaded_functor{ [] (const shared_ptr& func) { @@ -605,40 +604,31 @@ prepare_function_call(const expr::function_call& fc, database& db, const sstring fun->name(), fun->arg_types().size(), fc.args.size())); } - std::vector> parameters; + std::vector parameters; parameters.reserve(fc.args.size()); bool all_terminal = true; for (size_t i = 0; i < fc.args.size(); ++i) { - auto&& t = prepare_term(fc.args[i], db, keyspace, functions::make_arg_spec(receiver->ks_name, receiver->cf_name, *scalar_fun, i)); - if (dynamic_cast(t.get())) { + expr::expression e = prepare_term(fc.args[i], db, keyspace, functions::make_arg_spec(receiver->ks_name, receiver->cf_name, *scalar_fun, i)); + if (!expr::is(e)) { all_terminal = false; } - parameters.push_back(t); + parameters.push_back(std::move(e)); } // If all parameters are terminal and the function is pure, we can // evaluate it now, otherwise we'd have to wait execution time + expr::function_call fun_call { + .func = fun, + .args = std::move(parameters), + .lwt_cache_id = fc.lwt_cache_id + }; if (all_terminal && scalar_fun->is_pure()) { - return make_terminal(scalar_fun, cql3::raw_value::make_value(execute(*scalar_fun, parameters)), query_options::DEFAULT.get_cql_serialization_format()); + return expr::evaluate(fun_call, query_options::DEFAULT); } else { - return ::make_shared(scalar_fun, parameters); + return fun_call; } } -static -bytes_opt -execute(scalar_function& fun, std::vector> parameters) { - std::vector buffers; - buffers.reserve(parameters.size()); - for (auto&& t : parameters) { - assert(dynamic_cast(t.get())); - auto&& param = static_cast(t.get())->get(query_options::DEFAULT); - buffers.push_back(to_bytes_opt(param)); - } - - return execute_internal(cql_serialization_format::internal(), fun, buffers); -} - assignment_testable::test_result test_assignment_function_call(const cql3::expr::function_call& fc, database& db, const sstring& keyspace, const column_specification& receiver) { // Note: Functions.get() will return null if the function doesn't exist, or throw is no function matching diff --git a/cql3/multi_column_relation.hh b/cql3/multi_column_relation.hh index 1c691ad520..d09a008ef7 100644 --- a/cql3/multi_column_relation.hh +++ b/cql3/multi_column_relation.hh @@ -218,7 +218,7 @@ protected: virtual expr::expression to_expression(const std::vector>& receivers, const expr::expression& raw, database& db, const sstring& keyspace, prepare_context& ctx) const override { - auto e = expr::to_expression(prepare_term_multi_column(raw, db, keyspace, receivers)); + auto e = prepare_term_multi_column(raw, db, keyspace, receivers); expr::fill_prepare_context(e, ctx); return e; } diff --git a/cql3/operation.cc b/cql3/operation.cc index 7ffb57a9f0..5781e24e94 100644 --- a/cql3/operation.cc +++ b/cql3/operation.cc @@ -68,19 +68,19 @@ operation::set_element::prepare(database& db, const sstring& keyspace, const col } if (rtype->get_kind() == abstract_type::kind::list) { - auto&& lval = expr::to_expression(prepare_term(_value, db, keyspace, lists::value_spec_of(*receiver.column_specification))); + auto&& lval = prepare_term(_value, db, keyspace, lists::value_spec_of(*receiver.column_specification)); if (_by_uuid) { - auto&& idx = expr::to_expression(prepare_term(_selector, db, keyspace, lists::uuid_index_spec_of(*receiver.column_specification))); + auto&& idx = prepare_term(_selector, db, keyspace, lists::uuid_index_spec_of(*receiver.column_specification)); return make_shared(receiver, std::move(idx), std::move(lval)); } else { - auto&& idx = expr::to_expression(prepare_term(_selector, db, keyspace, lists::index_spec_of(*receiver.column_specification))); + auto&& idx = prepare_term(_selector, db, keyspace, lists::index_spec_of(*receiver.column_specification)); return make_shared(receiver, std::move(idx), std::move(lval)); } } else if (rtype->get_kind() == abstract_type::kind::set) { throw invalid_request_exception(format("Invalid operation ({}) for set column {}", to_string(receiver), receiver.name())); } else if (rtype->get_kind() == abstract_type::kind::map) { - auto key = expr::to_expression(prepare_term(_selector, db, keyspace, maps::key_spec_of(*receiver.column_specification))); - auto mval = expr::to_expression(prepare_term(_value, db, keyspace, maps::value_spec_of(*receiver.column_specification))); + auto key = prepare_term(_selector, db, keyspace, maps::key_spec_of(*receiver.column_specification)); + auto mval = prepare_term(_value, db, keyspace, maps::value_spec_of(*receiver.column_specification)); return make_shared(receiver, std::move(key), std::move(mval)); } abort(); @@ -115,7 +115,7 @@ operation::set_field::prepare(database& db, const sstring& keyspace, const colum format("UDT column {} does not have a field named {}", receiver.name_as_text(), *_field)); } - auto val = expr::to_expression(prepare_term(_value, db, keyspace, user_types::field_spec_of(*receiver.column_specification, *idx))); + auto val = prepare_term(_value, db, keyspace, user_types::field_spec_of(*receiver.column_specification, *idx)); return make_shared(receiver, *idx, std::move(val)); } @@ -161,7 +161,7 @@ operation::addition::to_string(const column_definition& receiver) const { shared_ptr operation::addition::prepare(database& db, const sstring& keyspace, const column_definition& receiver) const { - auto v = expr::to_expression(prepare_term(_value, db, keyspace, receiver.column_specification)); + auto v = prepare_term(_value, db, keyspace, receiver.column_specification); auto ctype = dynamic_pointer_cast(receiver.type); if (!ctype) { @@ -201,7 +201,7 @@ operation::subtraction::prepare(database& db, const sstring& keyspace, const col if (!receiver.is_counter()) { throw exceptions::invalid_request_exception(format("Invalid operation ({}) for non counter column {}", to_string(receiver), receiver.name())); } - auto v = expr::to_expression(prepare_term(_value, db, keyspace, receiver.column_specification)); + auto v = prepare_term(_value, db, keyspace, receiver.column_specification); return make_shared(receiver, std::move(v)); } if (!ctype->is_multi_cell()) { @@ -210,9 +210,9 @@ operation::subtraction::prepare(database& db, const sstring& keyspace, const col } if (ctype->get_kind() == abstract_type::kind::list) { - return make_shared(receiver, expr::to_expression(prepare_term(_value, db, keyspace, receiver.column_specification))); + return make_shared(receiver, prepare_term(_value, db, keyspace, receiver.column_specification)); } else if (ctype->get_kind() == abstract_type::kind::set) { - return make_shared(receiver, expr::to_expression(prepare_term(_value, db, keyspace, receiver.column_specification))); + return make_shared(receiver, prepare_term(_value, db, keyspace, receiver.column_specification)); } else if (ctype->get_kind() == abstract_type::kind::map) { auto&& mtype = dynamic_pointer_cast(ctype); // The value for a map subtraction is actually a set @@ -221,7 +221,7 @@ operation::subtraction::prepare(database& db, const sstring& keyspace, const col receiver.column_specification->cf_name, receiver.column_specification->name, set_type_impl::get_instance(mtype->get_keys_type(), false)); - return ::make_shared(receiver, expr::to_expression(prepare_term(_value, db, keyspace, std::move(vr)))); + return ::make_shared(receiver, prepare_term(_value, db, keyspace, std::move(vr))); } abort(); } @@ -238,7 +238,7 @@ operation::prepend::to_string(const column_definition& receiver) const { shared_ptr operation::prepend::prepare(database& db, const sstring& keyspace, const column_definition& receiver) const { - auto v = expr::to_expression(prepare_term(_value, db, keyspace, receiver.column_specification)); + auto v = prepare_term(_value, db, keyspace, receiver.column_specification); if (!dynamic_cast(receiver.type.get())) { throw exceptions::invalid_request_exception(format("Invalid operation ({}) for non list column {}", to_string(receiver), receiver.name())); @@ -257,7 +257,7 @@ operation::prepend::is_compatible_with(const std::unique_ptr& other) ::shared_ptr operation::set_value::prepare(database& db, const sstring& keyspace, const column_definition& receiver) const { - auto v = expr::to_expression(prepare_term(_value, db, keyspace, receiver.column_specification)); + auto v = prepare_term(_value, db, keyspace, receiver.column_specification); if (receiver.type->is_counter()) { throw exceptions::invalid_request_exception(format("Cannot set the value of counter column {} (counters can only be incremented/decremented, not set)", receiver.name_as_text())); @@ -295,7 +295,7 @@ operation::set_counter_value_from_tuple_list::prepare(database& db, const sstrin // We need to fake a column of list> to prepare the value term auto & os = receiver.column_specification; auto spec = make_lw_shared(os->ks_name, os->cf_name, os->name, counter_tuple_list_type); - auto v = expr::to_expression(prepare_term(_value, db, keyspace, spec)); + auto v = prepare_term(_value, db, keyspace, spec); // Will not be used elsewhere, so make it local. class counter_setter : public operation { @@ -380,13 +380,13 @@ operation::element_deletion::prepare(database& db, const sstring& keyspace, cons } auto ctype = static_pointer_cast(receiver.type); if (ctype->get_kind() == abstract_type::kind::list) { - auto&& idx = expr::to_expression(prepare_term(_element, db, keyspace, lists::index_spec_of(*receiver.column_specification))); + auto&& idx = prepare_term(_element, db, keyspace, lists::index_spec_of(*receiver.column_specification)); return make_shared(receiver, std::move(idx)); } else if (ctype->get_kind() == abstract_type::kind::set) { - auto&& elt = expr::to_expression(prepare_term(_element, db, keyspace, sets::value_spec_of(*receiver.column_specification))); + auto&& elt = prepare_term(_element, db, keyspace, sets::value_spec_of(*receiver.column_specification)); return make_shared(receiver, std::move(elt)); } else if (ctype->get_kind() == abstract_type::kind::map) { - auto&& key = expr::to_expression(prepare_term(_element, db, keyspace, maps::key_spec_of(*receiver.column_specification))); + auto&& key = prepare_term(_element, db, keyspace, maps::key_spec_of(*receiver.column_specification)); return make_shared(receiver, std::move(key)); } abort(); diff --git a/cql3/single_column_relation.cc b/cql3/single_column_relation.cc index ddc5aa1112..31c6d7f8fc 100644 --- a/cql3/single_column_relation.cc +++ b/cql3/single_column_relation.cc @@ -62,7 +62,7 @@ single_column_relation::to_expression(const std::vector("", true); auto column_spec = make_lw_shared("", "", dummy_ident, state_type); - auto initcond_term = prepare_term(_ival, db, _name.keyspace, {column_spec}); - bytes_opt initcond = to_bytes(*to_managed_bytes_opt(expr::evaluate_to_raw_view(initcond_term, cql3::query_options::DEFAULT))); + auto initcond_term = expr::evaluate(prepare_term(_ival, db, _name.keyspace, {column_spec}), query_options::DEFAULT); + bytes_opt initcond = std::move(initcond_term.value).to_bytes(); _aggregate = ::make_shared(_name, initcond, std::move(state_func), std::move(final_func)); return; diff --git a/cql3/statements/select_statement.cc b/cql3/statements/select_statement.cc index eec18ae575..66102fc71c 100644 --- a/cql3/statements/select_statement.cc +++ b/cql3/statements/select_statement.cc @@ -1486,7 +1486,7 @@ select_statement::prepare_limit(database& db, prepare_context& ctx, const std::o return std::nullopt; } - expr::expression prep_limit = expr::to_expression(prepare_term(*limit, db, keyspace(), limit_receiver())); + expr::expression prep_limit = prepare_term(*limit, db, keyspace(), limit_receiver()); expr::fill_prepare_context(prep_limit, ctx); return prep_limit; } diff --git a/cql3/statements/update_statement.cc b/cql3/statements/update_statement.cc index 42060ff8d6..6cb4e9eb57 100644 --- a/cql3/statements/update_statement.cc +++ b/cql3/statements/update_statement.cc @@ -363,7 +363,7 @@ insert_json_statement::prepare_internal(database& db, schema_ptr schema, (void)_if_not_exists; assert(expr::is(_json_value) || expr::is(_json_value)); auto json_column_placeholder = ::make_shared("", true); - auto prepared_json_value = expr::to_expression(prepare_term(_json_value, db, "", make_lw_shared("", "", json_column_placeholder, utf8_type))); + auto prepared_json_value = prepare_term(_json_value, db, "", make_lw_shared("", "", json_column_placeholder, utf8_type)); expr::fill_prepare_context(prepared_json_value, ctx); auto stmt = ::make_shared(ctx.bound_variables_size(), schema, std::move(attrs), stats, std::move(prepared_json_value), _default_unset); prepare_conditions(db, *schema, ctx, *stmt); diff --git a/cql3/token_relation.cc b/cql3/token_relation.cc index 284ee400ff..91c0e9c948 100644 --- a/cql3/token_relation.cc +++ b/cql3/token_relation.cc @@ -134,7 +134,7 @@ cql3::expr::expression cql3::token_relation::to_expression( const std::vector>& receivers, const expr::expression& raw, database& db, const sstring& keyspace, prepare_context& ctx) const { - auto e = expr::to_expression(expr::prepare_term(raw, db, keyspace, receivers.front())); + auto e = expr::prepare_term(raw, db, keyspace, receivers.front()); expr::fill_prepare_context(e, ctx); return e; } From dcd3199037457158f6e2516231906d04f9637da6 Mon Sep 17 00:00:00 2001 From: Jan Ciolek Date: Fri, 8 Oct 2021 21:45:54 +0200 Subject: [PATCH 22/24] cql3: Rename prepare_term to prepare_expression prepare_term now takes an expression and returns a prepared expression. It should be renamed to prepare_expression. Signed-off-by: Jan Ciolek --- CMakeLists.txt | 2 +- configure.py | 2 +- cql3/attributes.cc | 6 +- cql3/column_condition.cc | 12 +-- cql3/expr/expression.hh | 4 +- cql3/expr/{term_expr.cc => prepare_expr.cc} | 74 +++++++++---------- cql3/functions/functions.cc | 2 +- cql3/multi_column_relation.hh | 2 +- cql3/operation.cc | 34 ++++----- cql3/single_column_relation.cc | 2 +- cql3/statements/create_aggregate_statement.cc | 2 +- cql3/statements/select_statement.cc | 2 +- cql3/statements/update_statement.cc | 2 +- cql3/token_relation.cc | 2 +- 14 files changed, 74 insertions(+), 74 deletions(-) rename cql3/expr/{term_expr.cc => prepare_expr.cc} (91%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6c6bce7237..abfc4bcbb6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -349,7 +349,7 @@ set(scylla_sources cql3/constants.cc cql3/cql3_type.cc cql3/expr/expression.cc - cql3/expr/term_expr.cc + cql3/expr/prepare_expr.cc cql3/functions/aggregate_fcts.cc cql3/functions/castas_fcts.cc cql3/functions/error_injection_fcts.cc diff --git a/configure.py b/configure.py index 6f00d51f75..c134f87398 100755 --- a/configure.py +++ b/configure.py @@ -760,7 +760,7 @@ scylla_core = (['database.cc', 'cql3/maps.cc', 'cql3/values.cc', 'cql3/expr/expression.cc', - 'cql3/expr/term_expr.cc', + 'cql3/expr/prepare_expr.cc', 'cql3/functions/user_function.cc', 'cql3/functions/functions.cc', 'cql3/functions/aggregate_fcts.cc', diff --git a/cql3/attributes.cc b/cql3/attributes.cc index 37ad2c9f37..2f6b004bab 100644 --- a/cql3/attributes.cc +++ b/cql3/attributes.cc @@ -154,15 +154,15 @@ std::unique_ptr attributes::raw::prepare(database& db, const sstring std::optional ts, ttl, to; if (timestamp.has_value()) { - ts = prepare_term(*timestamp, db, ks_name, timestamp_receiver(ks_name, cf_name)); + ts = prepare_expression(*timestamp, db, ks_name, timestamp_receiver(ks_name, cf_name)); } if (time_to_live.has_value()) { - ttl = prepare_term(*time_to_live, db, ks_name, time_to_live_receiver(ks_name, cf_name)); + ttl = prepare_expression(*time_to_live, db, ks_name, time_to_live_receiver(ks_name, cf_name)); } if (timeout.has_value()) { - to = prepare_term(*timeout, db, ks_name, timeout_receiver(ks_name, cf_name)); + to = prepare_expression(*timeout, db, ks_name, timeout_receiver(ks_name, cf_name)); } return std::unique_ptr{new attributes{std::move(ts), std::move(ttl), std::move(to)}}; diff --git a/cql3/column_condition.cc b/cql3/column_condition.cc index 28b2296cf8..a7366f6ec7 100644 --- a/cql3/column_condition.cc +++ b/cql3/column_condition.cc @@ -314,13 +314,13 @@ column_condition::raw::prepare(database& db, const sstring& keyspace, const colu throw exceptions::invalid_request_exception( format("Unsupported collection type {} in a condition with element access", ctype->cql3_type_name())); } - collection_element_expression = prepare_term(*_collection_element, db, keyspace, element_spec); + collection_element_expression = prepare_expression(*_collection_element, db, keyspace, element_spec); } if (is_compare(_op)) { validate_operation_on_durations(*receiver.type, _op); return column_condition::condition(receiver, std::move(collection_element_expression), - prepare_term(*_value, db, keyspace, value_spec), nullptr, _op); + prepare_expression(*_value, db, keyspace, value_spec), nullptr, _op); } if (_op == expr::oper_t::LIKE) { @@ -329,14 +329,14 @@ column_condition::raw::prepare(database& db, const sstring& keyspace, const colu // Pass matcher object const sstring& pattern = literal_term->raw_text; return column_condition::condition(receiver, std::move(collection_element_expression), - prepare_term(*_value, db, keyspace, value_spec), + prepare_expression(*_value, db, keyspace, value_spec), std::make_unique(bytes_view(reinterpret_cast(pattern.data()), pattern.size())), _op); } else { // Pass through rhs value, matcher object built on execution // TODO: caller should validate parametrized LIKE pattern return column_condition::condition(receiver, std::move(collection_element_expression), - prepare_term(*_value, db, keyspace, value_spec), nullptr, _op); + prepare_expression(*_value, db, keyspace, value_spec), nullptr, _op); } } @@ -346,14 +346,14 @@ column_condition::raw::prepare(database& db, const sstring& keyspace, const colu if (_in_marker) { assert(_in_values.empty()); - expr::expression multi_item_term = prepare_term(*_in_marker, db, keyspace, value_spec); + expr::expression multi_item_term = prepare_expression(*_in_marker, db, keyspace, value_spec); return column_condition::in_condition(receiver, collection_element_expression, std::move(multi_item_term), {}); } // Both _in_values and in _in_marker can be missing in case of empty IN list: "a IN ()" std::vector terms; terms.reserve(_in_values.size()); for (auto&& value : _in_values) { - terms.push_back(prepare_term(value, db, keyspace, value_spec)); + terms.push_back(prepare_expression(value, db, keyspace, value_spec)); } return column_condition::in_condition(receiver, std::move(collection_element_expression), std::nullopt, std::move(terms)); diff --git a/cql3/expr/expression.hh b/cql3/expr/expression.hh index e0e6ec49a3..1d0d15d39f 100644 --- a/cql3/expr/expression.hh +++ b/cql3/expr/expression.hh @@ -669,8 +669,8 @@ extern expression replace_token(const expression&, const column_definition*); extern expression search_and_replace(const expression& e, const noncopyable_function (const expression& candidate)>& replace_candidate); -extern expression prepare_term(const expression& expr, database& db, const sstring& keyspace, lw_shared_ptr receiver); -extern expression prepare_term_multi_column(const expression& expr, database& db, const sstring& keyspace, const std::vector>& receivers); +extern expression prepare_expression(const expression& expr, database& db, const sstring& keyspace, lw_shared_ptr receiver); +extern expression prepare_expression_multi_column(const expression& expr, database& db, const sstring& keyspace, const std::vector>& receivers); /** diff --git a/cql3/expr/term_expr.cc b/cql3/expr/prepare_expr.cc similarity index 91% rename from cql3/expr/term_expr.cc rename to cql3/expr/prepare_expr.cc index bab07a812f..d98ecbfcf1 100644 --- a/cql3/expr/term_expr.cc +++ b/cql3/expr/prepare_expr.cc @@ -84,7 +84,7 @@ usertype_constructor_test_assignment(const usertype_constructor& u, database& db static expression -usertype_constructor_prepare_term(const usertype_constructor& u, database& db, const sstring& keyspace, lw_shared_ptr receiver) { +usertype_constructor_prepare_expression(const usertype_constructor& u, database& db, const sstring& keyspace, lw_shared_ptr receiver) { usertype_constructor_validate_assignable_to(u, db, keyspace, *receiver); auto&& ut = static_pointer_cast(receiver->type); bool all_terminal = true; @@ -101,7 +101,7 @@ usertype_constructor_prepare_term(const usertype_constructor& u, database& db, c raw = iraw->second; ++found_values; } - expression value = prepare_term(raw, db, keyspace, usertype_field_spec_of(*receiver, i)); + expression value = prepare_expression(raw, db, keyspace, usertype_field_spec_of(*receiver, i)); if (!is(value)) { all_terminal = false; @@ -202,7 +202,7 @@ map_test_assignment(const collection_constructor& c, database& db, const sstring static expression -map_prepare_term(const collection_constructor& c, database& db, const sstring& keyspace, lw_shared_ptr receiver) { +map_prepare_expression(const collection_constructor& c, database& db, const sstring& keyspace, lw_shared_ptr receiver) { map_validate_assignable_to(c, db, keyspace, *receiver); auto key_spec = maps::key_spec_of(*receiver); @@ -215,8 +215,8 @@ map_prepare_term(const collection_constructor& c, database& db, const sstring& k if (entry_tuple.elements.size() != 2) { on_internal_error(expr_logger, "map element is not a tuple of arity 2"); } - expression k = prepare_term(entry_tuple.elements[0], db, keyspace, key_spec); - expression v = prepare_term(entry_tuple.elements[1], db, keyspace, value_spec); + expression k = prepare_expression(entry_tuple.elements[0], db, keyspace, key_spec); + expression v = prepare_expression(entry_tuple.elements[1], db, keyspace, value_spec); if (contains_bind_marker(k) || contains_bind_marker(k)) { throw exceptions::invalid_request_exception(format("Invalid map literal for {}: bind variables are not supported inside collection literals", *receiver->name)); @@ -297,7 +297,7 @@ set_test_assignment(const collection_constructor& c, database& db, const sstring static expression -set_prepare_term(const collection_constructor& c, database& db, const sstring& keyspace, lw_shared_ptr receiver) { +set_prepare_expression(const collection_constructor& c, database& db, const sstring& keyspace, lw_shared_ptr receiver) { set_validate_assignable_to(c, db, keyspace, *receiver); if (c.elements.empty()) { @@ -328,7 +328,7 @@ set_prepare_term(const collection_constructor& c, database& db, const sstring& k bool all_terminal = true; for (auto& e : c.elements) { - expression elem = prepare_term(e, db, keyspace, value_spec); + expression elem = prepare_expression(e, db, keyspace, value_spec); if (contains_bind_marker(elem)) { throw exceptions::invalid_request_exception(format("Invalid set literal for {}: bind variables are not supported inside collection literals", *receiver->name)); @@ -397,7 +397,7 @@ list_test_assignment(const collection_constructor& c, database& db, const sstrin static expression -list_prepare_term(const collection_constructor& c, database& db, const sstring& keyspace, lw_shared_ptr receiver) { +list_prepare_expression(const collection_constructor& c, database& db, const sstring& keyspace, lw_shared_ptr receiver) { list_validate_assignable_to(c, db, keyspace, *receiver); // In Cassandra, an empty (unfrozen) map/set/list is equivalent to the column being null. In @@ -413,7 +413,7 @@ list_prepare_term(const collection_constructor& c, database& db, const sstring& values.reserve(c.elements.size()); bool all_terminal = true; for (auto& e : c.elements) { - expression elem = prepare_term(e, db, keyspace, value_spec); + expression elem = prepare_expression(e, db, keyspace, value_spec); if (contains_bind_marker(elem)) { throw exceptions::invalid_request_exception(format("Invalid list literal for {}: bind variables are not supported inside collection literals", *receiver->name)); @@ -484,7 +484,7 @@ tuple_constructor_prepare_nontuple(const tuple_constructor& tc, database& db, co std::vector values; bool all_terminal = true; for (size_t i = 0; i < tc.elements.size(); ++i) { - expression value = prepare_term(tc.elements[i], db, keyspace, component_spec_of(*receiver, i)); + expression value = prepare_expression(tc.elements[i], db, keyspace, component_spec_of(*receiver, i)); if (!is(value)) { all_terminal = false; } @@ -512,7 +512,7 @@ tuple_constructor_prepare_tuple(const tuple_constructor& tc, database& db, const std::vector types; bool all_terminal = true; for (size_t i = 0; i < tc.elements.size(); ++i) { - expression elem = prepare_term(tc.elements[i], db, keyspace, receivers[i]); + expression elem = prepare_expression(tc.elements[i], db, keyspace, receivers[i]); if (!is(elem)) { all_terminal = false; } @@ -641,7 +641,7 @@ untyped_constant_test_assignment(const untyped_constant& uc, database& db, const static constant -untyped_constant_prepare_term(const untyped_constant& uc, database& db, const sstring& keyspace, lw_shared_ptr receiver) +untyped_constant_prepare_expression(const untyped_constant& uc, database& db, const sstring& keyspace, lw_shared_ptr receiver) { if (!is_assignable(untyped_constant_test_assignment(uc, db, keyspace, *receiver))) { throw exceptions::invalid_request_exception(format("Invalid {} constant ({}) for \"{}\" of type {}", @@ -659,7 +659,7 @@ bind_variable_test_assignment(const bind_variable& bv, database& db, const sstri static bind_variable -bind_variable_scalar_prepare_term(const bind_variable& bv, database& db, const sstring& keyspace, lw_shared_ptr receiver) +bind_variable_scalar_prepare_expression(const bind_variable& bv, database& db, const sstring& keyspace, lw_shared_ptr receiver) { return bind_variable { .shape = bind_variable::shape_type::scalar, @@ -677,7 +677,7 @@ bind_variable_scalar_in_make_receiver(const column_specification& receiver) { static bind_variable -bind_variable_scalar_in_prepare_term(const bind_variable& bv, database& db, const sstring& keyspace, lw_shared_ptr receiver) { +bind_variable_scalar_in_prepare_expression(const bind_variable& bv, database& db, const sstring& keyspace, lw_shared_ptr receiver) { return bind_variable { .shape = bind_variable::shape_type::scalar, .bind_index = bv.bind_index, @@ -707,7 +707,7 @@ bind_variable_tuple_make_receiver(const std::vector>& receivers) { +bind_variable_tuple_prepare_expression(const bind_variable& bv, database& db, const sstring& keyspace, const std::vector>& receivers) { return bind_variable { .shape = bind_variable::shape_type::tuple, .bind_index = bv.bind_index, @@ -742,7 +742,7 @@ bind_variable_tuple_in_make_receiver(const std::vector>& receivers) { +bind_variable_tuple_in_prepare_expression(const bind_variable& bv, database& db, const sstring& keyspace, const std::vector>& receivers) { return bind_variable { .shape = bind_variable::shape_type::tuple_in, .bind_index = bv.bind_index, @@ -762,7 +762,7 @@ null_test_assignment(database& db, static constant -null_prepare_term(database& db, const sstring& keyspace, lw_shared_ptr receiver) { +null_prepare_expression(database& db, const sstring& keyspace, lw_shared_ptr receiver) { if (!is_assignable(null_test_assignment(db, keyspace, *receiver))) { throw exceptions::invalid_request_exception("Invalid null value for counter increment/decrement"); } @@ -803,7 +803,7 @@ cast_test_assignment(const cast& c, database& db, const sstring& keyspace, const static expression -cast_prepare_term(const cast& c, database& db, const sstring& keyspace, lw_shared_ptr receiver) { +cast_prepare_expression(const cast& c, database& db, const sstring& keyspace, lw_shared_ptr receiver) { auto type = std::get>(c.type); if (!is_assignable(test_assignment(c.arg, db, keyspace, *casted_spec_of(c, db, keyspace, *receiver)))) { throw exceptions::invalid_request_exception(format("Cannot cast value {} to type {}", c.arg, type)); @@ -811,11 +811,11 @@ cast_prepare_term(const cast& c, database& db, const sstring& keyspace, lw_share if (!is_assignable(cast_test_assignment(c, db, keyspace, *receiver))) { throw exceptions::invalid_request_exception(format("Cannot assign value {} to {} of type {}", c, receiver->name, receiver->type->as_cql3_type())); } - return prepare_term(c.arg, db, keyspace, receiver); + return prepare_expression(c.arg, db, keyspace, receiver); } expression -prepare_term(const expression& expr, database& db, const sstring& keyspace, lw_shared_ptr receiver) { +prepare_expression(const expression& expr, database& db, const sstring& keyspace, lw_shared_ptr receiver) { return expr::visit(overloaded_functor{ [] (const constant&) -> expression { on_internal_error(expr_logger, "Can't prepare constant_value, it should not appear in parser output"); @@ -842,52 +842,52 @@ prepare_term(const expression& expr, database& db, const sstring& keyspace, lw_s return functions::prepare_function_call(fc, db, keyspace, std::move(receiver)); }, [&] (const cast& c) -> expression { - return cast_prepare_term(c, db, keyspace, receiver); + return cast_prepare_expression(c, db, keyspace, receiver); }, [&] (const field_selection&) -> expression { on_internal_error(expr_logger, "field_selections are not yet reachable via term_raw_expr::prepare()"); }, [&] (const null&) -> expression { - return null_prepare_term(db, keyspace, receiver); + return null_prepare_expression(db, keyspace, receiver); }, [&] (const bind_variable& bv) -> expression { switch (bv.shape) { - case expr::bind_variable::shape_type::scalar: return bind_variable_scalar_prepare_term(bv, db, keyspace, receiver); - case expr::bind_variable::shape_type::scalar_in: return bind_variable_scalar_in_prepare_term(bv, db, keyspace, receiver); - case expr::bind_variable::shape_type::tuple: on_internal_error(expr_logger, "prepare_term(bind_variable(tuple))"); - case expr::bind_variable::shape_type::tuple_in: on_internal_error(expr_logger, "prepare_term(bind_variable(tuple_in))"); + case expr::bind_variable::shape_type::scalar: return bind_variable_scalar_prepare_expression(bv, db, keyspace, receiver); + case expr::bind_variable::shape_type::scalar_in: return bind_variable_scalar_in_prepare_expression(bv, db, keyspace, receiver); + case expr::bind_variable::shape_type::tuple: on_internal_error(expr_logger, "prepare_expression(bind_variable(tuple))"); + case expr::bind_variable::shape_type::tuple_in: on_internal_error(expr_logger, "prepare_expression(bind_variable(tuple_in))"); } on_internal_error(expr_logger, "unexpected shape in bind_variable"); }, [&] (const untyped_constant& uc) -> expression { - return untyped_constant_prepare_term(uc, db, keyspace, receiver); + return untyped_constant_prepare_expression(uc, db, keyspace, receiver); }, [&] (const tuple_constructor& tc) -> expression { return tuple_constructor_prepare_nontuple(tc, db, keyspace, receiver); }, [&] (const collection_constructor& c) -> expression { switch (c.style) { - case collection_constructor::style_type::list: return list_prepare_term(c, db, keyspace, receiver); - case collection_constructor::style_type::set: return set_prepare_term(c, db, keyspace, receiver); - case collection_constructor::style_type::map: return map_prepare_term(c, db, keyspace, receiver); + case collection_constructor::style_type::list: return list_prepare_expression(c, db, keyspace, receiver); + case collection_constructor::style_type::set: return set_prepare_expression(c, db, keyspace, receiver); + case collection_constructor::style_type::map: return map_prepare_expression(c, db, keyspace, receiver); } on_internal_error(expr_logger, fmt::format("unexpected collection_constructor style {}", static_cast(c.style))); }, [&] (const usertype_constructor& uc) -> expression { - return usertype_constructor_prepare_term(uc, db, keyspace, receiver); + return usertype_constructor_prepare_expression(uc, db, keyspace, receiver); }, }, expr); } expression -prepare_term_multi_column(const expression& expr, database& db, const sstring& keyspace, const std::vector>& receivers) { +prepare_expression_multi_column(const expression& expr, database& db, const sstring& keyspace, const std::vector>& receivers) { return expr::visit(overloaded_functor{ [&] (const bind_variable& bv) -> expression { switch (bv.shape) { - case expr::bind_variable::shape_type::scalar: on_internal_error(expr_logger, "prepare_term_multi_column(bind_variable(scalar))"); - case expr::bind_variable::shape_type::scalar_in: on_internal_error(expr_logger, "prepare_term_multi_column(bind_variable(scalar_in))"); - case expr::bind_variable::shape_type::tuple: return bind_variable_tuple_prepare_term(bv, db, keyspace, receivers); - case expr::bind_variable::shape_type::tuple_in: return bind_variable_tuple_in_prepare_term(bv, db, keyspace, receivers); + case expr::bind_variable::shape_type::scalar: on_internal_error(expr_logger, "prepare_expression_multi_column(bind_variable(scalar))"); + case expr::bind_variable::shape_type::scalar_in: on_internal_error(expr_logger, "prepare_expression_multi_column(bind_variable(scalar_in))"); + case expr::bind_variable::shape_type::tuple: return bind_variable_tuple_prepare_expression(bv, db, keyspace, receivers); + case expr::bind_variable::shape_type::tuple_in: return bind_variable_tuple_in_prepare_expression(bv, db, keyspace, receivers); } on_internal_error(expr_logger, "unexpected shape in bind_variable"); }, @@ -895,7 +895,7 @@ prepare_term_multi_column(const expression& expr, database& db, const sstring& k return tuple_constructor_prepare_tuple(tc, db, keyspace, receivers); }, [] (const auto& default_case) -> expression { - on_internal_error(expr_logger, fmt::format("prepare_term_multi_column({})", typeid(default_case).name())); + on_internal_error(expr_logger, fmt::format("prepare_expression_multi_column({})", typeid(default_case).name())); }, }, expr); } diff --git a/cql3/functions/functions.cc b/cql3/functions/functions.cc index 25c22efd19..478257f415 100644 --- a/cql3/functions/functions.cc +++ b/cql3/functions/functions.cc @@ -608,7 +608,7 @@ prepare_function_call(const expr::function_call& fc, database& db, const sstring parameters.reserve(fc.args.size()); bool all_terminal = true; for (size_t i = 0; i < fc.args.size(); ++i) { - expr::expression e = prepare_term(fc.args[i], db, keyspace, functions::make_arg_spec(receiver->ks_name, receiver->cf_name, *scalar_fun, i)); + expr::expression e = prepare_expression(fc.args[i], db, keyspace, functions::make_arg_spec(receiver->ks_name, receiver->cf_name, *scalar_fun, i)); if (!expr::is(e)) { all_terminal = false; } diff --git a/cql3/multi_column_relation.hh b/cql3/multi_column_relation.hh index d09a008ef7..5dd9390cb2 100644 --- a/cql3/multi_column_relation.hh +++ b/cql3/multi_column_relation.hh @@ -218,7 +218,7 @@ protected: virtual expr::expression to_expression(const std::vector>& receivers, const expr::expression& raw, database& db, const sstring& keyspace, prepare_context& ctx) const override { - auto e = prepare_term_multi_column(raw, db, keyspace, receivers); + auto e = prepare_expression_multi_column(raw, db, keyspace, receivers); expr::fill_prepare_context(e, ctx); return e; } diff --git a/cql3/operation.cc b/cql3/operation.cc index 5781e24e94..8c7d7f4b46 100644 --- a/cql3/operation.cc +++ b/cql3/operation.cc @@ -68,19 +68,19 @@ operation::set_element::prepare(database& db, const sstring& keyspace, const col } if (rtype->get_kind() == abstract_type::kind::list) { - auto&& lval = prepare_term(_value, db, keyspace, lists::value_spec_of(*receiver.column_specification)); + auto&& lval = prepare_expression(_value, db, keyspace, lists::value_spec_of(*receiver.column_specification)); if (_by_uuid) { - auto&& idx = prepare_term(_selector, db, keyspace, lists::uuid_index_spec_of(*receiver.column_specification)); + auto&& idx = prepare_expression(_selector, db, keyspace, lists::uuid_index_spec_of(*receiver.column_specification)); return make_shared(receiver, std::move(idx), std::move(lval)); } else { - auto&& idx = prepare_term(_selector, db, keyspace, lists::index_spec_of(*receiver.column_specification)); + auto&& idx = prepare_expression(_selector, db, keyspace, lists::index_spec_of(*receiver.column_specification)); return make_shared(receiver, std::move(idx), std::move(lval)); } } else if (rtype->get_kind() == abstract_type::kind::set) { throw invalid_request_exception(format("Invalid operation ({}) for set column {}", to_string(receiver), receiver.name())); } else if (rtype->get_kind() == abstract_type::kind::map) { - auto key = prepare_term(_selector, db, keyspace, maps::key_spec_of(*receiver.column_specification)); - auto mval = prepare_term(_value, db, keyspace, maps::value_spec_of(*receiver.column_specification)); + auto key = prepare_expression(_selector, db, keyspace, maps::key_spec_of(*receiver.column_specification)); + auto mval = prepare_expression(_value, db, keyspace, maps::value_spec_of(*receiver.column_specification)); return make_shared(receiver, std::move(key), std::move(mval)); } abort(); @@ -115,7 +115,7 @@ operation::set_field::prepare(database& db, const sstring& keyspace, const colum format("UDT column {} does not have a field named {}", receiver.name_as_text(), *_field)); } - auto val = prepare_term(_value, db, keyspace, user_types::field_spec_of(*receiver.column_specification, *idx)); + auto val = prepare_expression(_value, db, keyspace, user_types::field_spec_of(*receiver.column_specification, *idx)); return make_shared(receiver, *idx, std::move(val)); } @@ -161,7 +161,7 @@ operation::addition::to_string(const column_definition& receiver) const { shared_ptr operation::addition::prepare(database& db, const sstring& keyspace, const column_definition& receiver) const { - auto v = prepare_term(_value, db, keyspace, receiver.column_specification); + auto v = prepare_expression(_value, db, keyspace, receiver.column_specification); auto ctype = dynamic_pointer_cast(receiver.type); if (!ctype) { @@ -201,7 +201,7 @@ operation::subtraction::prepare(database& db, const sstring& keyspace, const col if (!receiver.is_counter()) { throw exceptions::invalid_request_exception(format("Invalid operation ({}) for non counter column {}", to_string(receiver), receiver.name())); } - auto v = prepare_term(_value, db, keyspace, receiver.column_specification); + auto v = prepare_expression(_value, db, keyspace, receiver.column_specification); return make_shared(receiver, std::move(v)); } if (!ctype->is_multi_cell()) { @@ -210,9 +210,9 @@ operation::subtraction::prepare(database& db, const sstring& keyspace, const col } if (ctype->get_kind() == abstract_type::kind::list) { - return make_shared(receiver, prepare_term(_value, db, keyspace, receiver.column_specification)); + return make_shared(receiver, prepare_expression(_value, db, keyspace, receiver.column_specification)); } else if (ctype->get_kind() == abstract_type::kind::set) { - return make_shared(receiver, prepare_term(_value, db, keyspace, receiver.column_specification)); + return make_shared(receiver, prepare_expression(_value, db, keyspace, receiver.column_specification)); } else if (ctype->get_kind() == abstract_type::kind::map) { auto&& mtype = dynamic_pointer_cast(ctype); // The value for a map subtraction is actually a set @@ -221,7 +221,7 @@ operation::subtraction::prepare(database& db, const sstring& keyspace, const col receiver.column_specification->cf_name, receiver.column_specification->name, set_type_impl::get_instance(mtype->get_keys_type(), false)); - return ::make_shared(receiver, prepare_term(_value, db, keyspace, std::move(vr))); + return ::make_shared(receiver, prepare_expression(_value, db, keyspace, std::move(vr))); } abort(); } @@ -238,7 +238,7 @@ operation::prepend::to_string(const column_definition& receiver) const { shared_ptr operation::prepend::prepare(database& db, const sstring& keyspace, const column_definition& receiver) const { - auto v = prepare_term(_value, db, keyspace, receiver.column_specification); + auto v = prepare_expression(_value, db, keyspace, receiver.column_specification); if (!dynamic_cast(receiver.type.get())) { throw exceptions::invalid_request_exception(format("Invalid operation ({}) for non list column {}", to_string(receiver), receiver.name())); @@ -257,7 +257,7 @@ operation::prepend::is_compatible_with(const std::unique_ptr& other) ::shared_ptr operation::set_value::prepare(database& db, const sstring& keyspace, const column_definition& receiver) const { - auto v = prepare_term(_value, db, keyspace, receiver.column_specification); + auto v = prepare_expression(_value, db, keyspace, receiver.column_specification); if (receiver.type->is_counter()) { throw exceptions::invalid_request_exception(format("Cannot set the value of counter column {} (counters can only be incremented/decremented, not set)", receiver.name_as_text())); @@ -295,7 +295,7 @@ operation::set_counter_value_from_tuple_list::prepare(database& db, const sstrin // We need to fake a column of list> to prepare the value term auto & os = receiver.column_specification; auto spec = make_lw_shared(os->ks_name, os->cf_name, os->name, counter_tuple_list_type); - auto v = prepare_term(_value, db, keyspace, spec); + auto v = prepare_expression(_value, db, keyspace, spec); // Will not be used elsewhere, so make it local. class counter_setter : public operation { @@ -380,13 +380,13 @@ operation::element_deletion::prepare(database& db, const sstring& keyspace, cons } auto ctype = static_pointer_cast(receiver.type); if (ctype->get_kind() == abstract_type::kind::list) { - auto&& idx = prepare_term(_element, db, keyspace, lists::index_spec_of(*receiver.column_specification)); + auto&& idx = prepare_expression(_element, db, keyspace, lists::index_spec_of(*receiver.column_specification)); return make_shared(receiver, std::move(idx)); } else if (ctype->get_kind() == abstract_type::kind::set) { - auto&& elt = prepare_term(_element, db, keyspace, sets::value_spec_of(*receiver.column_specification)); + auto&& elt = prepare_expression(_element, db, keyspace, sets::value_spec_of(*receiver.column_specification)); return make_shared(receiver, std::move(elt)); } else if (ctype->get_kind() == abstract_type::kind::map) { - auto&& key = prepare_term(_element, db, keyspace, maps::key_spec_of(*receiver.column_specification)); + auto&& key = prepare_expression(_element, db, keyspace, maps::key_spec_of(*receiver.column_specification)); return make_shared(receiver, std::move(key)); } abort(); diff --git a/cql3/single_column_relation.cc b/cql3/single_column_relation.cc index 31c6d7f8fc..7635b89b9a 100644 --- a/cql3/single_column_relation.cc +++ b/cql3/single_column_relation.cc @@ -62,7 +62,7 @@ single_column_relation::to_expression(const std::vector("", true); auto column_spec = make_lw_shared("", "", dummy_ident, state_type); - auto initcond_term = expr::evaluate(prepare_term(_ival, db, _name.keyspace, {column_spec}), query_options::DEFAULT); + auto initcond_term = expr::evaluate(prepare_expression(_ival, db, _name.keyspace, {column_spec}), query_options::DEFAULT); bytes_opt initcond = std::move(initcond_term.value).to_bytes(); _aggregate = ::make_shared(_name, initcond, std::move(state_func), std::move(final_func)); diff --git a/cql3/statements/select_statement.cc b/cql3/statements/select_statement.cc index 66102fc71c..b8c00efa98 100644 --- a/cql3/statements/select_statement.cc +++ b/cql3/statements/select_statement.cc @@ -1486,7 +1486,7 @@ select_statement::prepare_limit(database& db, prepare_context& ctx, const std::o return std::nullopt; } - expr::expression prep_limit = prepare_term(*limit, db, keyspace(), limit_receiver()); + expr::expression prep_limit = prepare_expression(*limit, db, keyspace(), limit_receiver()); expr::fill_prepare_context(prep_limit, ctx); return prep_limit; } diff --git a/cql3/statements/update_statement.cc b/cql3/statements/update_statement.cc index 6cb4e9eb57..fe42c1e71d 100644 --- a/cql3/statements/update_statement.cc +++ b/cql3/statements/update_statement.cc @@ -363,7 +363,7 @@ insert_json_statement::prepare_internal(database& db, schema_ptr schema, (void)_if_not_exists; assert(expr::is(_json_value) || expr::is(_json_value)); auto json_column_placeholder = ::make_shared("", true); - auto prepared_json_value = prepare_term(_json_value, db, "", make_lw_shared("", "", json_column_placeholder, utf8_type)); + auto prepared_json_value = prepare_expression(_json_value, db, "", make_lw_shared("", "", json_column_placeholder, utf8_type)); expr::fill_prepare_context(prepared_json_value, ctx); auto stmt = ::make_shared(ctx.bound_variables_size(), schema, std::move(attrs), stats, std::move(prepared_json_value), _default_unset); prepare_conditions(db, *schema, ctx, *stmt); diff --git a/cql3/token_relation.cc b/cql3/token_relation.cc index 91c0e9c948..c75cc30613 100644 --- a/cql3/token_relation.cc +++ b/cql3/token_relation.cc @@ -134,7 +134,7 @@ cql3::expr::expression cql3::token_relation::to_expression( const std::vector>& receivers, const expr::expression& raw, database& db, const sstring& keyspace, prepare_context& ctx) const { - auto e = expr::prepare_term(raw, db, keyspace, receivers.front()); + auto e = expr::prepare_expression(raw, db, keyspace, receivers.front()); expr::fill_prepare_context(e, ctx); return e; } From e45834082150b00b4442f08dc988cb07c3379ff4 Mon Sep 17 00:00:00 2001 From: Jan Ciolek Date: Fri, 8 Oct 2021 23:47:38 +0200 Subject: [PATCH 23/24] cql3: Remove term term isn't used anywhere now. We can remove it and all classes that derive from it. Signed-off-by: Jan Ciolek --- CMakeLists.txt | 1 - cdc/log.cc | 1 - configure.py | 1 - cql3/Cql.g | 2 - cql3/abstract_marker.cc | 14 -- cql3/abstract_marker.hh | 17 -- cql3/constants.cc | 13 -- cql3/constants.hh | 58 ----- cql3/expr/expression.cc | 54 ----- cql3/expr/expression.hh | 16 -- cql3/expr/prepare_expr.cc | 96 +++++++- cql3/functions/function_call.hh | 92 -------- cql3/functions/functions.cc | 216 ------------------ cql3/lists.cc | 158 ------------- cql3/lists.hh | 58 ----- cql3/maps.cc | 165 ------------- cql3/maps.hh | 39 ---- cql3/multi_column_relation.hh | 12 +- cql3/operation.hh | 1 - cql3/prepare_context.cc | 19 -- cql3/prepare_context.hh | 7 +- cql3/restrictions/multi_column_restriction.hh | 2 +- .../restrictions/single_column_restriction.hh | 1 - cql3/restrictions/statement_restrictions.cc | 1 - cql3/sets.cc | 148 ------------ cql3/sets.hh | 36 --- cql3/single_column_relation.hh | 1 - cql3/statements/batch_statement.hh | 1 + cql3/statements/delete_statement.cc | 1 - cql3/statements/raw/insert_statement.hh | 2 - cql3/statements/update_statement.hh | 2 - cql3/term.hh | 179 --------------- cql3/token_relation.cc | 1 - cql3/token_relation.hh | 1 - cql3/tuples.cc | 126 ---------- cql3/tuples.hh | 211 ----------------- cql3/user_types.cc | 102 --------- cql3/user_types.hh | 40 ---- 38 files changed, 103 insertions(+), 1792 deletions(-) delete mode 100644 cql3/functions/function_call.hh delete mode 100644 cql3/term.hh delete mode 100644 cql3/tuples.cc delete mode 100644 cql3/tuples.hh diff --git a/CMakeLists.txt b/CMakeLists.txt index abfc4bcbb6..b36cc14136 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -428,7 +428,6 @@ set(scylla_sources cql3/statements/update_statement.cc cql3/statements/use_statement.cc cql3/token_relation.cc - cql3/tuples.cc cql3/type_json.cc cql3/untyped_result_set.cc cql3/update_parameters.cc diff --git a/cdc/log.cc b/cdc/log.cc index fb57ca8a9a..3cbc418fa6 100644 --- a/cdc/log.cc +++ b/cdc/log.cc @@ -44,7 +44,6 @@ #include "types/tuple.hh" #include "cql3/statements/select_statement.hh" #include "cql3/multi_column_relation.hh" -#include "cql3/tuples.hh" #include "cql3/untyped_result_set.hh" #include "log.hh" #include "utils/rjson.hh" diff --git a/configure.py b/configure.py index c134f87398..ccb414a599 100755 --- a/configure.py +++ b/configure.py @@ -756,7 +756,6 @@ scylla_core = (['database.cc', 'cql3/keyspace_element_name.cc', 'cql3/lists.cc', 'cql3/sets.cc', - 'cql3/tuples.cc', 'cql3/maps.cc', 'cql3/values.cc', 'cql3/expr/expression.cc', diff --git a/cql3/Cql.g b/cql3/Cql.g index e3430d89aa..a3aff290b6 100644 --- a/cql3/Cql.g +++ b/cql3/Cql.g @@ -97,11 +97,9 @@ options { #include "cql3/lists.hh" #include "cql3/role_name.hh" #include "cql3/role_options.hh" -#include "cql3/tuples.hh" #include "cql3/user_types.hh" #include "cql3/ut_name.hh" #include "cql3/functions/function_name.hh" -#include "cql3/functions/function_call.hh" #include "cql3/expr/expression.hh" #include #include "CqlLexer.hpp" diff --git a/cql3/abstract_marker.cc b/cql3/abstract_marker.cc index 73f423c19d..f1a79a80c1 100644 --- a/cql3/abstract_marker.cc +++ b/cql3/abstract_marker.cc @@ -50,18 +50,4 @@ #include "types/list.hh" namespace cql3 { - -abstract_marker::abstract_marker(int32_t bind_index, lw_shared_ptr&& receiver) - : _bind_index{bind_index} - , _receiver{std::move(receiver)} -{ } - -void abstract_marker::fill_prepare_context(prepare_context& ctx) const { - ctx.add_variable_specification(_bind_index, _receiver); -} - -bool abstract_marker::contains_bind_marker() const { - return true; -} - } diff --git a/cql3/abstract_marker.hh b/cql3/abstract_marker.hh index d10cae218a..9af0af7649 100644 --- a/cql3/abstract_marker.hh +++ b/cql3/abstract_marker.hh @@ -41,26 +41,9 @@ #pragma once -#include "cql3/term.hh" - namespace cql3 { class column_specification; class prepare_context; -/** - * A single bind marker. - */ -class abstract_marker : public non_terminal { -protected: - const int32_t _bind_index; - const lw_shared_ptr _receiver; -public: - abstract_marker(int32_t bind_index, lw_shared_ptr&& receiver); - - virtual void fill_prepare_context(prepare_context& ctx) const override; - - virtual bool contains_bind_marker() const override; -}; - } diff --git a/cql3/constants.cc b/cql3/constants.cc index d1fb009195..ac72c3e812 100644 --- a/cql3/constants.cc +++ b/cql3/constants.cc @@ -43,10 +43,6 @@ #include "cql3/cql3_type.hh" namespace cql3 { - -thread_local const ::shared_ptr constants::UNSET_VALUE = ::make_shared(cql3::raw_value::make_unset_value(), empty_type); -thread_local const ::shared_ptr constants::NULL_VALUE = ::make_shared(); - void constants::deleter::execute(mutation& m, const clustering_key_prefix& prefix, const update_parameters& params) { if (column.type->is_multi_cell()) { collection_mutation_description coll_m; @@ -57,13 +53,4 @@ void constants::deleter::execute(mutation& m, const clustering_key_prefix& prefi m.set_cell(prefix, column, params.make_dead_cell()); } } - -expr::expression constants::marker::to_expression() { - return expr::bind_variable { - .shape = expr::bind_variable::shape_type::scalar, - .bind_index = _bind_index, - .receiver = _receiver - }; -} - } diff --git a/cql3/constants.hh b/cql3/constants.hh index 2491de487b..dda8e5ee1a 100644 --- a/cql3/constants.hh +++ b/cql3/constants.hh @@ -45,7 +45,6 @@ #include "cql3/update_parameters.hh" #include "cql3/operation.hh" #include "cql3/values.hh" -#include "cql3/term.hh" #include "mutation.hh" #include @@ -60,63 +59,6 @@ public: private static final Logger logger = LoggerFactory.getLogger(Constants.class); #endif public: - /** - * A constant value, i.e. a ByteBuffer. - */ - class value : public terminal { - public: - cql3::raw_value _bytes; - value(cql3::raw_value bytes_, data_type my_type) : terminal(std::move(my_type)), _bytes(std::move(bytes_)) {} - virtual cql3::raw_value get(const query_options& options) override { return _bytes; } - virtual sstring to_string() const override { return _bytes.to_view().with_value([] (const FragmentedView auto& v) { return to_hex(v); }); } - }; - - static thread_local const ::shared_ptr UNSET_VALUE; - - class null_value final : public value { - public: - null_value() : value(cql3::raw_value::make_null(), empty_type) {} - virtual ::shared_ptr bind(const query_options& options) override { return {}; } - virtual sstring to_string() const override { return "null"; } - }; - - static thread_local const ::shared_ptr NULL_VALUE; - - class marker : public abstract_marker { - public: - marker(int32_t bind_index, lw_shared_ptr receiver) - : abstract_marker{bind_index, std::move(receiver)} - { - assert(!_receiver->type->is_collection() && !_receiver->type->is_user_type()); - } - - virtual ::shared_ptr bind(const query_options& options) override { - auto bytes = bind_and_get_internal(options); - if (bytes.is_null()) { - return ::shared_ptr{}; - } - if (bytes.is_unset_value()) { - return UNSET_VALUE; - } - return ::make_shared(cql3::raw_value::make_value(bytes), _receiver->type); - } - - virtual expr::expression to_expression() override; - private: - cql3::raw_value_view bind_and_get_internal(const query_options& options) { - try { - auto value = options.get_value_at(_bind_index); - if (value) { - value.validate(*_receiver->type, options.get_cql_serialization_format()); - } - return value; - } catch (const marshal_exception& e) { - throw exceptions::invalid_request_exception( - format("Exception while binding column {:s}: {:s}", _receiver->name->to_cql_string(), e.what())); - } - } - }; - class setter : public operation { public: using operation::operation; diff --git a/cql3/expr/expression.cc b/cql3/expr/expression.cc index 4daa2b76bf..4da7895442 100644 --- a/cql3/expr/expression.cc +++ b/cql3/expr/expression.cc @@ -35,7 +35,6 @@ #include "cql3/constants.hh" #include "cql3/lists.hh" #include "cql3/statements/request_validations.hh" -#include "cql3/tuples.hh" #include "cql3/selection/selection.hh" #include "index/secondary_index_manager.hh" #include "types/list.hh" @@ -49,7 +48,6 @@ #include "cql3/maps.hh" #include "cql3/user_types.hh" #include "cql3/functions/scalar_function.hh" -#include "cql3/functions/function_call.hh" #include "cql3/prepare_context.hh" namespace cql3 { @@ -1312,40 +1310,6 @@ std::optional get_bool_value(const constant& constant_val) { return constant_val.view().deserialize(*boolean_type); } -constant evaluate(term* term_ptr, const query_options& options) { - if (term_ptr == nullptr) { - return constant::make_null(); - } - - return evaluate(term_ptr->to_expression(), options); -} - -constant evaluate(const ::shared_ptr& term_ptr, const query_options& options) { - return evaluate(term_ptr.get(), options); -} - -constant evaluate(term& term_ref, const query_options& options) { - return evaluate(&term_ref, options); -} - -constant evaluate_IN_list(const ::shared_ptr& term_ptr, const query_options& options) { - return evaluate_IN_list(term_ptr.get(), options); -} - -constant evaluate_IN_list(term& term_ref, const query_options& options) { - return evaluate_IN_list(&term_ref, options); -} - -cql3::raw_value_view evaluate_to_raw_view(const ::shared_ptr& term_ptr, const query_options& options) { - constant value = evaluate(term_ptr, options); - return cql3::raw_value_view::make_temporary(std::move(value.value)); -} - -cql3::raw_value_view evaluate_to_raw_view(term& term_ref, const query_options& options) { - constant value = evaluate(term_ref, options); - return cql3::raw_value_view::make_temporary(std::move(value.value)); -} - constant evaluate(const expression& e, const query_options& options) { return expr::visit(overloaded_functor { [](const binary_operator&) -> constant { @@ -1781,16 +1745,6 @@ constant evaluate(const function_call& fun_call, const query_options& options) { return constant(raw_value::make_value(std::move(*result)), scalar_fun->return_type()); } -constant evaluate_IN_list(term* term_ptr, const query_options& options) { - if (term_ptr == nullptr) { - return constant::make_null(); - } - - expression e = term_ptr->to_expression(); - - return evaluate_IN_list(e, options); -} - constant evaluate_IN_list(const expression& e, const query_options& options) { if (auto collection = expr::as_if(&e)) { if (collection->style == collection_constructor::style_type::list) { @@ -1968,13 +1922,5 @@ bool contains_bind_marker(const expression& e) { const bind_variable* search_res = find_in_expression(e, [](const bind_variable&) { return true; }); return search_res != nullptr; } - -expression to_expression(const ::shared_ptr& term_ptr) { - if (term_ptr.get() == nullptr) { - return constant::make_null(); - } - - return term_ptr->to_expression(); -} } // namespace expr } // namespace cql3 diff --git a/cql3/expr/expression.hh b/cql3/expr/expression.hh index 1d0d15d39f..993ea810d2 100644 --- a/cql3/expr/expression.hh +++ b/cql3/expr/expression.hh @@ -52,7 +52,6 @@ namespace query { } // namespace query namespace cql3 { -struct term; struct prepare_context; class column_identifier_raw; @@ -706,23 +705,10 @@ std::vector extract_single_column_restrictions_for_column(const expr std::optional get_bool_value(const constant&); -// Takes a prepared expression and calculates its value. -// Later term will be replaced with expression. -constant evaluate(const ::shared_ptr&, const query_options&); -constant evaluate(term*, const query_options&); -constant evaluate(term&, const query_options&); - // Similar to evaluate(), but ignores any NULL values in the final list value. // In an IN restriction nulls can be ignored, because nothing equals NULL. -constant evaluate_IN_list(const ::shared_ptr&, const query_options&); -constant evaluate_IN_list(term*, const query_options&); -constant evaluate_IN_list(term&, const query_options&); constant evaluate_IN_list(const expression&, const query_options&); -// Calls evaluate() on the term and then converts the constant to raw_value_view -cql3::raw_value_view evaluate_to_raw_view(const ::shared_ptr&, const query_options&); -cql3::raw_value_view evaluate_to_raw_view(term&, const query_options&); - // Takes a prepared expression and calculates its value. // Evaluates bound values, calls functions and returns just the bytes and type. constant evaluate(const expression& e, const query_options&); @@ -755,8 +741,6 @@ void fill_prepare_context(expression&, cql3::prepare_context&); // there can be other things that prevent immediate evaluation of an expression. // For example an expression can contain calls to nonpure functions. bool contains_bind_marker(const expression& e); - -expression to_expression(const ::shared_ptr&); } // namespace expr } // namespace cql3 diff --git a/cql3/expr/prepare_expr.cc b/cql3/expr/prepare_expr.cc index d98ecbfcf1..a78590a7a5 100644 --- a/cql3/expr/prepare_expr.cc +++ b/cql3/expr/prepare_expr.cc @@ -20,14 +20,13 @@ */ #include "expression.hh" -#include "cql3/functions/function_call.hh" +#include "cql3/functions/functions.hh" #include "cql3/column_identifier.hh" #include "cql3/constants.hh" #include "cql3/abstract_marker.hh" #include "cql3/lists.hh" #include "cql3/sets.hh" #include "cql3/user_types.hh" -#include "cql3/tuples.hh" #include "types/list.hh" #include "types/set.hh" #include "types/map.hh" @@ -814,6 +813,95 @@ cast_prepare_expression(const cast& c, database& db, const sstring& keyspace, lw return prepare_expression(c.arg, db, keyspace, receiver); } +expr::expression +prepare_function_call(const expr::function_call& fc, database& db, const sstring& keyspace, lw_shared_ptr receiver) { + auto&& fun = std::visit(overloaded_functor{ + [] (const shared_ptr& func) { + return func; + }, + [&] (const functions::function_name& name) { + auto args = boost::copy_range>>(fc.args | boost::adaptors::transformed(expr::as_assignment_testable)); + auto fun = functions::functions::get(db, keyspace, name, args, receiver->ks_name, receiver->cf_name, receiver.get()); + if (!fun) { + throw exceptions::invalid_request_exception(format("Unknown function {} called", name)); + } + return fun; + }, + }, fc.func); + if (fun->is_aggregate()) { + throw exceptions::invalid_request_exception("Aggregation function are not supported in the where clause"); + } + + // Can't use static_pointer_cast<> because function is a virtual base class of scalar_function + auto&& scalar_fun = dynamic_pointer_cast(fun); + + // Functions.get() will complain if no function "name" type check with the provided arguments. + // We still have to validate that the return type matches however + if (!receiver->type->is_value_compatible_with(*scalar_fun->return_type())) { + throw exceptions::invalid_request_exception(format("Type error: cannot assign result of function {} (type {}) to {} (type {})", + fun->name(), fun->return_type()->as_cql3_type(), + receiver->name, receiver->type->as_cql3_type())); + } + + if (scalar_fun->arg_types().size() != fc.args.size()) { + throw exceptions::invalid_request_exception(format("Incorrect number of arguments specified for function {} (expected {:d}, found {:d})", + fun->name(), fun->arg_types().size(), fc.args.size())); + } + + std::vector parameters; + parameters.reserve(fc.args.size()); + bool all_terminal = true; + for (size_t i = 0; i < fc.args.size(); ++i) { + expr::expression e = prepare_expression(fc.args[i], db, keyspace, + functions::functions::make_arg_spec(receiver->ks_name, receiver->cf_name, *scalar_fun, i)); + if (!expr::is(e)) { + all_terminal = false; + } + parameters.push_back(std::move(e)); + } + + // If all parameters are terminal and the function is pure, we can + // evaluate it now, otherwise we'd have to wait execution time + expr::function_call fun_call { + .func = fun, + .args = std::move(parameters), + .lwt_cache_id = fc.lwt_cache_id + }; + if (all_terminal && scalar_fun->is_pure()) { + return expr::evaluate(fun_call, query_options::DEFAULT); + } else { + return fun_call; + } +} + +assignment_testable::test_result +test_assignment_function_call(const cql3::expr::function_call& fc, database& db, const sstring& keyspace, const column_specification& receiver) { + // Note: Functions.get() will return null if the function doesn't exist, or throw is no function matching + // the arguments can be found. We may get one of those if an undefined/wrong function is used as argument + // of another, existing, function. In that case, we return true here because we'll throw a proper exception + // later with a more helpful error message that if we were to return false here. + try { + auto&& fun = std::visit(overloaded_functor{ + [&] (const functions::function_name& name) { + auto args = boost::copy_range>>(fc.args | boost::adaptors::transformed(expr::as_assignment_testable)); + return functions::functions::get(db, keyspace, name, args, receiver.ks_name, receiver.cf_name, &receiver); + }, + [] (const shared_ptr& func) { + return func; + }, + }, fc.func); + if (fun && receiver.type == fun->return_type()) { + return assignment_testable::test_result::EXACT_MATCH; + } else if (!fun || receiver.type->is_value_compatible_with(*fun->return_type())) { + return assignment_testable::test_result::WEAKLY_ASSIGNABLE; + } else { + return assignment_testable::test_result::NOT_ASSIGNABLE; + } + } catch (exceptions::invalid_request_exception& e) { + return assignment_testable::test_result::WEAKLY_ASSIGNABLE; + } +} + expression prepare_expression(const expression& expr, database& db, const sstring& keyspace, lw_shared_ptr receiver) { return expr::visit(overloaded_functor{ @@ -839,7 +927,7 @@ prepare_expression(const expression& expr, database& db, const sstring& keyspace on_internal_error(expr_logger, "column_mutation_attributes are not yet reachable via term_raw_expr::prepare()"); }, [&] (const function_call& fc) -> expression { - return functions::prepare_function_call(fc, db, keyspace, std::move(receiver)); + return prepare_function_call(fc, db, keyspace, std::move(receiver)); }, [&] (const cast& c) -> expression { return cast_prepare_expression(c, db, keyspace, receiver); @@ -927,7 +1015,7 @@ test_assignment(const expression& expr, database& db, const sstring& keyspace, c on_internal_error(expr_logger, "column_mutation_attributes are not yet reachable via term_raw_expr::test_assignment()"); }, [&] (const function_call& fc) -> test_result { - return functions::test_assignment_function_call(fc, db, keyspace, receiver); + return test_assignment_function_call(fc, db, keyspace, receiver); }, [&] (const cast& c) -> test_result { return cast_test_assignment(c, db, keyspace, receiver); diff --git a/cql3/functions/function_call.hh b/cql3/functions/function_call.hh deleted file mode 100644 index b570e1c7b3..0000000000 --- a/cql3/functions/function_call.hh +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * Modified by ScyllaDB - * - * Copyright (C) 2015-present 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 . - */ - -#pragma once - -#include "function.hh" -#include "scalar_function.hh" -#include "cql3/term.hh" -#include "exceptions/exceptions.hh" -#include "cql3/functions/function_name.hh" - -namespace cql3::expr { - -struct function_call; - -} - -namespace cql3 { -namespace functions { - -class function_call : public non_terminal { - const shared_ptr _fun; - const std::vector> _terms; - // 0-based index of the function call within a CQL statement. - // Used to populate the cache of execution results while passing to - // another shard (handling `bounce_to_shard` messages) in LWT statements. - // - // The id is set only for the function calls that are a part of LWT - // statement restrictions for the partition key. Otherwise, the id is not - // set and the call is not considered when using or populating the cache. - std::optional _id; -public: - function_call(shared_ptr fun, std::vector> terms) - : _fun(std::move(fun)), _terms(std::move(terms)) { - } - virtual void fill_prepare_context(prepare_context& ctx) const override; - void set_id(std::optional id) { - _id = id; - } - virtual shared_ptr bind(const query_options& options) override; - - virtual expr::expression to_expression() override; -public: - virtual bool contains_bind_marker() const override; -private: - cql3::raw_value_view bind_and_get_internal(const query_options& options); -}; - -expr::expression prepare_function_call(const expr::function_call& fc, database& db, const sstring& keyspace, lw_shared_ptr receiver); - -assignment_testable::test_result test_assignment_function_call(const cql3::expr::function_call& fc, database& db, const sstring& keyspace, const column_specification& receiver); - -} -} diff --git a/cql3/functions/functions.cc b/cql3/functions/functions.cc index 478257f415..4a00dc0f14 100644 --- a/cql3/functions/functions.cc +++ b/cql3/functions/functions.cc @@ -20,8 +20,6 @@ */ #include "functions.hh" - -#include "function_call.hh" #include "token_fct.hh" #include "cql3/maps.hh" #include "cql3/sets.hh" @@ -66,9 +64,6 @@ bool as_json_function::requires_thread() const { return false; } thread_local std::unordered_multimap> functions::_declared = init(); -static bytes_opt execute_internal(cql_serialization_format sf, scalar_function& fun, std::vector params); -static shared_ptr make_terminal(shared_ptr fun, cql3::raw_value result, cql_serialization_format sf); - void functions::clear_functions() noexcept { functions::_declared = init(); } @@ -446,217 +441,6 @@ functions::type_equals(const std::vector& t1, const std::vectoris_pure()) { - // Hacking around `const` specifier in the `collect_prepare_metadata` - // declaration since we also need to modify the current instance along - // with prepare metadata. - ctx.add_pk_function_call(static_pointer_cast( - const_cast(this)->shared_from_this())); - } - for (auto&& t : _terms) { - t->fill_prepare_context(ctx); - } -} - -shared_ptr -function_call::bind(const query_options& options) { - return make_terminal(_fun, cql3::raw_value::make_value(bind_and_get_internal(options)), options.get_cql_serialization_format()); -} - -cql3::raw_value_view -function_call::bind_and_get_internal(const query_options& options) { - std::vector buffers; - buffers.reserve(_terms.size()); - for (auto&& t : _terms) { - // For now, we don't allow nulls as argument as no existing function needs it and it - // simplify things. - auto val = expr::evaluate_to_raw_view(t, options); - if (!val) { - throw exceptions::invalid_request_exception(format("Invalid null value for argument to {}", *_fun)); - } - buffers.push_back(to_bytes_opt(val)); - } - if (_id) { - // Populate the cache only for LWT statements. Note that this code - // works only in places where `function_call::raw` AST nodes are - // created. - // These cases do not include selection clause in SELECT statement, - // hence no database inputs are possibly allowed to the functions - // evaluated here. - // We can cache every non-deterministic call here as this code branch - // acts the same way as if all arguments are equivalent to literal - // values at this point (already calculated). - auto val = options.find_cached_pk_function_call(*_id); - if (val) { - return raw_value_view::make_temporary(raw_value::make_value(*val)); - } - } - auto result = execute_internal(options.get_cql_serialization_format(), *_fun, std::move(buffers)); - if (_id) { - options.cache_pk_function_call(*_id, result); - } - return cql3::raw_value_view::make_temporary(cql3::raw_value::make_value(result)); -} - -expr::expression function_call::to_expression() { - std::vector args; - args.reserve(_terms.size()); - - for (const ::shared_ptr& t : _terms) { - args.emplace_back(expr::to_expression(t)); - } - - return expr::function_call { - .func = _fun, - .args = std::move(args), - .lwt_cache_id = ::make_shared>(_id), - }; -} - -static -bytes_opt -execute_internal(cql_serialization_format sf, scalar_function& fun, std::vector params) { - bytes_opt result = fun.execute(sf, params); - try { - // Check the method didn't lied on it's declared return type - if (result) { - fun.return_type()->validate(*result, sf); - } - return result; - } catch (marshal_exception& e) { - throw runtime_exception(format("Return of function {} ({}) is not a valid value for its declared return type {}", - fun, to_hex(result), - fun.return_type()->as_cql3_type() - )); - } -} - -bool -function_call::contains_bind_marker() const { - for (auto&& t : _terms) { - if (t->contains_bind_marker()) { - return true; - } - } - return false; -} - -static -shared_ptr -make_terminal(shared_ptr fun, cql3::raw_value result, cql_serialization_format sf) { - return visit(*fun->return_type(), make_visitor( - [&] (const list_type_impl& ltype) -> shared_ptr { - return make_shared(lists::value::from_serialized(result.to_view(), ltype, sf)); - }, - [&] (const set_type_impl& stype) -> shared_ptr { - return make_shared(sets::value::from_serialized(result.to_view(), stype, sf)); - }, - [&] (const map_type_impl& mtype) -> shared_ptr { - return make_shared(maps::value::from_serialized(result.to_view(), mtype, sf)); - }, - [&] (const user_type_impl& utype) -> shared_ptr { - // TODO (kbraun): write a test for this case when User Defined Functions are implemented - return make_shared(user_types::value::from_serialized(result.to_view(), utype)); - }, - [&] (const abstract_type& type) -> shared_ptr { - if (type.is_collection()) { - throw std::runtime_error(format("function_call::make_terminal: unhandled collection type {}", type.name())); - } - return make_shared(std::move(result), fun->return_type()); - } - )); -} - -expr::expression -prepare_function_call(const expr::function_call& fc, database& db, const sstring& keyspace, lw_shared_ptr receiver) { - auto&& fun = std::visit(overloaded_functor{ - [] (const shared_ptr& func) { - return func; - }, - [&] (const function_name& name) { - auto args = boost::copy_range>>(fc.args | boost::adaptors::transformed(expr::as_assignment_testable)); - auto fun = functions::functions::get(db, keyspace, name, args, receiver->ks_name, receiver->cf_name, receiver.get()); - if (!fun) { - throw exceptions::invalid_request_exception(format("Unknown function {} called", name)); - } - return fun; - }, - }, fc.func); - if (fun->is_aggregate()) { - throw exceptions::invalid_request_exception("Aggregation function are not supported in the where clause"); - } - - // Can't use static_pointer_cast<> because function is a virtual base class of scalar_function - auto&& scalar_fun = dynamic_pointer_cast(fun); - - // Functions.get() will complain if no function "name" type check with the provided arguments. - // We still have to validate that the return type matches however - if (!receiver->type->is_value_compatible_with(*scalar_fun->return_type())) { - throw exceptions::invalid_request_exception(format("Type error: cannot assign result of function {} (type {}) to {} (type {})", - fun->name(), fun->return_type()->as_cql3_type(), - receiver->name, receiver->type->as_cql3_type())); - } - - if (scalar_fun->arg_types().size() != fc.args.size()) { - throw exceptions::invalid_request_exception(format("Incorrect number of arguments specified for function {} (expected {:d}, found {:d})", - fun->name(), fun->arg_types().size(), fc.args.size())); - } - - std::vector parameters; - parameters.reserve(fc.args.size()); - bool all_terminal = true; - for (size_t i = 0; i < fc.args.size(); ++i) { - expr::expression e = prepare_expression(fc.args[i], db, keyspace, functions::make_arg_spec(receiver->ks_name, receiver->cf_name, *scalar_fun, i)); - if (!expr::is(e)) { - all_terminal = false; - } - parameters.push_back(std::move(e)); - } - - // If all parameters are terminal and the function is pure, we can - // evaluate it now, otherwise we'd have to wait execution time - expr::function_call fun_call { - .func = fun, - .args = std::move(parameters), - .lwt_cache_id = fc.lwt_cache_id - }; - if (all_terminal && scalar_fun->is_pure()) { - return expr::evaluate(fun_call, query_options::DEFAULT); - } else { - return fun_call; - } -} - -assignment_testable::test_result -test_assignment_function_call(const cql3::expr::function_call& fc, database& db, const sstring& keyspace, const column_specification& receiver) { - // Note: Functions.get() will return null if the function doesn't exist, or throw is no function matching - // the arguments can be found. We may get one of those if an undefined/wrong function is used as argument - // of another, existing, function. In that case, we return true here because we'll throw a proper exception - // later with a more helpful error message that if we were to return false here. - try { - auto&& fun = std::visit(overloaded_functor{ - [&] (const function_name& name) { - auto args = boost::copy_range>>(fc.args | boost::adaptors::transformed(expr::as_assignment_testable)); - return functions::get(db, keyspace, name, args, receiver.ks_name, receiver.cf_name, &receiver); - }, - [] (const shared_ptr& func) { - return func; - }, - }, fc.func); - if (fun && receiver.type == fun->return_type()) { - return assignment_testable::test_result::EXACT_MATCH; - } else if (!fun || receiver.type->is_value_compatible_with(*fun->return_type())) { - return assignment_testable::test_result::WEAKLY_ASSIGNABLE; - } else { - return assignment_testable::test_result::NOT_ASSIGNABLE; - } - } catch (exceptions::invalid_request_exception& e) { - return assignment_testable::test_result::WEAKLY_ASSIGNABLE; - } -} - } } diff --git a/cql3/lists.cc b/cql3/lists.cc index eb6f75aa85..c05306db42 100644 --- a/cql3/lists.cc +++ b/cql3/lists.cc @@ -42,164 +42,6 @@ lists::uuid_index_spec_of(const column_specification& column) { ::make_shared(format("uuid_idx({})", *column.name), true), uuid_type); } - -lists::value -lists::value::from_serialized(const raw_value_view& val, const list_type_impl& type, cql_serialization_format sf) { - try { - utils::chunked_vector elements; - if (sf.collection_format_unchanged()) { - utils::chunked_vector tmp = val.with_value([sf] (const FragmentedView auto& v) { - return partially_deserialize_listlike(v, sf); - }); - elements.reserve(tmp.size()); - for (auto&& element : tmp) { - elements.emplace_back(std::move(element)); - } - } else [[unlikely]] { - auto l = val.deserialize(type, sf); - elements.reserve(l.size()); - for (auto&& element : l) { - // elements can be null in lists that represent a set of IN values - elements.push_back(element.is_null() ? managed_bytes_opt() : managed_bytes_opt(type.get_elements_type()->decompose(element))); - } - } - return value(std::move(elements), type.shared_from_this()); - } catch (marshal_exception& e) { - throw exceptions::invalid_request_exception(e.what()); - } -} - -cql3::raw_value -lists::value::get(const query_options& options) { - return cql3::raw_value::make_value(get_with_protocol_version(cql_serialization_format::internal())); -} - -managed_bytes -lists::value::get_with_protocol_version(cql_serialization_format sf) { - // Can't use boost::indirect_iterator, because optional is not an iterator - auto deref = [] (managed_bytes_opt& x) { return *x; }; - return collection_type_impl::pack_fragmented( - boost::make_transform_iterator(_elements.begin(), deref), - boost::make_transform_iterator( _elements.end(), deref), - _elements.size(), sf); -} - -bool -lists::value::equals(const list_type_impl& lt, const value& v) { - if (_elements.size() != v._elements.size()) { - return false; - } - return std::equal(_elements.begin(), _elements.end(), - v._elements.begin(), - [t = lt.get_elements_type()] (const managed_bytes_opt& e1, const managed_bytes_opt& e2) { return t->equal(*e1, *e2); }); -} - -sstring -lists::value::to_string() const { - std::ostringstream os; - os << "["; - bool is_first = true; - for (auto&& e : _elements) { - if (!is_first) { - os << ", "; - } - is_first = false; - os << to_hex(e); - } - os << "]"; - return os.str(); -} - -bool -lists::delayed_value::contains_bind_marker() const { - // False since we don't support them in collection - return false; -} - -void -lists::delayed_value::fill_prepare_context(prepare_context& ctx) const { -} - -shared_ptr -lists::delayed_value::bind(const query_options& options) { - utils::chunked_vector buffers; - buffers.reserve(_elements.size()); - for (auto&& t : _elements) { - auto bo = expr::evaluate_to_raw_view(t, options); - - if (bo.is_null()) { - throw exceptions::invalid_request_exception("null is not supported inside collections"); - } - if (bo.is_unset_value()) { - return constants::UNSET_VALUE; - } - - buffers.push_back(bo.with_value([] (const FragmentedView auto& v) { return managed_bytes(v); })); - } - return ::make_shared(buffers, _my_type); -} - -shared_ptr -lists::delayed_value::bind_ignore_null(const query_options& options) { - utils::chunked_vector buffers; - buffers.reserve(_elements.size()); - for (auto&& t : _elements) { - auto bo = expr::evaluate_to_raw_view(t, options); - - if (bo.is_null()) { - continue; - } - if (bo.is_unset_value()) { - return constants::UNSET_VALUE; - } - - buffers.push_back(bo.with_value([] (const FragmentedView auto& v) { return managed_bytes(v); })); - } - return ::make_shared(buffers, _my_type); -} - -expr::expression lists::delayed_value::to_expression() { - std::vector new_elements; - new_elements.reserve(_elements.size()); - - for (shared_ptr& e : _elements) { - new_elements.emplace_back(expr::to_expression(e)); - } - - return expr::collection_constructor { - .style = expr::collection_constructor::style_type::list, - .elements = std::move(new_elements), - .type = _my_type - }; -} - -::shared_ptr -lists::marker::bind(const query_options& options) { - const auto& value = options.get_value_at(_bind_index); - auto& ltype = dynamic_cast(_receiver->type->without_reversed()); - if (value.is_null()) { - return nullptr; - } else if (value.is_unset_value()) { - return constants::UNSET_VALUE; - } else { - try { - value.validate(ltype, options.get_cql_serialization_format()); - return make_shared(value::from_serialized(value, ltype, options.get_cql_serialization_format())); - } catch (marshal_exception& e) { - throw exceptions::invalid_request_exception( - format("Exception while binding column {:s}: {:s}", _receiver->name->to_cql_string(), e.what())); - } - } -} - -expr::expression lists::marker::to_expression() { - return expr::bind_variable { - .shape = expr::bind_variable::shape_type::scalar, - .bind_index = _bind_index, - .receiver = _receiver - }; -} - void lists::setter::execute(mutation& m, const clustering_key_prefix& prefix, const update_parameters& params) { auto value = expr::evaluate(*_e, params._options); diff --git a/cql3/lists.hh b/cql3/lists.hh index 63711ebbfe..50a5ff06e3 100644 --- a/cql3/lists.hh +++ b/cql3/lists.hh @@ -57,64 +57,6 @@ public: static lw_shared_ptr index_spec_of(const column_specification&); static lw_shared_ptr value_spec_of(const column_specification&); static lw_shared_ptr uuid_index_spec_of(const column_specification&); - - class value : public terminal, collection_terminal { - public: - utils::chunked_vector _elements; - public: - explicit value(utils::chunked_vector elements, data_type my_type) - : terminal(std::move(my_type)), _elements(std::move(elements)) { - } - static value from_serialized(const raw_value_view& v, const list_type_impl& type, cql_serialization_format sf); - virtual cql3::raw_value get(const query_options& options) override; - virtual managed_bytes get_with_protocol_version(cql_serialization_format sf) override; - bool equals(const list_type_impl& lt, const value& v); - const utils::chunked_vector& get_elements() const; - virtual sstring to_string() const override; - friend class lists; - }; - /** - * Basically similar to a Value, but with some non-pure function (that need - * to be evaluated at execution time) in it. - * - * Note: this would also work for a list with bind markers, but we don't support - * that because 1) it's not excessively useful and 2) we wouldn't have a good - * column name to return in the ColumnSpecification for those markers (not a - * blocker per-se but we don't bother due to 1)). - */ - class delayed_value : public non_terminal { - std::vector> _elements; - data_type _my_type; - public: - explicit delayed_value(std::vector> elements, data_type my_type) - : _elements(std::move(elements)), _my_type(std::move(my_type)) { - } - virtual bool contains_bind_marker() const override; - virtual void fill_prepare_context(prepare_context& ctx) const override; - virtual shared_ptr bind(const query_options& options) override; - - // Binds the value, but skips all nulls inside the list - virtual shared_ptr bind_ignore_null(const query_options& options); - const std::vector>& get_elements() const { - return _elements; - } - - virtual expr::expression to_expression() override; - }; - - /** - * A marker for List values and IN relations - */ - class marker : public abstract_marker { - public: - marker(int32_t bind_index, lw_shared_ptr receiver) - : abstract_marker{bind_index, std::move(receiver)} - { } - virtual ::shared_ptr bind(const query_options& options) override; - - virtual expr::expression to_expression() override; - }; - public: class setter : public operation { public: diff --git a/cql3/maps.cc b/cql3/maps.cc index 71f501065c..704ec6e3c9 100644 --- a/cql3/maps.cc +++ b/cql3/maps.cc @@ -41,179 +41,14 @@ #include "maps.hh" #include "cql3/abstract_marker.hh" -#include "cql3/term.hh" #include "operation.hh" #include "update_parameters.hh" #include "exceptions/exceptions.hh" #include "cql3/cql3_type.hh" #include "constants.hh" #include "types/map.hh" -#include "cql3/tuples.hh" namespace cql3 { - -maps::value -maps::value::from_serialized(const raw_value_view& fragmented_value, const map_type_impl& type, cql_serialization_format sf) { - try { - // Collections have this small hack that validate cannot be called on a serialized object, - // but compose does the validation (so we're fine). - // FIXME: deserialize_for_native_protocol?! - auto m = fragmented_value.deserialize(type, sf); - std::map map(type.get_keys_type()->as_less_comparator()); - if (sf.collection_format_unchanged()) { - std::vector> tmp = fragmented_value.with_value([sf] (const FragmentedView auto& v) { - return partially_deserialize_map(v, sf); - }); - for (auto&& key_value : tmp) { - map.insert(std::move(key_value)); - } - } else [[unlikely]] { - auto m = fragmented_value.deserialize(type, sf); - for (auto&& e : m) { - map.emplace(type.get_keys_type()->decompose(e.first), - type.get_values_type()->decompose(e.second)); - } - } - return maps::value(std::move(map), type.shared_from_this()); - } catch (marshal_exception& e) { - throw exceptions::invalid_request_exception(e.what()); - } -} - -cql3::raw_value -maps::value::get(const query_options& options) { - return cql3::raw_value::make_value(get_with_protocol_version(cql_serialization_format::internal())); -} - -managed_bytes -maps::value::get_with_protocol_version(cql_serialization_format sf) { - //FIXME: share code with serialize_partially_deserialized_form - size_t len = collection_value_len(sf) * map.size() * 2 + collection_size_len(sf); - for (auto&& e : map) { - len += e.first.size() + e.second.size(); - } - managed_bytes b(managed_bytes::initialized_later(), len); - managed_bytes_mutable_view out(b); - - write_collection_size(out, map.size(), sf); - for (auto&& e : map) { - write_collection_value(out, sf, e.first); - write_collection_value(out, sf, e.second); - } - return b; -} - -bool -maps::value::equals(const map_type_impl& mt, const value& v) { - return std::equal(map.begin(), map.end(), - v.map.begin(), v.map.end(), - [&mt] (auto&& e1, auto&& e2) { - return mt.get_keys_type()->compare(e1.first, e2.first) == 0 - && mt.get_values_type()->compare(e1.second, e2.second) == 0; - }); -} - -sstring -maps::value::to_string() const { - // FIXME: - abort(); -} - -bool -maps::delayed_value::contains_bind_marker() const { - // False since we don't support them in collection - return false; -} - -void -maps::delayed_value::fill_prepare_context(prepare_context& ctx) const { -} - -shared_ptr -maps::delayed_value::bind(const query_options& options) { - const map_type_impl& my_map_type = dynamic_cast(_my_type->without_reversed()); - - serialized_compare comparator = my_map_type.get_keys_type()->as_less_comparator(); - std::map buffers(std::move(comparator)); - for (auto&& entry : _elements) { - auto&& key = entry.first; - auto&& value = entry.second; - - // We don't support values > 64K because the serialization format encode the length as an unsigned short. - auto key_bytes = expr::evaluate_to_raw_view(key, options); - if (key_bytes.is_null()) { - throw exceptions::invalid_request_exception("null is not supported inside collections"); - } - if (key_bytes.is_unset_value()) { - throw exceptions::invalid_request_exception("unset value is not supported inside collections"); - } - if (key_bytes.size_bytes() > std::numeric_limits::max()) { - throw exceptions::invalid_request_exception(format("Map key is too long. Map keys are limited to {:d} bytes but {:d} bytes keys provided", - std::numeric_limits::max(), - key_bytes.size_bytes())); - } - auto value_bytes = expr::evaluate_to_raw_view(value, options); - if (value_bytes.is_null()) { - throw exceptions::invalid_request_exception("null is not supported inside collections");\ - } - if (value_bytes.is_unset_value()) { - return constants::UNSET_VALUE; - } - buffers.emplace(*to_managed_bytes_opt(key_bytes), *to_managed_bytes_opt(value_bytes)); - } - return ::make_shared(std::move(buffers), _my_type); -} - -expr::expression maps::delayed_value::to_expression() { - std::vector new_elements; - new_elements.reserve(_elements.size()); - - const map_type_impl& mtype = dynamic_cast(_my_type->without_reversed()); - data_type ttype = tuple_type_impl::get_instance({mtype.get_keys_type(), mtype.get_values_type()}); - - for (auto&& [key, value] : _elements) { - expr::expression key_expr = expr::to_expression(key); - expr::expression value_expr = expr::to_expression(value); - new_elements.emplace_back(expr::tuple_constructor{{std::move(key_expr), std::move(value_expr)}, std::move(ttype)}); - } - - return expr::collection_constructor { - .style = expr::collection_constructor::style_type::map, - .elements = std::move(new_elements), - .type = _my_type - }; -} - -::shared_ptr -maps::marker::bind(const query_options& options) { - auto val = options.get_value_at(_bind_index); - if (val.is_null()) { - return nullptr; - } - if (val.is_unset_value()) { - return constants::UNSET_VALUE; - } - try { - val.validate(*_receiver->type, options.get_cql_serialization_format()); - } catch (marshal_exception& e) { - throw exceptions::invalid_request_exception( - format("Exception while binding column {:s}: {:s}", _receiver->name->to_cql_string(), e.what())); - } - return ::make_shared( - maps::value::from_serialized( - val, - dynamic_cast(_receiver->type->without_reversed()), - options.get_cql_serialization_format())); -} - -expr::expression maps::marker::to_expression() { - return expr::bind_variable { - .shape = expr::bind_variable::shape_type::scalar, - .bind_index = _bind_index, - .receiver = _receiver - }; -} - void maps::setter::execute(mutation& m, const clustering_key_prefix& row_key, const update_parameters& params) { expr::constant value = expr::evaluate(*_e, params._options); diff --git a/cql3/maps.hh b/cql3/maps.hh index 63c92b8c51..9fc2aade1e 100644 --- a/cql3/maps.hh +++ b/cql3/maps.hh @@ -42,7 +42,6 @@ #pragma once #include "cql3/abstract_marker.hh" -#include "cql3/term.hh" #include "operation.hh" #include "update_parameters.hh" #include "constants.hh" @@ -59,44 +58,6 @@ public: static lw_shared_ptr key_spec_of(const column_specification& column); static lw_shared_ptr value_spec_of(const column_specification& column); - class value : public terminal, collection_terminal { - public: - std::map map; - - value(std::map map, data_type my_type) - : terminal(std::move(my_type)), map(std::move(map)) { - } - static value from_serialized(const raw_value_view& value, const map_type_impl& type, cql_serialization_format sf); - virtual cql3::raw_value get(const query_options& options) override; - virtual managed_bytes get_with_protocol_version(cql_serialization_format sf) override; - bool equals(const map_type_impl& mt, const value& v); - virtual sstring to_string() const override; - }; - - // See Lists.DelayedValue - class delayed_value : public non_terminal { - std::unordered_map, shared_ptr> _elements; - data_type _my_type; - public: - delayed_value(std::unordered_map, shared_ptr> elements, data_type my_type) - : _elements(std::move(elements)), _my_type(std::move(my_type)) { - } - virtual bool contains_bind_marker() const override; - virtual void fill_prepare_context(prepare_context& ctx) const override; - virtual shared_ptr bind(const query_options& options) override; - - virtual expr::expression to_expression() override; - }; - - class marker : public abstract_marker { - public: - marker(int32_t bind_index, lw_shared_ptr receiver) - : abstract_marker{bind_index, std::move(receiver)} - { } - virtual ::shared_ptr bind(const query_options& options) override; - virtual expr::expression to_expression() override; - }; - class setter : public operation { public: setter(const column_definition& column, expr::expression e) diff --git a/cql3/multi_column_relation.hh b/cql3/multi_column_relation.hh index 5dd9390cb2..28dc0cb675 100644 --- a/cql3/multi_column_relation.hh +++ b/cql3/multi_column_relation.hh @@ -43,9 +43,6 @@ #include "cql3/expr/expression.hh" #include "cql3/relation.hh" -#include "cql3/term.hh" -#include "cql3/tuples.hh" - #include "cql3/restrictions/multi_column_restriction.hh" #include @@ -245,11 +242,16 @@ protected: return names; } + template + static sstring tuple_to_string(const std::vector& items) { + return format("({})", join(", ", items)); + } + virtual sstring to_string() const override { - sstring str = tuples::tuple_to_string(_entities); + sstring str = tuple_to_string(_entities); if (is_IN()) { str += " IN "; - str += !_in_marker ? "?" : tuples::tuple_to_string(_in_values); + str += !_in_marker ? "?" : tuple_to_string(_in_values); return str; } return format("{} {} {}", str, _relation_type, *_values_or_marker); diff --git a/cql3/operation.hh b/cql3/operation.hh index 9dd9eb26b6..2b9e9fcf35 100644 --- a/cql3/operation.hh +++ b/cql3/operation.hh @@ -44,7 +44,6 @@ #include #include "exceptions/exceptions.hh" #include "database_fwd.hh" -#include "term.hh" #include "update_parameters.hh" #include "cql3/column_identifier.hh" #include "cql3/expr/expression.hh" diff --git a/cql3/prepare_context.cc b/cql3/prepare_context.cc index 444100ffe6..cc22412700 100644 --- a/cql3/prepare_context.cc +++ b/cql3/prepare_context.cc @@ -42,7 +42,6 @@ #include "cql3/prepare_context.hh" #include "cql3/column_identifier.hh" #include "cql3/column_specification.hh" -#include "cql3/functions/function_call.hh" namespace cql3 { @@ -99,10 +98,6 @@ void prepare_context::set_bound_variables(const std::vector& fun_call : _pk_fn_calls) { - fun_call->set_id(std::nullopt); - } - for (::shared_ptr>& cache_id : _pk_function_calls_cache_ids) { if (cache_id.get() != nullptr) { *cache_id = std::nullopt; @@ -110,20 +105,6 @@ void prepare_context::clear_pk_function_calls_cache() { } } -void prepare_context::add_pk_function_call(::shared_ptr fn) { - constexpr auto fn_limit = std::numeric_limits::max(); - if (_pk_function_calls_cache_ids.size() == fn_limit) { - throw exceptions::invalid_request_exception( - format("Too many function calls within one statement. Max supported number is {}", fn_limit)); - } - - fn->set_id(_pk_function_calls_cache_ids.size()); - - _pk_fn_calls.emplace_back(std::move(fn)); - // Workaround for now, this will be removed later along with this method - _pk_function_calls_cache_ids.push_back({}); -} - void prepare_context::add_pk_function_call(expr::function_call& fn) { constexpr auto fn_limit = std::numeric_limits::max(); if (_pk_function_calls_cache_ids.size() == fn_limit) { diff --git a/cql3/prepare_context.hh b/cql3/prepare_context.hh index 93949a8010..78f65d108e 100644 --- a/cql3/prepare_context.hh +++ b/cql3/prepare_context.hh @@ -67,12 +67,10 @@ private: std::vector> _variable_names; std::vector> _specs; std::vector> _target_columns; - // A list of pointers to prepared `function_call` AST nodes, that + // A list of pointers to prepared `function_call` cache ids, that // participate in partition key ranges computation within an LWT statement. - using function_calls_t = std::vector<::shared_ptr>; - function_calls_t _pk_fn_calls; - std::vector<::shared_ptr>> _pk_function_calls_cache_ids; + // The flag denoting whether the context is currently in partition key // processing mode (inside query restrictions AST nodes). If set to true, // then every `function_call` instance will be recorded in the context and @@ -100,7 +98,6 @@ public: // Record a new function call, which evaluates a partition key constraint. // Also automatically assigns an id to the AST node for caching purposes. - void add_pk_function_call(::shared_ptr fn); void add_pk_function_call(cql3::expr::function_call& fn); // Inform the context object that it has started or ended processing the diff --git a/cql3/restrictions/multi_column_restriction.hh b/cql3/restrictions/multi_column_restriction.hh index 288b3662a2..3f3dfb1e8d 100644 --- a/cql3/restrictions/multi_column_restriction.hh +++ b/cql3/restrictions/multi_column_restriction.hh @@ -41,7 +41,6 @@ #pragma once -#include "cql3/tuples.hh" #include "cql3/statements/request_validations.hh" #include "cql3/restrictions/primary_key_restrictions.hh" #include "cql3/statements/request_validations.hh" @@ -50,6 +49,7 @@ #include "cql3/lists.hh" #include "cql3/expr/expression.hh" #include "types/list.hh" +#include "types/tuple.hh" namespace cql3 { diff --git a/cql3/restrictions/single_column_restriction.hh b/cql3/restrictions/single_column_restriction.hh index 70ef921dc9..60a5e7a650 100644 --- a/cql3/restrictions/single_column_restriction.hh +++ b/cql3/restrictions/single_column_restriction.hh @@ -45,7 +45,6 @@ #include "cql3/restrictions/restriction.hh" #include "cql3/restrictions/bounds_slice.hh" -#include "cql3/term.hh" #include "cql3/abstract_marker.hh" #include #include "schema_fwd.hh" diff --git a/cql3/restrictions/statement_restrictions.cc b/cql3/restrictions/statement_restrictions.cc index b67ecdbac6..fa951804bf 100644 --- a/cql3/restrictions/statement_restrictions.cc +++ b/cql3/restrictions/statement_restrictions.cc @@ -41,7 +41,6 @@ #include "cql3/selection/selection.hh" #include "cql3/single_column_relation.hh" #include "cql3/statements/request_validations.hh" -#include "cql3/tuples.hh" #include "types/list.hh" #include "types/map.hh" #include "types/set.hh" diff --git a/cql3/sets.cc b/cql3/sets.cc index 989809e10c..532c96ae23 100644 --- a/cql3/sets.cc +++ b/cql3/sets.cc @@ -26,154 +26,6 @@ #include "types/set.hh" namespace cql3 { - -sets::value -sets::value::from_serialized(const raw_value_view& val, const set_type_impl& type, cql_serialization_format sf) { - try { - std::set elements(type.get_elements_type()->as_less_comparator()); - if (sf.collection_format_unchanged()) { - utils::chunked_vector tmp = val.with_value([sf] (const FragmentedView auto& v) { - return partially_deserialize_listlike(v, sf); - }); - for (auto&& element : tmp) { - elements.insert(std::move(element)); - } - } else [[unlikely]] { - auto s = val.deserialize(type, sf); - for (auto&& element : s) { - elements.insert(elements.end(), managed_bytes(type.get_elements_type()->decompose(element))); - } - } - return value(std::move(elements), type.shared_from_this()); - } catch (marshal_exception& e) { - throw exceptions::invalid_request_exception(e.what()); - } -} - -cql3::raw_value -sets::value::get(const query_options& options) { - return cql3::raw_value::make_value(get_with_protocol_version(cql_serialization_format::internal())); -} - -managed_bytes -sets::value::get_with_protocol_version(cql_serialization_format sf) { - return collection_type_impl::pack_fragmented(_elements.begin(), _elements.end(), - _elements.size(), sf); -} - -bool -sets::value::equals(const set_type_impl& st, const value& v) { - if (_elements.size() != v._elements.size()) { - return false; - } - auto&& elements_type = st.get_elements_type(); - return std::equal(_elements.begin(), _elements.end(), - v._elements.begin(), - [elements_type] (managed_bytes_view v1, managed_bytes_view v2) { - return elements_type->equal(v1, v2); - }); -} - -sstring -sets::value::to_string() const { - sstring result = "{"; - bool first = true; - for (auto&& e : _elements) { - if (!first) { - result += ", "; - } - first = true; - result += to_hex(e); - } - result += "}"; - return result; -} - -bool -sets::delayed_value::contains_bind_marker() const { - // False since we don't support them in collection - return false; -} - -void -sets::delayed_value::fill_prepare_context(prepare_context& ctx) const { -} - -shared_ptr -sets::delayed_value::bind(const query_options& options) { - const set_type_impl& my_set_type = dynamic_cast(_my_type->without_reversed()); - - std::set buffers(my_set_type.get_elements_type()->as_less_comparator()); - for (auto&& t : _elements) { - auto b = expr::evaluate_to_raw_view(t, options); - - if (b.is_null()) { - throw exceptions::invalid_request_exception("null is not supported inside collections"); - } - if (b.is_unset_value()) { - return constants::UNSET_VALUE; - } - // We don't support value > 64K because the serialization format encode the length as an unsigned short. - if (b.size_bytes() > std::numeric_limits::max()) { - throw exceptions::invalid_request_exception(format("Set value is too long. Set values are limited to {:d} bytes but {:d} bytes value provided", - std::numeric_limits::max(), - b.size_bytes())); - } - buffers.insert(buffers.end(), *to_managed_bytes_opt(b)); - } - return ::make_shared(std::move(buffers), _my_type); -} - -expr::expression sets::delayed_value::to_expression() { - std::vector new_elements; - new_elements.reserve(_elements.size()); - - for (shared_ptr& e : _elements) { - new_elements.emplace_back(expr::to_expression(e)); - } - - return expr::collection_constructor { - .style = expr::collection_constructor::style_type::set, - .elements = std::move(new_elements), - .type = _my_type, - }; -} - -sets::marker::marker(int32_t bind_index, lw_shared_ptr receiver) - : abstract_marker{bind_index, std::move(receiver)} { - if (!_receiver->type->without_reversed().is_set()) { - throw std::runtime_error(format("Receiver {} for set marker has wrong type: {}", - _receiver->cf_name, _receiver->type->name())); - } -} - -::shared_ptr -sets::marker::bind(const query_options& options) { - const auto& value = options.get_value_at(_bind_index); - if (value.is_null()) { - return nullptr; - } else if (value.is_unset_value()) { - return constants::UNSET_VALUE; - } else { - auto& type = dynamic_cast(_receiver->type->without_reversed()); - try { - value.validate(type, options.get_cql_serialization_format()); - } catch (marshal_exception& e) { - throw exceptions::invalid_request_exception( - format("Exception while binding column {:s}: {:s}", _receiver->name->to_cql_string(), e.what())); - } - return make_shared(value::from_serialized(value, type, options.get_cql_serialization_format())); - } -} - -expr::expression sets::marker::to_expression() { - return expr::bind_variable { - .shape = expr::bind_variable::shape_type::scalar, - .bind_index = _bind_index, - .receiver = _receiver - }; -} - void sets::setter::execute(mutation& m, const clustering_key_prefix& row_key, const update_parameters& params) { expr::constant value = expr::evaluate(*_e, params._options); diff --git a/cql3/sets.hh b/cql3/sets.hh index 10d05fab7b..64cf6dec75 100644 --- a/cql3/sets.hh +++ b/cql3/sets.hh @@ -58,42 +58,6 @@ class sets { public: static lw_shared_ptr value_spec_of(const column_specification& column); - class value : public terminal, collection_terminal { - public: - std::set _elements; - public: - value(std::set elements, data_type my_type) - : terminal(std::move(my_type)), _elements(std::move(elements)) { - } - static value from_serialized(const raw_value_view& v, const set_type_impl& type, cql_serialization_format sf); - virtual cql3::raw_value get(const query_options& options) override; - virtual managed_bytes get_with_protocol_version(cql_serialization_format sf) override; - bool equals(const set_type_impl& st, const value& v); - virtual sstring to_string() const override; - }; - - // See Lists.DelayedValue - class delayed_value : public non_terminal { - std::vector> _elements; - data_type _my_type; - public: - delayed_value(std::vector> elements, data_type my_type) - : _elements(std::move(elements)), _my_type(std::move(my_type)) { - } - virtual bool contains_bind_marker() const override; - virtual void fill_prepare_context(prepare_context& ctx) const override; - virtual shared_ptr bind(const query_options& options) override; - - virtual expr::expression to_expression() override; - }; - - class marker : public abstract_marker { - public: - marker(int32_t bind_index, lw_shared_ptr receiver); - virtual ::shared_ptr bind(const query_options& options) override; - virtual expr::expression to_expression() override; - }; - class setter : public operation { public: setter(const column_definition& column, expr::expression e) diff --git a/cql3/single_column_relation.hh b/cql3/single_column_relation.hh index 6fecda2e5e..2db00fc280 100644 --- a/cql3/single_column_relation.hh +++ b/cql3/single_column_relation.hh @@ -51,7 +51,6 @@ #include "cql3/relation.hh" #include "cql3/column_identifier.hh" #include "cql3/expr/expression.hh" -#include "cql3/term.hh" #include "types/collection.hh" namespace cql3 { diff --git a/cql3/statements/batch_statement.hh b/cql3/statements/batch_statement.hh index a9c80947c0..9a51ba37e8 100644 --- a/cql3/statements/batch_statement.hh +++ b/cql3/statements/batch_statement.hh @@ -42,6 +42,7 @@ #include "raw/batch_statement.hh" #include "timestamp.hh" #include "log.hh" +#include "service_permit.hh" namespace cql_transport::messages { class result_message; diff --git a/cql3/statements/delete_statement.cc b/cql3/statements/delete_statement.cc index af26fc80c0..5b0827bd1a 100644 --- a/cql3/statements/delete_statement.cc +++ b/cql3/statements/delete_statement.cc @@ -42,7 +42,6 @@ #include #include -#include "cql3/tuples.hh" #include "database.hh" #include "delete_statement.hh" #include "raw/delete_statement.hh" diff --git a/cql3/statements/raw/insert_statement.hh b/cql3/statements/raw/insert_statement.hh index ccd5fc25b7..8d6ffbb707 100644 --- a/cql3/statements/raw/insert_statement.hh +++ b/cql3/statements/raw/insert_statement.hh @@ -43,9 +43,7 @@ #include "cql3/statements/raw/modification_statement.hh" #include "cql3/column_identifier.hh" -#include "cql3/term.hh" #include "cql3/expr/expression.hh" - #include "database_fwd.hh" #include diff --git a/cql3/statements/update_statement.hh b/cql3/statements/update_statement.hh index edc26cdca3..b84bf508d9 100644 --- a/cql3/statements/update_statement.hh +++ b/cql3/statements/update_statement.hh @@ -48,8 +48,6 @@ namespace cql3 { -class term; - namespace statements { /** diff --git a/cql3/term.hh b/cql3/term.hh deleted file mode 100644 index 3a4125c9da..0000000000 --- a/cql3/term.hh +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * Copyright (C) 2015-present ScyllaDB - * - * Modified by 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 . - */ - -#pragma once - -#include "cql3/assignment_testable.hh" -#include "cql3/query_options.hh" -#include "cql3/values.hh" -#include "cql3/expr/expression.hh" - -#include -#include - -namespace cql3 { - -class term; -class terminal; -class prepare_context; - -/** - * A CQL3 term, i.e. a column value with or without bind variables. - * - * A Term can be either terminal or non terminal. A term object is one that is typed and is obtained - * from a raw term (Term.Raw) by poviding the actual receiver to which the term is supposed to be a - * value of. - */ -class term : public ::enable_shared_from_this { -public: - virtual ~term() {} - - /** - * Collects the column specification for the bind variables in this Term. - * This is obviously a no-op if the term is Terminal. - * - * @param boundNames the variables specification where to collect the - * bind variables of this term in. - */ - virtual void fill_prepare_context(prepare_context& ctx) const = 0; - - /** - * Bind the values in this term to the values contained in {@code values}. - * This is obviously a no-op if the term is Terminal. - * - * @param options the values to bind markers to. - * @return the result of binding all the variables of this NonTerminal (or - * 'this' if the term is terminal). - */ - virtual ::shared_ptr bind(const query_options& options) = 0; - - /** - * Whether or not that term contains at least one bind marker. - * - * Note that this is slightly different from being or not a NonTerminal, - * because calls to non pure functions will be NonTerminal (see #5616) - * even if they don't have bind markers. - */ - virtual bool contains_bind_marker() const = 0; - - virtual sstring to_string() const { - return format("term@{:p}", static_cast(this)); - } - - friend std::ostream& operator<<(std::ostream& out, const term& t) { - return out << t.to_string(); - } - - virtual expr::expression to_expression() = 0; -}; - -/** - * A terminal term, one that can be reduced to a byte buffer directly. - * - * This includes most terms that don't have a bind marker (an exception - * being delayed call for non pure function that are NonTerminal even - * if they don't have bind markers). - * - * This can be only one of: - * - a constant value - * - a collection value - * - * Note that a terminal term will always have been type checked, and thus - * consumer can (and should) assume so. - */ -class terminal : public term { - data_type _my_type; - -public: - terminal(data_type my_type) : _my_type(std::move(my_type)) { - } - - virtual void fill_prepare_context(prepare_context& ctx) const override { - } - - virtual ::shared_ptr bind(const query_options& options) override { - return static_pointer_cast(this->shared_from_this()); - } - - // While some NonTerminal may not have bind markers, no Term can be Terminal - // with a bind marker - virtual bool contains_bind_marker() const override { - return false; - } - - /** - * @return the serialized value of this terminal. - */ - virtual cql3::raw_value get(const query_options& options) = 0; - - virtual sstring to_string() const override = 0; - - data_type get_value_type() const { - return _my_type; - } - - virtual expr::expression to_expression() override { - cql3::raw_value raw_val = get(query_options::DEFAULT); - return expr::constant(std::move(raw_val), get_value_type()); - } -}; - -class collection_terminal { -public: - virtual ~collection_terminal() {} - /** Gets the value of the collection when serialized with the given protocol version format */ - virtual managed_bytes get_with_protocol_version(cql_serialization_format sf) = 0; -}; - -/** - * A non terminal term, i.e. a term that can only be reduce to a byte buffer - * at execution time. - * - * We have the following type of NonTerminal: - * - marker for a constant value - * - marker for a collection value (list, set, map) - * - a function having bind marker - * - a non pure function (even if it doesn't have bind marker - see #5616) - */ -class non_terminal : public term { -}; - -} diff --git a/cql3/token_relation.cc b/cql3/token_relation.cc index c75cc30613..ec0910d1fb 100644 --- a/cql3/token_relation.cc +++ b/cql3/token_relation.cc @@ -41,7 +41,6 @@ #include "restrictions/token_restriction.hh" #include "token_relation.hh" #include "column_identifier.hh" -#include "term.hh" #include "to_string.hh" std::vector cql3::token_relation::get_column_definitions(const schema& s) { diff --git a/cql3/token_relation.hh b/cql3/token_relation.hh index ba720832c7..507d648cca 100644 --- a/cql3/token_relation.hh +++ b/cql3/token_relation.hh @@ -47,7 +47,6 @@ #include "relation.hh" #include "column_identifier.hh" -#include "term.hh" #include "restrictions/restriction.hh" #include "expr/expression.hh" diff --git a/cql3/tuples.cc b/cql3/tuples.cc deleted file mode 100644 index bcf5089109..0000000000 --- a/cql3/tuples.cc +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (C) 2015-present 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 . - */ - -#include - -#include "tuples.hh" -#include "types/list.hh" -#include "cql3/lists.hh" - -namespace cql3 { - -expr::expression tuples::delayed_value::to_expression() { - std::vector new_elements; - new_elements.reserve(_elements.size()); - - for (shared_ptr& e : _elements) { - new_elements.emplace_back(expr::to_expression(e)); - } - - return expr::tuple_constructor { - .elements = std::move(new_elements), - .type = _type, - }; -} - -tuples::in_value -tuples::in_value::from_serialized(const raw_value_view& value_view, const list_type_impl& type, const query_options& options) { - try { - // Collections have this small hack that validate cannot be called on a serialized object, - // but the deserialization does the validation (so we're fine). - auto l = value_view.deserialize(type, options.get_cql_serialization_format()); - auto ttype = dynamic_pointer_cast(type.get_elements_type()); - assert(ttype); - - utils::chunked_vector> elements; - elements.reserve(l.size()); - for (auto&& e : l) { - // FIXME: Avoid useless copies. - elements.emplace_back(ttype->split_fragmented(single_fragmented_view(ttype->decompose(e)))); - } - return tuples::in_value(elements, type.shared_from_this()); - } catch (marshal_exception& e) { - throw exceptions::invalid_request_exception(e.what()); - } -} - -cql3::raw_value tuples::in_value::get(const query_options& options) { - const list_type_impl& my_list_type = dynamic_cast(get_value_type()->without_reversed()); - data_type element_tuple_type = my_list_type.get_elements_type(); - - utils::chunked_vector list_elements; - list_elements.reserve(_elements.size()); - for (const std::vector& tuple_elements : _elements) { - ::shared_ptr tvalue = - ::make_shared(tuples::value(tuple_elements, element_tuple_type)); - - expr::constant tuple_val = expr::evaluate(tvalue, options); - list_elements.emplace_back(std::move(tuple_val.value).to_managed_bytes()); - } - - ::shared_ptr list_value = ::make_shared(std::move(list_elements), get_value_type()); - return list_value->get(options); -} - -expr::expression tuples::marker::to_expression() { - return expr::bind_variable { - .shape = expr::bind_variable::shape_type::tuple, - .bind_index = _bind_index, - .receiver = _receiver - }; -} - -expr::expression tuples::in_marker::to_expression() { - return expr::bind_variable { - .shape = expr::bind_variable::shape_type::tuple_in, - .bind_index = _bind_index, - .receiver = _receiver - }; -} - -tuples::in_marker::in_marker(int32_t bind_index, lw_shared_ptr receiver) - : abstract_marker(bind_index, std::move(receiver)) -{ - assert(dynamic_pointer_cast(_receiver->type)); -} - -shared_ptr tuples::in_marker::bind(const query_options& options) { - const auto& value = options.get_value_at(_bind_index); - if (value.is_null()) { - return nullptr; - } else if (value.is_unset_value()) { - throw exceptions::invalid_request_exception(format("Invalid unset value for tuple {}", _receiver->name->text())); - } else { - auto& type = static_cast(*_receiver->type); - auto& elem_type = static_cast(*type.get_elements_type()); - try { - auto l = value.validate_and_deserialize(type, options.get_cql_serialization_format()); - for (auto&& element : l) { - elem_type.validate(elem_type.decompose(element), options.get_cql_serialization_format()); - } - } catch (marshal_exception& e) { - throw exceptions::invalid_request_exception(e.what()); - } - return make_shared(tuples::in_value::from_serialized(value, type, options)); - } -} - -} diff --git a/cql3/tuples.hh b/cql3/tuples.hh deleted file mode 100644 index 4f98e8e133..0000000000 --- a/cql3/tuples.hh +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* - * Modified by ScyllaDB - * Copyright (C) 2015-present 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 . - */ - -#pragma once - -#include "term.hh" -#include "abstract_marker.hh" -#include "types/tuple.hh" -#include "types/collection.hh" -#include "utils/chunked_vector.hh" -#include "cql3/column_identifier.hh" -#include "cql3/expr/expression.hh" - -class list_type_impl; - -namespace cql3 { - -/** - * Static helper methods and classes for tuples. - */ -class tuples { -public: - /** - * A tuple of terminal values (e.g (123, 'abc')). - */ - class value : public terminal { - public: - std::vector _elements; - public: - value(std::vector elements, data_type my_type) - : terminal(std::move(my_type)), _elements(std::move(elements)) { - } - static value from_serialized(const raw_value_view& buffer, const tuple_type_impl& type) { - return buffer.with_value([&] (const FragmentedView auto& view) { - return value(type.split_fragmented(view), type.shared_from_this()); - }); - } - virtual cql3::raw_value get(const query_options& options) override { - return cql3::raw_value::make_value(tuple_type_impl::build_value_fragmented(_elements)); - } - size_t size() const { - return _elements.size(); - } - virtual sstring to_string() const override { - return format("({})", join(", ", _elements)); - } - }; - - /** - * Similar to Value, but contains at least one NonTerminal, such as a non-pure functions or bind marker. - */ - class delayed_value : public non_terminal { - tuple_type _type; - std::vector> _elements; - public: - delayed_value(tuple_type type, std::vector> elements) - : _type(std::move(type)), _elements(std::move(elements)) { - } - - virtual bool contains_bind_marker() const override { - return std::any_of(_elements.begin(), _elements.end(), std::mem_fn(&term::contains_bind_marker)); - } - - virtual void fill_prepare_context(prepare_context& ctx) const override { - for (auto&& term : _elements) { - term->fill_prepare_context(ctx); - } - } - private: - std::vector bind_internal(const query_options& options) { - std::vector buffers; - buffers.resize(_elements.size()); - for (size_t i = 0; i < _elements.size(); ++i) { - const auto& value = expr::evaluate_to_raw_view(_elements[i], options); - if (value.is_unset_value()) { - throw exceptions::invalid_request_exception(format("Invalid unset value for tuple field number {:d}", i)); - } - buffers[i] = to_managed_bytes_opt(value); - // Inside tuples, we must force the serialization of collections to v3 whatever protocol - // version is in use since we're going to store directly that serialized value. - if (options.get_cql_serialization_format() != cql_serialization_format::internal() - && _type->type(i)->is_collection()) { - if (buffers[i]) { - buffers[i] = static_pointer_cast(_type->type(i))->reserialize( - options.get_cql_serialization_format(), - cql_serialization_format::internal(), - managed_bytes_view(*buffers[i])); - } - } - } - return buffers; - } - - public: - virtual shared_ptr bind(const query_options& options) override { - return ::make_shared(bind_internal(options), _type); - } - - virtual expr::expression to_expression() override; - }; - - /** - * A terminal value for a list of IN values that are tuples. For example: "SELECT ... WHERE (a, b, c) IN ?" - * This is similar to Lists.Value, but allows us to keep components of the tuples in the list separate. - */ - class in_value : public terminal { - private: - utils::chunked_vector> _elements; - public: - in_value(utils::chunked_vector> items, data_type my_type) - : terminal(std::move(my_type)), _elements(std::move(items)) { } - - static in_value from_serialized(const raw_value_view& value_view, const list_type_impl& type, const query_options& options); - - virtual cql3::raw_value get(const query_options& options) override; - - utils::chunked_vector> get_split_values() const { - return _elements; - } - - virtual sstring to_string() const override { - std::vector tuples(_elements.size()); - std::transform(_elements.begin(), _elements.end(), tuples.begin(), &tuples::tuple_to_string); - return tuple_to_string(tuples); - } - }; - - /** - * Represents a marker for a single tuple, like "SELECT ... WHERE (a, b, c) > ?" - */ - class marker : public abstract_marker { - public: - marker(int32_t bind_index, lw_shared_ptr receiver) - : abstract_marker(bind_index, std::move(receiver)) - { } - - virtual shared_ptr bind(const query_options& options) override { - const auto& value = options.get_value_at(_bind_index); - if (value.is_null()) { - return nullptr; - } else if (value.is_unset_value()) { - throw exceptions::invalid_request_exception(format("Invalid unset value for tuple {}", _receiver->name->text())); - } else { - auto& type = static_cast(*_receiver->type); - try { - value.validate(type, options.get_cql_serialization_format()); - } catch (marshal_exception& e) { - throw exceptions::invalid_request_exception( - format("Exception while binding column {:s}: {:s}", _receiver->name->to_cql_string(), e.what())); - } - return make_shared(value::from_serialized(value, type)); - } - } - - virtual expr::expression to_expression() override; - }; - - /** - * Represents a marker for a set of IN values that are tuples, like "SELECT ... WHERE (a, b, c) IN ?" - */ - class in_marker : public abstract_marker { - public: - in_marker(int32_t bind_index, lw_shared_ptr receiver); - - virtual shared_ptr bind(const query_options& options) override; - virtual expr::expression to_expression() override; - }; - - template - static sstring tuple_to_string(const std::vector& items) { - return format("({})", join(", ", items)); - } -}; - -} diff --git a/cql3/user_types.cc b/cql3/user_types.cc index f2ee7b5b23..cbb0d93f1c 100644 --- a/cql3/user_types.cc +++ b/cql3/user_types.cc @@ -49,108 +49,6 @@ #include "types/user.hh" namespace cql3 { - -user_types::value::value(std::vector elements, data_type my_type) - : terminal(std::move(my_type)), _elements(std::move(elements)) { -} - -user_types::value user_types::value::from_serialized(const raw_value_view& v, const user_type_impl& type) { - return v.with_value([&] (const FragmentedView auto& val) { - std::vector elements = type.split_fragmented(val); - if (elements.size() > type.size()) { - throw exceptions::invalid_request_exception( - format("User Defined Type value contained too many fields (expected {}, got {})", type.size(), elements.size())); - } - - return value(std::move(elements), type.shared_from_this()); - }); -} - -cql3::raw_value user_types::value::get(const query_options&) { - return cql3::raw_value::make_value(tuple_type_impl::build_value_fragmented(_elements)); -} - -sstring user_types::value::to_string() const { - return format("({})", join(", ", _elements)); -} - -user_types::delayed_value::delayed_value(user_type type, std::vector> values) - : _type(std::move(type)), _values(std::move(values)) { -} -bool user_types::delayed_value::contains_bind_marker() const { - return boost::algorithm::any_of(_values, std::mem_fn(&term::contains_bind_marker)); -} - -void user_types::delayed_value::fill_prepare_context(prepare_context& ctx) const { - for (auto&& v : _values) { - v->fill_prepare_context(ctx); - } -} - -std::vector user_types::delayed_value::bind_internal(const query_options& options) { - auto sf = options.get_cql_serialization_format(); - - // user_types::literal::prepare makes sure that every field gets a corresponding value. - // For missing fields the values become nullopts. - assert(_type->size() == _values.size()); - - std::vector buffers; - for (size_t i = 0; i < _type->size(); ++i) { - const auto& value = expr::evaluate_to_raw_view(_values[i], options); - if (!_type->is_multi_cell() && value.is_unset_value()) { - throw exceptions::invalid_request_exception(format("Invalid unset value for field '{}' of user defined type {}", - _type->field_name_as_string(i), _type->get_name_as_string())); - } - - buffers.push_back(to_managed_bytes_opt(value)); - - // Inside UDT values, we must force the serialization of collections to v3 whatever protocol - // version is in use since we're going to store directly that serialized value. - if (!sf.collection_format_unchanged() && _type->field_type(i)->is_collection() && buffers.back()) { - auto&& ctype = static_pointer_cast(_type->field_type(i)); - buffers.back() = ctype->reserialize(sf, cql_serialization_format::latest(), managed_bytes_view(*buffers.back())); - } - } - return buffers; -} - -shared_ptr user_types::delayed_value::bind(const query_options& options) { - return ::make_shared(bind_internal(options), _type); -} - -expr::expression user_types::delayed_value::to_expression() { - expr::usertype_constructor::elements_map_type new_elements; - for (size_t i = 0; i < _values.size(); i++) { - column_identifier field_name(_type->field_names().at(i), _type->string_field_names().at(i)); - expr::expression field_value(expr::to_expression(_values[i])); - new_elements.emplace(std::move(field_name), std::move(field_value)); - } - - return expr::usertype_constructor { - .elements = std::move(new_elements), - .type = _type - }; -} - -shared_ptr user_types::marker::bind(const query_options& options) { - auto value = options.get_value_at(_bind_index); - if (value.is_null()) { - return nullptr; - } - if (value.is_unset_value()) { - return constants::UNSET_VALUE; - } - return make_shared(value::from_serialized(value, static_cast(*_receiver->type))); -} - -expr::expression user_types::marker::to_expression() { - return expr::bind_variable { - .shape = expr::bind_variable::shape_type::scalar, - .bind_index = _bind_index, - .receiver = _receiver - }; -} - void user_types::setter::execute(mutation& m, const clustering_key_prefix& row_key, const update_parameters& params) { const expr::constant value = expr::evaluate(*_e, params._options); execute(m, row_key, params, column, value); diff --git a/cql3/user_types.hh b/cql3/user_types.hh index dcb9c5081c..30654d734c 100644 --- a/cql3/user_types.hh +++ b/cql3/user_types.hh @@ -42,7 +42,6 @@ #include "cql3/abstract_marker.hh" #include "column_specification.hh" -#include "term.hh" #include "column_identifier.hh" #include "operation.hh" #include "to_string.hh" @@ -57,45 +56,6 @@ class user_types { public: static lw_shared_ptr field_spec_of(const column_specification& column, size_t field); - class value : public terminal { - std::vector _elements; - public: - explicit value(std::vector, data_type my_type); - - static value from_serialized(const raw_value_view&, const user_type_impl&); - - virtual cql3::raw_value get(const query_options&) override; - virtual sstring to_string() const override; - }; - - // Same purpose than Lists.DelayedValue, except we do handle bind marker in that case - class delayed_value : public non_terminal { - user_type _type; - std::vector> _values; - public: - delayed_value(user_type type, std::vector> values); - virtual bool contains_bind_marker() const override; - virtual void fill_prepare_context(prepare_context& ctx) const override; - private: - std::vector bind_internal(const query_options& options); - public: - virtual shared_ptr bind(const query_options& options) override; - - virtual expr::expression to_expression() override; - }; - - class marker : public abstract_marker { - public: - marker(int32_t bind_index, lw_shared_ptr receiver) - : abstract_marker{bind_index, std::move(receiver)} - { - assert(_receiver->type->is_user_type()); - } - - virtual shared_ptr bind(const query_options& options) override; - virtual expr::expression to_expression() override; - }; - class setter : public operation { public: using operation::operation; From 51a8a1f89ba608bee449fa203f2105fb0c863620 Mon Sep 17 00:00:00 2001 From: Jan Ciolek Date: Thu, 28 Oct 2021 19:30:00 +0200 Subject: [PATCH 24/24] cql3: Remove remaining mentions of term There were a few places where term was still mentioned. Removed/replaced term with expression. search_and_replace is still done only on LHS of binary_operator because the existing code would break otherwise. Signed-off-by: Jan Ciolek --- cql3/expr/prepare_expr.cc | 30 ++++++++++++------------- cql3/operation.cc | 2 +- cql3/operation.hh | 4 ++-- cql3/statements/raw/update_statement.hh | 2 +- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/cql3/expr/prepare_expr.cc b/cql3/expr/prepare_expr.cc index a78590a7a5..72ac60d7a8 100644 --- a/cql3/expr/prepare_expr.cc +++ b/cql3/expr/prepare_expr.cc @@ -909,22 +909,22 @@ prepare_expression(const expression& expr, database& db, const sstring& keyspace on_internal_error(expr_logger, "Can't prepare constant_value, it should not appear in parser output"); }, [&] (const binary_operator&) -> expression { - on_internal_error(expr_logger, "binary_operators are not yet reachable via term_raw_expr::prepare()"); + on_internal_error(expr_logger, "binary_operators are not yet reachable via prepare_expression()"); }, [&] (const conjunction&) -> expression { - on_internal_error(expr_logger, "conjunctions are not yet reachable via term_raw_expr::prepare()"); + on_internal_error(expr_logger, "conjunctions are not yet reachable via prepare_expression()"); }, [&] (const column_value&) -> expression { - on_internal_error(expr_logger, "column_values are not yet reachable via term_raw_expr::prepare()"); + on_internal_error(expr_logger, "column_values are not yet reachable via prepare_expression()"); }, [&] (const token&) -> expression { - on_internal_error(expr_logger, "tokens are not yet reachable via term_raw_expr::prepare()"); + on_internal_error(expr_logger, "tokens are not yet reachable via prepare_expression()"); }, [&] (const unresolved_identifier&) -> expression { - on_internal_error(expr_logger, "unresolved_identifiers are not yet reachable via term_raw_expr::prepare()"); + on_internal_error(expr_logger, "unresolved_identifiers are not yet reachable via prepare_expression()"); }, [&] (const column_mutation_attribute&) -> expression { - on_internal_error(expr_logger, "column_mutation_attributes are not yet reachable via term_raw_expr::prepare()"); + on_internal_error(expr_logger, "column_mutation_attributes are not yet reachable via prepare_expression()"); }, [&] (const function_call& fc) -> expression { return prepare_function_call(fc, db, keyspace, std::move(receiver)); @@ -933,7 +933,7 @@ prepare_expression(const expression& expr, database& db, const sstring& keyspace return cast_prepare_expression(c, db, keyspace, receiver); }, [&] (const field_selection&) -> expression { - on_internal_error(expr_logger, "field_selections are not yet reachable via term_raw_expr::prepare()"); + on_internal_error(expr_logger, "field_selections are not yet reachable via prepare_expression()"); }, [&] (const null&) -> expression { return null_prepare_expression(db, keyspace, receiver); @@ -994,25 +994,25 @@ test_assignment(const expression& expr, database& db, const sstring& keyspace, c return expr::visit(overloaded_functor{ [&] (const constant&) -> test_result { // constants shouldn't appear in parser output, only untyped_constants - on_internal_error(expr_logger, "constants are not yet reachable via term_raw_expr::test_assignment()"); + on_internal_error(expr_logger, "constants are not yet reachable via test_assignment()"); }, [&] (const binary_operator&) -> test_result { - on_internal_error(expr_logger, "binary_operators are not yet reachable via term_raw_expr::test_assignment()"); + on_internal_error(expr_logger, "binary_operators are not yet reachable via test_assignment()"); }, [&] (const conjunction&) -> test_result { - on_internal_error(expr_logger, "conjunctions are not yet reachable via term_raw_expr::test_assignment()"); + on_internal_error(expr_logger, "conjunctions are not yet reachable via test_assignment()"); }, [&] (const column_value&) -> test_result { - on_internal_error(expr_logger, "column_values are not yet reachable via term_raw_expr::test_assignment()"); + on_internal_error(expr_logger, "column_values are not yet reachable via test_assignment()"); }, [&] (const token&) -> test_result { - on_internal_error(expr_logger, "tokens are not yet reachable via term_raw_expr::test_assignment()"); + on_internal_error(expr_logger, "tokens are not yet reachable via test_assignment()"); }, [&] (const unresolved_identifier&) -> test_result { - on_internal_error(expr_logger, "unresolved_identifiers are not yet reachable via term_raw_expr::test_assignment()"); + on_internal_error(expr_logger, "unresolved_identifiers are not yet reachable via test_assignment()"); }, [&] (const column_mutation_attribute&) -> test_result { - on_internal_error(expr_logger, "column_mutation_attributes are not yet reachable via term_raw_expr::test_assignment()"); + on_internal_error(expr_logger, "column_mutation_attributes are not yet reachable via test_assignment()"); }, [&] (const function_call& fc) -> test_result { return test_assignment_function_call(fc, db, keyspace, receiver); @@ -1021,7 +1021,7 @@ test_assignment(const expression& expr, database& db, const sstring& keyspace, c return cast_test_assignment(c, db, keyspace, receiver); }, [&] (const field_selection&) -> test_result { - on_internal_error(expr_logger, "field_selections are not yet reachable via term_raw_expr::test_assignment()"); + on_internal_error(expr_logger, "field_selections are not yet reachable via test_assignment()"); }, [&] (const null&) -> test_result { return null_test_assignment(db, keyspace, receiver); diff --git a/cql3/operation.cc b/cql3/operation.cc index 8c7d7f4b46..5d29e1516f 100644 --- a/cql3/operation.cc +++ b/cql3/operation.cc @@ -292,7 +292,7 @@ operation::set_counter_value_from_tuple_list::prepare(database& db, const sstrin throw exceptions::invalid_request_exception(format("Column {} is not a counter", receiver.name_as_text())); } - // We need to fake a column of list> to prepare the value term + // We need to fake a column of list> to prepare the value expression auto & os = receiver.column_specification; auto spec = make_lw_shared(os->ks_name, os->cf_name, os->name, counter_tuple_list_type); auto v = prepare_expression(_value, db, keyspace, spec); diff --git a/cql3/operation.hh b/cql3/operation.hh index 2b9e9fcf35..50454e92b0 100644 --- a/cql3/operation.hh +++ b/cql3/operation.hh @@ -77,7 +77,7 @@ public: protected: // Value involved in the operation. In theory this should not be here since some operation - // may require none of more than one term, but most need 1 so it simplify things a bit. + // may require none of more than one expression, but most need 1 so it simplify things a bit. std::optional _e; public: @@ -139,7 +139,7 @@ public: * Operation. * * @param receiver the "column" this operation applies to. Note that - * contrarly to the method of same name in Term.Raw, the receiver should always + * contrarly to the method of same name in raw expression, the receiver should always * be a true column. * @return the prepared update operation. */ diff --git a/cql3/statements/raw/update_statement.hh b/cql3/statements/raw/update_statement.hh index 2757a2a417..889cec1423 100644 --- a/cql3/statements/raw/update_statement.hh +++ b/cql3/statements/raw/update_statement.hh @@ -67,7 +67,7 @@ private: public: /** * Creates a new UpdateStatement from a column family name, columns map, consistency - * level, and key term. + * level, and key expression. * * @param name column family being operated on * @param attrs additional attributes for statement (timestamp, timeToLive)