Commit Graph

83 Commits

Author SHA1 Message Date
Avi Kivity
494561c4f3 cql3: expr: drop boost usage
Replace boost usage with <ranges>, modernizing the code a little
and reducing dependencies on a redundant library.

Closes scylladb/scylladb#20919
2024-10-03 15:39:40 +03:00
Marcin Maliszkiewicz
16b770ff1a cql3: functions: make functions class non-static
This is done to ease code reuse in the following commit.
It'd also help should we ever want properly mount functions
class to schema object instead of static storage.
2024-07-04 10:24:57 +02:00
Kefu Chai
168ade72f8 treewide: replace formatter<std::string_view> with formatter<string_view>
in in {fmt} before v10, it provides the specialization of `fmt::formatter<..>`
for `std::string_view` as well as the specialization of `fmt::formatter<..>`
for `fmt::string_view` which is an implementation builtin in {fmt} for
compatibility of pre-C++17. and this type is used even if the code is
compiled with C++ stadandard greater or equal to C++17. also, before v10,
the `fmt::formatter<std::string_view>::format()` is defined so it accepts
`std::string_view`. after v10, `fmt::formatter<std::string_view>` still
exists, but it is now defined using `format_as()` machinery, so it's
`format()` method does not actually accept `std::string_view`, it
accepts `fmt::string_view`, as the former can be converted to
`fmt::string_view`.

this is why we can inherit from `fmt::formatter<std::string_view>` and
use `formatter<std::string_view>::format(foo, ctx);` to implement the
`format()` method with {fmt} v9, but we cannot do this with {fmt} v10,
and we would have following compilation failure:

```
FAILED: service/CMakeFiles/service.dir/RelWithDebInfo/topology_state_machine.cc.o
/home/kefu/.local/bin/clang++ -DFMT_DEPRECATED_OSTREAM -DFMT_SHARED -DSCYLLA_BUILD_MODE=release -DSEASTAR_API_LEVEL=7 -DSEASTAR_LOGGER_COMPILE_TIME_FMT -DSEASTAR_LOGGER_TYPE_STDOUT -DSEASTAR_SCHEDULING_GROUPS_COUNT=16 -DSEASTAR_SSTRING -DXXH_PRIVATE_API -DCMAKE_INTDIR=\"RelWithDebInfo\" -I/home/kefu/dev/scylladb -I/home/kefu/dev/scylladb/build/gen -I/home/kefu/dev/scylladb/seastar/include -I/home/kefu/dev/scylladb/build/seastar/gen/include -I/home/kefu/dev/scylladb/build/seastar/gen/src -ffunction-sections -fdata-sections -O3 -g -gz -std=gnu++20 -fvisibility=hidden -Wall -Werror -Wextra -Wno-error=deprecated-declarations -Wimplicit-fallthrough -Wno-c++11-narrowing -Wno-deprecated-copy -Wno-mismatched-tags -Wno-missing-field-initializers -Wno-overloaded-virtual -Wno-unsupported-friend -Wno-enum-constexpr-conversion -Wno-unused-parameter -ffile-prefix-map=/home/kefu/dev/scylladb=. -march=westmere -mllvm -inline-threshold=2500 -fno-slp-vectorize -U_FORTIFY_SOURCE -Werror=unused-result -MD -MT service/CMakeFiles/service.dir/RelWithDebInfo/topology_state_machine.cc.o -MF service/CMakeFiles/service.dir/RelWithDebInfo/topology_state_machine.cc.o.d -o service/CMakeFiles/service.dir/RelWithDebInfo/topology_state_machine.cc.o -c /home/kefu/dev/scylladb/service/topology_state_machine.cc
/home/kefu/dev/scylladb/service/topology_state_machine.cc:254:41: error: no matching member function for call to 'format'
  254 |     return formatter<std::string_view>::format(it->second, ctx);
      |            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~
/usr/include/fmt/core.h:2759:22: note: candidate function template not viable: no known conversion from 'seastar::basic_sstring<char, unsigned int, 15>' to 'const fmt::basic_string_view<char>' for 1st argument
 2759 |   FMT_CONSTEXPR auto format(const T& val, FormatContext& ctx) const
      |                      ^      ~~~~~~~~~~~~
```

because the inherited `format()` method actually comes from
`fmt::formatter<fmt::string_view>`. to reduce the confusion, in this
change, we just inherit from `fmt::format<string_view>`, where
`string_view` is actually `fmt::string_view`. this follows
the document at
https://fmt.dev/latest/api.html#formatting-user-defined-types,
and since there is less indirection under the hood -- we do not
use the specialization created by `FMT_FORMAT_AS` which inherit
from `formatter<fmt::string_view>`, hopefully this can improve
the compilation speed a little bit. also, this change addresses
the build failure with {fmt} v10.

Signed-off-by: Kefu Chai <kefu.chai@scylladb.com>

Closes scylladb/scylladb#18299
2024-04-19 07:44:07 +03:00
Kefu Chai
ed6dc6e3b4 cql3: add fmt::formatter for untyped_constant::type_class
before this change, we rely on the default-generated fmt::formatter
created from operator<<, but fmt v10 dropped the default-generated
formatter.

in this change, we define formatters for untyped_constant::type_class,
and drop its operator<<.

Refs #13245

Signed-off-by: Kefu Chai <kefu.chai@scylladb.com>
2024-03-02 10:52:50 +08:00
Kefu Chai
2dbf044b91 cql3: do not include unused headers
these unused includes were identified by clangd. see
https://clangd.llvm.org/guides/include-cleaner#unused-include-warning
for more details on the "Unused include" warning.

Signed-off-by: Kefu Chai <kefu.chai@scylladb.com>

Closes scylladb/scylladb#16791
2024-01-16 16:43:17 +02:00
Yaniv Kaul
c658bdb150 Typos: fix typos in comments
Fixes some typos as found by codespell run on the code.
In this commit, I was hoping to fix only comments, not user-visible alerts, output, etc.
Follow-up commits will take care of them.

Refs: https://github.com/scylladb/scylladb/issues/16255
Signed-off-by: Yaniv Kaul <yaniv.kaul@scylladb.com>
2023-12-02 22:37:22 +02:00
Alexander Turetskiy
024ba84637 cql3: SELECT CAST column names should match Cassandra's
When doing a SELECT CAST(b AS int), Cassandra returns a column named
cast(b as int). Currently, Scylla uses a different name -
system.castasint(b). For Cassandra compatibility, we should switch to
the same name.

fixes #14508

Closes scylladb/scylladb#14800
2023-09-26 17:26:14 +03:00
Jan Ciolek
e5f0468761 cql/prepare_expr: fix wrong receiver in field_selection_test_assignment
When preparing a `field_selection`, we need to prepare the UDT value,
and then verify that it has this field.

`field_selection_test_assignment` prepares the UDT value using the same
receiver as the whole `field_selection`. This is wrong, this receiver
has the type of the field, and not the UDT.

It's impossible to create a receiver for the UDT. Many different UDTs
can produce an `int` value when the field `a` is selected.
Therefore the receiver should be `nullptr`.

No unit test is added, as this bug doesn't currently cause any issues.
Preparing a column value doesn't do any type checks, so nothing fails.
Still it's good to fix it, just to be correct.

Signed-off-by: Jan Ciolek <jan.ciolek@scylladb.com>

Closes scylladb/scylladb#14788
2023-09-26 11:15:00 +03:00
Jan Ciolek
decbc841b7 cql3/prepare_expr: fix partially preparing function arguments
Before choosing a function, we prepare the arguments that can be
prepared without a receiver. Preparing an argument makes
its type known, which allows to choose the best overload
among many possible functions.

The function that prepared the argument passes the unprepared
argument by mistake. Let's fix it so that it actually uses
the prepared argument.

Signed-off-by: Jan Ciolek <jan.ciolek@scylladb.com>

Closes #14786
2023-07-21 18:59:56 +03:00
Avi Kivity
61be544431 cql3: expr: prepare_expression: avoid default-constructed expression
We're about to remove expression's default constructor, so adjust
the usertype_constructor code that checks whether a field has an
initializer or whether we must supply a NULL to not rely on it.
2023-07-14 15:49:51 +03:00
Avi Kivity
778ae2b461 cql3: expression: introduce temporaries
Temporaries are similar to bind variables - they are values provided from
outside the expression. While bind variables are provided by the user, temporaries
are generated internally.

The intended use is for aggregate accumulator storage. Currently aggregates
store the accumulator in aggregate_function_selector::_accumulator, which
means the entire selector hierarchy must be cloned for every query. With
expressions, we can have a single expression object reused for many computations,
but we need a way to inject the accumulator into an aggregation, which this
new expression element provides.
2023-07-03 19:45:17 +03:00
Avi Kivity
b858a4669d cql3: expr: break up expression.hh header
Adding a function declaration to expression.hh causes many
recompilations. Reduce that by:

 - moving some restrictions-related definitions to
   the existing expr/restrictions.hh
 - moving evaluation related names to a new header
   expr/evaluate.hh
 - move utilities to a new header
   expr/expr-utilities.hh

expression.hh contains only expression definitions and the most
basic and common helpers, like printing.
2023-06-22 14:21:03 +03:00
Avi Kivity
6c55bdc417 cql3: expr: match counter arguments to function parameters expecting bigint
assignment_testable is used to convey type information to function overload
selection. The implementation for `selector` recognizes that counters are
really bigints and special cases them. The equivalent implementation for
expressions doesn't, so bring over that nuance here too.

With this, things like sum(counter_column) match the overload for
sum(bigint) rather than failing.
2023-06-13 21:04:49 +03:00
Avi Kivity
2c1e36d0ac cql3: expr: avoid function constant-folding if a thread is needed
Our prepare phase performs constant-folding: if an expression
is composed of constants, and is pure, it is evalauted during
the preparation phase rather than during query execution.

This however can't work for user-defined functions as these require
running in a thread, and we aren't running in a thread during
prepration time. Skip the optimization in this case.
2023-06-13 21:04:49 +03:00
Avi Kivity
8d3d8eeedb cql3: add optional type annotation to assignment_testable
Before this series, function overload resolution peeked
at function arguments to see if they happened to be selectors,
and if so grabbed their type. If they did not happen to be
selectors, we woudln't know their type, but as it happened
all generic functions are aggregates, and aggregates are only
legal in the SELECT clause, so that didn't matter.

In a previous patch, we changed assignment_testable to carry
an optional type and wired it to selector, so we wouldn't
need to dynamic_cast<selector>.

Now, we wire the optional type to assignment_testable_expression,
so overload resolution of generic functions can happen during
expression preparation.

The code that bridges the function argument expressions to
assignment_testable is extracted into a function, since it's
too complicated to be written as a transform.
2023-06-13 21:04:49 +03:00
Avi Kivity
2cb15d0829 cql3: expr: wire unresolved_identifier to test_assignment() 2023-06-13 21:04:49 +03:00
Avi Kivity
b7bbcdd178 cql3: expr: support preparing column_mutation_attribute
Fairly straightforward. A unit test is added.
2023-06-13 21:04:49 +03:00
Avi Kivity
73b6b6e3d1 cql3: expr: support preparing SQL-style casts
We convert the cast to a function, just like the existing
with_function selectable.
2023-06-13 21:04:49 +03:00
Avi Kivity
521a128a2a cql3: expr: support preparing field_selection expressions
The field_selection structure is augmented with the field
index so that does not need to be done at evaluation time,
similar to the current with_field_selection selectable.
2023-06-13 21:04:49 +03:00
Avi Kivity
ecfe4ad53a cql3: expr: make the two styles of cast expressions explicit
CQL supports two cast styles:

 - C-style: (type) expr, used for casts between binary-compatible types
  and for type hinting of bind variables
 - SQL-tyle: (expr AS type), used for real type convertions

Currently, the expression system differentiates them by the cast::type
field, which is a data_type for SQL-style casts and a cql3_type::raw
for C-style casts, but that won't work after the prepare phase is applied
to SQL-style casts when the type field will be prepared into a data_type.

Prepare for this by adding a separate enum to distinguish between the
two styles.
2023-06-13 21:04:49 +03:00
Avi Kivity
c0f59f0789 cql3: eliminate dynamic_cast<selector> from functions::get()
Type inference for function calls is a bit complicated:
 - a function argument can be inferred from the signature: a call to
   my_func(:arg) will infer :arg's type from the function signature
 - a function signature can be inferred from its argument types:
   a call to max(my_column) will select the correct max() signature
   (as max is generic) from my_column's type

Currently, functions::get() implements this by invoking
dynamic_cast<selector*> on the argument. If the caller of
functions::get() is the SELECT clause preparation, then the
cast will succeed and we'll be able to find the type. If not,
we fail (and fall back to inferring the argument types from a
non-generic function signature).

Since we're about to move selectors to expressions, the dynamic_cast
will fail, so we must replace it with a less fragile approach.

The fix is to augment assignment_testable (the interface representing
a function argument) with an intentionally-awkwardly-named
assignment_testable_type_opt(), that sees whether we happen to know
the type for the argument in order to implement signature-from-argument
inference.

A note about assignment_testable: this is a bridge interface
that is the least common denominator of anything that calls functions.
Since we're moving towards expressions, there are fewer implementations of
the interface as the code evolves.
2023-06-13 21:04:49 +03:00
Avi Kivity
5983e9e7b2 cql3: test_assignment: pass optional schema everywhere
test_assignment() and related functions check for type compatibility between
a right-hand-side and a left-hand-side.

It started its life with a limited functionality for INSERT and UPDATE,
but now it's about to be used for cast expression in selectors, which
can cast a column_value. A column_value is still an unresolved_identifier
during the prepare phase, and cannot be resolved without a schema.

To prepare for this, pass an optional schema everywhere.

Ultimately, test_assignment likely needs to be folded into prepare_expr(),
but before that prepare_expr() has to be used everywhere.
2023-06-13 21:04:49 +03:00
Avi Kivity
8dc22293bf cql3: expr: prepare_expr(): allow aggregate functions
prepare_expr() began its life as a replacement for the WHERE clause,
so it shares its restrictions, one of which is not supporting aggregate
functions.

In previous patches, we added an explicit check to all users, so we can
now remove the check here, so that we can later prepare selectors.

In addition to dropping the check, we drop the dynamic_cast<scalar_function>,
as it can now fail. It turns out it's unnecessary since everything is available
from the base class.

Note we don't allow constant folding involving aggregate functions: first,
our evaluator doesn't support it, and second, we don't have the iteration count
at prepare time.
2023-06-13 21:04:49 +03:00
Avi Kivity
79bfe04d2a cql3: remove abstract_marker vestiges
Removed by e458340821 ("cql3: Remove term")

Closes #14192
2023-06-12 10:41:04 +03:00
Jan Ciolek
55fb91bf10 exceptions: remove relation field from unrecognized_entity_exception
The exception unrecognized_entity_exception used to have two fields:
* entity - the name that wasn't recognized
* relation_str - part of the WHERE clause that contained this entity

In 4e0a089f3e the places that throw
this exception were modified, the thrower started passing unrecognized
column name to both fields - entity and relation_str. It was easier to
do things this way, accessing the whole WHERE clause can be problematic.

The problem is that this caused error messages to get weird, e.g:
"Undefined name x in where clause ('x')".
x is not the WHERE clause, it's the unrecognized name.

Let's remove the `relation_str` field as it isn't used anymore,
it only causes confusion. After this change the message would be:
"Unrecognized name x"
Which makes much more sense.

Refs #10632

Signed-off-by: Jan Ciolek <jan.ciolek@scylladb.com>

Closes #13944
2023-05-24 19:35:26 +03:00
Jan Ciolek
8a256f63db cql3/prepare_expr: force token() receiver name to be partition key token
Let's say that we have a prepared statement with a token restriction:
```cql
SELECT * FROM some_table WHERE token(p1, p2) = ?
```

After calling `prepare` the drivers receives some information
about the prepared statment, including names of values bound
to each bind marker.

In case of a partition token restriction (`token(p1, p2) = ?`)
there's an expectation that the name assigned to this bind marker
will be `"partition key token"`.

In a recent change the code handling `token()` expressions has been
unified with the code that handles generic function calls,
and as a result the name has changed to `token(p1, p2)`.

It turns out that the Java driver relies on the name being
`"partition key token"`, so a change to `token(p1, p2)`
broke some things.

This patch sets the name back to `"partition key token"`.
To achieve this we detect any restrictions that match
the pattern `token(p1, p2, p3) = X` and set the receiver
name for X to `"partition key token"`.

Fixes: #13769

Signed-off-by: Jan Ciolek <jan.ciolek@scylladb.com>
2023-05-09 12:32:57 +02:00
Jan Ciolek
be8ef63bf5 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 <jan.ciolek@scylladb.com>
2023-04-29 13:11:31 +02:00
Jan Ciolek
16bc1c930f cql3/prepare_expr: make get_lhs_receiver handle any function_call
get_lhs_receiver looks at the prepared LHS of a binary operator
and creates a receiver corresponding to this LHS expression.
This receiver is later used to prepare the RHS of the binary operator.

It's able to handle a few expression types - the ones that are currently
allowed to be on the LHS.
One of those types is `expr::token`, to handle restrictions like `token(p1, p2) = 3`.

Soon token will be replaced by `expr::function_call`, so the function will need
to handle `function_calls` to the token function.

Although we expect there to be only calls to the `token()` function,
as other functions are not allowed on the LHS, it can be made generic
over all function calls, which will help in future grammar extensions.

The functions call that it can currently get are calls to the token function,
but they're not validated yet, so it could also be something like `token(pk, pk, ck)`.

Signed-off-by: Jan Ciolek <jan.ciolek@scylladb.com>
2023-04-29 13:04:53 +02:00
Jan Ciolek
f7cac10fe0 cql3/expr: implement preparing function_call without a receiver
Currently trying to do prepare_expression(function_call)
with a nullptr receiver fails.

It should be possible to prepare function calls without
a known receiver.

When the user types in: `token(1, 2, 3)`
the code should be able to figure out that
they are looking for a function with name `token`,
which takes 3 integers as arguments.

In order to support that we need to prepare
all arguments that can be prepared before
attempting to find a function.

Prepared expressions have a known type,
which helps to find the right function
for the given arguments.

Additionally the current code for finding
a function requires all arguments to be
assignment_testable, which requires to prepare
some expression types, e.g column_values.

Signed-off-by: Jan Ciolek <jan.ciolek@scylladb.com>
2023-04-29 13:04:51 +02:00
Jan Ciolek
b3d05f3525 cql3/expr: make it possible to prepare expr::constant
try_prepare_expression(constant) used to throw an error
when trying to prepeare expr::constant.

It would be useful to be able to do this
and it's not hard to implement.

Signed-off-by: Jan Ciolek <jan.ciolek@scylladb.com>
2023-04-28 14:34:59 +02:00
Jan Ciolek
bf36cde29a cql3/expr: implement test_assignment for column_value
Make it possible to do test_assignment for column_values.
It's implemented using the generic expression assignment
testing function.

Signed-off-by: Jan Ciolek <jan.ciolek@scylladb.com>
2023-04-28 14:34:59 +02:00
Jan Ciolek
fd174bda60 cql3/expr: implement test_assignment for expr::constant
test_assignment checks whether a value of some type
can be assigned to a value of different type.

There is no implementation of test_assignment
for expr::constant, but I would like to have one.

Currently there is a custom implementation
of test_assignment for each type of expression,
but generally each of them boils down to checking:
```
type1->is_value_compatible_with(type2)
```

Instead of implementing another type-specific funtion
I added expresion_test_assignment and used it to
implement test_assignment for constant.

Signed-off-by: Jan Ciolek <jan.ciolek@scylladb.com>
2023-04-28 14:34:56 +02:00
Nadav Har'El
bd09dc308c cql3: fix printing of column_specification::name in some error messages
column_specification::name is a shared pointer, so it should be
dereferenced before printing - because we want to print the name, not
the pointer.

Fix a few instances of this mistake in prepare_expr.cc. Other instances
were already correct.

Signed-off-by: Nadav Har'El <nyh@scylladb.com>
2023-04-25 10:46:56 +03:00
Avi Kivity
3e0aacc8b5 db, cql3: functions: pass function parameters as a span instead of a vector
Spans are more flexible and can be constructed from any contiguous
container (such as small_vector), or a subrange of such a container.
This can save allocations, so change the signature to accept a span.

Spans cannot be constructed from std::initializer_list, so one such
call site is changed to use construct a span directly from the single
argument.
2023-04-19 20:38:55 +03:00
Jan Ciolek
a08eb5cb76 prepare_expr: implement preparing expr::cast with no receiver
Type inference in cast_prepare_expression was very limited.
Without a receiver it just gave up and said that it can't
infer the type.

It's possible to infer the type - an expression that
casts something to type bigint also has type bigint.

This can be implemented by creating a fake receiver
when the caller didn't specify one.
Type of this fake receiver will be c.type
and c.arg will be prepared using this receiver.

Note that the previous change (changing receiver
to cast_type_receiver in prepare_expression) is required
to keep the behaviour consistent.
Without it we would sometimes prepare c.arg using the
original receiver, and sometimes using a receiver
with type c.type.

Currently it's impossible to test this change
on live code. Every place that uses expr::cast
specifies a receiver.
A unit test is all that can be done at the moment
to ensure correctness.

In the future this functionality will be used in UDFs.
In https://github.com/scylladb/scylladb/pull/12900
it was requested to be able to use a type hint
to specify whether WASM code of the function
will be sent in binary or text form.

The user can convey this by typing
either `(blob)?` or `(text)?`.
In this case there will be no receiver
and type inference would fail.

After this change it will work - it's now possible
to prepare either of those and get an expression
with a known type.

Signed-off-by: Jan Ciolek <jan.ciolek@scylladb.com>
2023-03-09 18:31:45 +01:00
Jan Ciolek
9f8340d211 prepare_expr: use :user formatting in cast_prepare_expression
By default expressions are printed using the {:debug} formatting,
wich is intended for internal use. Error messages should use the
{:user} formatting instead.

cast_prepare_expression uses the default formatting in a few places
that are user facing, so let's change it to use {:user} formatting.

Signed-off-by: Jan Ciolek <jan.ciolek@scylladb.com>
2023-03-09 18:31:45 +01:00
Jan Ciolek
12560b5745 prepare_expr: remove std::get<> in cast_prepare_expression
A few times throughout cast_prepare_expression there's
a line which uses std::get<> to get the raw type of the cast.
`std::get<shared_ptr<cql3_type::raw>>(c.type)`

This is a dangerous thing to do. It might turn out that the variant
holds a different alternative and then it'll start throwing bad_variant_access.

In this case this would happen if someone called cast_prepare_expression
on an expression that is already prepared.

It's possible to modify the code in a way that avoids doing the std::get
altogether.
It makes the code more resilient and gives me a piece of mind.

Signed-off-by: Jan Ciolek <jan.ciolek@scylladb.com>
2023-03-09 18:31:45 +01:00
Jan Ciolek
7c384de476 prepare_expr: improve cast_prepare_expression
Preparing expr::cast had some artificial limitations.
Things like this worked:
`blob_col = (blob)funcReturnsInt()`
But this didn't:
`blob_col = (blob)(int)1234`

This is caused by the line:
`prepare_expression(c.arg, db, keyspace, schema_opt, receiver)`

Here the code prepares the expression to be cast using the original
receiver which was passed to cast_prepare_expression.

In the example above this meant that it tried to prepare
untyped_constant(1234) using a receiver with type blob.
This failed because an integer literal is invalid for a blob column.

To me it looks like a mistake. What it should do instead
is prepare the int literal using the type (int) and then
see if int can be cast to blob, by checking if these types
have compatible binary representation.

This can be achieved by using `cast_type_receiver` instead of `receiver`.

Making this small change makes it possible to use the cast
in many situations where it was previously impossible.
The tests have to be updated to reflect the change,
some of them ow deviate from Cassandra, so they have
to be marked scylla_only.

Signed-off-by: Jan Ciolek <jan.ciolek@scylladb.com>
2023-03-09 18:31:41 +01:00
Jan Ciolek
63a7235017 prepare_expr: improve readability in cast_prepare_expression
cast_prepare_expression takes care of preparing expr::cast,
which is responsible for CQL C-style casts.

At the first glance it can be hard to figure out what exactly
does it do, so I added some comments to make things clearer.

Signed-off-by: Jan Ciolek <jan.ciolek@scylladb.com>
2023-03-08 03:24:17 +01:00
Kefu Chai
0cb842797a treewide: do not define/capture unused variables
these warnings are found by Clang-17 after removing
`-Wno-unused-lambda-capture` and '-Wno-unused-variable' from
the list of disabled warnings in `configure.py`.

Signed-off-by: Kefu Chai <kefu.chai@scylladb.com>
2023-02-15 22:57:18 +02:00
Avi Kivity
31ee13c0c9 cql3: expr: move check for ordering on duration types from restrictions to prepare
Both LWT IF clause and SELECT WHERE clause check that a duration type
isn't used in an ordered comparison, since duration types are unordered
(is 1mo more or less than 30d?). As a first step towards centralizing this
check, move the check from restrictions into prepare. When LWT starts using
prepare, the duplication will be removed.

The error message was changed: the word "slice" is an internal term, and
a comparison does not necessarily have to be in a restriction (which is
also an internal term).

Tests were adjusted.
2023-02-12 17:17:01 +02:00
Avi Kivity
db2fa44a9a cql3: expr: add optimizer for LIKE with constant pattern
Compiling a pattern is expensive and so we should try to do it
at prepare time, if the pattern is a constant. Add an optimizer
that looks for such cases and replaces them with a unary function
that embeds the compiled pattern.

This isn't integrated yet with prepare_expr(), since the filtering
code isn't ready for generic expressions. Its first user will be LWT,
which contains the optimization already (filtering had it as well,
but lost it sometime during the expression rewrite).

A unit test is added.
2023-02-12 17:16:58 +02:00
Avi Kivity
0f15ff740d cql3: expr: simplify user/debug formatting
We have a cql3::expr::expression::printer wrapper that annotates
an expression with a debug_mode boolean prior to formatting. The
fmt library, however, provides a much simpler alterantive: a custom
format specifier. With this, we can write format("{:user}", expr) for
user-oriented prints, or format("{:debug}", expr) for debug-oriented
prints (if nothing is specified, the default remains debug).

This is done by implementing fmt::formatter::parse() for the
expression type, can using expression::printer internally.

Since sometimes we pass expression element types rather than
the expression variant, we also provide a custom formatter for all
ExpressionElement Types.

Uses for expression::printer are updated to use the nicer syntax. In
one place we eliminate a temporary that is no longer needed since
ExpressionElement:s can be formatted directly.

Closes #12702
2023-02-08 12:24:58 +02:00
Jan Ciolek
da3c07955a cql3: expr: make it possible to prepare binary_operator using prepare_expression
prepare_expression didn't allow to prepare binary_operators.
so it's now implemented.

If prepare_binary_operator is unable to infer
the types it will fail with an exception instead
of returning std::nullopt, but we can live with
that for now.

Preparing binary_operators inside the WHERE
clause is currently more complicated than just
calling prepare_binary_operator. Preparation
of the WHERE clause is done inside statement_restrictions
constructor. It's done by iterating over all binary_operators,
validating them and then preparing. The validation contains
additional checks with custom error messages.
Preparation has to be done after validation,
because otherwise the error messages will change
and some tests will start failing.
Because of that we can't just call prepare_expression
on the WHERE clause yet.

It's still useful to have the ability to prepare
binary_operators using prepare_expression.
In cases where we know that the WHERE clause is valid,
we can just call prepare_expression and be done with it.

Once grammar is fully relaxed the artificial constraints
checked by the validation code will be removed and
it will be possible to prepare the whole WHERE clause
using just prepare_expression.

prepare_expression does a bit more than
prepare_binary_operator. In case where
both sides of the binary_operator are known
it will evaluate the whole binary_operator
to a constant value.

Query analysis code is NOT ready
to encounter constant boolean values inside
the WHERE clause, so for the WHERE we still use
prepare_binary_operator which doesn't
evaluate the binary_operator to a
constant value.

Signed-off-by: Jan Ciolek <jan.ciolek@scylladb.com>
2023-01-18 12:04:43 +01:00
Jan Ciolek
5f8b1a1a60 cql3/expr: check that RHS of IS NOT NULL is a null value when preparing binary operators
When preparing a binary operator we first prepare the LHS,
which gives us information about its type and allows
to infer the desired type of RHS.

Then the RHS is prepared with the expectation that it
is compatible with the inferred type.

This is enough for all types of operations apart
from IS NOT NULL.

For IS NOT we should also check that the RHS value
is actually null. It's not enough to check that
RHS is of right type.

Before this change preparing `int_col IS NOT 123`
would end in success, which is wrong.

The missing check doesn't cause any real problems,
it's impossible for the user to produce such input
because the parser will reject it.
Still it's better to have the check because
in the future the grammar might get more relaxed
and the parser could become more generic,
making it possible to write such things.

It would be better to introduce unary_operators,
but that's a bigger change.

Signed-off-by: Jan Ciolek <jan.ciolek@scylladb.com>
2023-01-18 12:04:43 +01:00
Jan Ciolek
703e9f21ff cql3: expr: pass non-empty keyspace name in prepare_binary_operator
For some reason we passed an empty keyspace name
to prepare_expression when preparing the LHS
of a binary operator.

This doesn't look correct. We have keyspace
name available from the schema_ptr so let's use that.

Signed-off-by: Jan Ciolek <jan.ciolek@scylladb.com>
2023-01-18 12:04:43 +01:00
Jan Ciolek
9a0c5789a2 cql3: expr: take reference to schema in prepare_binary_operator
prepare_binary_operator takes a schema_ptr,
but it would be useful to take a reference to schema instead.
Every schema_ptr can be easily converted to a reference
so there is no loss of functionality.

Signed-off-by: Jan Ciolek <jan.ciolek@scylladb.com>
2023-01-18 12:04:40 +01:00
Jan Ciolek
8d7e35caef cql3: expr: remove reference to temporary in get_rhs_receiver
The function underlying_type() returns an data_type by value,
but the code assigned it to a reference.

At first I was sure this is an error
(assigning temporary value to a reference), but it turns out
that this is most likely correct due to C++ lifetime
extension rules.

I think it's better to avoid such unituitive tricks.
Assigning to value makes it clearer that the code
is correct and there are no dangling references.

Signed-off-by: Jan Ciolek <jan.ciolek@scylladb.com>

Closes #12485
2023-01-10 09:42:49 +02:00
Jan Ciolek
dde86a2da6 cql3: expr: make it possible to prepare conjunctions
prepare_expression used to throw an error
when encountering a conjunction.

Now it's possible to use prepare_expression
to prepare an expression that contains
conjunctions.

Signed-off-by: Jan Ciolek <jan.ciolek@scylladb.com>
2022-12-13 20:23:17 +01:00
Avi Kivity
ea901fdb9d cql3: expr: fold null into untyped_constant/constant
Our `null` expression, after the prepare stage, is redundant with a
`constant` expression containing the value NULL.

Remove it. Its role in the unprepared stage is taken over by
untyped_constant, which gains a new type_class enumeration to
represent it.

Some subtleties:
 - Usually, handling of null and untyped_constant, or null and constant
   was the same, so they are just folded into each other
 - LWT "like" operator now has to discriminate between a literal
   string and a literal NULL
 - prepare and test_assignment were folded into the corresponing
   untyped_constant functions. Some care had to be taken to preserve
   error messages.

Closes #12118
2022-11-29 11:02:18 +02:00