From be8ef63bf503da88c55cd0ec84514d9e31db019f Mon Sep 17 00:00:00 2001 From: Jan Ciolek Date: Thu, 30 Mar 2023 22:13:47 +0200 Subject: [PATCH] cql3: remove expr::token Let's remove expr::token and replace all of its functionality with expr::function_call. expr::token is a struct whose job is to represent a partition key token. The idea is that when the user types in `token(p1, p2) < 1234`, this will be internally represented as an expression which uses expr::token to represent the `token(p1, p2)` part. The situation with expr::token is a bit complicated. On one hand side it's supposed to represent the partition token, but sometimes it's also assumed that it can represent a generic call to the token() function, for example `token(1, 2, 3)` could be a function_call, but it could also be expr::token. The query planning code assumes that each occurence of expr::token represents the partition token without checking the arguments. Because of this allowing `token(1, 2, 3)` to be represented as expr::token is dangerous - the query planning might think that it is `token(p1, p2, p3)` and plan the query based on this, which would be wrong. Currently expr::token is created only in one specific case. When the parser detects that the user typed in a restriction which has a call to `token` on the LHS it generates expr::token. In all other cases it generates an `expr::function_call`. Even when the `function_call` represents a valid partition token, it stays a `function_call`. During preparation there is no check to see if a `function_call` to `token` could be turned into `expr::token`. This is a bit inconsistent - sometimes `token(p1, p2, p3)` is represented as `expr::token` and the query planner handles that, but sometimes it might be represented as `function_call`, which the query planner doesn't handle. There is also a problem because there's a lot of duplication between a `function_call` and `expr::token`. All of the evaluation and preparation is the same for `expr::token` as it's for a `function_call` to the token function. Currently it's impossible to evaluate `expr::token` and preparation has some flaws, but implementing it would basically consist of copy-pasting the corresponding code from token `function_call`. One more aspect is multi-table queries. With `expr::token` we turn a call to the `token()` function into a struct that is schema-specific. What happens when a single expression is used to make queries to multiple tables? The schema is different, so something that is representad as `expr::token` for one schema would be represented as `function_call` in the context of a different schema. Translating expressions to different tables would require careful manipulation to convert `expr::token` to `function_call` and vice versa. This could cause trouble for index queries. Overall I think it would be best to remove expr::token. Although having a clear marker for the partition token is sometimes nice for query planning, in my opinion the pros are outweighted by the cons. I'm a big fan of having a single way to represent things, having two separate representations of the same thing without clear boundaries between them causes trouble. Instead of having expr::token and function_call we can just have the function_call and check if it represents a partition token when needed. Signed-off-by: Jan Ciolek --- cql3/Cql.g | 7 ++- cql3/expr/expression.cc | 67 +++------------------ cql3/expr/expression.hh | 36 +++-------- cql3/expr/prepare_expr.cc | 27 --------- cql3/expr/restrictions.cc | 9 ++- cql3/restrictions/statement_restrictions.cc | 27 ++++----- cql3/selection/selectable.cc | 17 ------ test/boost/expr_test.cc | 30 +++++---- test/boost/statement_restrictions_test.cc | 5 +- test/cql-pytest/test_filtering.py | 8 ++- 10 files changed, 69 insertions(+), 164 deletions(-) diff --git a/cql3/Cql.g b/cql3/Cql.g index f635512051..c6af420620 100644 --- a/cql3/Cql.g +++ b/cql3/Cql.g @@ -1773,7 +1773,12 @@ relation returns [expression e] : name=cident type=relationType t=term { $e = binary_operator(unresolved_identifier{std::move(name)}, type, std::move(t)); } | K_TOKEN l=tupleOfIdentifiers type=relationType t=term - { $e = binary_operator(token{std::move(l.elements)}, type, std::move(t)); } + { + $e = binary_operator( + function_call{functions::function_name::native_function("token"), std::move(l.elements)}, + type, + std::move(t)); + } | name=cident K_IS K_NOT K_NULL { $e = binary_operator(unresolved_identifier{std::move(name)}, oper_t::IS_NOT, make_untyped_null()); } | name=cident K_IN marker1=marker diff --git a/cql3/expr/expression.cc b/cql3/expr/expression.cc index 2a53c6986d..3ebc8d5f04 100644 --- a/cql3/expr/expression.cc +++ b/cql3/expr/expression.cc @@ -66,24 +66,6 @@ expression::operator=(const expression& o) { return *this; } -token::token(std::vector args_in) - : args(std::move(args_in)) { -} - -token::token(const std::vector& col_defs) { - args.reserve(col_defs.size()); - for (const column_definition* col_def : col_defs) { - args.push_back(column_value(col_def)); - } -} - -token::token(const std::vector<::shared_ptr>& cols) { - args.reserve(cols.size()); - for(const ::shared_ptr& col : cols) { - args.push_back(unresolved_identifier{col}); - } -} - binary_operator::binary_operator(expression lhs, oper_t op, expression rhs, comparison_order order) : lhs(std::move(lhs)) , op(op) @@ -789,7 +771,11 @@ static value_set possible_lhs_values(const column_definition* cdef, } return unbounded_value_set; }, - [&] (token) -> value_set { + [&] (const function_call& token_fun_call) -> value_set { + if (!is_partition_token_for_schema(token_fun_call, *table_schema_opt)) { + on_internal_error(expr_logger, "possible_lhs_values: function calls are not supported as the LHS of a binary expression"); + } + if (cdef) { return unbounded_value_set; } @@ -831,9 +817,6 @@ static value_set possible_lhs_values(const column_definition* cdef, [] (const column_mutation_attribute&) -> value_set { on_internal_error(expr_logger, "possible_lhs_values: writetime/ttl are not supported as the LHS of a binary expression"); }, - [] (const function_call&) -> value_set { - on_internal_error(expr_logger, "possible_lhs_values: function calls are not supported as the LHS of a binary expression"); - }, [] (const cast&) -> value_set { on_internal_error(expr_logger, "possible_lhs_values: typecasts are not supported as the LHS of a binary expression"); }, @@ -860,9 +843,6 @@ static value_set possible_lhs_values(const column_definition* cdef, [] (const subscript&) -> value_set { on_internal_error(expr_logger, "possible_lhs_values: a subscript cannot serve as a restriction by itself"); }, - [] (const token&) -> value_set { - on_internal_error(expr_logger, "possible_lhs_values: the token function cannot serve as a restriction by itself"); - }, [] (const unresolved_identifier&) -> value_set { on_internal_error(expr_logger, "possible_lhs_values: an unresolved identifier cannot serve as a restriction"); }, @@ -951,7 +931,7 @@ secondary_index::index::supports_expression_v is_supported_by_helper(const expre // We don't use index table for multi-column restrictions, as it cannot avoid filtering. return index::supports_expression_v::from_bool(false); }, - [&] (const token&) { return index::supports_expression_v::from_bool(false); }, + [&] (const function_call&) { return index::supports_expression_v::from_bool(false); }, [&] (const subscript& s) -> ret_t { const column_value& col = get_subscripted_column(s); return idx.supports_subscript_expression(*col.col, oper.op); @@ -971,9 +951,6 @@ secondary_index::index::supports_expression_v is_supported_by_helper(const expre [&] (const column_mutation_attribute&) -> ret_t { on_internal_error(expr_logger, "is_supported_by: writetime/ttl are not supported as the LHS of a binary expression"); }, - [&] (const function_call&) -> ret_t { - on_internal_error(expr_logger, "is_supported_by: function calls are not supported as the LHS of a binary expression"); - }, [&] (const cast&) -> ret_t { on_internal_error(expr_logger, "is_supported_by: typecasts are not supported as the LHS of a binary expression"); }, @@ -1097,9 +1074,6 @@ std::ostream& operator<<(std::ostream& os, const expression::printer& pr) { } } }, - [&] (const token& t) { - fmt::print(os, "token({})", fmt::join(t.args | transformed(to_printer), ", ")); - }, [&] (const column_value& col) { fmt::print(os, "{}", cql3::util::maybe_quote(col.col->name_as_text())); }, @@ -1307,7 +1281,7 @@ expression replace_column_def(const expression& expr, const column_definition* n expression replace_partition_token(const expression& expr, const column_definition* new_cdef, const schema& table_schema) { return search_and_replace(expr, [&] (const expression& expr) -> std::optional { - if (expr::is(expr)) { + if (is_partition_token_for_schema(expr, table_schema)) { return column_value{new_cdef}; } else { return std::nullopt; @@ -1381,14 +1355,6 @@ bool recurse_until(const expression& e, const noncopyable_function expression { - return token { - boost::copy_range>( - tok.args | boost::adaptors::transformed(recurse) - ) - }; - }, [&] (LeafExpression auto const& e) -> expression { return e; }, @@ -1545,7 +1504,6 @@ std::vector extract_single_column_restrictions_for_column(const expr } } - void operator()(const token&) {} void operator()(const unresolved_identifier&) {} void operator()(const column_mutation_attribute&) {} void operator()(const function_call&) {} @@ -1709,9 +1667,6 @@ cql3::raw_value evaluate(const expression& e, const evaluation_inputs& inputs) { [&](const conjunction& conj) -> cql3::raw_value { return evaluate(conj, inputs); }, - [](const token&) -> cql3::raw_value { - on_internal_error(expr_logger, "Can't evaluate token"); - }, [](const unresolved_identifier&) -> cql3::raw_value { on_internal_error(expr_logger, "Can't evaluate unresolved_identifier"); }, @@ -2251,11 +2206,6 @@ void fill_prepare_context(expression& e, prepare_context& ctx) { fill_prepare_context(child, ctx); } }, - [&](token& tok) { - for (expression& arg : tok.args) { - fill_prepare_context(arg, ctx); - } - }, [](unresolved_identifier&) {}, [&](column_mutation_attribute& a) { fill_prepare_context(a.column, ctx); @@ -2305,9 +2255,6 @@ type_of(const expression& e) { [] (const column_value& e) { return e.col->type; }, - [] (const token& e) { - return long_type; - }, [] (const unresolved_identifier& e) -> data_type { on_internal_error(expr_logger, "evaluating type of unresolved_identifier"); }, diff --git a/cql3/expr/expression.hh b/cql3/expr/expression.hh index 068237190e..dcb4c067db 100644 --- a/cql3/expr/expression.hh +++ b/cql3/expr/expression.hh @@ -70,7 +70,6 @@ struct binary_operator; struct conjunction; struct column_value; struct subscript; -struct token; struct unresolved_identifier; struct column_mutation_attribute; struct function_call; @@ -89,7 +88,6 @@ concept ExpressionElement || std::same_as || std::same_as || std::same_as - || std::same_as || std::same_as || std::same_as || std::same_as @@ -109,7 +107,6 @@ concept invocable_on_expression && std::invocable && std::invocable && std::invocable - && std::invocable && std::invocable && std::invocable && std::invocable @@ -129,7 +126,6 @@ concept invocable_on_expression_ref && std::invocable && std::invocable && std::invocable - && std::invocable && std::invocable && std::invocable && std::invocable @@ -229,18 +225,6 @@ const column_value& get_subscripted_column(const subscript&); /// Only columns can be subscripted in CQL, so we can expect that the subscripted expression is a column_value. const column_value& get_subscripted_column(const expression&); -/// Represents token(c1, c2) function on LHS of an operator relation. -/// args contains arguments to the token function. -struct token { - std::vector args; - - explicit token(std::vector); - explicit token(const std::vector&); - explicit token(const std::vector<::shared_ptr>&); - - friend bool operator==(const token&, const token&) = default; -}; - enum class oper_t { EQ, NEQ, LT, LTE, GTE, GT, IN, CONTAINS, CONTAINS_KEY, IS_NOT, LIKE }; /// Describes the nature of clustering-key comparisons. Useful for implementing SCYLLA_CLUSTERING_BOUND. @@ -429,7 +413,7 @@ struct usertype_constructor { // now that all expression types are fully defined, we can define expression::impl struct expression::impl final { using variant_type = std::variant< - conjunction, binary_operator, column_value, token, unresolved_identifier, + conjunction, binary_operator, column_value, unresolved_identifier, column_mutation_attribute, function_call, cast, field_selection, bind_variable, untyped_constant, constant, tuple_constructor, collection_constructor, usertype_constructor, subscript>; @@ -643,13 +627,21 @@ 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 expr::is(o.lhs); }); + 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) { @@ -831,14 +823,6 @@ bytes_opt value_for(const column_definition&, const expression&, const query_opt bool contains_multi_column_restriction(const expression&); bool has_only_eq_binops(const expression&); - -// 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&); } // namespace expr } // namespace cql3 diff --git a/cql3/expr/prepare_expr.cc b/cql3/expr/prepare_expr.cc index aaa67d2333..c2ca1e41df 100644 --- a/cql3/expr/prepare_expr.cc +++ b/cql3/expr/prepare_expr.cc @@ -1057,24 +1057,6 @@ try_prepare_expression(const expression& expr, data_dictionary::database db, con .type = static_cast(sub_col_type).value_comparator(), }; }, - [&] (const token& tk) -> std::optional { - if (!schema_opt) { - throw exceptions::invalid_request_exception("cannot process token() function without schema"); - } - - std::vector prepared_token_args; - prepared_token_args.reserve(tk.args.size()); - - for (const expression& arg : tk.args) { - auto prepared_arg_opt = try_prepare_expression(arg, db, keyspace, schema_opt, receiver); - if (!prepared_arg_opt) { - return std::nullopt; - } - prepared_token_args.emplace_back(std::move(*prepared_arg_opt)); - } - - return token(std::move(prepared_token_args)); - }, [&] (const unresolved_identifier& unin) -> std::optional { if (!schema_opt) { throw exceptions::invalid_request_exception(fmt::format("Cannot resolve column {} without schema", unin.ident->to_cql_string())); @@ -1135,9 +1117,6 @@ test_assignment(const expression& expr, data_dictionary::database db, const sstr [&] (const subscript&) -> test_result { on_internal_error(expr_logger, "subscripts are not yet reachable via test_assignment()"); }, - [&] (const token&) -> test_result { - 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 test_assignment()"); }, @@ -1264,12 +1243,6 @@ static lw_shared_ptr get_lhs_receiver(const expression& pr data_type tuple_type = tuple_type_impl::get_instance(tuple_types); return make_lw_shared(schema.ks_name(), schema.cf_name(), std::move(identifier), std::move(tuple_type)); }, - [&](const token& col_val) -> lw_shared_ptr { - return make_lw_shared(schema.ks_name(), - schema.cf_name(), - ::make_shared("partition key token", true), - dht::token::get_token_validator()); - }, [&](const function_call& fun_call) -> lw_shared_ptr { data_type return_type = std::visit( overloaded_functor{ diff --git a/cql3/expr/restrictions.cc b/cql3/expr/restrictions.cc index f4bf594099..a6af61d0fb 100644 --- a/cql3/expr/restrictions.cc +++ b/cql3/expr/restrictions.cc @@ -152,7 +152,10 @@ void preliminary_binop_vaidation_checks(const binary_operator& binop) { } } - if (is(binop.lhs)) { + // Right now a token() on the LHS means that there's a partition token there. + // In the future with relaxed grammar this might no longer be true and this check will have to be revisisted. + // Moving the check after preparation would break tests and cassandra compatability. + if (is_token_function(binop.lhs)) { if (binop.op == oper_t::IN) { throw exceptions::invalid_request_exception("IN cannot be used with the token function"); } @@ -214,9 +217,9 @@ binary_operator validate_and_prepare_new_restriction(const binary_operator& rest } validate_multi_column_relation(lhs_cols, prepared_binop.op); - } else if (auto lhs_token = as_if(&prepared_binop.lhs)) { + } else if (is_token_function(prepared_binop.lhs)) { // Token restriction - std::vector column_defs = to_column_definitions(lhs_token->args); + std::vector column_defs = to_column_definitions(as(prepared_binop.lhs).args); validate_token_relation(column_defs, prepared_binop.op, *schema); } else { // Anything else diff --git a/cql3/restrictions/statement_restrictions.cc b/cql3/restrictions/statement_restrictions.cc index 2ab4f150c3..79e14f6a69 100644 --- a/cql3/restrictions/statement_restrictions.cc +++ b/cql3/restrictions/statement_restrictions.cc @@ -107,7 +107,11 @@ static std::vector extract_partition_range( current_binary_operator = nullptr; } - void operator()(const token&) { + void operator()(const function_call& token_fun_call) { + if (!is_partition_token_for_schema(token_fun_call, *table_schema)) { + on_internal_error(rlogger, "extract_partition_range(function_call)"); + } + with_current_binary_operator(*this, [&] (const binary_operator& b) { if (tokens) { tokens = make_conjunction(std::move(*tokens), b); @@ -160,10 +164,6 @@ static std::vector extract_partition_range( on_internal_error(rlogger, "extract_partition_range(column_mutation_attribute)"); } - void operator()(const function_call&) { - on_internal_error(rlogger, "extract_partition_range(function_call)"); - } - void operator()(const cast&) { on_internal_error(rlogger, "extract_partition_range(cast)"); } @@ -273,8 +273,13 @@ static std::vector extract_clustering_prefix_restrictions( }); } - void operator()(const token&) { - // A token cannot be a clustering prefix restriction + void operator()(const function_call& fun_call) { + if (is_partition_token_for_schema(fun_call, *table_schema)) { + // A token cannot be a clustering prefix restriction + return; + } + + on_internal_error(rlogger, "extract_clustering_prefix_restrictions(function_call)"); } void operator()(const constant&) {} @@ -287,10 +292,6 @@ static std::vector extract_clustering_prefix_restrictions( on_internal_error(rlogger, "extract_clustering_prefix_restrictions(column_mutation_attribute)"); } - void operator()(const function_call&) { - on_internal_error(rlogger, "extract_clustering_prefix_restrictions(function_call)"); - } - void operator()(const cast&) { on_internal_error(rlogger, "extract_clustering_prefix_restrictions(cast)"); } @@ -1246,10 +1247,6 @@ struct multi_column_range_accumulator { on_internal_error(rlogger, "Subscript encountered outside binary operator"); } - void operator()(const token&) { - on_internal_error(rlogger, "Token encountered outside binary operator"); - } - void operator()(const unresolved_identifier&) { on_internal_error(rlogger, "Unresolved identifier encountered outside binary operator"); } diff --git a/cql3/selection/selectable.cc b/cql3/selection/selectable.cc index 336a6f597f..0b841d3674 100644 --- a/cql3/selection/selectable.cc +++ b/cql3/selection/selectable.cc @@ -173,18 +173,6 @@ prepare_selectable(const schema& s, const expr::expression& raw_selectable) { [&] (const expr::subscript& sub) -> shared_ptr { on_internal_error(slogger, "no way to express 'SELECT a[b]' in the grammar yet"); }, - [&] (const expr::token& tok) -> shared_ptr { - // expr::token implicitly the partition key as arguments, but - // the selectable equivalent (with_function) needs explicit arguments, - // so construct them here. - auto name = functions::function_name("system", "token"); - auto args = boost::copy_range>>( - s.partition_key_columns() - | boost::adaptors::transformed([&] (const column_definition& cdef) { - return ::make_shared(column_identifier(cdef.name(), cdef.name_as_text())); - })); - return ::make_shared(std::move(name), std::move(args)); - }, [&] (const expr::unresolved_identifier& ui) -> shared_ptr { return make_shared(*ui.ident->prepare(s)); }, @@ -260,11 +248,6 @@ selectable_processes_selection(const expr::expression& raw_selectable) { // so bridge them. return false; }, - [&] (const expr::token&) -> bool { - // Arguably, should return false, because it only processes the partition key. - // But selectable::with_function considers it true now, so return that. - return true; - }, [&] (const expr::unresolved_identifier& ui) -> bool { return ui.ident->processes_selection(); }, diff --git a/test/boost/expr_test.cc b/test/boost/expr_test.cc index 6efc27f7d6..b9f552365e 100644 --- a/test/boost/expr_test.cc +++ b/test/boost/expr_test.cc @@ -110,6 +110,13 @@ static unresolved_identifier make_column(const char* col_name) { return unresolved_identifier{::make_shared(col_name, true)}; } +static function_call make_token(std::vector args) { + return function_call { + .func = functions::function_name::native_function("token"), + .args = args + }; +} + BOOST_AUTO_TEST_CASE(expr_printer_test) { expression col_eq_1234 = binary_operator( make_column("col"), @@ -119,7 +126,7 @@ BOOST_AUTO_TEST_CASE(expr_printer_test) { BOOST_REQUIRE_EQUAL(expr_print(col_eq_1234), "col = 1234"); expression token_p1_p2_lt_min_56 = binary_operator( - token({ + make_token({ unresolved_identifier{::make_shared("p1", true)}, unresolved_identifier{::make_shared("p2", true)}, }), @@ -1332,21 +1339,23 @@ BOOST_AUTO_TEST_CASE(prepare_token) { .build(); auto [db, db_data] = make_data_dictionary_database(table_schema); - expression tok = token({unresolved_identifier{::make_shared("p1", true)}, + expression tok = make_token({unresolved_identifier{::make_shared("p1", true)}, unresolved_identifier{::make_shared("p2", true)}, unresolved_identifier{::make_shared("p3", true)}}); expression prepared = prepare_expression(tok, db, "test_ks", table_schema.get(), nullptr); - expression expected = token({column_value(table_schema->get_column_definition("p1")), + std::vector expected_args = {column_value(table_schema->get_column_definition("p1")), column_value(table_schema->get_column_definition("p2")), - column_value(table_schema->get_column_definition("p3"))}); + column_value(table_schema->get_column_definition("p3"))}; - BOOST_REQUIRE_EQUAL(prepared, expected); + const function_call* token_fun_call = as_if(&prepared); + BOOST_REQUIRE(token_fun_call != nullptr); + BOOST_REQUIRE(is_token_function(*token_fun_call)); + BOOST_REQUIRE(std::holds_alternative>(token_fun_call->func)); + BOOST_REQUIRE_EQUAL(token_fun_call->args, expected_args); } -// prepare_expression(token) doesn't validate its arguments, -// validation is done in a different place BOOST_AUTO_TEST_CASE(prepare_token_no_args) { schema_ptr table_schema = schema_builder("test_ks", "test_cf") .with_column("p1", int32_type, column_kind::partition_key) @@ -1355,11 +1364,10 @@ BOOST_AUTO_TEST_CASE(prepare_token_no_args) { .build(); auto [db, db_data] = make_data_dictionary_database(table_schema); - expression tok = token(std::vector()); + expression tok = make_token(std::vector()); - expression prepared = prepare_expression(tok, db, "test_ks", table_schema.get(), nullptr); - - BOOST_REQUIRE_EQUAL(tok, prepared); + BOOST_REQUIRE_THROW(prepare_expression(tok, db, "test_ks", table_schema.get(), nullptr), + exceptions::invalid_request_exception); } BOOST_AUTO_TEST_CASE(prepare_cast_int_int) { diff --git a/test/boost/statement_restrictions_test.cc b/test/boost/statement_restrictions_test.cc index fa2abeb461..5d9379e1eb 100644 --- a/test/boost/statement_restrictions_test.cc +++ b/test/boost/statement_restrictions_test.cc @@ -449,7 +449,10 @@ BOOST_AUTO_TEST_CASE(expression_extract_column_restrictions) { expression ck1_ck2_restriction = make_multi_column_restriction({&col_ck1, &col_ck2}, oper_t::LT); expression conjunction_expr = conjunction{std::vector{ck1_ck2_restriction, r1_restriction2}}; - token token_expr(std::vector{&col_pk1, &col_pk2}); + function_call token_expr = function_call { + .func = functions::function_name::native_function("token"), + .args = {column_value(&col_pk1), column_value(&col_pk2)} + }; expression token_lt_restriction = binary_operator(token_expr, oper_t::LT, zero_value); expression token_gt_restriction = binary_operator(token_expr, oper_t::GT, zero_value); diff --git a/test/cql-pytest/test_filtering.py b/test/cql-pytest/test_filtering.py index 156f1f9753..5075c33792 100644 --- a/test/cql-pytest/test_filtering.py +++ b/test/cql-pytest/test_filtering.py @@ -399,12 +399,14 @@ def test_filter_and_fetch_size(cql, test_keyspace, use_index, driver_bug_1): # Reproduces #13468 def test_filter_token(cql, test_keyspace): with new_test_table(cql, test_keyspace, 'pk int, ck int, x int, PRIMARY KEY (pk, ck)') as table: - with pytest.raises(InvalidRequest, match='duplicate partition key'): + with pytest.raises(InvalidRequest, match='Invalid number of arguments'): cql.execute(f'SELECT pk FROM {table} WHERE token(pk, pk) = 0') with new_test_table(cql, test_keyspace, 'pk int, ck int, x int, PRIMARY KEY ((pk, ck))') as table: - with pytest.raises(InvalidRequest, match='all partition key components'): + with pytest.raises(InvalidRequest, match='duplicate partition key'): + cql.execute(f'SELECT pk FROM {table} WHERE token(pk, pk) = 0') + with pytest.raises(InvalidRequest, match='Invalid number of arguments'): cql.execute(f'SELECT pk FROM {table} WHERE token(x) = 0') - with pytest.raises(InvalidRequest, match='all partition key components'): + with pytest.raises(InvalidRequest, match='Invalid number of arguments'): cql.execute(f'SELECT pk FROM {table} WHERE token(pk) = 0') with pytest.raises(InvalidRequest, match='partition key order'): cql.execute(f'SELECT pk FROM {table} WHERE token(ck, pk) = 0')