diff --git a/cql3/attributes.cc b/cql3/attributes.cc index 77eb23d7b9..0c62cd919e 100644 --- a/cql3/attributes.cc +++ b/cql3/attributes.cc @@ -10,6 +10,8 @@ #include "cql3/attributes.hh" #include "cql3/column_identifier.hh" +#include "cql3/expr/evaluate.hh" +#include "cql3/expr/expr-utils.hh" #include namespace cql3 { diff --git a/cql3/constants.cc b/cql3/constants.cc index f6ce51fbdc..552135c888 100644 --- a/cql3/constants.cc +++ b/cql3/constants.cc @@ -11,6 +11,7 @@ #include "cql3/constants.hh" #include "cql3/cql3_type.hh" #include "cql3/statements/strongly_consistent_modification_statement.hh" +#include "cql3/expr/evaluate.hh" namespace cql3 { diff --git a/cql3/expr/evaluate.hh b/cql3/expr/evaluate.hh new file mode 100644 index 0000000000..0ba72ee869 --- /dev/null +++ b/cql3/expr/evaluate.hh @@ -0,0 +1,38 @@ +// Copyright (C) 2023-present ScyllaDB +// SPDX-License-Identifier: AGPL-3.0-or-later + +#pragma once + +#include "expression.hh" + +#include "bytes.hh" +#include + +namespace cql3 { + +class query_options; + +} + +namespace cql3::expr { + +// Input data needed to evaluate an expression. Individual members can be +// null if not applicable (e.g. evaluating outside a row context) +struct evaluation_inputs { + std::span partition_key; + std::span clustering_key; + std::span static_and_regular_columns; // indexes match `selection` member + const cql3::selection::selection* selection = nullptr; + const query_options* options = nullptr; + std::span static_and_regular_timestamps; // indexes match `selection` member + std::span static_and_regular_ttls; // indexes match `selection` member +}; + +// Takes a prepared expression and calculates its value. +// Evaluates bound values, calls functions and returns just the bytes and type. +cql3::raw_value evaluate(const expression& e, const evaluation_inputs&); + +cql3::raw_value evaluate(const expression& e, const query_options&); + + +} diff --git a/cql3/expr/expr-utils.hh b/cql3/expr/expr-utils.hh new file mode 100644 index 0000000000..f7a7e1ba2d --- /dev/null +++ b/cql3/expr/expr-utils.hh @@ -0,0 +1,356 @@ +// Copyright (C) 2023-present ScyllaDB +// SPDX-License-Identifier: AGPL-3.0-or-later + +#pragma once + +#include "expression.hh" + +#include "bytes.hh" +#include "keys.hh" +#include "range.hh" +#include "cql3/expr/restrictions.hh" +#include "cql3/assignment_testable.hh" +#include "cql3/statements/bound.hh" + +namespace cql3 { + +struct prepare_context; + +class column_identifier_raw; +class query_options; + +namespace selection { + class selection; +} // namespace selection + +} // namespace cql3 + +namespace cql3::expr { + +class evaluation_inputs; + +/// Helper for generating evaluation_inputs::static_and_regular_columns +std::vector get_non_pk_values(const cql3::selection::selection& selection, const query::result_row_view& static_row, + const query::result_row_view* row); + +/// Helper for accessing a column value from evaluation_inputs +managed_bytes_opt extract_column_value(const column_definition* cdef, const evaluation_inputs& inputs); + +/// True iff restr evaluates to true, given these inputs +extern bool is_satisfied_by( + const expression& restr, const evaluation_inputs& inputs); + + +/// A set of discrete values. +using value_list = std::vector; // Sorted and deduped using value comparator. + +/// General set of values. Empty set and single-element sets are always value_list. nonwrapping_range is +/// never singular and never has start > end. Universal set is a nonwrapping_range with both bounds null. +using value_set = std::variant>; + +/// A set of all column values that would satisfy an expression. The _token_values variant finds +/// matching values for the partition token function call instead of the column. +/// +/// An expression restricts possible values of a column or token: +/// - `A>5` restricts A from below +/// - `A>5 AND A>6 AND B<10 AND A=12 AND B>0` restricts A to 12 and B to between 0 and 10 +/// - `A IN (1, 3, 5)` restricts A to 1, 3, or 5 +/// - `A IN (1, 3, 5) AND A>3` restricts A to just 5 +/// - `A=1 AND A<=0` restricts A to an empty list; no value is able to satisfy the expression +/// - `A>=NULL` also restricts A to an empty list; all comparisons to NULL are false +/// - an expression without A "restricts" A to unbounded range +extern value_set possible_column_values(const column_definition*, const expression&, const query_options&); +extern value_set possible_partition_token_values(const expression&, const query_options&, const schema& table_schema); + +/// Turns value_set into a range, unless it's a multi-valued list (in which case this throws). +extern nonwrapping_range to_range(const value_set&); + +/// A range of all X such that X op val. +nonwrapping_range to_range(oper_t op, const clustering_key_prefix& val); + +/// True iff the index can support the entire expression. +extern bool is_supported_by(const expression&, const secondary_index::index&); + +/// True iff any of the indices from the manager can support the entire expression. If allow_local, use all +/// indices; otherwise, use only global indices. +extern bool has_supporting_index( + const expression&, const secondary_index::secondary_index_manager&, allow_local_index allow_local); + +// Looks at each column indivudually and checks whether some index can support restrictions on this single column. +// Expression has to consist only of single column restrictions. +extern bool index_supports_some_column( + const expression&, + const secondary_index::secondary_index_manager&, + allow_local_index allow_local); + +extern bool recurse_until(const expression& e, const noncopyable_function& predicate_fun); + +// 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) { + const ExprElem* ret = nullptr; + recurse_until(e, [&] (const expression& e) { + if (auto expr_elem = as_if(&e)) { + if (predicate_fun(*expr_elem)) { + ret = expr_elem; + return true; + } + } + return false; + }); + return ret; +} + +/// 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); +} + +// Goes over each expression of the specified type and calls for_each_func for each of them. +// For example: +// for_each_expression(e, [](const column_value& cval) {std::cout << cval << '\n';}); +// Will print all column values in an expression +template +requires std::invocable +void for_each_expression(const expression& e, Fn for_each_func) { + recurse_until(e, [&] (const expression& cur_expr) -> bool { + if (auto expr_elem = as_if(&cur_expr)) { + for_each_func(*expr_elem); + } + return false; + }); +} + +/// Counts binary_operator atoms b for which f(b) is true. +size_t count_if(const expression& e, const noncopyable_function& f); + +inline const binary_operator* find(const expression& e, oper_t op) { + return find_binop(e, [&] (const binary_operator& o) { return o.op == op; }); +} + +inline bool needs_filtering(oper_t op) { + return (op == oper_t::CONTAINS) || (op == oper_t::CONTAINS_KEY) || (op == oper_t::LIKE) || + (op == oper_t::IS_NOT) || (op == oper_t::NEQ) ; +} + +inline auto find_needs_filtering(const expression& e) { + return find_binop(e, [] (const binary_operator& bo) { return needs_filtering(bo.op); }); +} + +inline bool is_slice(oper_t op) { + return (op == oper_t::LT) || (op == oper_t::LTE) || (op == oper_t::GT) || (op == oper_t::GTE); +} + +inline bool has_slice(const expression& e) { + return find_binop(e, [] (const binary_operator& bo) { return is_slice(bo.op); }); +} + +inline bool is_compare(oper_t op) { + switch (op) { + case oper_t::EQ: + case oper_t::LT: + case oper_t::LTE: + case oper_t::GT: + case oper_t::GTE: + case oper_t::NEQ: + return true; + default: + return false; + } +} + +inline bool is_multi_column(const binary_operator& op) { + return expr::is(op.lhs); +} + +// Check whether the given expression represents +// a call to the token() function. +bool is_token_function(const function_call&); +bool is_token_function(const expression&); + +bool is_partition_token_for_schema(const function_call&, const schema&); +bool is_partition_token_for_schema(const expression&, const schema&); + +/// Check whether the expression contains a binary_operator whose LHS is a call to the token +/// function representing a partition key token. +/// Examples: +/// For expression: "token(p1, p2, p3) < 123 AND c = 2" returns true +/// For expression: "p1 = token(1, 2, 3) AND c = 2" return false +inline bool has_partition_token(const expression& e, const schema& table_schema) { + return find_binop(e, [&] (const binary_operator& o) { return is_partition_token_for_schema(o.lhs, table_schema); }); +} + +inline bool has_slice_or_needs_filtering(const expression& e) { + 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) { + return op.order == comparison_order::clustering; +} + +inline auto find_clustering_order(const expression& e) { + return find_binop(e, is_clustering_order); +} + +/// Given a Boolean expression, compute its factors such as e=f1 AND f2 AND f3 ... +/// If the expression is TRUE, may return no factors (happens today for an +/// empty conjunction). +std::vector boolean_factors(expression e); + +/// Run the given function for each element in the top level conjunction. +void for_each_boolean_factor(const expression& e, const noncopyable_function& for_each_func); + +/// True iff binary_operator involves a collection. +extern bool is_on_collection(const binary_operator&); + +// Checks whether the given column occurs in the expression. +// Uses column_defintion::operator== for comparison, columns with the same name but different schema will not be equal. +bool contains_column(const column_definition& column, const expression& e); + +// Checks whether this expression contains a nonpure function. +// The expression must be prepared, so that function names are converted to function pointers. +bool contains_nonpure_function(const expression&); + +// Checks whether the given column has an EQ restriction in the expression. +// EQ restriction is `col = ...` or `(col, col2) = ...` +// IN restriction is NOT an EQ restriction, this function will not look for IN restrictions. +// Uses column_defintion::operator== for comparison, columns with the same name but different schema will not be equal. +bool has_eq_restriction_on_column(const column_definition& column, const expression& e); + +/// Replaces every column_definition in an expression with this one. Throws if any LHS is not a single +/// column_value. +extern expression replace_column_def(const expression&, const column_definition*); + +// Replaces all occurences of token(p1, p2) on the left hand side with the given colum. +// For example this changes token(p1, p2) < token(1, 2) to my_column_name < token(1, 2). +// Schema is needed to find out which calls to token() describe the partition token. +extern expression replace_partition_token(const expression&, const column_definition*, const schema&); + +// 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. +extern expression search_and_replace(const expression& e, + const noncopyable_function (const expression& candidate)>& replace_candidate); + +// Adjust an expression for rows that were fetched using query::partition_slice::options::collections_as_maps +expression adjust_for_collection_as_maps(const expression& e); + +extern expression prepare_expression(const expression& expr, data_dictionary::database db, const sstring& keyspace, const schema* schema_opt, lw_shared_ptr receiver); +std::optional try_prepare_expression(const expression& expr, data_dictionary::database db, const sstring& keyspace, const schema* schema_opt, lw_shared_ptr receiver); + +// Check that a prepared expression has no aggregate functions. Throws on error. +void verify_no_aggregate_functions(const expression& expr, std::string_view context_for_errors); + +// Prepares a binary operator received from the parser. +// Does some basic type checks but no advanced validation. +extern binary_operator prepare_binary_operator(binary_operator binop, data_dictionary::database db, const schema& table_schema); + +// Pre-compile any constant LIKE patterns and return equivalent expression +expression optimize_like(const expression& e); + + +/** + * @return whether this object can be assigned to the provided receiver. We distinguish + * between 3 values: + * - EXACT_MATCH if this object is exactly of the type expected by the receiver + * - WEAKLY_ASSIGNABLE if this object is not exactly the expected type but is assignable nonetheless + * - NOT_ASSIGNABLE if it's not assignable + * Most caller should just call the is_assignable() method on the result, though functions have a use for + * testing "strong" equality to decide the most precise overload to pick when multiple could match. + */ +extern assignment_testable::test_result test_assignment(const expression& expr, data_dictionary::database db, const sstring& keyspace, const schema* schema_opt, const column_specification& receiver); + +// Test all elements of exprs for assignment. If all are exact match, return exact match. If any is not assignable, +// return not assignable. Otherwise, return weakly assignable. +extern assignment_testable::test_result test_assignment_all(const std::vector& exprs, data_dictionary::database db, const sstring& keyspace, const schema* schema_opt, const column_specification& receiver); + +extern shared_ptr as_assignment_testable(expression e, std::optional type_opt); + +inline oper_t pick_operator(statements::bound b, bool inclusive) { + return is_start(b) ? + (inclusive ? oper_t::GTE : oper_t::GT) : + (inclusive ? oper_t::LTE : oper_t::LT); +} + +// Extracts all binary operators which have the given column on their left hand side. +// Extracts only single-column restrictions. +// Does not include multi-column restrictions. +// Does not include token() restrictions. +// Does not include boolean constant restrictions. +// For example "WHERE c = 1 AND (a, c) = (2, 1) AND token(p) < 2 AND FALSE" will return {"c = 1"}. +std::vector extract_single_column_restrictions_for_column(const expression&, const column_definition&); + +std::optional get_bool_value(const constant&); + +utils::chunked_vector get_list_elements(const cql3::raw_value&); +utils::chunked_vector get_set_elements(const cql3::raw_value&); +std::vector get_tuple_elements(const cql3::raw_value&, const abstract_type& type); +std::vector get_user_type_elements(const cql3::raw_value&, const abstract_type& type); +std::vector> get_map_elements(const cql3::raw_value&); + +// Gets the elements of a constant which can be a list, set, tuple or user type +std::vector get_elements(const cql3::raw_value&, const abstract_type& type); + +// Get elements of list> as vector +// It is useful with IN restrictions like (a, b) IN [(1, 2), (3, 4)]. +// `type` parameter refers to the list> type. +utils::chunked_vector> get_list_of_tuples_elements(const cql3::raw_value&, const abstract_type& type); + +// 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); + +// Checks whether this expression contains restrictions on one single column. +// There might be more than one restriction, but exactly one column. +// The expression must be prepared. +bool is_single_column_restriction(const expression&); + +// Gets the only column from a single_column_restriction expression. +const column_value& get_the_only_column(const expression&); + +// Extracts column_defs from the expression and sorts them using schema_pos_column_definition_comparator. +std::vector get_sorted_column_defs(const expression&); + +// Extracts column_defs and returns the last one according to schema_pos_column_definition_comparator. +const column_definition* get_last_column_def(const expression&); + +// Extracts map of single column restrictions for each column from expression +single_column_restrictions_map get_single_column_restrictions_map(const expression&); + +// Checks whether this expression is empty - doesn't restrict anything +bool is_empty_restriction(const expression&); + +// Finds common columns between both expressions and prints them to a string. +// Uses schema_pos_column_definition_comparator for comparison. +sstring get_columns_in_commons(const expression& a, const expression& b); + +// Finds the value of the given column in the expression +// In case of multpiple possible values calls on_internal_error +bytes_opt value_for(const column_definition&, const expression&, const query_options&); + +bool contains_multi_column_restriction(const expression&); + +bool has_only_eq_binops(const expression&); + +/// Finds the data type of writetime(x) or ttl(x) +data_type column_mutation_attribute_type(const column_mutation_attribute& e); + + + +} diff --git a/cql3/expr/expression.cc b/cql3/expr/expression.cc index 6a36e03221..8d5ea545d6 100644 --- a/cql3/expr/expression.cc +++ b/cql3/expr/expression.cc @@ -8,6 +8,9 @@ #include "expression.hh" +#include "cql3/expr/evaluate.hh" +#include "cql3/expr/expr-utils.hh" + #include #include diff --git a/cql3/expr/expression.hh b/cql3/expr/expression.hh index e4f06261a0..4efd41ad70 100644 --- a/cql3/expr/expression.hh +++ b/cql3/expr/expression.hh @@ -13,23 +13,14 @@ #include #include #include -#include -#include -#include "bytes.hh" -#include "cql3/statements/bound.hh" #include "cql3/column_identifier.hh" -#include "cql3/assignment_testable.hh" #include "cql3/cql3_type.hh" #include "cql3/functions/function_name.hh" -#include "data_dictionary/data_dictionary.hh" -#include "gc_clock.hh" -#include "range.hh" #include "seastarx.hh" #include "utils/overloaded_functor.hh" #include "utils/variant_element.hh" #include "cql3/values.hh" -#include "replica/database_fwd.hh" class row; @@ -469,72 +460,6 @@ extern expression make_conjunction(expression a, expression b); extern std::ostream& operator<<(std::ostream&, oper_t); -// Input data needed to evaluate an expression. Individual members can be -// null if not applicable (e.g. evaluating outside a row context) -struct evaluation_inputs { - std::span partition_key; - std::span clustering_key; - std::span static_and_regular_columns; // indexes match `selection` member - const cql3::selection::selection* selection = nullptr; - const query_options* options = nullptr; - std::span static_and_regular_timestamps; // indexes match `selection` member - std::span static_and_regular_ttls; // indexes match `selection` member -}; - -/// Helper for generating evaluation_inputs::static_and_regular_columns -std::vector get_non_pk_values(const cql3::selection::selection& selection, const query::result_row_view& static_row, - const query::result_row_view* row); - -/// Helper for accessing a column value from evaluation_inputs -managed_bytes_opt extract_column_value(const column_definition* cdef, const evaluation_inputs& inputs); - -/// True iff restr evaluates to true, given these inputs -extern bool is_satisfied_by( - const expression& restr, const evaluation_inputs& inputs); - - -/// A set of discrete values. -using value_list = std::vector; // Sorted and deduped using value comparator. - -/// General set of values. Empty set and single-element sets are always value_list. nonwrapping_range is -/// never singular and never has start > end. Universal set is a nonwrapping_range with both bounds null. -using value_set = std::variant>; - -/// A set of all column values that would satisfy an expression. The _token_values variant finds -/// matching values for the partition token function call instead of the column. -/// -/// An expression restricts possible values of a column or token: -/// - `A>5` restricts A from below -/// - `A>5 AND A>6 AND B<10 AND A=12 AND B>0` restricts A to 12 and B to between 0 and 10 -/// - `A IN (1, 3, 5)` restricts A to 1, 3, or 5 -/// - `A IN (1, 3, 5) AND A>3` restricts A to just 5 -/// - `A=1 AND A<=0` restricts A to an empty list; no value is able to satisfy the expression -/// - `A>=NULL` also restricts A to an empty list; all comparisons to NULL are false -/// - an expression without A "restricts" A to unbounded range -extern value_set possible_column_values(const column_definition*, const expression&, const query_options&); -extern value_set possible_partition_token_values(const expression&, const query_options&, const schema& table_schema); - -/// Turns value_set into a range, unless it's a multi-valued list (in which case this throws). -extern nonwrapping_range to_range(const value_set&); - -/// A range of all X such that X op val. -nonwrapping_range to_range(oper_t op, const clustering_key_prefix& val); - -/// True iff the index can support the entire expression. -extern bool is_supported_by(const expression&, const secondary_index::index&); - -/// True iff any of the indices from the manager can support the entire expression. If allow_local, use all -/// indices; otherwise, use only global indices. -extern bool has_supporting_index( - const expression&, const secondary_index::secondary_index_manager&, allow_local_index allow_local); - -// Looks at each column indivudually and checks whether some index can support restrictions on this single column. -// Expression has to consist only of single column restrictions. -extern bool index_supports_some_column( - const expression&, - const secondary_index::secondary_index_manager&, - allow_local_index allow_local); - extern sstring to_string(const expression&); extern std::ostream& operator<<(std::ostream&, const column_value&); @@ -543,299 +468,8 @@ extern std::ostream& operator<<(std::ostream&, const expression&); extern std::ostream& operator<<(std::ostream&, const expression::printer&); -extern bool recurse_until(const expression& e, const noncopyable_function& predicate_fun); - -// 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) { - const ExprElem* ret = nullptr; - recurse_until(e, [&] (const expression& e) { - if (auto expr_elem = as_if(&e)) { - if (predicate_fun(*expr_elem)) { - ret = expr_elem; - return true; - } - } - return false; - }); - return ret; -} - -/// 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); -} - -// Goes over each expression of the specified type and calls for_each_func for each of them. -// For example: -// for_each_expression(e, [](const column_value& cval) {std::cout << cval << '\n';}); -// Will print all column values in an expression -template -requires std::invocable -void for_each_expression(const expression& e, Fn for_each_func) { - recurse_until(e, [&] (const expression& cur_expr) -> bool { - if (auto expr_elem = as_if(&cur_expr)) { - for_each_func(*expr_elem); - } - return false; - }); -} - -/// Counts binary_operator atoms b for which f(b) is true. -size_t count_if(const expression& e, const noncopyable_function& f); - -inline const binary_operator* find(const expression& e, oper_t op) { - return find_binop(e, [&] (const binary_operator& o) { return o.op == op; }); -} - -inline bool needs_filtering(oper_t op) { - return (op == oper_t::CONTAINS) || (op == oper_t::CONTAINS_KEY) || (op == oper_t::LIKE) || - (op == oper_t::IS_NOT) || (op == oper_t::NEQ) ; -} - -inline auto find_needs_filtering(const expression& e) { - return find_binop(e, [] (const binary_operator& bo) { return needs_filtering(bo.op); }); -} - -inline bool is_slice(oper_t op) { - return (op == oper_t::LT) || (op == oper_t::LTE) || (op == oper_t::GT) || (op == oper_t::GTE); -} - -inline bool has_slice(const expression& e) { - return find_binop(e, [] (const binary_operator& bo) { return is_slice(bo.op); }); -} - -inline bool is_compare(oper_t op) { - switch (op) { - case oper_t::EQ: - case oper_t::LT: - case oper_t::LTE: - case oper_t::GT: - case oper_t::GTE: - case oper_t::NEQ: - return true; - default: - return false; - } -} - -inline bool is_multi_column(const binary_operator& op) { - return expr::is(op.lhs); -} - -// Check whether the given expression represents -// a call to the token() function. -bool is_token_function(const function_call&); -bool is_token_function(const expression&); - -bool is_partition_token_for_schema(const function_call&, const schema&); -bool is_partition_token_for_schema(const expression&, const schema&); - -/// Check whether the expression contains a binary_operator whose LHS is a call to the token -/// function representing a partition key token. -/// Examples: -/// For expression: "token(p1, p2, p3) < 123 AND c = 2" returns true -/// For expression: "p1 = token(1, 2, 3) AND c = 2" return false -inline bool has_partition_token(const expression& e, const schema& table_schema) { - return find_binop(e, [&] (const binary_operator& o) { return is_partition_token_for_schema(o.lhs, table_schema); }); -} - -inline bool has_slice_or_needs_filtering(const expression& e) { - 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) { - return op.order == comparison_order::clustering; -} - -inline auto find_clustering_order(const expression& e) { - return find_binop(e, is_clustering_order); -} - -/// Given a Boolean expression, compute its factors such as e=f1 AND f2 AND f3 ... -/// If the expression is TRUE, may return no factors (happens today for an -/// empty conjunction). -std::vector boolean_factors(expression e); - -/// Run the given function for each element in the top level conjunction. -void for_each_boolean_factor(const expression& e, const noncopyable_function& for_each_func); - -/// True iff binary_operator involves a collection. -extern bool is_on_collection(const binary_operator&); - -// Checks whether the given column occurs in the expression. -// Uses column_defintion::operator== for comparison, columns with the same name but different schema will not be equal. -bool contains_column(const column_definition& column, const expression& e); - -// Checks whether this expression contains a nonpure function. -// The expression must be prepared, so that function names are converted to function pointers. -bool contains_nonpure_function(const expression&); - -// Checks whether the given column has an EQ restriction in the expression. -// EQ restriction is `col = ...` or `(col, col2) = ...` -// IN restriction is NOT an EQ restriction, this function will not look for IN restrictions. -// Uses column_defintion::operator== for comparison, columns with the same name but different schema will not be equal. -bool has_eq_restriction_on_column(const column_definition& column, const expression& e); - -/// Replaces every column_definition in an expression with this one. Throws if any LHS is not a single -/// column_value. -extern expression replace_column_def(const expression&, const column_definition*); - -// Replaces all occurences of token(p1, p2) on the left hand side with the given colum. -// For example this changes token(p1, p2) < token(1, 2) to my_column_name < token(1, 2). -// Schema is needed to find out which calls to token() describe the partition token. -extern expression replace_partition_token(const expression&, const column_definition*, const schema&); - -// 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. -extern expression search_and_replace(const expression& e, - const noncopyable_function (const expression& candidate)>& replace_candidate); - -// Adjust an expression for rows that were fetched using query::partition_slice::options::collections_as_maps -expression adjust_for_collection_as_maps(const expression& e); - -extern expression prepare_expression(const expression& expr, data_dictionary::database db, const sstring& keyspace, const schema* schema_opt, lw_shared_ptr receiver); -std::optional try_prepare_expression(const expression& expr, data_dictionary::database db, const sstring& keyspace, const schema* schema_opt, lw_shared_ptr receiver); - -// Check that a prepared expression has no aggregate functions. Throws on error. -void verify_no_aggregate_functions(const expression& expr, std::string_view context_for_errors); - -// Prepares a binary operator received from the parser. -// Does some basic type checks but no advanced validation. -extern binary_operator prepare_binary_operator(binary_operator binop, data_dictionary::database db, const schema& table_schema); - -// Pre-compile any constant LIKE patterns and return equivalent expression -expression optimize_like(const expression& e); - - -/** - * @return whether this object can be assigned to the provided receiver. We distinguish - * between 3 values: - * - EXACT_MATCH if this object is exactly of the type expected by the receiver - * - WEAKLY_ASSIGNABLE if this object is not exactly the expected type but is assignable nonetheless - * - NOT_ASSIGNABLE if it's not assignable - * Most caller should just call the is_assignable() method on the result, though functions have a use for - * testing "strong" equality to decide the most precise overload to pick when multiple could match. - */ -extern assignment_testable::test_result test_assignment(const expression& expr, data_dictionary::database db, const sstring& keyspace, const schema* schema_opt, const column_specification& receiver); - -// Test all elements of exprs for assignment. If all are exact match, return exact match. If any is not assignable, -// return not assignable. Otherwise, return weakly assignable. -extern assignment_testable::test_result test_assignment_all(const std::vector& exprs, data_dictionary::database db, const sstring& keyspace, const schema* schema_opt, const column_specification& receiver); - -extern shared_ptr as_assignment_testable(expression e, std::optional type_opt); - -inline oper_t pick_operator(statements::bound b, bool inclusive) { - return is_start(b) ? - (inclusive ? oper_t::GTE : oper_t::GT) : - (inclusive ? oper_t::LTE : oper_t::LT); -} - -// Extracts all binary operators which have the given column on their left hand side. -// Extracts only single-column restrictions. -// Does not include multi-column restrictions. -// Does not include token() restrictions. -// Does not include boolean constant restrictions. -// For example "WHERE c = 1 AND (a, c) = (2, 1) AND token(p) < 2 AND FALSE" will return {"c = 1"}. -std::vector extract_single_column_restrictions_for_column(const expression&, const column_definition&); - -std::optional get_bool_value(const constant&); - data_type type_of(const expression& e); -// Takes a prepared expression and calculates its value. -// Evaluates bound values, calls functions and returns just the bytes and type. -cql3::raw_value evaluate(const expression& e, const evaluation_inputs&); - -cql3::raw_value evaluate(const expression& e, const query_options&); - -utils::chunked_vector get_list_elements(const cql3::raw_value&); -utils::chunked_vector get_set_elements(const cql3::raw_value&); -std::vector get_tuple_elements(const cql3::raw_value&, const abstract_type& type); -std::vector get_user_type_elements(const cql3::raw_value&, const abstract_type& type); -std::vector> get_map_elements(const cql3::raw_value&); - -// Gets the elements of a constant which can be a list, set, tuple or user type -std::vector get_elements(const cql3::raw_value&, const abstract_type& type); - -// Get elements of list> as vector -// It is useful with IN restrictions like (a, b) IN [(1, 2), (3, 4)]. -// `type` parameter refers to the list> type. -utils::chunked_vector> get_list_of_tuples_elements(const cql3::raw_value&, const abstract_type& type); - -// 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); - -// Checks whether this expression contains restrictions on one single column. -// There might be more than one restriction, but exactly one column. -// The expression must be prepared. -bool is_single_column_restriction(const expression&); - -// Gets the only column from a single_column_restriction expression. -const column_value& get_the_only_column(const expression&); - -// A comparator that orders columns by their position in the schema -// For primary key columns the `id` field is used to determine their position. -// Other columns are assumed to have position std::numeric_limits::max(). -// In case the position is the same they are compared by their name. -// This comparator has been used in the original restricitons code to keep -// restrictions for each column sorted by their place in the schema. -// It's not recommended to use this comparator with columns of different kind -// (partition/clustering/nonprimary) because the id field is unique -// for (kind, schema). So a partition and clustering column might -// have the same id within one schema. -struct schema_pos_column_definition_comparator { - bool operator()(const column_definition* def1, const column_definition* def2) const; -}; - -// Extracts column_defs from the expression and sorts them using schema_pos_column_definition_comparator. -std::vector get_sorted_column_defs(const expression&); - -// Extracts column_defs and returns the last one according to schema_pos_column_definition_comparator. -const column_definition* get_last_column_def(const expression&); - -// A map of single column restrictions for each column -using single_column_restrictions_map = std::map; - -// Extracts map of single column restrictions for each column from expression -single_column_restrictions_map get_single_column_restrictions_map(const expression&); - -// Checks whether this expression is empty - doesn't restrict anything -bool is_empty_restriction(const expression&); - -// Finds common columns between both expressions and prints them to a string. -// Uses schema_pos_column_definition_comparator for comparison. -sstring get_columns_in_commons(const expression& a, const expression& b); - -// Finds the value of the given column in the expression -// In case of multpiple possible values calls on_internal_error -bytes_opt value_for(const column_definition&, const expression&, const query_options&); - -bool contains_multi_column_restriction(const expression&); - -bool has_only_eq_binops(const expression&); - -/// Finds the data type of writetime(x) or ttl(x) -data_type column_mutation_attribute_type(const column_mutation_attribute& e); - } // namespace expr diff --git a/cql3/expr/prepare_expr.cc b/cql3/expr/prepare_expr.cc index b3c5814125..bc09097f08 100644 --- a/cql3/expr/prepare_expr.cc +++ b/cql3/expr/prepare_expr.cc @@ -7,6 +7,8 @@ */ #include "expression.hh" +#include "expr-utils.hh" +#include "evaluate.hh" #include "cql3/functions/functions.hh" #include "cql3/column_identifier.hh" #include "cql3/constants.hh" diff --git a/cql3/expr/restrictions.cc b/cql3/expr/restrictions.cc index 373e7292c4..5dce5f4f72 100644 --- a/cql3/expr/restrictions.cc +++ b/cql3/expr/restrictions.cc @@ -11,6 +11,7 @@ #include "schema/schema.hh" #include #include "cql3/prepare_context.hh" +#include "cql3/expr/expr-utils.hh" #include "types/list.hh" #include #include diff --git a/cql3/expr/restrictions.hh b/cql3/expr/restrictions.hh index a503766adc..9e5a36c161 100644 --- a/cql3/expr/restrictions.hh +++ b/cql3/expr/restrictions.hh @@ -9,15 +9,36 @@ #pragma once #include "expression.hh" +#include "data_dictionary/data_dictionary.hh" +#include namespace cql3 { namespace expr { +// A comparator that orders columns by their position in the schema +// For primary key columns the `id` field is used to determine their position. +// Other columns are assumed to have position std::numeric_limits::max(). +// In case the position is the same they are compared by their name. +// This comparator has been used in the original restricitons code to keep +// restrictions for each column sorted by their place in the schema. +// It's not recommended to use this comparator with columns of different kind +// (partition/clustering/nonprimary) because the id field is unique +// for (kind, schema). So a partition and clustering column might +// have the same id within one schema. +struct schema_pos_column_definition_comparator { + bool operator()(const column_definition* def1, const column_definition* def2) const; +}; + +// A map of single column restrictions for each column +using single_column_restrictions_map = std::map; + + // Given a restriction from the WHERE clause prepares it and performs some validation checks. // It will also fill the prepare context automatically, there's no need to do that later. binary_operator validate_and_prepare_new_restriction(const binary_operator& restriction, data_dictionary::database db, schema_ptr schema, prepare_context& ctx); + } // namespace expr } // namespace cql3 diff --git a/cql3/lists.cc b/cql3/lists.cc index c544c86994..5ad4da72cb 100644 --- a/cql3/lists.cc +++ b/cql3/lists.cc @@ -11,6 +11,8 @@ #include "column_identifier.hh" #include "cql3_type.hh" #include "constants.hh" +#include "cql3/expr/evaluate.hh" +#include "cql3/expr/expr-utils.hh" #include #include "types/list.hh" #include "utils/UUID_gen.hh" diff --git a/cql3/maps.cc b/cql3/maps.cc index 61e83ba1f5..c8957580a4 100644 --- a/cql3/maps.cc +++ b/cql3/maps.cc @@ -12,6 +12,8 @@ #include "operation.hh" #include "update_parameters.hh" #include "exceptions/exceptions.hh" +#include "cql3/expr/evaluate.hh" +#include "cql3/expr/expr-utils.hh" #include "cql3/cql3_type.hh" #include "constants.hh" #include "types/map.hh" diff --git a/cql3/operation.cc b/cql3/operation.cc index 337e082313..fd922e556a 100644 --- a/cql3/operation.cc +++ b/cql3/operation.cc @@ -13,6 +13,8 @@ #include "sets.hh" #include "lists.hh" #include "user_types.hh" +#include "cql3/expr/evaluate.hh" +#include "cql3/expr/expr-utils.hh" #include "types/tuple.hh" #include "types/map.hh" #include "types/list.hh" diff --git a/cql3/restrictions/bounds_slice.hh b/cql3/restrictions/bounds_slice.hh index c19b22b85d..bd0d8c150a 100644 --- a/cql3/restrictions/bounds_slice.hh +++ b/cql3/restrictions/bounds_slice.hh @@ -15,6 +15,7 @@ #include "exceptions/exceptions.hh" #include "index/secondary_index_manager.hh" #include "cql3/expr/expression.hh" +#include "cql3/statements/bound.hh" namespace cql3 { diff --git a/cql3/restrictions/statement_restrictions.cc b/cql3/restrictions/statement_restrictions.cc index 69ebb9b091..b9dce948e6 100644 --- a/cql3/restrictions/statement_restrictions.cc +++ b/cql3/restrictions/statement_restrictions.cc @@ -17,6 +17,8 @@ #include #include "cql3/expr/expression.hh" +#include "cql3/expr/evaluate.hh" +#include "cql3/expr/expr-utils.hh" #include "query-result-reader.hh" #include "statement_restrictions.hh" #include "data_dictionary/data_dictionary.hh" diff --git a/cql3/selection/selectable.cc b/cql3/selection/selectable.cc index ad488a6903..c5975528f1 100644 --- a/cql3/selection/selectable.cc +++ b/cql3/selection/selectable.cc @@ -17,6 +17,7 @@ #include "cql3/functions/castas_fcts.hh" #include "cql3/functions/aggregate_fcts.hh" #include "cql3/expr/expression.hh" +#include "cql3/expr/expr-utils.hh" #include "abstract_function_selector.hh" #include "writetime_or_ttl_selector.hh" diff --git a/cql3/selection/selection.cc b/cql3/selection/selection.cc index e00edbfe08..0cccf12ab2 100644 --- a/cql3/selection/selection.cc +++ b/cql3/selection/selection.cc @@ -20,6 +20,8 @@ #include "cql3/result_set.hh" #include "cql3/query_options.hh" #include "cql3/restrictions/statement_restrictions.hh" +#include "cql3/expr/evaluate.hh" +#include "cql3/expr/expr-utils.hh" namespace cql3 { diff --git a/cql3/sets.cc b/cql3/sets.cc index d509ed64e4..d21711d874 100644 --- a/cql3/sets.cc +++ b/cql3/sets.cc @@ -11,6 +11,8 @@ #include "cql3_type.hh" #include "types/map.hh" #include "types/set.hh" +#include "cql3/expr/evaluate.hh" +#include "cql3/expr/expr-utils.hh" namespace cql3 { void diff --git a/cql3/statements/cas_request.cc b/cql3/statements/cas_request.cc index 4e73abc838..5d1df9214a 100644 --- a/cql3/statements/cas_request.cc +++ b/cql3/statements/cas_request.cc @@ -13,6 +13,8 @@ #include "cas_request.hh" #include #include "cql3/result_set.hh" +#include "cql3/expr/evaluate.hh" +#include "cql3/expr/expr-utils.hh" #include "transport/messages/result_message.hh" #include "types/map.hh" #include "service/storage_proxy.hh" diff --git a/cql3/statements/create_aggregate_statement.cc b/cql3/statements/create_aggregate_statement.cc index ae9409f445..36f0259f0a 100644 --- a/cql3/statements/create_aggregate_statement.cc +++ b/cql3/statements/create_aggregate_statement.cc @@ -10,6 +10,8 @@ #include "cql3/statements/create_aggregate_statement.hh" #include "cql3/functions/functions.hh" #include "cql3/functions/user_aggregate.hh" +#include "cql3/expr/evaluate.hh" +#include "cql3/expr/expr-utils.hh" #include "prepared_statement.hh" #include "service/migration_manager.hh" #include "service/storage_proxy.hh" diff --git a/cql3/statements/delete_statement.cc b/cql3/statements/delete_statement.cc index fae3503c38..e712c2b9a4 100644 --- a/cql3/statements/delete_statement.cc +++ b/cql3/statements/delete_statement.cc @@ -17,6 +17,7 @@ #include "utils/overloaded_functor.hh" #include "mutation/mutation.hh" #include "cql3/expr/expression.hh" +#include "cql3/expr/expr-utils.hh" namespace cql3 { diff --git a/cql3/statements/modification_statement.cc b/cql3/statements/modification_statement.cc index 19a58001c8..28281d884b 100644 --- a/cql3/statements/modification_statement.cc +++ b/cql3/statements/modification_statement.cc @@ -14,6 +14,8 @@ #include "cql3/statements/strongly_consistent_modification_statement.hh" #include "cql3/statements/raw/modification_statement.hh" #include "cql3/statements/prepared_statement.hh" +#include "cql3/expr/expr-utils.hh" +#include "cql3/expr/evaluate.hh" #include "cql3/util.hh" #include "validation.hh" #include "db/consistency_level_validations.hh" diff --git a/cql3/statements/select_statement.cc b/cql3/statements/select_statement.cc index d3953af2c6..eaf337328e 100644 --- a/cql3/statements/select_statement.cc +++ b/cql3/statements/select_statement.cc @@ -10,6 +10,8 @@ #include "cql3/statements/select_statement.hh" #include "cql3/expr/expression.hh" +#include "cql3/expr/evaluate.hh" +#include "cql3/expr/expr-utils.hh" #include "cql3/statements/index_target.hh" #include "cql3/statements/raw/select_statement.hh" #include "cql3/query_processor.hh" diff --git a/cql3/statements/strongly_consistent_modification_statement.cc b/cql3/statements/strongly_consistent_modification_statement.cc index 2b021e464e..843d4a852b 100644 --- a/cql3/statements/strongly_consistent_modification_statement.cc +++ b/cql3/statements/strongly_consistent_modification_statement.cc @@ -20,6 +20,7 @@ #include "bytes.hh" #include "cql3/attributes.hh" #include "cql3/expr/expression.hh" +#include "cql3/expr/evaluate.hh" #include "cql3/operation.hh" #include "cql3/query_processor.hh" #include "cql3/values.hh" diff --git a/cql3/statements/strongly_consistent_select_statement.cc b/cql3/statements/strongly_consistent_select_statement.cc index 08ac954137..2d3fe30b2d 100644 --- a/cql3/statements/strongly_consistent_select_statement.cc +++ b/cql3/statements/strongly_consistent_select_statement.cc @@ -15,6 +15,7 @@ #include #include "cql3/restrictions/statement_restrictions.hh" +#include "cql3/expr/evaluate.hh" #include "cql3/query_processor.hh" #include "service/broadcast_tables/experimental/lang.hh" #include "db/system_keyspace.hh" diff --git a/cql3/statements/update_statement.cc b/cql3/statements/update_statement.cc index b3e107ac5f..d48906e039 100644 --- a/cql3/statements/update_statement.cc +++ b/cql3/statements/update_statement.cc @@ -10,6 +10,8 @@ #include "update_statement.hh" #include "cql3/expr/expression.hh" +#include "cql3/expr/evaluate.hh" +#include "cql3/expr/expr-utils.hh" #include "cql3/statements/strongly_consistent_modification_statement.hh" #include "service/broadcast_tables/experimental/lang.hh" #include "raw/update_statement.hh" diff --git a/cql3/update_parameters.cc b/cql3/update_parameters.cc index d4a55874a4..b38bd2f45b 100644 --- a/cql3/update_parameters.cc +++ b/cql3/update_parameters.cc @@ -11,6 +11,8 @@ #include "cql3/update_parameters.hh" #include "cql3/selection/selection.hh" #include "cql3/expr/expression.hh" +#include "cql3/expr/evaluate.hh" +#include "cql3/expr/expr-utils.hh" #include "query-result-reader.hh" #include "types/map.hh" diff --git a/cql3/user_types.cc b/cql3/user_types.cc index 06d6c2c8d4..633daaa065 100644 --- a/cql3/user_types.cc +++ b/cql3/user_types.cc @@ -13,6 +13,9 @@ #include "cql3/cql3_type.hh" #include "cql3/constants.hh" +#include "cql3/expr/evaluate.hh" +#include "cql3/expr/expr-utils.hh" + #include #include diff --git a/cql3/util.cc b/cql3/util.cc index b338297dd4..4d2da1a340 100644 --- a/cql3/util.cc +++ b/cql3/util.cc @@ -5,6 +5,7 @@ /* Copyright 2020-present ScyllaDB */ #include "util.hh" +#include "cql3/expr/expr-utils.hh" #ifdef DEBUG diff --git a/db/schema_tables.cc b/db/schema_tables.cc index 298a946f13..8714fbb4b6 100644 --- a/db/schema_tables.cc +++ b/db/schema_tables.cc @@ -32,6 +32,8 @@ #include "cql3/functions/functions.hh" #include "cql3/functions/user_function.hh" #include "cql3/functions/user_aggregate.hh" +#include "cql3/expr/evaluate.hh" +#include "cql3/expr/expr-utils.hh" #include "cql3/util.hh" #include "types/list.hh" #include "types/set.hh" diff --git a/db/view/view.cc b/db/view/view.cc index 0a93997763..7ad15e47bc 100644 --- a/db/view/view.cc +++ b/db/view/view.cc @@ -32,6 +32,8 @@ #include "cql3/statements/select_statement.hh" #include "cql3/util.hh" #include "cql3/restrictions/statement_restrictions.hh" +#include "cql3/expr/expr-utils.hh" +#include "cql3/expr/evaluate.hh" #include "db/view/view.hh" #include "db/view/view_builder.hh" #include "db/view/view_updating_consumer.hh" diff --git a/test/boost/expr_test.cc b/test/boost/expr_test.cc index 9ca0c1d5fb..5f37b81e63 100644 --- a/test/boost/expr_test.cc +++ b/test/boost/expr_test.cc @@ -16,6 +16,8 @@ #include "types/set.hh" #include "types/user.hh" #include "test/lib/expr_test_utils.hh" +#include "cql3/expr/evaluate.hh" +#include "cql3/expr/expr-utils.hh" using namespace cql3; using namespace cql3::expr; diff --git a/test/boost/statement_restrictions_test.cc b/test/boost/statement_restrictions_test.cc index 4649cc55a7..8279dc9adb 100644 --- a/test/boost/statement_restrictions_test.cc +++ b/test/boost/statement_restrictions_test.cc @@ -12,6 +12,7 @@ #include #include "cql3/restrictions/statement_restrictions.hh" +#include "cql3/expr/expr-utils.hh" #include "cql3/util.hh" #include "test/lib/cql_assertions.hh" #include "test/lib/cql_test_env.hh" diff --git a/test/lib/expr_test_utils.hh b/test/lib/expr_test_utils.hh index 88f1d679a8..73a3cb5496 100644 --- a/test/lib/expr_test_utils.hh +++ b/test/lib/expr_test_utils.hh @@ -9,6 +9,7 @@ #pragma once #include "cql3/expr/expression.hh" +#include "cql3/expr/evaluate.hh" #include "cql3/query_options.hh" #include "cql3/selection/selection.hh" #include "data_dictionary/data_dictionary.hh"