diff --git a/CMakeLists.txt b/CMakeLists.txt index 6c6bce7237..b36cc14136 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 @@ -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 2d7c7bfe5f..870c209e8b 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 884f96b699..150dd4170d 100755 --- a/configure.py +++ b/configure.py @@ -756,11 +756,10 @@ 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', - '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/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/attributes.cc b/cql3/attributes.cc index 510e9d93dc..2f6b004bab 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 = prepare_expression(*timestamp, db, ks_name, timestamp_receiver(ks_name, cf_name)); + } + + if (time_to_live.has_value()) { + ttl = prepare_expression(*time_to_live, db, ks_name, time_to_live_receiver(ks_name, cf_name)); + } + + if (timeout.has_value()) { + 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/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: diff --git a/cql3/column_condition.cc b/cql3/column_condition.cc index d4cbd28a91..a7366f6ec7 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 = prepare_expression(*_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), + prepare_expression(*_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), + 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, collection_element_term, - prepare_term(*_value, db, keyspace, value_spec), nullptr, _op); + return column_condition::condition(receiver, std::move(collection_element_expression), + prepare_expression(*_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 = 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<::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(prepare_expression(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); } diff --git a/cql3/constants.cc b/cql3/constants.cc index e23a3b4ef3..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, - .value_type = _receiver->type - }; -} - } diff --git a/cql3/constants.hh b/cql3/constants.hh index 2b769bd1ce..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,70 +59,13 @@ 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; 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 +81,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 +96,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 +113,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/expr/expression.cc b/cql3/expr/expression.cc index 57dfbbfbe3..36074c5a0d 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,7 @@ #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 { namespace expr { @@ -69,7 +68,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)) @@ -93,7 +92,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; @@ -103,7 +102,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; }; @@ -119,9 +118,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; @@ -166,13 +165,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"); @@ -211,7 +210,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"); } @@ -219,17 +218,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"); @@ -396,13 +395,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 {}", 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); @@ -410,10 +409,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) { @@ -457,30 +456,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)); @@ -606,26 +608,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 {}", 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 @@ -703,7 +705,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. } @@ -726,7 +728,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. } @@ -748,7 +750,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. } @@ -954,10 +956,10 @@ 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; + os << "(" << opr.lhs << ") " << opr.op << ' ' << opr.rhs; }, [&] (const token& t) { os << "TOKEN"; }, [&] (const column_value& col) { @@ -1110,7 +1112,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)}; @@ -1153,6 +1155,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; }, @@ -1282,6 +1290,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; @@ -1295,41 +1307,7 @@ std::optional get_bool_value(const constant& constant_val) { return std::nullopt; } - return constant_val.value.to_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)); + return constant_val.view().deserialize(*boolean_type); } constant evaluate(const expression& e, const query_options& options) { @@ -1374,45 +1352,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, @@ -1481,7 +1420,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()) { @@ -1503,28 +1442,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 +1470,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) { @@ -1629,6 +1567,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()); } @@ -1657,14 +1605,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(), @@ -1681,7 +1629,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) { @@ -1765,8 +1714,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()); } @@ -1774,8 +1725,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()) { @@ -1794,13 +1745,7 @@ 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(); - +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); @@ -1831,7 +1776,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()); }); } @@ -1839,7 +1784,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()); }); } @@ -1847,7 +1792,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()); }); } @@ -1855,7 +1800,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); }); @@ -1864,7 +1809,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); }); @@ -1912,12 +1857,70 @@ utils::chunked_vector> get_list_of_tuples_element return tuples_list; } -expression to_expression(const ::shared_ptr& term_ptr) { - if (term_ptr.get() == nullptr) { - return constant::make_null(); - } +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); + } - return term_ptr->to_expression(); + 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) { + if (cv.sub.has_value()) { + fill_prepare_context(*cv.sub, ctx); + } + }, + [](untyped_constant&) {}, + [](null&) {}, + [](constant&) {}, + }, 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; } } // namespace expr } // namespace cql3 diff --git a/cql3/expr/expression.hh b/cql3/expr/expression.hh index 12058b4e71..993ea810d2 100644 --- a/cql3/expr/expression.hh +++ b/cql3/expr/expression.hh @@ -52,7 +52,7 @@ namespace query { } // namespace query namespace cql3 { -struct term; +struct prepare_context; class column_identifier_raw; class query_options; @@ -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); @@ -161,7 +182,6 @@ public: template concept LeafExpression = std::same_as - || std::same_as || std::same_as || std::same_as || std::same_as @@ -170,14 +190,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 @@ -196,10 +216,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. @@ -241,7 +261,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 { @@ -263,10 +297,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 @@ -295,6 +328,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. @@ -350,6 +385,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); @@ -418,75 +457,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&) -> const binary_operator* { 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_value& cv) -> const ExprElem* { + if (cv.sub.has_value()) { + return find_in_expression(*cv.sub, predicate_fun); + } + return nullptr; + }, + [&] (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_binop(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 @@ -536,7 +598,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) { @@ -545,7 +607,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) { @@ -553,7 +615,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) { @@ -575,11 +637,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) { @@ -587,7 +649,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. @@ -603,14 +665,11 @@ 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); -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_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); /** @@ -646,21 +705,9 @@ 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&); - -// 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&); +constant evaluate_IN_list(const expression&, const query_options&); // Takes a prepared expression and calculates its value. // Evaluates bound values, calls functions and returns just the bytes and type. @@ -684,7 +731,16 @@ 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&); -expression to_expression(const ::shared_ptr&); +// 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&); + +// 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); } // namespace expr } // namespace cql3 diff --git a/cql3/expr/term_expr.cc b/cql3/expr/prepare_expr.cc similarity index 71% rename from cql3/expr/term_expr.cc rename to cql3/expr/prepare_expr.cc index 635ac6b739..72ac60d7a8 100644 --- a/cql3/expr/term_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" @@ -83,13 +82,13 @@ usertype_constructor_test_assignment(const usertype_constructor& u, database& db } static -shared_ptr -usertype_constructor_prepare_term(const usertype_constructor& u, database& db, const sstring& keyspace, lw_shared_ptr receiver) { +expression +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; - 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 +100,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_expression(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 +118,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 +200,13 @@ map_test_assignment(const collection_constructor& c, database& db, const sstring } static -::shared_ptr -map_prepare_term(const collection_constructor& c, database& db, const sstring& keyspace, lw_shared_ptr receiver) { +expression +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); 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 +214,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_expression(entry_tuple.elements[0], db, keyspace, key_spec); + expression v = prepare_expression(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,8 +295,8 @@ set_test_assignment(const collection_constructor& c, database& db, const sstring } static -shared_ptr -set_prepare_term(const collection_constructor& c, database& db, const sstring& keyspace, lw_shared_ptr receiver) { +expression +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()) { @@ -294,40 +306,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_expression(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,8 +395,8 @@ list_test_assignment(const collection_constructor& c, database& db, const sstrin static -shared_ptr -list_prepare_term(const collection_constructor& c, database& db, const sstring& keyspace, lw_shared_ptr receiver) { +expression +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 @@ -384,29 +404,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_expression(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 +477,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_expression(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_expression(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,15 +639,15 @@ untyped_constant_test_assignment(const untyped_constant& uc, database& db, const } static -::shared_ptr -untyped_constant_prepare_term(const untyped_constant& uc, database& db, const sstring& keyspace, lw_shared_ptr receiver) +constant +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 {}", 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 +657,14 @@ bind_variable_test_assignment(const bind_variable& bv, database& db, const sstri } static -::shared_ptr -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); +bind_variable +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, + .bind_index = bv.bind_index, + .receiver = receiver + }; } static @@ -656,9 +675,13 @@ bind_variable_scalar_in_make_receiver(const column_specification& receiver) { } static -::shared_ptr -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)); +bind_variable +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, + .receiver = bind_variable_scalar_in_make_receiver(*receiver) + }; } static @@ -682,9 +705,13 @@ bind_variable_tuple_make_receiver(const std::vector -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)); +bind_variable +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, + .receiver = bind_variable_tuple_make_receiver(receivers) + }; } static @@ -713,9 +740,13 @@ bind_variable_tuple_in_make_receiver(const std::vector -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)); +bind_variable +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, + .receiver = bind_variable_tuple_in_make_receiver(receivers) + }; } static @@ -729,12 +760,12 @@ null_test_assignment(database& db, } static -::shared_ptr -null_prepare_term(database& db, const sstring& keyspace, lw_shared_ptr receiver) { +constant +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"); } - return constants::NULL_VALUE; + return constant::make_null(receiver->type); } static @@ -770,8 +801,8 @@ cast_test_assignment(const cast& c, database& db, const sstring& keyspace, const } static -shared_ptr -cast_prepare_term(const cast& c, database& db, const sstring& keyspace, lw_shared_ptr receiver) { +expression +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)); @@ -779,91 +810,180 @@ 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); } -::shared_ptr -prepare_term(const expression& expr, 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) { + 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{ - [] (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 { - on_internal_error(expr_logger, "binary_operators are not yet reachable via term_raw_expr::prepare()"); + [&] (const binary_operator&) -> expression { + on_internal_error(expr_logger, "binary_operators are not yet reachable via prepare_expression()"); }, - [&] (const conjunction&) -> ::shared_ptr { - on_internal_error(expr_logger, "conjunctions are not yet reachable via term_raw_expr::prepare()"); + [&] (const conjunction&) -> expression { + on_internal_error(expr_logger, "conjunctions are not yet reachable via prepare_expression()"); }, - [&] (const column_value&) -> ::shared_ptr { - on_internal_error(expr_logger, "column_values are not yet reachable via term_raw_expr::prepare()"); + [&] (const column_value&) -> expression { + on_internal_error(expr_logger, "column_values are not yet reachable via prepare_expression()"); }, - [&] (const token&) -> ::shared_ptr { - on_internal_error(expr_logger, "tokens are not yet reachable via term_raw_expr::prepare()"); + [&] (const token&) -> expression { + on_internal_error(expr_logger, "tokens are not yet reachable via prepare_expression()"); }, - [&] (const unresolved_identifier&) -> ::shared_ptr { - on_internal_error(expr_logger, "unresolved_identifiers are not yet reachable via term_raw_expr::prepare()"); + [&] (const unresolved_identifier&) -> expression { + on_internal_error(expr_logger, "unresolved_identifiers are not yet reachable via prepare_expression()"); }, - [&] (const column_mutation_attribute&) -> ::shared_ptr { - on_internal_error(expr_logger, "column_mutation_attributes are not yet reachable via term_raw_expr::prepare()"); + [&] (const column_mutation_attribute&) -> expression { + on_internal_error(expr_logger, "column_mutation_attributes are not yet reachable via prepare_expression()"); }, - [&] (const function_call& fc) -> ::shared_ptr { - return functions::prepare_function_call(fc, db, keyspace, std::move(receiver)); + [&] (const function_call& fc) -> expression { + return prepare_function_call(fc, db, keyspace, std::move(receiver)); }, - [&] (const cast& c) -> ::shared_ptr { - return cast_prepare_term(c, db, keyspace, receiver); + [&] (const cast& c) -> expression { + return cast_prepare_expression(c, db, keyspace, receiver); }, - [&] (const field_selection&) -> ::shared_ptr { - on_internal_error(expr_logger, "field_selections are not yet reachable via term_raw_expr::prepare()"); + [&] (const field_selection&) -> expression { + on_internal_error(expr_logger, "field_selections are not yet reachable via prepare_expression()"); }, - [&] (const null&) -> ::shared_ptr { - return null_prepare_term(db, keyspace, receiver); + [&] (const null&) -> expression { + return null_prepare_expression(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); - 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) -> ::shared_ptr { - return untyped_constant_prepare_term(uc, db, keyspace, receiver); + [&] (const untyped_constant& uc) -> expression { + return untyped_constant_prepare_expression(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); - 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) -> ::shared_ptr { - return usertype_constructor_prepare_term(uc, db, keyspace, receiver); + [&] (const usertype_constructor& uc) -> expression { + return usertype_constructor_prepare_expression(uc, db, keyspace, receiver); }, }, expr); } -::shared_ptr -prepare_term_multi_column(const expression& expr, database& db, const sstring& keyspace, const std::vector>& receivers) { +expression +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) -> ::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))"); - 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"); }, - [&] (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 { - on_internal_error(expr_logger, fmt::format("prepare_term_multi_column({})", typeid(default_case).name())); + [] (const auto& default_case) -> expression { + on_internal_error(expr_logger, fmt::format("prepare_expression_multi_column({})", typeid(default_case).name())); }, }, expr); } @@ -874,34 +994,34 @@ 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 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); }, [&] (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/functions/function_call.hh b/cql3/functions/function_call.hh deleted file mode 100644 index 1d39540e6a..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); -}; - -::shared_ptr 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 4cdf5f4f41..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,10 +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 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 { functions::_declared = init(); } @@ -447,226 +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 = _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()); - } - )); -} - -::shared_ptr -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) { - 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())) { - all_terminal = false; - } - parameters.push_back(t); - } - - // If all parameters are terminal and the function is pure, we can - // evaluate it now, otherwise we'd have to wait execution time - 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()); - } else { - return ::make_shared(scalar_fun, parameters); - } -} - -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 - // 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 84930bef0d..c05306db42 100644 --- a/cql3/lists.cc +++ b/cql3/lists.cc @@ -42,167 +42,9 @@ 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, - .value_type = _receiver->type - }; -} - 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 +69,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 +79,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 +108,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 +128,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 +157,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; } @@ -355,7 +203,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())); } } } @@ -363,7 +211,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 +267,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 +316,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"); } @@ -477,7 +325,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/lists.hh b/cql3/lists.hh index 3f67492fdb..50a5ff06e3 100644 --- a/cql3/lists.hh +++ b/cql3/lists.hh @@ -57,69 +57,11 @@ 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: - 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 +69,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 +108,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 +117,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 889de7d0c6..704ec6e3c9 100644 --- a/cql3/maps.cc +++ b/cql3/maps.cc @@ -41,182 +41,17 @@ #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, - .value_type = _receiver->type - }; -} - 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 +70,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 +103,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); } @@ -293,7 +130,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())); } } } @@ -301,7 +138,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..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,48 +58,10 @@ 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, 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 +69,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 +91,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/multi_column_relation.hh b/cql3/multi_column_relation.hh index ba9922983f..732023ec66 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 @@ -159,8 +156,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, 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,18 +168,18 @@ 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, std::move(ts[0])); + if (es.size() == 1) { + return ::make_shared(schema, rs, std::move(es[0])); } - return ::make_shared(schema, rs, ts); + return ::make_shared(schema, rs, std::move(es)); } } @@ -194,8 +191,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, @@ -215,12 +212,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 = prepare_expression_multi_column(raw, db, keyspace, receivers); + expr::fill_prepare_context(e, ctx); + return e; } std::vector receivers(database& db, const schema& schema) { @@ -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.cc b/cql3/operation.cc index 4fbc3058c4..5d29e1516f 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 = 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)); - return make_shared(receiver, idx, lval); + 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)); - return make_shared(receiver, idx, lval); + 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)); - return make_shared(receiver, key, mval); + 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,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 = prepare_expression(_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 = prepare_expression(_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, 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())); @@ -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 @@ -292,10 +292,10 @@ 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_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 { @@ -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 = 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/operation.hh b/cql3/operation.hh index 772f2aaa4b..50454e92b0 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" @@ -77,14 +76,14 @@ public: const column_definition& column; protected: - // Term 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; + // Value involved in the operation. In theory this should not be here since some operation + // may require none of more than one expression, but most need 1 so it simplify things a bit. + 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 +106,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); } } @@ -140,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/prepare_context.cc b/cql3/prepare_context.cc index f4b644d4e1..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 { @@ -98,18 +97,23 @@ void prepare_context::set_bound_variables(const std::vector>& 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) { +void prepare_context::add_pk_function_call(expr::function_call& 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()); - _pk_fn_calls.emplace_back(std::move(fn)); + + 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..78f65d108e 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; @@ -66,10 +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 @@ -93,11 +94,11 @@ 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/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/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 603520594d..967aa3192d 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 { @@ -196,9 +196,9 @@ 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)) { @@ -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, - ::make_shared(_values, std::move(in_list_type))}; + std::move(values_list)}; } }; @@ -340,37 +346,37 @@ 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, - std::move(marker)}; + expr::expression(std::move(marker))}; } }; 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_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), - std::move(term), + std::move(e), m}; } @@ -472,17 +478,16 @@ 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::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]); - 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/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/single_column_restriction.hh b/cql3/restrictions/single_column_restriction.hh index fd7bcb871c..60a5e7a650 100644 --- a/cql3/restrictions/single_column_restriction.hh +++ b/cql3/restrictions/single_column_restriction.hh @@ -44,8 +44,7 @@ #include #include "cql3/restrictions/restriction.hh" -#include "cql3/restrictions/term_slice.hh" -#include "cql3/term.hh" +#include "cql3/restrictions/bounds_slice.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 e624b14168..3e5934557e 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" @@ -452,7 +451,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 +498,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 +688,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 +763,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 +1427,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; @@ -1617,7 +1616,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) { @@ -1647,11 +1646,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; } @@ -1689,8 +1688,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); @@ -1733,8 +1732,8 @@ 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 find_binop(expr, [&] (const binary_operator& binop) { + return binop.op == oper_t::EQ && evaluate(binop.rhs, options).is_null(); }); } @@ -1742,6 +1741,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/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; diff --git a/cql3/sets.cc b/cql3/sets.cc index cfabfc129b..532c96ae23 100644 --- a/cql3/sets.cc +++ b/cql3/sets.cc @@ -26,157 +26,9 @@ #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, - .value_type = _receiver->type - }; -} - 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 +48,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; } @@ -229,7 +81,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()); } @@ -239,7 +91,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 +109,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..64cf6dec75 100644 --- a/cql3/sets.hh +++ b/cql3/sets.hh @@ -58,46 +58,10 @@ 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, 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 +69,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 +80,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/single_column_relation.cc b/cql3/single_column_relation.cc index 537748d4e5..29978c4a70 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 = prepare_expression(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 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 = 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 = 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 = 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 term = 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(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); + 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, 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, - ::make_shared(std::move(terms), list_type_impl::get_instance(column_def.type, false))}; + std::move(list_value)}; return r; } @@ -128,9 +133,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 = 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(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..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 { @@ -115,9 +114,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,9 +168,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 = 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(term)}; + r->expression = expr::binary_operator{expr::column_value{&column_def}, _relation_type, std::move(e)}; return r; } @@ -179,10 +178,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 = 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(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/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/create_aggregate_statement.cc b/cql3/statements/create_aggregate_statement.cc index d93c29ce68..33ec9e6511 100644 --- a/cql3/statements/create_aggregate_statement.cc +++ b/cql3/statements/create_aggregate_statement.cc @@ -57,8 +57,8 @@ void create_aggregate_statement::create(service::storage_proxy& proxy, functions auto dummy_ident = ::make_shared("", 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_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)); return; 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/modification_statement.cc b/cql3/statements/modification_statement.cc index 9c77624534..10a7659516 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; } 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/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/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) diff --git a/cql3/statements/select_statement.cc b/cql3/statements/select_statement.cc index 8cae31b6e6..811d7833b3 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, @@ -1477,16 +1479,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 = prepare_expression(*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, diff --git a/cql3/statements/update_statement.cc b/cql3/statements/update_statement.cc index 6e0bc33150..fe42c1e71d 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()) { @@ -180,8 +180,9 @@ 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()); + 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()); } void @@ -212,29 +213,26 @@ insert_prepared_json_statement::execute_set_value(mutation& m, const clustering_ return; } - cql_serialization_format sf = params._options.get_cql_serialization_format(); + + 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 = 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); return stmt; diff --git a/cql3/statements/update_statement.hh b/cql3/statements/update_statement.hh index b6d38d3291..b84bf508d9 100644 --- a/cql3/statements/update_statement.hh +++ b/cql3/statements/update_statement.hh @@ -48,8 +48,6 @@ namespace cql3 { -class term; - namespace statements { /** @@ -82,7 +80,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 +88,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/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 cded9f73bb..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) { @@ -83,11 +82,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, + 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(term)}; + r->expression = binary_operator{token{}, oper_t::EQ, std::move(e)}; return r; } @@ -105,11 +104,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, + 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(term)}; + r->expression = binary_operator{token{}, pick_operator(bound, inclusive), std::move(e)}; return r; } @@ -130,13 +129,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::prepare_expression(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..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" @@ -123,11 +122,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; }; } diff --git a/cql3/tuples.cc b/cql3/tuples.cc deleted file mode 100644 index ad29691463..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, - .value_type = _receiver->type - }; -} - -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 - }; -} - -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/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); } diff --git a/cql3/user_types.cc b/cql3/user_types.cc index 4b3bcad382..cbb0d93f1c 100644 --- a/cql3/user_types.cc +++ b/cql3/user_types.cc @@ -49,110 +49,8 @@ #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, - .value_type = _receiver->type - }; -} - 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); } @@ -200,7 +98,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()); } @@ -210,7 +108,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 +116,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..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; @@ -107,8 +67,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 +78,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; 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); 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; +}