Merge 'cql3: Remove term, replace with expression' from Jan Ciołek

This PR finally removes the `term` class and replaces it with `expression`.

* There was some trouble with `lwt_cache_id` in `expr::function_call`.
  The current code works the following way:
  * for each `function_call` inside a `term` that describes a pk restriction, `prepare_context::add_pk_function_call` is called.
  * `add_pk_function_call` takes a `::shared_ptr<cql3::functions::function_call>`, sets its `cache_id` and pushes this shared pointer onto a vector of all collected function calls
  * Later when some condiition is met we want to clear cache ids of all those collected function calls. To do this we iterate through shared pointers collected in `prepare_context` and clear cache id for each of them.

  This doesn't work with `expr::function_call` because it isn't kept inside a shared pointer.
  To solve this I put the `lwt_cache_id` inside a shared pointer and then `prepare_context` collects these shared pointers to cache ids.

  I also experimented with doing this without any shared pointers, maybe we could just walk through the expression and clear the cache ids ourselves. But the problem is that expressions are copied all the time, we could clear the cache in one place, but forget about a copy. Doing it using shared pointers more closely matches the original behaviour.
The experiment is on the [term2-pr3-backup-altcache](https://github.com/cvybhu/scylla/tree/term2-pr3-backup-altcache) branch
* `shared_ptr<term>` being `nullptr` could mean:
  * It represents a cql value `null`
  * That there is no value, like `std::nullopt` (for example in `attributes.hh`)
  * That it's a mistake, it shouldn't be possible

  A good way to distinguish between optional and mistake is to look for `my_term->bind_and_get()`, we then know that it's not an optional value.

* On the other hand `raw_value` cased to bool means:
   * `false` - null or unset
   * `true` - some value, maybe empty

I ran a simple benchmark on my laptop to see how performance is affected:
```
build/release/test/perf/perf_simple_query --smp 1 -m 1G --operations-per-shard 1000000 --task-quota-ms 10
```
* On master (a21b1fbb2f) I get:
  ```
  176506.60 tps ( 77.0 allocs/op,  12.0 tasks/op,   45831 insns/op)

  median 176506.60 tps ( 77.0 allocs/op,  12.0 tasks/op,   45831 insns/op)
  median absolute deviation: 0.00
  maximum: 176506.60
  minimum: 176506.60
  ```
* On this branch I get:
  ```
  172225.30 tps ( 75.1 allocs/op,  12.1 tasks/op,   46106 insns/op)

  median 172225.30 tps ( 75.1 allocs/op,  12.1 tasks/op,   46106 insns/op)
  median absolute deviation: 0.00
  maximum: 172225.30
  minimum: 172225.30
  ```

Closes #9481

* github.com:scylladb/scylla:
  cql3: Remove remaining mentions of term
  cql3: Remove term
  cql3: Rename prepare_term to prepare_expression
  cql3: Make prepare_term return an expression instead of term
  cql3: expr: Add size check to evaluate_set
  cql3: expr: Add expr::contains_bind_marker
  cql3: expr: Rename find_atom to find_binop
  cql3: expr: Add find_in_expression
  cql3: Remove term in operations
  cql3: Remove term in relations
  cql3: Remove term in multi_column_restrictions
  cql3: Remove term in term_slice, rename to bounds_slice
  cql3: expr: Remove term in expression
  cql3: expr: Add evaluate_IN_list(expression, options)
  cql3: Remove term in column_condition
  cql3: Remove term in select_statement
  cql3: Remove term in update_statement
  cql3: Use internal cql format in insert_prepared_json_statement cache
  types: Add map_type_impl::serialize(range of <bytes, bytes>)
  cql3: Remove term in cql3/attributes
  cql3: expr: Add constant::view() method
  cql3: expr: Implement fill_prepare_context(expression)
  cql3: expr: add expr::visit that takes a mutable expression
  cql3: expr: Add receiver to expr::bind_variable
This commit is contained in:
Avi Kivity
2021-11-30 16:39:39 +02:00
59 changed files with 1085 additions and 2493 deletions

View File

@@ -349,7 +349,7 @@ set(scylla_sources
cql3/constants.cc
cql3/cql3_type.cc
cql3/expr/expression.cc
cql3/expr/term_expr.cc
cql3/expr/prepare_expr.cc
cql3/functions/aggregate_fcts.cc
cql3/functions/castas_fcts.cc
cql3/functions/error_injection_fcts.cc
@@ -428,7 +428,6 @@ set(scylla_sources
cql3/statements/update_statement.cc
cql3/statements/use_statement.cc
cql3/token_relation.cc
cql3/tuples.cc
cql3/type_json.cc
cql3/untyped_result_set.cc
cql3/update_parameters.cc

View File

@@ -44,7 +44,6 @@
#include "types/tuple.hh"
#include "cql3/statements/select_statement.hh"
#include "cql3/multi_column_relation.hh"
#include "cql3/tuples.hh"
#include "cql3/untyped_result_set.hh"
#include "log.hh"
#include "utils/rjson.hh"

View File

@@ -756,11 +756,10 @@ scylla_core = (['database.cc',
'cql3/keyspace_element_name.cc',
'cql3/lists.cc',
'cql3/sets.cc',
'cql3/tuples.cc',
'cql3/maps.cc',
'cql3/values.cc',
'cql3/expr/expression.cc',
'cql3/expr/term_expr.cc',
'cql3/expr/prepare_expr.cc',
'cql3/functions/user_function.cc',
'cql3/functions/functions.cc',
'cql3/functions/aggregate_fcts.cc',

View File

@@ -97,11 +97,9 @@ options {
#include "cql3/lists.hh"
#include "cql3/role_name.hh"
#include "cql3/role_options.hh"
#include "cql3/tuples.hh"
#include "cql3/user_types.hh"
#include "cql3/ut_name.hh"
#include "cql3/functions/function_name.hh"
#include "cql3/functions/function_call.hh"
#include "cql3/expr/expression.hh"
#include <seastar/core/sstring.hh>
#include "CqlLexer.hpp"

View File

@@ -50,18 +50,4 @@
#include "types/list.hh"
namespace cql3 {
abstract_marker::abstract_marker(int32_t bind_index, lw_shared_ptr<column_specification>&& receiver)
: _bind_index{bind_index}
, _receiver{std::move(receiver)}
{ }
void abstract_marker::fill_prepare_context(prepare_context& ctx) const {
ctx.add_variable_specification(_bind_index, _receiver);
}
bool abstract_marker::contains_bind_marker() const {
return true;
}
}

View File

@@ -41,26 +41,9 @@
#pragma once
#include "cql3/term.hh"
namespace cql3 {
class column_specification;
class prepare_context;
/**
* A single bind marker.
*/
class abstract_marker : public non_terminal {
protected:
const int32_t _bind_index;
const lw_shared_ptr<column_specification> _receiver;
public:
abstract_marker(int32_t bind_index, lw_shared_ptr<column_specification>&& receiver);
virtual void fill_prepare_context(prepare_context& ctx) const override;
virtual bool contains_bind_marker() const override;
};
}

View File

@@ -48,7 +48,9 @@ std::unique_ptr<attributes> attributes::none() {
return std::unique_ptr<attributes>{new attributes{{}, {}, {}}};
}
attributes::attributes(::shared_ptr<term>&& timestamp, ::shared_ptr<term>&& time_to_live, ::shared_ptr<term>&& timeout)
attributes::attributes(std::optional<cql3::expr::expression>&& timestamp,
std::optional<cql3::expr::expression>&& time_to_live,
std::optional<cql3::expr::expression>&& timeout)
: _timestamp{std::move(timestamp)}
, _time_to_live{std::move(time_to_live)}
, _timeout{std::move(timeout)}
@@ -67,11 +69,11 @@ bool attributes::is_timeout_set() const {
}
int64_t attributes::get_timestamp(int64_t now, const query_options& options) {
if (!_timestamp) {
if (!_timestamp.has_value()) {
return now;
}
auto tval = expr::evaluate_to_raw_view(_timestamp, options);
expr::constant tval = expr::evaluate(*_timestamp, options);
if (tval.is_null()) {
throw exceptions::invalid_request_exception("Invalid null value of timestamp");
}
@@ -79,17 +81,17 @@ int64_t attributes::get_timestamp(int64_t now, const query_options& options) {
return now;
}
try {
return tval.validate_and_deserialize<int64_t>(*long_type, options.get_cql_serialization_format());
return tval.view().validate_and_deserialize<int64_t>(*long_type, cql_serialization_format::internal());
} catch (marshal_exception& e) {
throw exceptions::invalid_request_exception("Invalid timestamp value");
}
}
int32_t attributes::get_time_to_live(const query_options& options) {
if (!_time_to_live)
if (!_time_to_live.has_value())
return 0;
auto tval = expr::evaluate_to_raw_view(_time_to_live, options);
expr::constant tval = expr::evaluate(*_time_to_live, options);
if (tval.is_null()) {
throw exceptions::invalid_request_exception("Invalid null value of TTL");
}
@@ -99,7 +101,7 @@ int32_t attributes::get_time_to_live(const query_options& options) {
int32_t ttl;
try {
ttl = tval.validate_and_deserialize<int32_t>(*int32_type, options.get_cql_serialization_format());
ttl = tval.view().validate_and_deserialize<int32_t>(*int32_type, cql_serialization_format::internal());
}
catch (marshal_exception& e) {
throw exceptions::invalid_request_exception("Invalid TTL value");
@@ -119,11 +121,11 @@ int32_t attributes::get_time_to_live(const query_options& options) {
db::timeout_clock::duration attributes::get_timeout(const query_options& options) const {
auto timeout = expr::evaluate_to_raw_view(_timeout, options);
expr::constant timeout = expr::evaluate(*_timeout, options);
if (timeout.is_null() || timeout.is_unset_value()) {
throw exceptions::invalid_request_exception("Timeout value cannot be unset/null");
}
cql_duration duration = timeout.deserialize<cql_duration>(*duration_type);
cql_duration duration = timeout.view().deserialize<cql_duration>(*duration_type);
if (duration.months || duration.days) {
throw exceptions::invalid_request_exception("Timeout values cannot be expressed in days/months");
}
@@ -136,22 +138,33 @@ db::timeout_clock::duration attributes::get_timeout(const query_options& options
return std::chrono::duration_cast<db::timeout_clock::duration>(std::chrono::nanoseconds(duration.nanoseconds));
}
void attributes::fill_prepare_context(prepare_context& ctx) const {
if (_timestamp) {
_timestamp->fill_prepare_context(ctx);
void attributes::fill_prepare_context(prepare_context& ctx) {
if (_timestamp.has_value()) {
expr::fill_prepare_context(*_timestamp, ctx);
}
if (_time_to_live) {
_time_to_live->fill_prepare_context(ctx);
if (_time_to_live.has_value()) {
expr::fill_prepare_context(*_time_to_live, ctx);
}
if (_timeout) {
_timeout->fill_prepare_context(ctx);
if (_timeout.has_value()) {
expr::fill_prepare_context(*_timeout, ctx);
}
}
std::unique_ptr<attributes> attributes::raw::prepare(database& db, const sstring& ks_name, const sstring& cf_name) const {
auto ts = !timestamp ? ::shared_ptr<term>{} : prepare_term(*timestamp, db, ks_name, timestamp_receiver(ks_name, cf_name));
auto ttl = !time_to_live ? ::shared_ptr<term>{} : prepare_term(*time_to_live, db, ks_name, time_to_live_receiver(ks_name, cf_name));
auto to = !timeout ? ::shared_ptr<term>{} : prepare_term(*timeout, db, ks_name, timeout_receiver(ks_name, cf_name));
std::optional<expr::expression> ts, ttl, to;
if (timestamp.has_value()) {
ts = prepare_expression(*timestamp, db, ks_name, timestamp_receiver(ks_name, cf_name));
}
if (time_to_live.has_value()) {
ttl = prepare_expression(*time_to_live, db, ks_name, time_to_live_receiver(ks_name, cf_name));
}
if (timeout.has_value()) {
to = prepare_expression(*timeout, db, ks_name, timeout_receiver(ks_name, cf_name));
}
return std::unique_ptr<attributes>{new attributes{std::move(ts), std::move(ttl), std::move(to)}};
}

View File

@@ -41,8 +41,8 @@
#pragma once
#include "cql3/term.hh"
#include "cql3/expr/expression.hh"
#include "db/timeout_clock.hh"
namespace cql3 {
@@ -55,13 +55,15 @@ class prepare_context;
*/
class attributes final {
private:
const ::shared_ptr<term> _timestamp;
const ::shared_ptr<term> _time_to_live;
const ::shared_ptr<term> _timeout;
std::optional<cql3::expr::expression> _timestamp;
std::optional<cql3::expr::expression> _time_to_live;
std::optional<cql3::expr::expression> _timeout;
public:
static std::unique_ptr<attributes> none();
private:
attributes(::shared_ptr<term>&& timestamp, ::shared_ptr<term>&& time_to_live, ::shared_ptr<term>&& timeout);
attributes(std::optional<cql3::expr::expression>&& timestamp,
std::optional<cql3::expr::expression>&& time_to_live,
std::optional<cql3::expr::expression>&& timeout);
public:
bool is_timestamp_set() const;
@@ -75,7 +77,7 @@ public:
db::timeout_clock::duration get_timeout(const query_options& options) const;
void fill_prepare_context(prepare_context& ctx) const;
void fill_prepare_context(prepare_context& ctx);
class raw final {
public:

View File

@@ -118,17 +118,15 @@ uint32_t read_and_check_list_index(const cql3::raw_value_view& key) {
namespace cql3 {
void column_condition::collect_marker_specificaton(prepare_context& ctx) const {
void column_condition::collect_marker_specificaton(prepare_context& ctx) {
if (_collection_element) {
_collection_element->fill_prepare_context(ctx);
expr::fill_prepare_context(*_collection_element, ctx);
}
if (!_in_values.empty()) {
for (auto&& value : _in_values) {
value->fill_prepare_context(ctx);
}
for (auto&& value : _in_values) {
expr::fill_prepare_context(value, ctx);
}
if (_value) {
_value->fill_prepare_context(ctx);
expr::fill_prepare_context(*_value, ctx);
}
}
@@ -150,12 +148,13 @@ bool column_condition::applies_to(const data_value* cell_value, const query_opti
// The code below implements these rules in a way compatible with Cassandra.
// Use a map/list value instead of entire collection if a key is present in the predicate.
if (_collection_element != nullptr && cell_value != nullptr) {
if (_collection_element.has_value() && cell_value != nullptr) {
// Checked in column_condition::raw::prepare()
assert(cell_value->type()->is_collection());
const collection_type_impl& cell_type = static_cast<const collection_type_impl&>(*cell_value->type());
cql3::raw_value_view key = expr::evaluate_to_raw_view(_collection_element, options);
expr::constant key_constant = expr::evaluate(*_collection_element, options);
cql3::raw_value_view key = key_constant.view();
if (key.is_unset_value()) {
throw exceptions::invalid_request_exception(
format("Invalid 'unset' value in {} element access", cell_type.cql3_type_name()));
@@ -211,7 +210,7 @@ bool column_condition::applies_to(const data_value* cell_value, const query_opti
if (is_compare(_op)) {
// <, >, >=, <=, !=
cql3::raw_value_view param = expr::evaluate_to_raw_view(_value, options);
expr::constant param = expr::evaluate(*_value, options);
if (param.is_unset_value()) {
throw exceptions::invalid_request_exception("Invalid 'unset' value in condition");
@@ -230,7 +229,7 @@ bool column_condition::applies_to(const data_value* cell_value, const query_opti
}
// type::validate() is called earlier when creating the value, so it's safe to pass to_bytes() result
// directly to compare.
return is_satisfied_by(_op, *cell_value->type(), *column.type, *cell_value, to_bytes(param));
return is_satisfied_by(_op, *cell_value->type(), *column.type, *cell_value, to_bytes(param.view()));
}
if (_op == expr::oper_t::LIKE) {
@@ -240,14 +239,14 @@ bool column_condition::applies_to(const data_value* cell_value, const query_opti
if (_matcher) {
return (*_matcher)(bytes_view(cell_value->serialize_nonnull()));
} else {
auto param = expr::evaluate_to_raw_view(_value, options); // LIKE pattern
auto param = expr::evaluate(*_value, options); // LIKE pattern
if (param.is_unset_value()) {
throw exceptions::invalid_request_exception("Invalid 'unset' value in LIKE pattern");
}
if (param.is_null()) {
throw exceptions::invalid_request_exception("Invalid NULL value in LIKE pattern");
}
like_matcher matcher(to_bytes(param));
like_matcher matcher(to_bytes(param.view()));
return matcher(bytes_view(cell_value->serialize_nonnull()));
}
}
@@ -257,8 +256,8 @@ bool column_condition::applies_to(const data_value* cell_value, const query_opti
// FIXME Use managed_bytes_opt
std::vector<bytes_opt> in_values;
if (_value) {
expr::constant lval = expr::evaluate(_value, options);
if (_value.has_value()) {
expr::constant lval = expr::evaluate(*_value, options);
if (lval.is_null()) {
throw exceptions::invalid_request_exception("Invalid null value for IN condition");
}
@@ -271,7 +270,7 @@ bool column_condition::applies_to(const data_value* cell_value, const query_opti
}
} else {
for (auto&& v : _in_values) {
in_values.emplace_back(to_bytes_opt(expr::evaluate_to_raw_view(v, options)));
in_values.emplace_back(to_bytes_opt(expr::evaluate(v, options).view()));
}
}
// If cell value is NULL, IN list must contain NULL or an empty set/list. Otherwise it must contain cell value.
@@ -289,7 +288,7 @@ column_condition::raw::prepare(database& db, const sstring& keyspace, const colu
if (receiver.type->is_counter()) {
throw exceptions::invalid_request_exception("Conditions on counters are not supported");
}
shared_ptr<term> collection_element_term;
std::optional<expr::expression> collection_element_expression;
lw_shared_ptr<column_specification> value_spec = receiver.column_specification;
if (_collection_element) {
@@ -315,13 +314,13 @@ column_condition::raw::prepare(database& db, const sstring& keyspace, const colu
throw exceptions::invalid_request_exception(
format("Unsupported collection type {} in a condition with element access", ctype->cql3_type_name()));
}
collection_element_term = prepare_term(*_collection_element, db, keyspace, element_spec);
collection_element_expression = prepare_expression(*_collection_element, db, keyspace, element_spec);
}
if (is_compare(_op)) {
validate_operation_on_durations(*receiver.type, _op);
return column_condition::condition(receiver, collection_element_term,
prepare_term(*_value, db, keyspace, value_spec), nullptr, _op);
return column_condition::condition(receiver, std::move(collection_element_expression),
prepare_expression(*_value, db, keyspace, value_spec), nullptr, _op);
}
if (_op == expr::oper_t::LIKE) {
@@ -329,15 +328,15 @@ column_condition::raw::prepare(database& db, const sstring& keyspace, const colu
if (literal_term) {
// Pass matcher object
const sstring& pattern = literal_term->raw_text;
return column_condition::condition(receiver, collection_element_term,
prepare_term(*_value, db, keyspace, value_spec),
return column_condition::condition(receiver, std::move(collection_element_expression),
prepare_expression(*_value, db, keyspace, value_spec),
std::make_unique<like_matcher>(bytes_view(reinterpret_cast<const int8_t*>(pattern.data()), pattern.size())),
_op);
} else {
// Pass through rhs value, matcher object built on execution
// TODO: caller should validate parametrized LIKE pattern
return column_condition::condition(receiver, collection_element_term,
prepare_term(*_value, db, keyspace, value_spec), nullptr, _op);
return column_condition::condition(receiver, std::move(collection_element_expression),
prepare_expression(*_value, db, keyspace, value_spec), nullptr, _op);
}
}
@@ -347,16 +346,17 @@ column_condition::raw::prepare(database& db, const sstring& keyspace, const colu
if (_in_marker) {
assert(_in_values.empty());
shared_ptr<term> multi_item_term = prepare_term(*_in_marker, db, keyspace, value_spec);
return column_condition::in_condition(receiver, collection_element_term, multi_item_term, {});
expr::expression multi_item_term = prepare_expression(*_in_marker, db, keyspace, value_spec);
return column_condition::in_condition(receiver, collection_element_expression, std::move(multi_item_term), {});
}
// Both _in_values and in _in_marker can be missing in case of empty IN list: "a IN ()"
std::vector<::shared_ptr<term>> terms;
std::vector<expr::expression> terms;
terms.reserve(_in_values.size());
for (auto&& value : _in_values) {
terms.push_back(prepare_term(value, db, keyspace, value_spec));
terms.push_back(prepare_expression(value, db, keyspace, value_spec));
}
return column_condition::in_condition(receiver, collection_element_term, {}, std::move(terms));
return column_condition::in_condition(receiver, std::move(collection_element_expression),
std::nullopt, std::move(terms));
}
} // end of namespace cql3

View File

@@ -41,7 +41,6 @@
#pragma once
#include "cql3/term.hh"
#include "cql3/abstract_marker.hh"
#include "cql3/expr/expression.hh"
#include "utils/like_matcher.hh"
@@ -60,17 +59,17 @@ public:
// object stands for the string cell. See column_condition::raw::prepare() for details.
const column_definition& column;
private:
// For collection, when testing the equality of a specific element, nullptr otherwise.
::shared_ptr<term> _collection_element;
// For collection, when testing the equality of a specific element, nullopt otherwise.
std::optional<expr::expression> _collection_element;
// A literal value for comparison predicates or a multi item terminal for "a IN ?"
::shared_ptr<term> _value;
std::optional<expr::expression> _value;
// List of terminals for "a IN (value, value, ...)"
std::vector<::shared_ptr<term>> _in_values;
std::vector<expr::expression> _in_values;
const std::unique_ptr<like_matcher> _matcher;
expr::oper_t _op;
public:
column_condition(const column_definition& column, ::shared_ptr<term> collection_element,
::shared_ptr<term> value, std::vector<::shared_ptr<term>> in_values,
column_condition(const column_definition& column, std::optional<expr::expression> collection_element,
std::optional<expr::expression> value, std::vector<expr::expression> in_values,
std::unique_ptr<like_matcher> matcher, expr::oper_t op)
: column(column)
, _collection_element(std::move(collection_element))
@@ -89,7 +88,7 @@ public:
* @param boundNames the list of column specification where to collect the
* bind variables of this term in.
*/
void collect_marker_specificaton(prepare_context& ctx) const;
void collect_marker_specificaton(prepare_context& ctx);
// Retrieve parameter marker values, if any, find the appropriate collection
// element if the cell is a collection and an element access is used in the expression,
@@ -102,15 +101,15 @@ public:
* "IF col = 'foo'"
* "IF col LIKE <pattern>"
*/
static lw_shared_ptr<column_condition> condition(const column_definition& def, ::shared_ptr<term> collection_element,
::shared_ptr<term> value, std::unique_ptr<like_matcher> matcher, expr::oper_t op) {
static lw_shared_ptr<column_condition> condition(const column_definition& def, std::optional<expr::expression> collection_element,
expr::expression value, std::unique_ptr<like_matcher> matcher, expr::oper_t op) {
return make_lw_shared<column_condition>(def, std::move(collection_element), std::move(value),
std::vector<::shared_ptr<term>>{}, std::move(matcher), op);
std::vector<expr::expression>{}, std::move(matcher), op);
}
// Helper constructor wrapper for "IF col IN ... and IF col['key'] IN ... */
static lw_shared_ptr<column_condition> in_condition(const column_definition& def, ::shared_ptr<term> collection_element,
::shared_ptr<term> in_marker, std::vector<::shared_ptr<term>> in_values) {
static lw_shared_ptr<column_condition> in_condition(const column_definition& def, std::optional<expr::expression> collection_element,
std::optional<expr::expression> in_marker, std::vector<expr::expression> in_values) {
return make_lw_shared<column_condition>(def, std::move(collection_element), std::move(in_marker),
std::move(in_values), nullptr, expr::oper_t::IN);
}

View File

@@ -43,10 +43,6 @@
#include "cql3/cql3_type.hh"
namespace cql3 {
thread_local const ::shared_ptr<constants::value> constants::UNSET_VALUE = ::make_shared<constants::value>(cql3::raw_value::make_unset_value(), empty_type);
thread_local const ::shared_ptr<terminal> constants::NULL_VALUE = ::make_shared<constants::null_value>();
void constants::deleter::execute(mutation& m, const clustering_key_prefix& prefix, const update_parameters& params) {
if (column.type->is_multi_cell()) {
collection_mutation_description coll_m;
@@ -57,13 +53,4 @@ void constants::deleter::execute(mutation& m, const clustering_key_prefix& prefi
m.set_cell(prefix, column, params.make_dead_cell());
}
}
expr::expression constants::marker::to_expression() {
return expr::bind_variable {
.shape = expr::bind_variable::shape_type::scalar,
.bind_index = _bind_index,
.value_type = _receiver->type
};
}
}

View File

@@ -45,7 +45,6 @@
#include "cql3/update_parameters.hh"
#include "cql3/operation.hh"
#include "cql3/values.hh"
#include "cql3/term.hh"
#include "mutation.hh"
#include <seastar/core/shared_ptr.hh>
@@ -60,70 +59,13 @@ public:
private static final Logger logger = LoggerFactory.getLogger(Constants.class);
#endif
public:
/**
* A constant value, i.e. a ByteBuffer.
*/
class value : public terminal {
public:
cql3::raw_value _bytes;
value(cql3::raw_value bytes_, data_type my_type) : terminal(std::move(my_type)), _bytes(std::move(bytes_)) {}
virtual cql3::raw_value get(const query_options& options) override { return _bytes; }
virtual sstring to_string() const override { return _bytes.to_view().with_value([] (const FragmentedView auto& v) { return to_hex(v); }); }
};
static thread_local const ::shared_ptr<value> UNSET_VALUE;
class null_value final : public value {
public:
null_value() : value(cql3::raw_value::make_null(), empty_type) {}
virtual ::shared_ptr<terminal> bind(const query_options& options) override { return {}; }
virtual sstring to_string() const override { return "null"; }
};
static thread_local const ::shared_ptr<terminal> NULL_VALUE;
class marker : public abstract_marker {
public:
marker(int32_t bind_index, lw_shared_ptr<column_specification> receiver)
: abstract_marker{bind_index, std::move(receiver)}
{
assert(!_receiver->type->is_collection() && !_receiver->type->is_user_type());
}
virtual ::shared_ptr<terminal> bind(const query_options& options) override {
auto bytes = bind_and_get_internal(options);
if (bytes.is_null()) {
return ::shared_ptr<terminal>{};
}
if (bytes.is_unset_value()) {
return UNSET_VALUE;
}
return ::make_shared<constants::value>(cql3::raw_value::make_value(bytes), _receiver->type);
}
virtual expr::expression to_expression() override;
private:
cql3::raw_value_view bind_and_get_internal(const query_options& options) {
try {
auto value = options.get_value_at(_bind_index);
if (value) {
value.validate(*_receiver->type, options.get_cql_serialization_format());
}
return value;
} catch (const marshal_exception& e) {
throw exceptions::invalid_request_exception(
format("Exception while binding column {:s}: {:s}", _receiver->name->to_cql_string(), e.what()));
}
}
};
class setter : public operation {
public:
using operation::operation;
virtual void execute(mutation& m, const clustering_key_prefix& prefix, const update_parameters& params) override {
auto value = expr::evaluate_to_raw_view(_t, params._options);
execute(m, prefix, params, column, std::move(value));
auto value = expr::evaluate(*_e, params._options);
execute(m, prefix, params, column, value.view());
}
static void execute(mutation& m, const clustering_key_prefix& prefix, const update_parameters& params, const column_definition& column, cql3::raw_value_view value) {
@@ -139,13 +81,13 @@ public:
using operation::operation;
virtual void execute(mutation& m, const clustering_key_prefix& prefix, const update_parameters& params) override {
auto value = expr::evaluate_to_raw_view(_t, params._options);
auto value = expr::evaluate(*_e, params._options);
if (value.is_null()) {
throw exceptions::invalid_request_exception("Invalid null value for counter increment");
} else if (value.is_unset_value()) {
return;
}
auto increment = value.deserialize<int64_t>(*long_type);
auto increment = value.view().deserialize<int64_t>(*long_type);
m.set_cell(prefix, column, params.make_counter_update_cell(increment));
}
};
@@ -154,13 +96,13 @@ public:
using operation::operation;
virtual void execute(mutation& m, const clustering_key_prefix& prefix, const update_parameters& params) override {
auto value = expr::evaluate_to_raw_view(_t, params._options);
auto value = expr::evaluate(*_e, params._options);
if (value.is_null()) {
throw exceptions::invalid_request_exception("Invalid null value for counter increment");
} else if (value.is_unset_value()) {
return;
}
auto increment = value.deserialize<int64_t>(*long_type);
auto increment = value.view().deserialize<int64_t>(*long_type);
if (increment == std::numeric_limits<int64_t>::min()) {
throw exceptions::invalid_request_exception(format("The negation of {:d} overflows supported counter precision (signed 8 bytes integer)", increment));
}
@@ -171,7 +113,7 @@ public:
class deleter : public operation {
public:
deleter(const column_definition& column)
: operation(column, {})
: operation(column, std::nullopt)
{ }
virtual void execute(mutation& m, const clustering_key_prefix& prefix, const update_parameters& params) override;

View File

@@ -35,7 +35,6 @@
#include "cql3/constants.hh"
#include "cql3/lists.hh"
#include "cql3/statements/request_validations.hh"
#include "cql3/tuples.hh"
#include "cql3/selection/selection.hh"
#include "index/secondary_index_manager.hh"
#include "types/list.hh"
@@ -49,7 +48,7 @@
#include "cql3/maps.hh"
#include "cql3/user_types.hh"
#include "cql3/functions/scalar_function.hh"
#include "cql3/functions/function_call.hh"
#include "cql3/prepare_context.hh"
namespace cql3 {
namespace expr {
@@ -69,7 +68,7 @@ expression::operator=(const expression& o) {
return *this;
}
binary_operator::binary_operator(expression lhs, oper_t op, ::shared_ptr<term> rhs, comparison_order order)
binary_operator::binary_operator(expression lhs, oper_t op, expression rhs, comparison_order order)
: lhs(std::move(lhs))
, op(op)
, rhs(std::move(rhs))
@@ -93,7 +92,7 @@ children_t explode_conjunction(expression e) {
using cql3::selection::selection;
/// Serialized values for all types of cells, plus selection (to find a column's index) and options (for
/// subscript term's value).
/// subscript expression's value).
struct row_data_from_partition_slice {
const std::vector<bytes>& partition_key;
const std::vector<bytes>& clustering_key;
@@ -103,7 +102,7 @@ struct row_data_from_partition_slice {
/// Everything needed to compute column values during restriction evaluation.
struct column_value_eval_bag {
const query_options& options; // For evaluating subscript terms.
const query_options& options; // For evaluating subscript expressions.
row_data_from_partition_slice row_data;
};
@@ -119,9 +118,9 @@ managed_bytes_opt get_value(const column_value& col, const column_value_eval_bag
}
const auto deserialized = cdef->type->deserialize(managed_bytes_view(*data.other_columns[data.sel.index_of(*cdef)]));
const auto& data_map = value_cast<map_type_impl::native_type>(deserialized);
const auto key = evaluate_to_raw_view(col.sub, options);
const auto key = evaluate(*col.sub, options);
auto&& key_type = col_type->name_comparator();
const auto found = key.with_linearized([&] (bytes_view key_bv) {
const auto found = key.view().with_linearized([&] (bytes_view key_bv) {
using entry = std::pair<data_value, data_value>;
return std::find_if(data_map.cbegin(), data_map.cend(), [&] (const entry& element) {
return key_type->compare(element.first.serialize_nonnull(), key_bv) == 0;
@@ -166,13 +165,13 @@ bool equal(const managed_bytes_opt& rhs, const column_value& lhs, const column_v
return get_value_comparator(lhs)->equal(managed_bytes_view(*value), managed_bytes_view(*rhs));
}
/// Convenience overload for term.
bool equal(term& rhs, const column_value& lhs, const column_value_eval_bag& bag) {
return equal(to_managed_bytes_opt(evaluate_to_raw_view(rhs, bag.options)), lhs, bag);
/// Convenience overload for expression.
bool equal(const expression& rhs, const column_value& lhs, const column_value_eval_bag& bag) {
return equal(evaluate(rhs, bag.options).value.to_managed_bytes_opt(), lhs, bag);
}
/// True iff columns' values equal t.
bool equal(term& t, const tuple_constructor& columns_tuple, const column_value_eval_bag& bag) {
bool equal(const expression& t, const tuple_constructor& columns_tuple, const column_value_eval_bag& bag) {
const constant tup = evaluate(t, bag.options);
if (!tup.type->is_tuple()) {
throw exceptions::invalid_request_exception("multi-column equality has right-hand side that isn't a tuple");
@@ -211,7 +210,7 @@ bool limits(managed_bytes_view lhs, oper_t op, managed_bytes_view rhs, const abs
}
/// True iff the column value is limited by rhs in the manner prescribed by op.
bool limits(const column_value& col, oper_t op, term& rhs, const column_value_eval_bag& bag) {
bool limits(const column_value& col, oper_t op, const expression& rhs, const column_value_eval_bag& bag) {
if (!is_slice(op)) { // For EQ or NEQ, use equal().
throw std::logic_error("limits() called on non-slice op");
}
@@ -219,17 +218,17 @@ bool limits(const column_value& col, oper_t op, term& rhs, const column_value_ev
if (!lhs) {
return false;
}
const auto b = to_managed_bytes_opt(evaluate_to_raw_view(rhs, bag.options));
const auto b = evaluate(rhs, bag.options).value.to_managed_bytes_opt();
return b ? limits(*lhs, op, *b, *get_value_comparator(col)) : false;
}
/// True iff the column values are limited by t in the manner prescribed by op.
bool limits(const tuple_constructor& columns_tuple, const oper_t op, term& t,
bool limits(const tuple_constructor& columns_tuple, const oper_t op, const expression& e,
const column_value_eval_bag& bag) {
if (!is_slice(op)) { // For EQ or NEQ, use equal().
throw std::logic_error("limits() called on non-slice op");
}
const constant tup = evaluate(t, bag.options);
const constant tup = evaluate(e, bag.options);
if (!tup.type->is_tuple()) {
throw exceptions::invalid_request_exception(
"multi-column comparison has right-hand side that isn't a tuple");
@@ -396,13 +395,13 @@ bool like(const column_value& cv, const raw_value_view& pattern, const column_va
}
/// True iff the column value is in the set defined by rhs.
bool is_one_of(const column_value& col, term& rhs, const column_value_eval_bag& bag) {
bool is_one_of(const column_value& col, const expression& rhs, const column_value_eval_bag& bag) {
const constant in_list = evaluate_IN_list(rhs, bag.options);
statements::request_validations::check_false(
in_list.is_null(), "Invalid null value for column {}", col.col->name_as_text());
if (!in_list.type->without_reversed().is_list()) {
throw std::logic_error("unexpected term type in is_one_of(single column)");
throw std::logic_error("unexpected expression type in is_one_of(single column)");
}
return boost::algorithm::any_of(get_list_elements(in_list), [&] (const managed_bytes_opt& b) {
return equal(b, col, bag);
@@ -410,10 +409,10 @@ bool is_one_of(const column_value& col, term& rhs, const column_value_eval_bag&
}
/// True iff the tuple of column values is in the set defined by rhs.
bool is_one_of(const tuple_constructor& tuple, term& rhs, const column_value_eval_bag& bag) {
bool is_one_of(const tuple_constructor& tuple, const expression& rhs, const column_value_eval_bag& bag) {
constant in_list = evaluate_IN_list(rhs, bag.options);
if (!in_list.type->without_reversed().is_list()) {
throw std::logic_error("unexpected term type in is_one_of(multi-column)");
throw std::logic_error("unexpected expression type in is_one_of(multi-column)");
}
return boost::algorithm::any_of(get_list_of_tuples_elements(in_list), [&] (const std::vector<managed_bytes_opt>& el) {
return boost::equal(tuple.elements, el, [&] (const expression& c, const managed_bytes_opt& b) {
@@ -457,30 +456,33 @@ bool is_satisfied_by(const binary_operator& opr, const column_value_eval_bag& ba
return expr::visit(overloaded_functor{
[&] (const column_value& col) {
if (opr.op == oper_t::EQ) {
return equal(*opr.rhs, col, bag);
return equal(opr.rhs, col, bag);
} else if (opr.op == oper_t::NEQ) {
return !equal(*opr.rhs, col, bag);
return !equal(opr.rhs, col, bag);
} else if (is_slice(opr.op)) {
return limits(col, opr.op, *opr.rhs, bag);
return limits(col, opr.op, opr.rhs, bag);
} else if (opr.op == oper_t::CONTAINS) {
return contains(col, evaluate_to_raw_view(opr.rhs, bag.options), bag);
constant val = evaluate(opr.rhs, bag.options);
return contains(col, val.view(), bag);
} else if (opr.op == oper_t::CONTAINS_KEY) {
return contains_key(col, evaluate_to_raw_view(opr.rhs, bag.options), bag);
constant val = evaluate(opr.rhs, bag.options);
return contains_key(col, val.view(), bag);
} else if (opr.op == oper_t::LIKE) {
return like(col, evaluate_to_raw_view(opr.rhs, bag.options), bag);
constant val = evaluate(opr.rhs, bag.options);
return like(col, val.view(), bag);
} else if (opr.op == oper_t::IN) {
return is_one_of(col, *opr.rhs, bag);
return is_one_of(col, opr.rhs, bag);
} else {
throw exceptions::unsupported_operation_exception(format("Unhandled binary_operator: {}", opr));
}
},
[&] (const tuple_constructor& cvs) {
if (opr.op == oper_t::EQ) {
return equal(*opr.rhs, cvs, bag);
return equal(opr.rhs, cvs, bag);
} else if (is_slice(opr.op)) {
return limits(cvs, opr.op, *opr.rhs, bag);
return limits(cvs, opr.op, opr.rhs, bag);
} else if (opr.op == oper_t::IN) {
return is_one_of(cvs, *opr.rhs, bag);
return is_one_of(cvs, opr.rhs, bag);
} else {
throw exceptions::unsupported_operation_exception(
format("Unhandled multi-column binary_operator: {}", opr));
@@ -606,26 +608,26 @@ const auto deref = boost::adaptors::transformed([] (const managed_bytes_opt& b)
/// Returns possible values from t, which must be RHS of IN.
value_list get_IN_values(
const ::shared_ptr<term>& t, const query_options& options, const serialized_compare& comparator,
const expression& e, const query_options& options, const serialized_compare& comparator,
sstring_view column_name) {
const constant in_list = evaluate_IN_list(t, options);
const constant in_list = evaluate_IN_list(e, options);
if (in_list.is_unset_value()) {
throw exceptions::invalid_request_exception(format("Invalid unset value for column {}", column_name));
}
statements::request_validations::check_false(in_list.is_null(), "Invalid null value for column {}", column_name);
if (!in_list.type->without_reversed().is_list()) {
throw std::logic_error(format("get_IN_values(single-column) on invalid term {}", *t));
throw std::logic_error(format("get_IN_values(single-column) on invalid expression {}", e));
}
utils::chunked_vector<managed_bytes> list_elems = get_list_elements(in_list);
return to_sorted_vector(std::move(list_elems) | non_null | deref, comparator);
}
/// Returns possible values for k-th column from t, which must be RHS of IN.
value_list get_IN_values(const ::shared_ptr<term>& t, size_t k, const query_options& options,
value_list get_IN_values(const expression& e, size_t k, const query_options& options,
const serialized_compare& comparator) {
const constant in_list = evaluate_IN_list(t, options);
const constant in_list = evaluate_IN_list(e, options);
if (!in_list.type->without_reversed().is_list()) {
throw std::logic_error(format("get_IN_values(multi-column) on invalid term {}", *t));
throw std::logic_error(format("get_IN_values(multi-column) on invalid expression {}", e));
}
const auto split_values = get_list_of_tuples_elements(in_list); // Need lvalue from which to make std::view.
const auto result_range = split_values
@@ -703,7 +705,7 @@ value_set possible_lhs_values(const column_definition* cdef, const expression& e
return unbounded_value_set;
}
if (is_compare(oper.op)) {
managed_bytes_opt val = to_managed_bytes_opt(evaluate_to_raw_view(oper.rhs, options));
managed_bytes_opt val = evaluate(oper.rhs, options).value.to_managed_bytes_opt();
if (!val) {
return empty_value_set; // All NULL comparisons fail; no column values match.
}
@@ -726,7 +728,7 @@ value_set possible_lhs_values(const column_definition* cdef, const expression& e
const auto column_index_on_lhs = std::distance(tuple.elements.begin(), found);
if (is_compare(oper.op)) {
// RHS must be a tuple due to upstream checks.
managed_bytes_opt val = get_tuple_elements(evaluate(*oper.rhs, options)).at(column_index_on_lhs);
managed_bytes_opt val = get_tuple_elements(evaluate(oper.rhs, options)).at(column_index_on_lhs);
if (!val) {
return empty_value_set; // All NULL comparisons fail; no column values match.
}
@@ -748,7 +750,7 @@ value_set possible_lhs_values(const column_definition* cdef, const expression& e
if (cdef) {
return unbounded_value_set;
}
const auto val = to_managed_bytes_opt(evaluate_to_raw_view(oper.rhs, options));
const auto val = evaluate(oper.rhs, options).value.to_managed_bytes_opt();
if (!val) {
return empty_value_set; // All NULL comparisons fail; no token values match.
}
@@ -954,10 +956,10 @@ std::ostream& operator<<(std::ostream& os, const column_value& cv) {
std::ostream& operator<<(std::ostream& os, const expression& expr) {
expr::visit(overloaded_functor{
[&] (const constant& v) { os << v.value.to_view(); },
[&] (const constant& v) { os << v.view(); },
[&] (const conjunction& conj) { fmt::print(os, "({})", fmt::join(conj.children, ") AND (")); },
[&] (const binary_operator& opr) {
os << "(" << opr.lhs << ") " << opr.op << ' ' << *opr.rhs;
os << "(" << opr.lhs << ") " << opr.op << ' ' << opr.rhs;
},
[&] (const token& t) { os << "TOKEN"; },
[&] (const column_value& col) {
@@ -1110,7 +1112,7 @@ expression search_and_replace(const expression& e,
};
},
[&] (const binary_operator& oper) -> expression {
return binary_operator(recurse(oper.lhs), oper.op, oper.rhs);
return binary_operator(recurse(oper.lhs), oper.op, recurse(oper.rhs));
},
[&] (const column_mutation_attribute& cma) -> expression {
return column_mutation_attribute{cma.kind, recurse(cma.column)};
@@ -1153,6 +1155,12 @@ expression search_and_replace(const expression& e,
[&] (const field_selection& fs) -> expression {
return field_selection{recurse(fs.structure), fs.field};
},
[&] (const column_value& cv) -> expression {
if (cv.sub.has_value()) {
return column_value(cv.col, recurse(*cv.sub));
}
return cv;
},
[&] (LeafExpression auto const& e) -> expression {
return e;
},
@@ -1282,6 +1290,10 @@ bool constant::is_null_or_unset() const {
return is_null() || is_unset_value();
}
cql3::raw_value_view constant::view() const {
return value.to_view();
}
std::optional<bool> get_bool_value(const constant& constant_val) {
if (constant_val.type->get_kind() != abstract_type::kind::boolean) {
return std::nullopt;
@@ -1295,41 +1307,7 @@ std::optional<bool> get_bool_value(const constant& constant_val) {
return std::nullopt;
}
return constant_val.value.to_view().deserialize<bool>(*boolean_type);
}
constant evaluate(term* term_ptr, const query_options& options) {
if (term_ptr == nullptr) {
return constant::make_null();
}
return evaluate(term_ptr->to_expression(), options);
}
constant evaluate(const ::shared_ptr<term>& term_ptr, const query_options& options) {
return evaluate(term_ptr.get(), options);
}
constant evaluate(term& term_ref, const query_options& options) {
return evaluate(&term_ref, options);
}
constant evaluate_IN_list(const ::shared_ptr<term>& term_ptr, const query_options& options) {
return evaluate_IN_list(term_ptr.get(), options);
}
constant evaluate_IN_list(term& term_ref, const query_options& options) {
return evaluate_IN_list(&term_ref, options);
}
cql3::raw_value_view evaluate_to_raw_view(const ::shared_ptr<term>& term_ptr, const query_options& options) {
constant value = evaluate(term_ptr, options);
return cql3::raw_value_view::make_temporary(std::move(value.value));
}
cql3::raw_value_view evaluate_to_raw_view(term& term_ref, const query_options& options) {
constant value = evaluate(term_ref, options);
return cql3::raw_value_view::make_temporary(std::move(value.value));
return constant_val.view().deserialize<bool>(*boolean_type);
}
constant evaluate(const expression& e, const query_options& options) {
@@ -1374,45 +1352,6 @@ constant evaluate(const expression& e, const query_options& options) {
}, e);
}
// Serializes a map using the internal cql serialization format
// Takes a range of pair<managed_bytes, managed_bytes>
template <std::ranges::range Range>
requires std::convertible_to<std::ranges::range_value_t<Range>, std::pair<const managed_bytes, managed_bytes>>
static managed_bytes serialize_map(const Range& map_range) {
size_t serialized_len = 4;
size_t map_size = 0;
for (const std::pair<const managed_bytes, managed_bytes>& elem : map_range) {
serialized_len += 4 + elem.first.size() + 4 + elem.second.size();
map_size += 1;
}
if (map_size > std::numeric_limits<int32_t>::max()) {
throw exceptions::invalid_request_exception(
fmt::format("Map size too large: {} > {}", map_size, std::numeric_limits<int32_t>::max()));
}
managed_bytes result(managed_bytes::initialized_later(), serialized_len);
managed_bytes_mutable_view out(result);
write_collection_size(out, map_size, cql_serialization_format::internal());
for (const std::pair<const managed_bytes, managed_bytes>& elem : map_range) {
if (elem.first.size() > std::numeric_limits<int32_t>::max()) {
throw exceptions::invalid_request_exception(
fmt::format("Map key size too large: {} bytes > {}", map_size, std::numeric_limits<int32_t>::max()));
}
if (elem.second.size() > std::numeric_limits<int32_t>::max()) {
throw exceptions::invalid_request_exception(
fmt::format("Map value size too large: {} bytes > {}", map_size, std::numeric_limits<int32_t>::max()));
}
write_collection_value(out, cql_serialization_format::internal(), elem.first);
write_collection_value(out, cql_serialization_format::internal(), elem.second);
}
return result;
}
// Takes a value and reserializes it where needs_to_be_reserialized() says it's needed
template <FragmentedView View>
static managed_bytes reserialize_value(View value_bytes,
@@ -1481,7 +1420,7 @@ static managed_bytes reserialize_value(View value_bytes,
values_map.emplace(std::move(element));
}
return serialize_map(values_map);
return map_type_impl::serialize_to_managed_bytes(values_map);
}
if (type.is_tuple() || type.is_user_type()) {
@@ -1503,28 +1442,27 @@ static managed_bytes reserialize_value(View value_bytes,
}
constant evaluate(const bind_variable& bind_var, const query_options& options) {
if (bind_var.value_type.get() == nullptr) {
if (bind_var.receiver.get() == nullptr) {
on_internal_error(expr_logger,
"evaluate(bind_variable) called with nullptr type, should be prepared first");
"evaluate(bind_variable) called with nullptr receiver, should be prepared first");
}
cql3::raw_value_view value = options.get_value_at(bind_var.bind_index);
if (value.is_null()) {
return constant::make_null(bind_var.value_type);
return constant::make_null(bind_var.receiver->type);
}
if (value.is_unset_value()) {
return constant::make_unset_value(bind_var.value_type);
return constant::make_unset_value(bind_var.receiver->type);
}
const abstract_type& value_type = bind_var.value_type->without_reversed();
const abstract_type& value_type = bind_var.receiver->type->without_reversed();
try {
value.validate(value_type, options.get_cql_serialization_format());
} catch (const marshal_exception& e) {
throw exceptions::invalid_request_exception(
format("Exception while binding value of type {}: {}",
bind_var.value_type->name(), e.what()));
throw exceptions::invalid_request_exception(format("Exception while binding column {:s}: {:s}",
bind_var.receiver->name->to_cql_string(), e.what()));
}
if (value_type.bound_value_needs_to_be_reserialized(options.get_cql_serialization_format())) {
@@ -1532,10 +1470,10 @@ constant evaluate(const bind_variable& bind_var, const query_options& options) {
return reserialize_value(value_bytes, value_type, options.get_cql_serialization_format());
});
return constant(raw_value::make_value(std::move(new_value)), bind_var.value_type);
return constant(raw_value::make_value(std::move(new_value)), bind_var.receiver->type);
}
return constant(raw_value::make_value(value), bind_var.value_type);
return constant(raw_value::make_value(value), bind_var.receiver->type);
}
constant evaluate(const tuple_constructor& tuple, const query_options& options) {
@@ -1629,6 +1567,16 @@ static constant evaluate_set(const collection_constructor& collection, const que
return constant::make_unset_value(collection.type);
}
if (evaluated_element.view().size_bytes() > std::numeric_limits<uint16_t>::max()) {
// TODO: Behaviour copied from sets::delayed_value::bind(), but this seems incorrect
// The original reasoning is:
// "We don't support values > 64K because the serialization format encode the length as an unsigned short."
// but CQL uses int32_t to encode length of a set value length
throw exceptions::invalid_request_exception(format("Set value is too long. Set values are limited to {:d} bytes but {:d} bytes value provided",
std::numeric_limits<uint16_t>::max(),
evaluated_element.view().size_bytes()));
}
evaluated_elements.emplace(std::move(evaluated_element).value.to_managed_bytes());
}
@@ -1657,14 +1605,14 @@ static constant evaluate_map(const collection_constructor& collection, const que
return constant::make_unset_value(collection.type);
}
if (key.value.to_view().size_bytes() > std::numeric_limits<uint16_t>::max()) {
if (key.view().size_bytes() > std::numeric_limits<uint16_t>::max()) {
// TODO: Behaviour copied from maps::delayed_value::bind(), but this seems incorrect
// The original reasoning is:
// "We don't support values > 64K because the serialization format encode the length as an unsigned short."
// but CQL uses int32_t to encode length of a map key
throw exceptions::invalid_request_exception(format("Map key is too long. Map keys are limited to {:d} bytes but {:d} bytes keys provided",
std::numeric_limits<uint16_t>::max(),
key.value.to_view().size_bytes()));
key.view().size_bytes()));
}
evaluated_elements.emplace(std::move(key.value).to_managed_bytes(),
@@ -1681,7 +1629,8 @@ static constant evaluate_map(const collection_constructor& collection, const que
}
}
return constant(raw_value::make_value(serialize_map(evaluated_elements)), collection.type);
managed_bytes serialized_map = map_type_impl::serialize_to_managed_bytes(evaluated_elements);
return constant(raw_value::make_value(std::move(serialized_map)), collection.type);
}
constant evaluate(const collection_constructor& collection, const query_options& options) {
@@ -1765,8 +1714,10 @@ constant evaluate(const function_call& fun_call, const query_options& options) {
arguments.emplace_back(to_bytes_opt(std::move(arg_val.value)));
}
if (fun_call.lwt_cache_id.has_value()) {
computed_function_values::mapped_type* cached_value = options.find_cached_pk_function_call(*fun_call.lwt_cache_id);
bool has_cache_id = fun_call.lwt_cache_id.get() != nullptr && fun_call.lwt_cache_id->has_value();
if (has_cache_id) {
computed_function_values::mapped_type* cached_value =
options.find_cached_pk_function_call(**fun_call.lwt_cache_id);
if (cached_value != nullptr) {
return constant(raw_value::make_value(*cached_value), scalar_fun->return_type());
}
@@ -1774,8 +1725,8 @@ constant evaluate(const function_call& fun_call, const query_options& options) {
bytes_opt result = scalar_fun->execute(cql_serialization_format::internal(), arguments);
if (fun_call.lwt_cache_id.has_value()) {
options.cache_pk_function_call(*fun_call.lwt_cache_id, result);
if (has_cache_id) {
options.cache_pk_function_call(**fun_call.lwt_cache_id, result);
}
if (!result.has_value()) {
@@ -1794,13 +1745,7 @@ constant evaluate(const function_call& fun_call, const query_options& options) {
return constant(raw_value::make_value(std::move(*result)), scalar_fun->return_type());
}
constant evaluate_IN_list(term* term_ptr, const query_options& options) {
if (term_ptr == nullptr) {
return constant::make_null();
}
expression e = term_ptr->to_expression();
constant evaluate_IN_list(const expression& e, const query_options& options) {
if (auto collection = expr::as_if<collection_constructor>(&e)) {
if (collection->style == collection_constructor::style_type::list) {
return evaluate_list(*collection, options, true);
@@ -1831,7 +1776,7 @@ static void ensure_can_get_value_elements(const constant& val,
utils::chunked_vector<managed_bytes> get_list_elements(const constant& val) {
ensure_can_get_value_elements(val, abstract_type::kind::list, "expr::get_list_elements");
return val.value.to_view().with_value([](const FragmentedView auto& value_bytes) {
return val.view().with_value([](const FragmentedView auto& value_bytes) {
return partially_deserialize_listlike(value_bytes, cql_serialization_format::internal());
});
}
@@ -1839,7 +1784,7 @@ utils::chunked_vector<managed_bytes> get_list_elements(const constant& val) {
utils::chunked_vector<managed_bytes> get_set_elements(const constant& val) {
ensure_can_get_value_elements(val, abstract_type::kind::set, "expr::get_set_elements");
return val.value.to_view().with_value([](const FragmentedView auto& value_bytes) {
return val.view().with_value([](const FragmentedView auto& value_bytes) {
return partially_deserialize_listlike(value_bytes, cql_serialization_format::internal());
});
}
@@ -1847,7 +1792,7 @@ utils::chunked_vector<managed_bytes> get_set_elements(const constant& val) {
std::vector<std::pair<managed_bytes, managed_bytes>> get_map_elements(const constant& val) {
ensure_can_get_value_elements(val, abstract_type::kind::map, "expr::get_map_elements");
return val.value.to_view().with_value([](const FragmentedView auto& value_bytes) {
return val.view().with_value([](const FragmentedView auto& value_bytes) {
return partially_deserialize_map(value_bytes, cql_serialization_format::internal());
});
}
@@ -1855,7 +1800,7 @@ std::vector<std::pair<managed_bytes, managed_bytes>> get_map_elements(const cons
std::vector<managed_bytes_opt> get_tuple_elements(const constant& val) {
ensure_can_get_value_elements(val, abstract_type::kind::tuple, "expr::get_tuple_elements");
return val.value.to_view().with_value([&](const FragmentedView auto& value_bytes) {
return val.view().with_value([&](const FragmentedView auto& value_bytes) {
const tuple_type_impl& ttype = static_cast<const tuple_type_impl&>(val.type->without_reversed());
return ttype.split_fragmented(value_bytes);
});
@@ -1864,7 +1809,7 @@ std::vector<managed_bytes_opt> get_tuple_elements(const constant& val) {
std::vector<managed_bytes_opt> get_user_type_elements(const constant& val) {
ensure_can_get_value_elements(val, abstract_type::kind::user, "expr::get_user_type_elements");
return val.value.to_view().with_value([&](const FragmentedView auto& value_bytes) {
return val.view().with_value([&](const FragmentedView auto& value_bytes) {
const user_type_impl& utype = static_cast<const user_type_impl&>(val.type->without_reversed());
return utype.split_fragmented(value_bytes);
});
@@ -1912,12 +1857,70 @@ utils::chunked_vector<std::vector<managed_bytes_opt>> get_list_of_tuples_element
return tuples_list;
}
expression to_expression(const ::shared_ptr<term>& term_ptr) {
if (term_ptr.get() == nullptr) {
return constant::make_null();
}
void fill_prepare_context(expression& e, prepare_context& ctx) {
expr::visit(overloaded_functor {
[&](bind_variable& bind_var) {
ctx.add_variable_specification(bind_var.bind_index, bind_var.receiver);
},
[&](collection_constructor& c) {
for (expr::expression& element : c.elements) {
fill_prepare_context(element, ctx);
}
},
[&](tuple_constructor& t) {
for (expr::expression& element : t.elements) {
fill_prepare_context(element, ctx);
}
},
[&](usertype_constructor& u) {
for (auto& [field_name, field_val] : u.elements) {
fill_prepare_context(field_val, ctx);
}
},
[&](function_call& f) {
const shared_ptr<functions::function>& func = std::get<shared_ptr<functions::function>>(f.func);
if (ctx.is_processing_pk_restrictions() && !func->is_pure()) {
ctx.add_pk_function_call(f);
}
return term_ptr->to_expression();
for (expr::expression& argument : f.args) {
fill_prepare_context(argument, ctx);
}
},
[&](binary_operator& binop) {
fill_prepare_context(binop.lhs, ctx);
fill_prepare_context(binop.rhs, ctx);
},
[&](conjunction& c) {
for (expression& child : c.children) {
fill_prepare_context(child, ctx);
}
},
[](token&) {},
[](unresolved_identifier&) {},
[&](column_mutation_attribute& a) {
fill_prepare_context(a.column, ctx);
},
[&](cast& c) {
fill_prepare_context(c.arg, ctx);
},
[&](field_selection& fs) {
fill_prepare_context(fs.structure, ctx);
},
[&](column_value& cv) {
if (cv.sub.has_value()) {
fill_prepare_context(*cv.sub, ctx);
}
},
[](untyped_constant&) {},
[](null&) {},
[](constant&) {},
}, e);
}
bool contains_bind_marker(const expression& e) {
const bind_variable* search_res = find_in_expression<bind_variable>(e, [](const bind_variable&) { return true; });
return search_res != nullptr;
}
} // namespace expr
} // namespace cql3

View File

@@ -52,7 +52,7 @@ namespace query {
} // namespace query
namespace cql3 {
struct term;
struct prepare_context;
class column_identifier_raw;
class query_options;
@@ -129,6 +129,26 @@ concept invocable_on_expression
&& std::invocable<Func, usertype_constructor>
;
template <typename Func>
concept invocable_on_expression_ref
= std::invocable<Func, conjunction&>
&& std::invocable<Func, binary_operator&>
&& std::invocable<Func, column_value&>
&& std::invocable<Func, token&>
&& std::invocable<Func, unresolved_identifier&>
&& std::invocable<Func, column_mutation_attribute&>
&& std::invocable<Func, function_call&>
&& std::invocable<Func, cast&>
&& std::invocable<Func, field_selection&>
&& std::invocable<Func, null&>
&& std::invocable<Func, bind_variable&>
&& std::invocable<Func, untyped_constant&>
&& std::invocable<Func, constant&>
&& std::invocable<Func, tuple_constructor&>
&& std::invocable<Func, collection_constructor&>
&& std::invocable<Func, usertype_constructor&>
;
/// A CQL expression -- union of all possible expression types. bool means a Boolean constant.
class expression final {
// 'impl' holds a variant of all expression types, but since
@@ -146,6 +166,7 @@ public:
expression& operator=(expression&&) noexcept = default;
friend auto visit(invocable_on_expression auto&& visitor, const expression& e);
friend auto visit(invocable_on_expression_ref auto&& visitor, expression& e);
template <ExpressionElement E>
friend bool is(const expression& e);
@@ -161,7 +182,6 @@ public:
template <typename E>
concept LeafExpression
= std::same_as<bool, E>
|| std::same_as<column_value, E>
|| std::same_as<token, E>
|| std::same_as<unresolved_identifier, E>
|| std::same_as<null, E>
@@ -170,14 +190,14 @@ concept LeafExpression
|| std::same_as<constant, E>
;
/// A column, optionally subscripted by a term (eg, c1 or c2['abc']).
/// A column, optionally subscripted by a value (eg, c1 or c2['abc']).
struct column_value {
const column_definition* col;
::shared_ptr<term> sub; ///< If present, this LHS is col[sub], otherwise just col.
std::optional<expression> sub; ///< If present, this LHS is col[sub], otherwise just col.
/// For easy creation of vector<column_value> from vector<column_definition*>.
column_value(const column_definition* col) : col(col) {}
/// The compiler doesn't auto-generate this due to the other constructor's existence.
column_value(const column_definition* col, ::shared_ptr<term> sub) : col(col), sub(sub) {}
column_value(const column_definition* col, expr::expression sub) : col(col), sub(std::move(sub)) {}
};
/// Represents token function on LHS of an operator relation. No need to list column definitions
@@ -196,10 +216,10 @@ enum class comparison_order : char {
struct binary_operator {
expression lhs;
oper_t op;
::shared_ptr<term> rhs;
expression rhs;
comparison_order order;
binary_operator(expression lhs, oper_t op, ::shared_ptr<term> rhs, comparison_order order = comparison_order::cql);
binary_operator(expression lhs, oper_t op, expression rhs, comparison_order order = comparison_order::cql);
};
/// A conjunction of restrictions.
@@ -241,7 +261,21 @@ struct function_call {
// The query should be executed on a shard that has the pk partition,
// but it changes with each uuid() call.
// uuid() call result is cached and sent to the proper shard.
std::optional<uint8_t> lwt_cache_id;
//
// Cache id is kept in shared_ptr because of how prepare_context works.
// During fill_prepare_context all function cache ids are collected
// inside prepare_context.
// Later when some condition occurs we might decide to clear
// cache ids of all function calls found in prepare_context.
// However by this time these function calls could have been
// copied multiple times. Prepare_context keeps a shared_ptr
// to function_call ids, and then clearing the shared id
// clears it in all possible copies.
// This logic was introduced back when everything was shared_ptr<term>,
// now a better solution might exist.
//
// This field can be nullptr, it means that there is no cache id set.
::shared_ptr<std::optional<uint8_t>> lwt_cache_id;
};
struct cast {
@@ -263,10 +297,9 @@ struct bind_variable {
shape_type shape;
int32_t bind_index;
// Type of the bound value.
// Before preparing can be nullptr.
// After preparing always holds a valid type.
data_type value_type;
// Describes where this bound value will be assigned.
// Contains value type and other useful information.
::lw_shared_ptr<column_specification> receiver;
};
// A constant which does not yet have a date type. It is partially typed
@@ -295,6 +328,8 @@ struct constant {
bool is_unset_value() const;
bool is_null_or_unset() const;
bool has_empty_value_bytes() const;
cql3::raw_value_view view() const;
};
// Denotes construction of a tuple from its elements, e.g. ('a', ?, some_column) in CQL.
@@ -350,6 +385,10 @@ auto visit(invocable_on_expression auto&& visitor, const expression& e) {
return std::visit(visitor, e._v->v);
}
auto visit(invocable_on_expression_ref auto&& visitor, expression& e) {
return std::visit(visitor, e._v->v);
}
template <ExpressionElement E>
bool is(const expression& e) {
return std::holds_alternative<E>(e._v->v);
@@ -418,75 +457,98 @@ extern std::ostream& operator<<(std::ostream&, const column_value&);
extern std::ostream& operator<<(std::ostream&, const expression&);
/// If there is a binary_operator atom b for which f(b) is true, returns it. Otherwise returns null.
template<typename Fn>
requires std::regular_invocable<Fn, const binary_operator&>
const binary_operator* find_atom(const expression& e, Fn f) {
// Looks into the expression and finds the given expression variant
// for which the predicate function returns true.
// If nothing is found returns nullptr.
// For example:
// find_in_expression<binary_operator>(e, [](const binary_operator&) {return true;})
// Will return the first binary operator found in the expression
template<ExpressionElement ExprElem, class Fn>
requires std::invocable<Fn, const ExprElem&>
&& std::same_as<std::invoke_result_t<Fn, const ExprElem&>, bool>
const ExprElem* find_in_expression(const expression& e, Fn predicate_fun) {
if (auto expr_elem = as_if<ExprElem>(&e)) {
if (predicate_fun(*expr_elem)) {
return expr_elem;
}
}
return expr::visit(overloaded_functor{
[&] (const binary_operator& op) { return f(op) ? &op : nullptr; },
[] (const constant&) -> const binary_operator* { return nullptr; },
[&] (const conjunction& conj) -> const binary_operator* {
[&] (const binary_operator& op) -> const ExprElem* {
if (auto found = find_in_expression<ExprElem>(op.lhs, predicate_fun)) {
return found;
}
return find_in_expression<ExprElem>(op.rhs, predicate_fun);
},
[&] (const conjunction& conj) -> const ExprElem* {
for (auto& child : conj.children) {
if (auto found = find_atom(child, f)) {
if (auto found = find_in_expression<ExprElem>(child, predicate_fun)) {
return found;
}
}
return nullptr;
},
[] (const column_value&) -> const binary_operator* { return nullptr; },
[] (const token&) -> const binary_operator* { return nullptr; },
[] (const unresolved_identifier&) -> const binary_operator* { return nullptr; },
[] (const column_mutation_attribute&) -> const binary_operator* { return nullptr; },
[&] (const function_call& fc) -> const binary_operator* {
[&] (const column_value& cv) -> const ExprElem* {
if (cv.sub.has_value()) {
return find_in_expression<ExprElem>(*cv.sub, predicate_fun);
}
return nullptr;
},
[&] (const column_mutation_attribute& a) -> const ExprElem* {
return find_in_expression<ExprElem>(a.column, predicate_fun);
},
[&] (const function_call& fc) -> const ExprElem* {
for (auto& arg : fc.args) {
if (auto found = find_atom(arg, f)) {
if (auto found = find_in_expression<ExprElem>(arg, predicate_fun)) {
return found;
}
}
return nullptr;
},
[&] (const cast& c) -> const binary_operator* {
return find_atom(c.arg, f);
[&] (const cast& c) -> const ExprElem* {
return find_in_expression<ExprElem>(c.arg, predicate_fun);
},
[&] (const field_selection& fs) -> const binary_operator* {
return find_atom(fs.structure, f);
[&] (const field_selection& fs) -> const ExprElem* {
return find_in_expression<ExprElem>(fs.structure, predicate_fun);
},
[&] (const null&) -> const binary_operator* {
return nullptr;
},
[&] (const bind_variable&) -> const binary_operator* {
return nullptr;
},
[&] (const untyped_constant&) -> const binary_operator* {
return nullptr;
},
[&] (const tuple_constructor& t) -> const binary_operator* {
[&] (const tuple_constructor& t) -> const ExprElem* {
for (auto& e : t.elements) {
if (auto found = find_atom(e, f)) {
if (auto found = find_in_expression<ExprElem>(e, predicate_fun)) {
return found;
}
}
return nullptr;
},
[&] (const collection_constructor& c) -> const binary_operator* {
[&] (const collection_constructor& c) -> const ExprElem* {
for (auto& e : c.elements) {
if (auto found = find_atom(e, f)) {
if (auto found = find_in_expression<ExprElem>(e, predicate_fun)) {
return found;
}
}
return nullptr;
},
[&] (const usertype_constructor& c) -> const binary_operator* {
[&] (const usertype_constructor& c) -> const ExprElem* {
for (auto& [k, v] : c.elements) {
if (auto found = find_atom(v, f)) {
if (auto found = find_in_expression<ExprElem>(v, predicate_fun)) {
return found;
}
}
return nullptr;
},
[](LeafExpression auto const&) -> const ExprElem* {
return nullptr;
}
}, e);
}
/// If there is a binary_operator atom b for which f(b) is true, returns it. Otherwise returns null.
template<class Fn>
requires std::invocable<Fn, const binary_operator&>
&& std::same_as<std::invoke_result_t<Fn, const binary_operator&>, bool>
const binary_operator* find_binop(const expression& e, Fn predicate_fun) {
return find_in_expression<binary_operator>(e, predicate_fun);
}
/// Counts binary_operator atoms b for which f(b) is true.
template<typename Fn>
requires std::regular_invocable<Fn, const binary_operator&>
@@ -536,7 +598,7 @@ size_t count_if(const expression& e, Fn f) {
}
inline const binary_operator* find(const expression& e, oper_t op) {
return find_atom(e, [&] (const binary_operator& o) { return o.op == op; });
return find_binop(e, [&] (const binary_operator& o) { return o.op == op; });
}
inline bool needs_filtering(oper_t op) {
@@ -545,7 +607,7 @@ inline bool needs_filtering(oper_t op) {
}
inline auto find_needs_filtering(const expression& e) {
return find_atom(e, [] (const binary_operator& bo) { return needs_filtering(bo.op); });
return find_binop(e, [] (const binary_operator& bo) { return needs_filtering(bo.op); });
}
inline bool is_slice(oper_t op) {
@@ -553,7 +615,7 @@ inline bool is_slice(oper_t op) {
}
inline bool has_slice(const expression& e) {
return find_atom(e, [] (const binary_operator& bo) { return is_slice(bo.op); });
return find_binop(e, [] (const binary_operator& bo) { return is_slice(bo.op); });
}
inline bool is_compare(oper_t op) {
@@ -575,11 +637,11 @@ inline bool is_multi_column(const binary_operator& op) {
}
inline bool has_token(const expression& e) {
return find_atom(e, [] (const binary_operator& o) { return expr::is<token>(o.lhs); });
return find_binop(e, [] (const binary_operator& o) { return expr::is<token>(o.lhs); });
}
inline bool has_slice_or_needs_filtering(const expression& e) {
return find_atom(e, [] (const binary_operator& o) { return is_slice(o.op) || needs_filtering(o.op); });
return find_binop(e, [] (const binary_operator& o) { return is_slice(o.op) || needs_filtering(o.op); });
}
inline bool is_clustering_order(const binary_operator& op) {
@@ -587,7 +649,7 @@ inline bool is_clustering_order(const binary_operator& op) {
}
inline auto find_clustering_order(const expression& e) {
return find_atom(e, is_clustering_order);
return find_binop(e, is_clustering_order);
}
/// True iff binary_operator involves a collection.
@@ -603,14 +665,11 @@ extern expression replace_token(const expression&, const column_definition*);
// Recursively copies e and returns it. Calls replace_candidate() on all nodes. If it returns nullopt,
// continue with the copying. If it returns an expression, that expression replaces the current node.
//
// Note only binary_operator's LHS is searched. The RHS is not an expression, but a term, so it is left
// unmodified.
extern expression search_and_replace(const expression& e,
const noncopyable_function<std::optional<expression> (const expression& candidate)>& replace_candidate);
extern ::shared_ptr<term> prepare_term(const expression& expr, database& db, const sstring& keyspace, lw_shared_ptr<column_specification> receiver);
extern ::shared_ptr<term> prepare_term_multi_column(const expression& expr, database& db, const sstring& keyspace, const std::vector<lw_shared_ptr<column_specification>>& receivers);
extern expression prepare_expression(const expression& expr, database& db, const sstring& keyspace, lw_shared_ptr<column_specification> receiver);
extern expression prepare_expression_multi_column(const expression& expr, database& db, const sstring& keyspace, const std::vector<lw_shared_ptr<column_specification>>& receivers);
/**
@@ -646,21 +705,9 @@ std::vector<expression> extract_single_column_restrictions_for_column(const expr
std::optional<bool> get_bool_value(const constant&);
// Takes a prepared expression and calculates its value.
// Later term will be replaced with expression.
constant evaluate(const ::shared_ptr<term>&, const query_options&);
constant evaluate(term*, const query_options&);
constant evaluate(term&, const query_options&);
// Similar to evaluate(), but ignores any NULL values in the final list value.
// In an IN restriction nulls can be ignored, because nothing equals NULL.
constant evaluate_IN_list(const ::shared_ptr<term>&, const query_options&);
constant evaluate_IN_list(term*, const query_options&);
constant evaluate_IN_list(term&, const query_options&);
// Calls evaluate() on the term and then converts the constant to raw_value_view
cql3::raw_value_view evaluate_to_raw_view(const ::shared_ptr<term>&, const query_options&);
cql3::raw_value_view evaluate_to_raw_view(term&, const query_options&);
constant evaluate_IN_list(const expression&, const query_options&);
// Takes a prepared expression and calculates its value.
// Evaluates bound values, calls functions and returns just the bytes and type.
@@ -684,7 +731,16 @@ std::vector<managed_bytes_opt> get_elements(const constant&);
// It is useful with IN restrictions like (a, b) IN [(1, 2), (3, 4)].
utils::chunked_vector<std::vector<managed_bytes_opt>> get_list_of_tuples_elements(const constant&);
expression to_expression(const ::shared_ptr<term>&);
// Retrieves information needed in prepare_context.
// Collects the column specification for the bind variables in this expression.
// Sets lwt_cache_id field in function_calls.
void fill_prepare_context(expression&, cql3::prepare_context&);
// Checks whether there is a bind_variable inside this expression
// It's important to note, that even when there are no bind markers,
// there can be other things that prevent immediate evaluation of an expression.
// For example an expression can contain calls to nonpure functions.
bool contains_bind_marker(const expression& e);
} // namespace expr
} // namespace cql3

View File

@@ -20,14 +20,13 @@
*/
#include "expression.hh"
#include "cql3/functions/function_call.hh"
#include "cql3/functions/functions.hh"
#include "cql3/column_identifier.hh"
#include "cql3/constants.hh"
#include "cql3/abstract_marker.hh"
#include "cql3/lists.hh"
#include "cql3/sets.hh"
#include "cql3/user_types.hh"
#include "cql3/tuples.hh"
#include "types/list.hh"
#include "types/set.hh"
#include "types/map.hh"
@@ -83,13 +82,13 @@ usertype_constructor_test_assignment(const usertype_constructor& u, database& db
}
static
shared_ptr<term>
usertype_constructor_prepare_term(const usertype_constructor& u, database& db, const sstring& keyspace, lw_shared_ptr<column_specification> receiver) {
expression
usertype_constructor_prepare_expression(const usertype_constructor& u, database& db, const sstring& keyspace, lw_shared_ptr<column_specification> receiver) {
usertype_constructor_validate_assignable_to(u, db, keyspace, *receiver);
auto&& ut = static_pointer_cast<const user_type_impl>(receiver->type);
bool all_terminal = true;
std::vector<shared_ptr<term>> values;
values.reserve(u.elements.size());
usertype_constructor::elements_map_type prepared_elements;
size_t found_values = 0;
for (size_t i = 0; i < ut->size(); ++i) {
auto&& field = column_identifier(to_bytes(ut->field_name(i)), utf8_type);
@@ -101,13 +100,13 @@ usertype_constructor_prepare_term(const usertype_constructor& u, database& db, c
raw = iraw->second;
++found_values;
}
auto&& value = prepare_term(raw, db, keyspace, usertype_field_spec_of(*receiver, i));
expression value = prepare_expression(raw, db, keyspace, usertype_field_spec_of(*receiver, i));
if (dynamic_cast<non_terminal*>(value.get())) {
if (!is<constant>(value)) {
all_terminal = false;
}
values.push_back(std::move(value));
prepared_elements.emplace(std::move(field), std::move(value));
}
if (found_values != u.elements.size()) {
// We had some field that are not part of the type
@@ -119,11 +118,15 @@ usertype_constructor_prepare_term(const usertype_constructor& u, database& db, c
}
}
user_types::delayed_value value(ut, values);
usertype_constructor value {
.elements = std::move(prepared_elements),
.type = ut
};
if (all_terminal) {
return value.bind(query_options::DEFAULT);
return evaluate(value, query_options::DEFAULT);
} else {
return make_shared<user_types::delayed_value>(std::move(value));
return value;
}
}
@@ -197,13 +200,13 @@ map_test_assignment(const collection_constructor& c, database& db, const sstring
}
static
::shared_ptr<term>
map_prepare_term(const collection_constructor& c, database& db, const sstring& keyspace, lw_shared_ptr<column_specification> receiver) {
expression
map_prepare_expression(const collection_constructor& c, database& db, const sstring& keyspace, lw_shared_ptr<column_specification> receiver) {
map_validate_assignable_to(c, db, keyspace, *receiver);
auto key_spec = maps::key_spec_of(*receiver);
auto value_spec = maps::value_spec_of(*receiver);
std::unordered_map<shared_ptr<term>, shared_ptr<term>> values;
std::vector<expression> values;
values.reserve(c.elements.size());
bool all_terminal = true;
for (auto&& entry : c.elements) {
@@ -211,24 +214,33 @@ map_prepare_term(const collection_constructor& c, database& db, const sstring& k
if (entry_tuple.elements.size() != 2) {
on_internal_error(expr_logger, "map element is not a tuple of arity 2");
}
auto k = prepare_term(entry_tuple.elements[0], db, keyspace, key_spec);
auto v = prepare_term(entry_tuple.elements[1], db, keyspace, value_spec);
expression k = prepare_expression(entry_tuple.elements[0], db, keyspace, key_spec);
expression v = prepare_expression(entry_tuple.elements[1], db, keyspace, value_spec);
if (k->contains_bind_marker() || v->contains_bind_marker()) {
if (contains_bind_marker(k) || contains_bind_marker(k)) {
throw exceptions::invalid_request_exception(format("Invalid map literal for {}: bind variables are not supported inside collection literals", *receiver->name));
}
if (dynamic_pointer_cast<non_terminal>(k) || dynamic_pointer_cast<non_terminal>(v)) {
// Even when there are no bind markers, the value can still contain a nonpure function
if (!is<constant>(k) || !is<constant>(v)) {
all_terminal = false;
}
values.emplace(k, v);
values.emplace_back(tuple_constructor {
.elements = {std::move(k), std::move(v)},
.type = entry_tuple.type
});
}
maps::delayed_value value(values, receiver->type);
collection_constructor map_value {
.style = collection_constructor::style_type::map,
.elements = std::move(values),
.type = receiver->type
};
if (all_terminal) {
return value.bind(query_options::DEFAULT);
return evaluate(map_value, query_options::DEFAULT);
} else {
return make_shared<maps::delayed_value>(std::move(value));
return map_value;
}
}
@@ -283,8 +295,8 @@ set_test_assignment(const collection_constructor& c, database& db, const sstring
}
static
shared_ptr<term>
set_prepare_term(const collection_constructor& c, database& db, const sstring& keyspace, lw_shared_ptr<column_specification> receiver) {
expression
set_prepare_expression(const collection_constructor& c, database& db, const sstring& keyspace, lw_shared_ptr<column_specification> receiver) {
set_validate_assignable_to(c, db, keyspace, *receiver);
if (c.elements.empty()) {
@@ -294,40 +306,48 @@ set_prepare_term(const collection_constructor& c, database& db, const sstring& k
// away to simplify predicate evaluation. See also
// https://issues.apache.org/jira/browse/CASSANDRA-5141
if (receiver->type->is_multi_cell()) {
return cql3::constants::NULL_VALUE;
return constant::make_null(receiver->type);
}
// We've parsed empty maps as a set literal to break the ambiguity so
// handle that case now. This branch works for frozen sets/maps only.
const map_type_impl* maybe_map_type = dynamic_cast<const map_type_impl*>(receiver->type.get());
if (maybe_map_type != nullptr) {
serialized_compare comparator = maybe_map_type->get_keys_type()->as_less_comparator();
std::map<managed_bytes, managed_bytes, serialized_compare> m(empty_type->as_less_comparator());
return ::make_shared<maps::value>(std::move(m), receiver->type);
collection_constructor map_value {
.style = collection_constructor::style_type::map,
.elements = {},
.type = receiver->type
};
return expr::evaluate(map_value, query_options::DEFAULT);
}
}
auto value_spec = set_value_spec_of(*receiver);
std::vector<shared_ptr<term>> values;
std::vector<expression> values;
values.reserve(c.elements.size());
bool all_terminal = true;
for (auto& e : c.elements)
{
auto t = prepare_term(e, db, keyspace, value_spec);
expression elem = prepare_expression(e, db, keyspace, value_spec);
if (t->contains_bind_marker()) {
if (contains_bind_marker(elem)) {
throw exceptions::invalid_request_exception(format("Invalid set literal for {}: bind variables are not supported inside collection literals", *receiver->name));
}
if (dynamic_pointer_cast<non_terminal>(t)) {
if (!is<constant>(elem)) {
all_terminal = false;
}
values.push_back(std::move(t));
values.push_back(std::move(elem));
}
auto value = ::make_shared<sets::delayed_value>(std::move(values), receiver->type);
collection_constructor value {
.style = collection_constructor::style_type::set,
.elements = std::move(values),
.type = receiver->type
};
if (all_terminal) {
return value->bind(query_options::DEFAULT);
return evaluate(value, query_options::DEFAULT);
} else {
return value;
}
@@ -375,8 +395,8 @@ list_test_assignment(const collection_constructor& c, database& db, const sstrin
static
shared_ptr<term>
list_prepare_term(const collection_constructor& c, database& db, const sstring& keyspace, lw_shared_ptr<column_specification> receiver) {
expression
list_prepare_expression(const collection_constructor& c, database& db, const sstring& keyspace, lw_shared_ptr<column_specification> receiver) {
list_validate_assignable_to(c, db, keyspace, *receiver);
// In Cassandra, an empty (unfrozen) map/set/list is equivalent to the column being null. In
@@ -384,29 +404,33 @@ list_prepare_term(const collection_constructor& c, database& db, const sstring&
// away to simplify predicate evaluation. See also
// https://issues.apache.org/jira/browse/CASSANDRA-5141
if (receiver->type->is_multi_cell() && c.elements.empty()) {
return cql3::constants::NULL_VALUE;
return constant::make_null(receiver->type);
}
auto&& value_spec = list_value_spec_of(*receiver);
std::vector<shared_ptr<term>> values;
std::vector<expression> values;
values.reserve(c.elements.size());
bool all_terminal = true;
for (auto& e : c.elements) {
auto&& t = prepare_term(e, db, keyspace, value_spec);
expression elem = prepare_expression(e, db, keyspace, value_spec);
if (t->contains_bind_marker()) {
if (contains_bind_marker(elem)) {
throw exceptions::invalid_request_exception(format("Invalid list literal for {}: bind variables are not supported inside collection literals", *receiver->name));
}
if (dynamic_pointer_cast<non_terminal>(t)) {
if (!is<constant>(elem)) {
all_terminal = false;
}
values.push_back(std::move(t));
values.push_back(std::move(elem));
}
lists::delayed_value value(values, receiver->type);
collection_constructor value {
.style = collection_constructor::style_type::list,
.elements = std::move(values),
.type = receiver->type
};
if (all_terminal) {
return value.bind(query_options::DEFAULT);
return evaluate(value, query_options::DEFAULT);
} else {
return make_shared<lists::delayed_value>(std::move(value));
return value;
}
}
@@ -453,49 +477,55 @@ tuple_constructor_test_assignment(const tuple_constructor& tc, database& db, con
}
static
shared_ptr<term>
expression
tuple_constructor_prepare_nontuple(const tuple_constructor& tc, database& db, const sstring& keyspace, lw_shared_ptr<column_specification> receiver) {
tuple_constructor_validate_assignable_to(tc, db, keyspace, *receiver);
std::vector<shared_ptr<term>> values;
std::vector<expression> values;
bool all_terminal = true;
for (size_t i = 0; i < tc.elements.size(); ++i) {
auto&& value = prepare_term(tc.elements[i], db, keyspace, component_spec_of(*receiver, i));
if (dynamic_pointer_cast<non_terminal>(value)) {
expression value = prepare_expression(tc.elements[i], db, keyspace, component_spec_of(*receiver, i));
if (!is<constant>(value)) {
all_terminal = false;
}
values.push_back(std::move(value));
}
tuples::delayed_value value(static_pointer_cast<const tuple_type_impl>(receiver->type), values);
tuple_constructor value {
.elements = std::move(values),
.type = static_pointer_cast<const tuple_type_impl>(receiver->type)
};
if (all_terminal) {
return value.bind(query_options::DEFAULT);
return evaluate(value, query_options::DEFAULT);
} else {
return make_shared<tuples::delayed_value>(std::move(value));
return value;
}
}
static
shared_ptr<term>
expression
tuple_constructor_prepare_tuple(const tuple_constructor& tc, database& db, const sstring& keyspace, const std::vector<lw_shared_ptr<column_specification>>& receivers) {
if (tc.elements.size() != receivers.size()) {
throw exceptions::invalid_request_exception(format("Expected {:d} elements in value tuple, but got {:d}: {}", receivers.size(), tc.elements.size(), tc));
}
std::vector<shared_ptr<term>> values;
std::vector<expression> values;
std::vector<data_type> types;
bool all_terminal = true;
for (size_t i = 0; i < tc.elements.size(); ++i) {
auto&& t = prepare_term(tc.elements[i], db, keyspace, receivers[i]);
if (dynamic_pointer_cast<non_terminal>(t)) {
expression elem = prepare_expression(tc.elements[i], db, keyspace, receivers[i]);
if (!is<constant>(elem)) {
all_terminal = false;
}
values.push_back(t);
values.push_back(std::move(elem));
types.push_back(receivers[i]->type);
}
tuples::delayed_value value(tuple_type_impl::get_instance(std::move(types)), std::move(values));
tuple_constructor value {
.elements = std::move(values),
.type = tuple_type_impl::get_instance(std::move(types))
};
if (all_terminal) {
return value.bind(query_options::DEFAULT);
return evaluate(value, query_options::DEFAULT);
} else {
return make_shared<tuples::delayed_value>(std::move(value));
return value;
}
}
@@ -609,15 +639,15 @@ untyped_constant_test_assignment(const untyped_constant& uc, database& db, const
}
static
::shared_ptr<term>
untyped_constant_prepare_term(const untyped_constant& uc, database& db, const sstring& keyspace, lw_shared_ptr<column_specification> receiver)
constant
untyped_constant_prepare_expression(const untyped_constant& uc, database& db, const sstring& keyspace, lw_shared_ptr<column_specification> receiver)
{
if (!is_assignable(untyped_constant_test_assignment(uc, db, keyspace, *receiver))) {
throw exceptions::invalid_request_exception(format("Invalid {} constant ({}) for \"{}\" of type {}",
uc.partial_type, uc.raw_text, *receiver->name, receiver->type->as_cql3_type().to_string()));
}
raw_value raw_val = cql3::raw_value::make_value(untyped_constant_parsed_value(uc, receiver->type));
return ::make_shared<constants::value>(std::move(raw_val), receiver->type);
return constant(std::move(raw_val), receiver->type);
}
static
@@ -627,25 +657,14 @@ bind_variable_test_assignment(const bind_variable& bv, database& db, const sstri
}
static
::shared_ptr<term>
bind_variable_scalar_prepare_term(const bind_variable& bv, database& db, const sstring& keyspace, lw_shared_ptr<column_specification> receiver)
{
if (receiver->type->is_collection()) {
if (receiver->type->without_reversed().is_list()) {
return ::make_shared<lists::marker>(bv.bind_index, receiver);
} else if (receiver->type->without_reversed().is_set()) {
return ::make_shared<sets::marker>(bv.bind_index, receiver);
} else if (receiver->type->without_reversed().is_map()) {
return ::make_shared<maps::marker>(bv.bind_index, receiver);
}
assert(0);
}
if (receiver->type->is_user_type()) {
return ::make_shared<user_types::marker>(bv.bind_index, receiver);
}
return ::make_shared<constants::marker>(bv.bind_index, receiver);
bind_variable
bind_variable_scalar_prepare_expression(const bind_variable& bv, database& db, const sstring& keyspace, lw_shared_ptr<column_specification> receiver)
{
return bind_variable {
.shape = bind_variable::shape_type::scalar,
.bind_index = bv.bind_index,
.receiver = receiver
};
}
static
@@ -656,9 +675,13 @@ bind_variable_scalar_in_make_receiver(const column_specification& receiver) {
}
static
::shared_ptr<term>
bind_variable_scalar_in_prepare_term(const bind_variable& bv, database& db, const sstring& keyspace, lw_shared_ptr<column_specification> receiver) {
return ::make_shared<lists::marker>(bv.bind_index, bind_variable_scalar_in_make_receiver(*receiver));
bind_variable
bind_variable_scalar_in_prepare_expression(const bind_variable& bv, database& db, const sstring& keyspace, lw_shared_ptr<column_specification> receiver) {
return bind_variable {
.shape = bind_variable::shape_type::scalar,
.bind_index = bv.bind_index,
.receiver = bind_variable_scalar_in_make_receiver(*receiver)
};
}
static
@@ -682,9 +705,13 @@ bind_variable_tuple_make_receiver(const std::vector<lw_shared_ptr<column_specifi
}
static
::shared_ptr<term>
bind_variable_tuple_prepare_term(const bind_variable& bv, database& db, const sstring& keyspace, const std::vector<lw_shared_ptr<column_specification>>& receivers) {
return make_shared<tuples::marker>(bv.bind_index, bind_variable_tuple_make_receiver(receivers));
bind_variable
bind_variable_tuple_prepare_expression(const bind_variable& bv, database& db, const sstring& keyspace, const std::vector<lw_shared_ptr<column_specification>>& receivers) {
return bind_variable {
.shape = bind_variable::shape_type::tuple,
.bind_index = bv.bind_index,
.receiver = bind_variable_tuple_make_receiver(receivers)
};
}
static
@@ -713,9 +740,13 @@ bind_variable_tuple_in_make_receiver(const std::vector<lw_shared_ptr<column_spec
}
static
::shared_ptr<term>
bind_variable_tuple_in_prepare_term(const bind_variable& bv, database& db, const sstring& keyspace, const std::vector<lw_shared_ptr<column_specification>>& receivers) {
return make_shared<tuples::in_marker>(bv.bind_index, bind_variable_tuple_in_make_receiver(receivers));
bind_variable
bind_variable_tuple_in_prepare_expression(const bind_variable& bv, database& db, const sstring& keyspace, const std::vector<lw_shared_ptr<column_specification>>& receivers) {
return bind_variable {
.shape = bind_variable::shape_type::tuple_in,
.bind_index = bv.bind_index,
.receiver = bind_variable_tuple_in_make_receiver(receivers)
};
}
static
@@ -729,12 +760,12 @@ null_test_assignment(database& db,
}
static
::shared_ptr<term>
null_prepare_term(database& db, const sstring& keyspace, lw_shared_ptr<column_specification> receiver) {
constant
null_prepare_expression(database& db, const sstring& keyspace, lw_shared_ptr<column_specification> receiver) {
if (!is_assignable(null_test_assignment(db, keyspace, *receiver))) {
throw exceptions::invalid_request_exception("Invalid null value for counter increment/decrement");
}
return constants::NULL_VALUE;
return constant::make_null(receiver->type);
}
static
@@ -770,8 +801,8 @@ cast_test_assignment(const cast& c, database& db, const sstring& keyspace, const
}
static
shared_ptr<term>
cast_prepare_term(const cast& c, database& db, const sstring& keyspace, lw_shared_ptr<column_specification> receiver) {
expression
cast_prepare_expression(const cast& c, database& db, const sstring& keyspace, lw_shared_ptr<column_specification> receiver) {
auto type = std::get<shared_ptr<cql3_type::raw>>(c.type);
if (!is_assignable(test_assignment(c.arg, db, keyspace, *casted_spec_of(c, db, keyspace, *receiver)))) {
throw exceptions::invalid_request_exception(format("Cannot cast value {} to type {}", c.arg, type));
@@ -779,91 +810,180 @@ cast_prepare_term(const cast& c, database& db, const sstring& keyspace, lw_share
if (!is_assignable(cast_test_assignment(c, db, keyspace, *receiver))) {
throw exceptions::invalid_request_exception(format("Cannot assign value {} to {} of type {}", c, receiver->name, receiver->type->as_cql3_type()));
}
return prepare_term(c.arg, db, keyspace, receiver);
return prepare_expression(c.arg, db, keyspace, receiver);
}
::shared_ptr<term>
prepare_term(const expression& expr, database& db, const sstring& keyspace, lw_shared_ptr<column_specification> receiver) {
expr::expression
prepare_function_call(const expr::function_call& fc, database& db, const sstring& keyspace, lw_shared_ptr<column_specification> receiver) {
auto&& fun = std::visit(overloaded_functor{
[] (const shared_ptr<functions::function>& func) {
return func;
},
[&] (const functions::function_name& name) {
auto args = boost::copy_range<std::vector<::shared_ptr<assignment_testable>>>(fc.args | boost::adaptors::transformed(expr::as_assignment_testable));
auto fun = functions::functions::get(db, keyspace, name, args, receiver->ks_name, receiver->cf_name, receiver.get());
if (!fun) {
throw exceptions::invalid_request_exception(format("Unknown function {} called", name));
}
return fun;
},
}, fc.func);
if (fun->is_aggregate()) {
throw exceptions::invalid_request_exception("Aggregation function are not supported in the where clause");
}
// Can't use static_pointer_cast<> because function is a virtual base class of scalar_function
auto&& scalar_fun = dynamic_pointer_cast<functions::scalar_function>(fun);
// Functions.get() will complain if no function "name" type check with the provided arguments.
// We still have to validate that the return type matches however
if (!receiver->type->is_value_compatible_with(*scalar_fun->return_type())) {
throw exceptions::invalid_request_exception(format("Type error: cannot assign result of function {} (type {}) to {} (type {})",
fun->name(), fun->return_type()->as_cql3_type(),
receiver->name, receiver->type->as_cql3_type()));
}
if (scalar_fun->arg_types().size() != fc.args.size()) {
throw exceptions::invalid_request_exception(format("Incorrect number of arguments specified for function {} (expected {:d}, found {:d})",
fun->name(), fun->arg_types().size(), fc.args.size()));
}
std::vector<expr::expression> parameters;
parameters.reserve(fc.args.size());
bool all_terminal = true;
for (size_t i = 0; i < fc.args.size(); ++i) {
expr::expression e = prepare_expression(fc.args[i], db, keyspace,
functions::functions::make_arg_spec(receiver->ks_name, receiver->cf_name, *scalar_fun, i));
if (!expr::is<expr::constant>(e)) {
all_terminal = false;
}
parameters.push_back(std::move(e));
}
// If all parameters are terminal and the function is pure, we can
// evaluate it now, otherwise we'd have to wait execution time
expr::function_call fun_call {
.func = fun,
.args = std::move(parameters),
.lwt_cache_id = fc.lwt_cache_id
};
if (all_terminal && scalar_fun->is_pure()) {
return expr::evaluate(fun_call, query_options::DEFAULT);
} else {
return fun_call;
}
}
assignment_testable::test_result
test_assignment_function_call(const cql3::expr::function_call& fc, database& db, const sstring& keyspace, const column_specification& receiver) {
// Note: Functions.get() will return null if the function doesn't exist, or throw is no function matching
// the arguments can be found. We may get one of those if an undefined/wrong function is used as argument
// of another, existing, function. In that case, we return true here because we'll throw a proper exception
// later with a more helpful error message that if we were to return false here.
try {
auto&& fun = std::visit(overloaded_functor{
[&] (const functions::function_name& name) {
auto args = boost::copy_range<std::vector<::shared_ptr<assignment_testable>>>(fc.args | boost::adaptors::transformed(expr::as_assignment_testable));
return functions::functions::get(db, keyspace, name, args, receiver.ks_name, receiver.cf_name, &receiver);
},
[] (const shared_ptr<functions::function>& func) {
return func;
},
}, fc.func);
if (fun && receiver.type == fun->return_type()) {
return assignment_testable::test_result::EXACT_MATCH;
} else if (!fun || receiver.type->is_value_compatible_with(*fun->return_type())) {
return assignment_testable::test_result::WEAKLY_ASSIGNABLE;
} else {
return assignment_testable::test_result::NOT_ASSIGNABLE;
}
} catch (exceptions::invalid_request_exception& e) {
return assignment_testable::test_result::WEAKLY_ASSIGNABLE;
}
}
expression
prepare_expression(const expression& expr, database& db, const sstring& keyspace, lw_shared_ptr<column_specification> receiver) {
return expr::visit(overloaded_functor{
[] (const constant&) -> ::shared_ptr<term> {
[] (const constant&) -> expression {
on_internal_error(expr_logger, "Can't prepare constant_value, it should not appear in parser output");
},
[&] (const binary_operator&) -> ::shared_ptr<term> {
on_internal_error(expr_logger, "binary_operators are not yet reachable via term_raw_expr::prepare()");
[&] (const binary_operator&) -> expression {
on_internal_error(expr_logger, "binary_operators are not yet reachable via prepare_expression()");
},
[&] (const conjunction&) -> ::shared_ptr<term> {
on_internal_error(expr_logger, "conjunctions are not yet reachable via term_raw_expr::prepare()");
[&] (const conjunction&) -> expression {
on_internal_error(expr_logger, "conjunctions are not yet reachable via prepare_expression()");
},
[&] (const column_value&) -> ::shared_ptr<term> {
on_internal_error(expr_logger, "column_values are not yet reachable via term_raw_expr::prepare()");
[&] (const column_value&) -> expression {
on_internal_error(expr_logger, "column_values are not yet reachable via prepare_expression()");
},
[&] (const token&) -> ::shared_ptr<term> {
on_internal_error(expr_logger, "tokens are not yet reachable via term_raw_expr::prepare()");
[&] (const token&) -> expression {
on_internal_error(expr_logger, "tokens are not yet reachable via prepare_expression()");
},
[&] (const unresolved_identifier&) -> ::shared_ptr<term> {
on_internal_error(expr_logger, "unresolved_identifiers are not yet reachable via term_raw_expr::prepare()");
[&] (const unresolved_identifier&) -> expression {
on_internal_error(expr_logger, "unresolved_identifiers are not yet reachable via prepare_expression()");
},
[&] (const column_mutation_attribute&) -> ::shared_ptr<term> {
on_internal_error(expr_logger, "column_mutation_attributes are not yet reachable via term_raw_expr::prepare()");
[&] (const column_mutation_attribute&) -> expression {
on_internal_error(expr_logger, "column_mutation_attributes are not yet reachable via prepare_expression()");
},
[&] (const function_call& fc) -> ::shared_ptr<term> {
return functions::prepare_function_call(fc, db, keyspace, std::move(receiver));
[&] (const function_call& fc) -> expression {
return prepare_function_call(fc, db, keyspace, std::move(receiver));
},
[&] (const cast& c) -> ::shared_ptr<term> {
return cast_prepare_term(c, db, keyspace, receiver);
[&] (const cast& c) -> expression {
return cast_prepare_expression(c, db, keyspace, receiver);
},
[&] (const field_selection&) -> ::shared_ptr<term> {
on_internal_error(expr_logger, "field_selections are not yet reachable via term_raw_expr::prepare()");
[&] (const field_selection&) -> expression {
on_internal_error(expr_logger, "field_selections are not yet reachable via prepare_expression()");
},
[&] (const null&) -> ::shared_ptr<term> {
return null_prepare_term(db, keyspace, receiver);
[&] (const null&) -> expression {
return null_prepare_expression(db, keyspace, receiver);
},
[&] (const bind_variable& bv) -> ::shared_ptr<term> {
[&] (const bind_variable& bv) -> expression {
switch (bv.shape) {
case expr::bind_variable::shape_type::scalar: return bind_variable_scalar_prepare_term(bv, db, keyspace, receiver);
case expr::bind_variable::shape_type::scalar_in: return bind_variable_scalar_in_prepare_term(bv, db, keyspace, receiver);
case expr::bind_variable::shape_type::tuple: on_internal_error(expr_logger, "prepare_term(bind_variable(tuple))");
case expr::bind_variable::shape_type::tuple_in: on_internal_error(expr_logger, "prepare_term(bind_variable(tuple_in))");
case expr::bind_variable::shape_type::scalar: return bind_variable_scalar_prepare_expression(bv, db, keyspace, receiver);
case expr::bind_variable::shape_type::scalar_in: return bind_variable_scalar_in_prepare_expression(bv, db, keyspace, receiver);
case expr::bind_variable::shape_type::tuple: on_internal_error(expr_logger, "prepare_expression(bind_variable(tuple))");
case expr::bind_variable::shape_type::tuple_in: on_internal_error(expr_logger, "prepare_expression(bind_variable(tuple_in))");
}
on_internal_error(expr_logger, "unexpected shape in bind_variable");
},
[&] (const untyped_constant& uc) -> ::shared_ptr<term> {
return untyped_constant_prepare_term(uc, db, keyspace, receiver);
[&] (const untyped_constant& uc) -> expression {
return untyped_constant_prepare_expression(uc, db, keyspace, receiver);
},
[&] (const tuple_constructor& tc) -> ::shared_ptr<term> {
[&] (const tuple_constructor& tc) -> expression {
return tuple_constructor_prepare_nontuple(tc, db, keyspace, receiver);
},
[&] (const collection_constructor& c) -> ::shared_ptr<term> {
[&] (const collection_constructor& c) -> expression {
switch (c.style) {
case collection_constructor::style_type::list: return list_prepare_term(c, db, keyspace, receiver);
case collection_constructor::style_type::set: return set_prepare_term(c, db, keyspace, receiver);
case collection_constructor::style_type::map: return map_prepare_term(c, db, keyspace, receiver);
case collection_constructor::style_type::list: return list_prepare_expression(c, db, keyspace, receiver);
case collection_constructor::style_type::set: return set_prepare_expression(c, db, keyspace, receiver);
case collection_constructor::style_type::map: return map_prepare_expression(c, db, keyspace, receiver);
}
on_internal_error(expr_logger, fmt::format("unexpected collection_constructor style {}", static_cast<unsigned>(c.style)));
},
[&] (const usertype_constructor& uc) -> ::shared_ptr<term> {
return usertype_constructor_prepare_term(uc, db, keyspace, receiver);
[&] (const usertype_constructor& uc) -> expression {
return usertype_constructor_prepare_expression(uc, db, keyspace, receiver);
},
}, expr);
}
::shared_ptr<term>
prepare_term_multi_column(const expression& expr, database& db, const sstring& keyspace, const std::vector<lw_shared_ptr<column_specification>>& receivers) {
expression
prepare_expression_multi_column(const expression& expr, database& db, const sstring& keyspace, const std::vector<lw_shared_ptr<column_specification>>& receivers) {
return expr::visit(overloaded_functor{
[&] (const bind_variable& bv) -> ::shared_ptr<term> {
[&] (const bind_variable& bv) -> expression {
switch (bv.shape) {
case expr::bind_variable::shape_type::scalar: on_internal_error(expr_logger, "prepare_term_multi_column(bind_variable(scalar))");
case expr::bind_variable::shape_type::scalar_in: on_internal_error(expr_logger, "prepare_term_multi_column(bind_variable(scalar_in))");
case expr::bind_variable::shape_type::tuple: return bind_variable_tuple_prepare_term(bv, db, keyspace, receivers);
case expr::bind_variable::shape_type::tuple_in: return bind_variable_tuple_in_prepare_term(bv, db, keyspace, receivers);
case expr::bind_variable::shape_type::scalar: on_internal_error(expr_logger, "prepare_expression_multi_column(bind_variable(scalar))");
case expr::bind_variable::shape_type::scalar_in: on_internal_error(expr_logger, "prepare_expression_multi_column(bind_variable(scalar_in))");
case expr::bind_variable::shape_type::tuple: return bind_variable_tuple_prepare_expression(bv, db, keyspace, receivers);
case expr::bind_variable::shape_type::tuple_in: return bind_variable_tuple_in_prepare_expression(bv, db, keyspace, receivers);
}
on_internal_error(expr_logger, "unexpected shape in bind_variable");
},
[&] (const tuple_constructor& tc) -> ::shared_ptr<term> {
[&] (const tuple_constructor& tc) -> expression {
return tuple_constructor_prepare_tuple(tc, db, keyspace, receivers);
},
[] (const auto& default_case) -> ::shared_ptr<term> {
on_internal_error(expr_logger, fmt::format("prepare_term_multi_column({})", typeid(default_case).name()));
[] (const auto& default_case) -> expression {
on_internal_error(expr_logger, fmt::format("prepare_expression_multi_column({})", typeid(default_case).name()));
},
}, expr);
}
@@ -874,34 +994,34 @@ test_assignment(const expression& expr, database& db, const sstring& keyspace, c
return expr::visit(overloaded_functor{
[&] (const constant&) -> test_result {
// constants shouldn't appear in parser output, only untyped_constants
on_internal_error(expr_logger, "constants are not yet reachable via term_raw_expr::test_assignment()");
on_internal_error(expr_logger, "constants are not yet reachable via test_assignment()");
},
[&] (const binary_operator&) -> test_result {
on_internal_error(expr_logger, "binary_operators are not yet reachable via term_raw_expr::test_assignment()");
on_internal_error(expr_logger, "binary_operators are not yet reachable via test_assignment()");
},
[&] (const conjunction&) -> test_result {
on_internal_error(expr_logger, "conjunctions are not yet reachable via term_raw_expr::test_assignment()");
on_internal_error(expr_logger, "conjunctions are not yet reachable via test_assignment()");
},
[&] (const column_value&) -> test_result {
on_internal_error(expr_logger, "column_values are not yet reachable via term_raw_expr::test_assignment()");
on_internal_error(expr_logger, "column_values are not yet reachable via test_assignment()");
},
[&] (const token&) -> test_result {
on_internal_error(expr_logger, "tokens are not yet reachable via term_raw_expr::test_assignment()");
on_internal_error(expr_logger, "tokens are not yet reachable via test_assignment()");
},
[&] (const unresolved_identifier&) -> test_result {
on_internal_error(expr_logger, "unresolved_identifiers are not yet reachable via term_raw_expr::test_assignment()");
on_internal_error(expr_logger, "unresolved_identifiers are not yet reachable via test_assignment()");
},
[&] (const column_mutation_attribute&) -> test_result {
on_internal_error(expr_logger, "column_mutation_attributes are not yet reachable via term_raw_expr::test_assignment()");
on_internal_error(expr_logger, "column_mutation_attributes are not yet reachable via test_assignment()");
},
[&] (const function_call& fc) -> test_result {
return functions::test_assignment_function_call(fc, db, keyspace, receiver);
return test_assignment_function_call(fc, db, keyspace, receiver);
},
[&] (const cast& c) -> test_result {
return cast_test_assignment(c, db, keyspace, receiver);
},
[&] (const field_selection&) -> test_result {
on_internal_error(expr_logger, "field_selections are not yet reachable via term_raw_expr::test_assignment()");
on_internal_error(expr_logger, "field_selections are not yet reachable via test_assignment()");
},
[&] (const null&) -> test_result {
return null_test_assignment(db, keyspace, receiver);

View File

@@ -1,92 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Modified by ScyllaDB
*
* Copyright (C) 2015-present ScyllaDB
*/
/*
* This file is part of Scylla.
*
* Scylla is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Scylla is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Scylla. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "function.hh"
#include "scalar_function.hh"
#include "cql3/term.hh"
#include "exceptions/exceptions.hh"
#include "cql3/functions/function_name.hh"
namespace cql3::expr {
struct function_call;
}
namespace cql3 {
namespace functions {
class function_call : public non_terminal {
const shared_ptr<scalar_function> _fun;
const std::vector<shared_ptr<term>> _terms;
// 0-based index of the function call within a CQL statement.
// Used to populate the cache of execution results while passing to
// another shard (handling `bounce_to_shard` messages) in LWT statements.
//
// The id is set only for the function calls that are a part of LWT
// statement restrictions for the partition key. Otherwise, the id is not
// set and the call is not considered when using or populating the cache.
std::optional<uint8_t> _id;
public:
function_call(shared_ptr<scalar_function> fun, std::vector<shared_ptr<term>> terms)
: _fun(std::move(fun)), _terms(std::move(terms)) {
}
virtual void fill_prepare_context(prepare_context& ctx) const override;
void set_id(std::optional<uint8_t> id) {
_id = id;
}
virtual shared_ptr<terminal> bind(const query_options& options) override;
virtual expr::expression to_expression() override;
public:
virtual bool contains_bind_marker() const override;
private:
cql3::raw_value_view bind_and_get_internal(const query_options& options);
};
::shared_ptr<term> prepare_function_call(const expr::function_call& fc, database& db, const sstring& keyspace, lw_shared_ptr<column_specification> receiver);
assignment_testable::test_result test_assignment_function_call(const cql3::expr::function_call& fc, database& db, const sstring& keyspace, const column_specification& receiver);
}
}

View File

@@ -20,8 +20,6 @@
*/
#include "functions.hh"
#include "function_call.hh"
#include "token_fct.hh"
#include "cql3/maps.hh"
#include "cql3/sets.hh"
@@ -66,10 +64,6 @@ bool as_json_function::requires_thread() const { return false; }
thread_local std::unordered_multimap<function_name, shared_ptr<function>> functions::_declared = init();
static bytes_opt execute_internal(cql_serialization_format sf, scalar_function& fun, std::vector<bytes_opt> params);
static bytes_opt execute(scalar_function& fun, std::vector<shared_ptr<term>> parameters);
static shared_ptr<terminal> make_terminal(shared_ptr<function> fun, cql3::raw_value result, cql_serialization_format sf);
void functions::clear_functions() noexcept {
functions::_declared = init();
}
@@ -447,226 +441,6 @@ functions::type_equals(const std::vector<data_type>& t1, const std::vector<data_
return t1 == t2;
}
void
function_call::fill_prepare_context(prepare_context& ctx) const {
if (ctx.is_processing_pk_restrictions() && !_fun->is_pure()) {
// Hacking around `const` specifier in the `collect_prepare_metadata`
// declaration since we also need to modify the current instance along
// with prepare metadata.
ctx.add_pk_function_call(static_pointer_cast<function_call>(
const_cast<function_call*>(this)->shared_from_this()));
}
for (auto&& t : _terms) {
t->fill_prepare_context(ctx);
}
}
shared_ptr<terminal>
function_call::bind(const query_options& options) {
return make_terminal(_fun, cql3::raw_value::make_value(bind_and_get_internal(options)), options.get_cql_serialization_format());
}
cql3::raw_value_view
function_call::bind_and_get_internal(const query_options& options) {
std::vector<bytes_opt> buffers;
buffers.reserve(_terms.size());
for (auto&& t : _terms) {
// For now, we don't allow nulls as argument as no existing function needs it and it
// simplify things.
auto val = expr::evaluate_to_raw_view(t, options);
if (!val) {
throw exceptions::invalid_request_exception(format("Invalid null value for argument to {}", *_fun));
}
buffers.push_back(to_bytes_opt(val));
}
if (_id) {
// Populate the cache only for LWT statements. Note that this code
// works only in places where `function_call::raw` AST nodes are
// created.
// These cases do not include selection clause in SELECT statement,
// hence no database inputs are possibly allowed to the functions
// evaluated here.
// We can cache every non-deterministic call here as this code branch
// acts the same way as if all arguments are equivalent to literal
// values at this point (already calculated).
auto val = options.find_cached_pk_function_call(*_id);
if (val) {
return raw_value_view::make_temporary(raw_value::make_value(*val));
}
}
auto result = execute_internal(options.get_cql_serialization_format(), *_fun, std::move(buffers));
if (_id) {
options.cache_pk_function_call(*_id, result);
}
return cql3::raw_value_view::make_temporary(cql3::raw_value::make_value(result));
}
expr::expression function_call::to_expression() {
std::vector<expr::expression> args;
args.reserve(_terms.size());
for (const ::shared_ptr<term>& t : _terms) {
args.emplace_back(expr::to_expression(t));
}
return expr::function_call {
.func = _fun,
.args = std::move(args),
.lwt_cache_id = _id,
};
}
static
bytes_opt
execute_internal(cql_serialization_format sf, scalar_function& fun, std::vector<bytes_opt> params) {
bytes_opt result = fun.execute(sf, params);
try {
// Check the method didn't lied on it's declared return type
if (result) {
fun.return_type()->validate(*result, sf);
}
return result;
} catch (marshal_exception& e) {
throw runtime_exception(format("Return of function {} ({}) is not a valid value for its declared return type {}",
fun, to_hex(result),
fun.return_type()->as_cql3_type()
));
}
}
bool
function_call::contains_bind_marker() const {
for (auto&& t : _terms) {
if (t->contains_bind_marker()) {
return true;
}
}
return false;
}
static
shared_ptr<terminal>
make_terminal(shared_ptr<function> fun, cql3::raw_value result, cql_serialization_format sf) {
return visit(*fun->return_type(), make_visitor(
[&] (const list_type_impl& ltype) -> shared_ptr<terminal> {
return make_shared<lists::value>(lists::value::from_serialized(result.to_view(), ltype, sf));
},
[&] (const set_type_impl& stype) -> shared_ptr<terminal> {
return make_shared<sets::value>(sets::value::from_serialized(result.to_view(), stype, sf));
},
[&] (const map_type_impl& mtype) -> shared_ptr<terminal> {
return make_shared<maps::value>(maps::value::from_serialized(result.to_view(), mtype, sf));
},
[&] (const user_type_impl& utype) -> shared_ptr<terminal> {
// TODO (kbraun): write a test for this case when User Defined Functions are implemented
return make_shared<user_types::value>(user_types::value::from_serialized(result.to_view(), utype));
},
[&] (const abstract_type& type) -> shared_ptr<terminal> {
if (type.is_collection()) {
throw std::runtime_error(format("function_call::make_terminal: unhandled collection type {}", type.name()));
}
return make_shared<constants::value>(std::move(result), fun->return_type());
}
));
}
::shared_ptr<term>
prepare_function_call(const expr::function_call& fc, database& db, const sstring& keyspace, lw_shared_ptr<column_specification> receiver) {
auto&& fun = std::visit(overloaded_functor{
[] (const shared_ptr<function>& func) {
return func;
},
[&] (const function_name& name) {
auto args = boost::copy_range<std::vector<::shared_ptr<assignment_testable>>>(fc.args | boost::adaptors::transformed(expr::as_assignment_testable));
auto fun = functions::functions::get(db, keyspace, name, args, receiver->ks_name, receiver->cf_name, receiver.get());
if (!fun) {
throw exceptions::invalid_request_exception(format("Unknown function {} called", name));
}
return fun;
},
}, fc.func);
if (fun->is_aggregate()) {
throw exceptions::invalid_request_exception("Aggregation function are not supported in the where clause");
}
// Can't use static_pointer_cast<> because function is a virtual base class of scalar_function
auto&& scalar_fun = dynamic_pointer_cast<scalar_function>(fun);
// Functions.get() will complain if no function "name" type check with the provided arguments.
// We still have to validate that the return type matches however
if (!receiver->type->is_value_compatible_with(*scalar_fun->return_type())) {
throw exceptions::invalid_request_exception(format("Type error: cannot assign result of function {} (type {}) to {} (type {})",
fun->name(), fun->return_type()->as_cql3_type(),
receiver->name, receiver->type->as_cql3_type()));
}
if (scalar_fun->arg_types().size() != fc.args.size()) {
throw exceptions::invalid_request_exception(format("Incorrect number of arguments specified for function {} (expected {:d}, found {:d})",
fun->name(), fun->arg_types().size(), fc.args.size()));
}
std::vector<shared_ptr<term>> parameters;
parameters.reserve(fc.args.size());
bool all_terminal = true;
for (size_t i = 0; i < fc.args.size(); ++i) {
auto&& t = prepare_term(fc.args[i], db, keyspace, functions::make_arg_spec(receiver->ks_name, receiver->cf_name, *scalar_fun, i));
if (dynamic_cast<non_terminal*>(t.get())) {
all_terminal = false;
}
parameters.push_back(t);
}
// If all parameters are terminal and the function is pure, we can
// evaluate it now, otherwise we'd have to wait execution time
if (all_terminal && scalar_fun->is_pure()) {
return make_terminal(scalar_fun, cql3::raw_value::make_value(execute(*scalar_fun, parameters)), query_options::DEFAULT.get_cql_serialization_format());
} else {
return ::make_shared<function_call>(scalar_fun, parameters);
}
}
static
bytes_opt
execute(scalar_function& fun, std::vector<shared_ptr<term>> parameters) {
std::vector<bytes_opt> buffers;
buffers.reserve(parameters.size());
for (auto&& t : parameters) {
assert(dynamic_cast<terminal*>(t.get()));
auto&& param = static_cast<terminal*>(t.get())->get(query_options::DEFAULT);
buffers.push_back(to_bytes_opt(param));
}
return execute_internal(cql_serialization_format::internal(), fun, buffers);
}
assignment_testable::test_result
test_assignment_function_call(const cql3::expr::function_call& fc, database& db, const sstring& keyspace, const column_specification& receiver) {
// Note: Functions.get() will return null if the function doesn't exist, or throw is no function matching
// the arguments can be found. We may get one of those if an undefined/wrong function is used as argument
// of another, existing, function. In that case, we return true here because we'll throw a proper exception
// later with a more helpful error message that if we were to return false here.
try {
auto&& fun = std::visit(overloaded_functor{
[&] (const function_name& name) {
auto args = boost::copy_range<std::vector<::shared_ptr<assignment_testable>>>(fc.args | boost::adaptors::transformed(expr::as_assignment_testable));
return functions::get(db, keyspace, name, args, receiver.ks_name, receiver.cf_name, &receiver);
},
[] (const shared_ptr<function>& func) {
return func;
},
}, fc.func);
if (fun && receiver.type == fun->return_type()) {
return assignment_testable::test_result::EXACT_MATCH;
} else if (!fun || receiver.type->is_value_compatible_with(*fun->return_type())) {
return assignment_testable::test_result::WEAKLY_ASSIGNABLE;
} else {
return assignment_testable::test_result::NOT_ASSIGNABLE;
}
} catch (exceptions::invalid_request_exception& e) {
return assignment_testable::test_result::WEAKLY_ASSIGNABLE;
}
}
}
}

View File

@@ -42,167 +42,9 @@ lists::uuid_index_spec_of(const column_specification& column) {
::make_shared<column_identifier>(format("uuid_idx({})", *column.name), true), uuid_type);
}
lists::value
lists::value::from_serialized(const raw_value_view& val, const list_type_impl& type, cql_serialization_format sf) {
try {
utils::chunked_vector<managed_bytes_opt> elements;
if (sf.collection_format_unchanged()) {
utils::chunked_vector<managed_bytes> tmp = val.with_value([sf] (const FragmentedView auto& v) {
return partially_deserialize_listlike(v, sf);
});
elements.reserve(tmp.size());
for (auto&& element : tmp) {
elements.emplace_back(std::move(element));
}
} else [[unlikely]] {
auto l = val.deserialize<list_type_impl::native_type>(type, sf);
elements.reserve(l.size());
for (auto&& element : l) {
// elements can be null in lists that represent a set of IN values
elements.push_back(element.is_null() ? managed_bytes_opt() : managed_bytes_opt(type.get_elements_type()->decompose(element)));
}
}
return value(std::move(elements), type.shared_from_this());
} catch (marshal_exception& e) {
throw exceptions::invalid_request_exception(e.what());
}
}
cql3::raw_value
lists::value::get(const query_options& options) {
return cql3::raw_value::make_value(get_with_protocol_version(cql_serialization_format::internal()));
}
managed_bytes
lists::value::get_with_protocol_version(cql_serialization_format sf) {
// Can't use boost::indirect_iterator, because optional is not an iterator
auto deref = [] (managed_bytes_opt& x) { return *x; };
return collection_type_impl::pack_fragmented(
boost::make_transform_iterator(_elements.begin(), deref),
boost::make_transform_iterator( _elements.end(), deref),
_elements.size(), sf);
}
bool
lists::value::equals(const list_type_impl& lt, const value& v) {
if (_elements.size() != v._elements.size()) {
return false;
}
return std::equal(_elements.begin(), _elements.end(),
v._elements.begin(),
[t = lt.get_elements_type()] (const managed_bytes_opt& e1, const managed_bytes_opt& e2) { return t->equal(*e1, *e2); });
}
sstring
lists::value::to_string() const {
std::ostringstream os;
os << "[";
bool is_first = true;
for (auto&& e : _elements) {
if (!is_first) {
os << ", ";
}
is_first = false;
os << to_hex(e);
}
os << "]";
return os.str();
}
bool
lists::delayed_value::contains_bind_marker() const {
// False since we don't support them in collection
return false;
}
void
lists::delayed_value::fill_prepare_context(prepare_context& ctx) const {
}
shared_ptr<terminal>
lists::delayed_value::bind(const query_options& options) {
utils::chunked_vector<managed_bytes_opt> buffers;
buffers.reserve(_elements.size());
for (auto&& t : _elements) {
auto bo = expr::evaluate_to_raw_view(t, options);
if (bo.is_null()) {
throw exceptions::invalid_request_exception("null is not supported inside collections");
}
if (bo.is_unset_value()) {
return constants::UNSET_VALUE;
}
buffers.push_back(bo.with_value([] (const FragmentedView auto& v) { return managed_bytes(v); }));
}
return ::make_shared<value>(buffers, _my_type);
}
shared_ptr<terminal>
lists::delayed_value::bind_ignore_null(const query_options& options) {
utils::chunked_vector<managed_bytes_opt> buffers;
buffers.reserve(_elements.size());
for (auto&& t : _elements) {
auto bo = expr::evaluate_to_raw_view(t, options);
if (bo.is_null()) {
continue;
}
if (bo.is_unset_value()) {
return constants::UNSET_VALUE;
}
buffers.push_back(bo.with_value([] (const FragmentedView auto& v) { return managed_bytes(v); }));
}
return ::make_shared<value>(buffers, _my_type);
}
expr::expression lists::delayed_value::to_expression() {
std::vector<expr::expression> new_elements;
new_elements.reserve(_elements.size());
for (shared_ptr<term>& e : _elements) {
new_elements.emplace_back(expr::to_expression(e));
}
return expr::collection_constructor {
.style = expr::collection_constructor::style_type::list,
.elements = std::move(new_elements),
.type = _my_type
};
}
::shared_ptr<terminal>
lists::marker::bind(const query_options& options) {
const auto& value = options.get_value_at(_bind_index);
auto& ltype = dynamic_cast<const list_type_impl&>(_receiver->type->without_reversed());
if (value.is_null()) {
return nullptr;
} else if (value.is_unset_value()) {
return constants::UNSET_VALUE;
} else {
try {
value.validate(ltype, options.get_cql_serialization_format());
return make_shared<lists::value>(value::from_serialized(value, ltype, options.get_cql_serialization_format()));
} catch (marshal_exception& e) {
throw exceptions::invalid_request_exception(
format("Exception while binding column {:s}: {:s}", _receiver->name->to_cql_string(), e.what()));
}
}
}
expr::expression lists::marker::to_expression() {
return expr::bind_variable {
.shape = expr::bind_variable::shape_type::scalar,
.bind_index = _bind_index,
.value_type = _receiver->type
};
}
void
lists::setter::execute(mutation& m, const clustering_key_prefix& prefix, const update_parameters& params) {
auto value = expr::evaluate(_t, params._options);
auto value = expr::evaluate(*_e, params._options);
execute(m, prefix, params, column, std::move(value));
}
@@ -227,9 +69,9 @@ lists::setter_by_index::requires_read() const {
}
void
lists::setter_by_index::fill_prepare_context(prepare_context& ctx) const {
lists::setter_by_index::fill_prepare_context(prepare_context& ctx) {
operation::fill_prepare_context(ctx);
_idx->fill_prepare_context(ctx);
expr::fill_prepare_context(_idx, ctx);
}
void
@@ -237,19 +79,19 @@ lists::setter_by_index::execute(mutation& m, const clustering_key_prefix& prefix
// we should not get here for frozen lists
assert(column.type->is_multi_cell()); // "Attempted to set an individual element on a frozen list";
auto index = expr::evaluate_to_raw_view(_idx, params._options);
auto index = expr::evaluate(_idx, params._options);
if (index.is_null()) {
throw exceptions::invalid_request_exception("Invalid null value for list index");
}
if (index.is_unset_value()) {
throw exceptions::invalid_request_exception("Invalid unset value for list index");
}
auto value = expr::evaluate_to_raw_view(_t, params._options);
auto value = expr::evaluate(*_e, params._options);
if (value.is_unset_value()) {
return;
}
auto idx = index.deserialize<int32_t>(*int32_type);
auto idx = index.view().deserialize<int32_t>(*int32_type);
auto&& existing_list_opt = params.get_prefetched_list(m.key(), prefix, column);
if (!existing_list_opt) {
throw exceptions::invalid_request_exception("Attempted to set an element on a list which is null");
@@ -266,11 +108,11 @@ lists::setter_by_index::execute(mutation& m, const clustering_key_prefix& prefix
bytes eidx = eidx_dv.type()->decompose(eidx_dv);
collection_mutation_description mut;
mut.cells.reserve(1);
if (!value) {
if (value.is_null()) {
mut.cells.emplace_back(std::move(eidx), params.make_dead_cell());
} else {
mut.cells.emplace_back(std::move(eidx),
params.make_cell(*ltype->value_comparator(), value, atomic_cell::collection_member::yes));
params.make_cell(*ltype->value_comparator(), value.view(), atomic_cell::collection_member::yes));
}
m.set_cell(prefix, column, mut.serialize(*ltype));
@@ -286,22 +128,28 @@ lists::setter_by_uuid::execute(mutation& m, const clustering_key_prefix& prefix,
// we should not get here for frozen lists
assert(column.type->is_multi_cell()); // "Attempted to set an individual element on a frozen list";
auto index = expr::evaluate_to_raw_view(_idx, params._options);
auto value = expr::evaluate_to_raw_view(_t, params._options);
auto index = expr::evaluate(_idx, params._options);
auto value = expr::evaluate(*_e, params._options);
if (!index) {
if (index.is_null()) {
throw exceptions::invalid_request_exception("Invalid null value for list index");
}
if (index.is_unset_value()) {
throw exceptions::invalid_request_exception("Invalid unset value for list index");
}
auto ltype = static_cast<const list_type_impl*>(column.type.get());
collection_mutation_description mut;
mut.cells.reserve(1);
if (!value) {
mut.cells.emplace_back(to_bytes(index), params.make_dead_cell());
if (value.is_null()) {
mut.cells.emplace_back(std::move(index.value).to_bytes(), params.make_dead_cell());
} else {
mut.cells.emplace_back(to_bytes(index), params.make_cell(*ltype->value_comparator(), value, atomic_cell::collection_member::yes));
mut.cells.emplace_back(
std::move(index.value).to_bytes(),
params.make_cell(*ltype->value_comparator(), value.view(), atomic_cell::collection_member::yes));
}
m.set_cell(prefix, column, mut.serialize(*ltype));
@@ -309,7 +157,7 @@ lists::setter_by_uuid::execute(mutation& m, const clustering_key_prefix& prefix,
void
lists::appender::execute(mutation& m, const clustering_key_prefix& prefix, const update_parameters& params) {
const expr::constant value = expr::evaluate(_t, params._options);
const expr::constant value = expr::evaluate(*_e, params._options);
if (value.is_unset_value()) {
return;
}
@@ -355,7 +203,7 @@ lists::do_append(const expr::constant& list_value,
if (list_value.is_null()) {
m.set_cell(prefix, column, params.make_dead_cell());
} else {
m.set_cell(prefix, column, params.make_cell(*column.type, list_value.value.to_view()));
m.set_cell(prefix, column, params.make_cell(*column.type, list_value.view()));
}
}
}
@@ -363,7 +211,7 @@ lists::do_append(const expr::constant& list_value,
void
lists::prepender::execute(mutation& m, const clustering_key_prefix& prefix, const update_parameters& params) {
assert(column.type->is_multi_cell()); // "Attempted to prepend to a frozen list";
expr::constant lvalue = expr::evaluate(_t, params._options);
expr::constant lvalue = expr::evaluate(*_e, params._options);
if (lvalue.is_null_or_unset()) {
return;
}
@@ -419,7 +267,7 @@ lists::discarder::execute(mutation& m, const clustering_key_prefix& prefix, cons
auto&& existing_list = params.get_prefetched_list(m.key(), prefix, column);
// We want to call bind before possibly returning to reject queries where the value provided is not a list.
expr::constant lvalue = expr::evaluate(_t, params._options);
expr::constant lvalue = expr::evaluate(*_e, params._options);
if (!existing_list) {
return;
@@ -468,7 +316,7 @@ lists::discarder_by_index::requires_read() const {
void
lists::discarder_by_index::execute(mutation& m, const clustering_key_prefix& prefix, const update_parameters& params) {
assert(column.type->is_multi_cell()); // "Attempted to delete an item by index from a frozen list";
expr::constant index = expr::evaluate(_t, params._options);
expr::constant index = expr::evaluate(*_e, params._options);
if (index.is_null()) {
throw exceptions::invalid_request_exception("Invalid null value for list index");
}
@@ -477,7 +325,7 @@ lists::discarder_by_index::execute(mutation& m, const clustering_key_prefix& pre
}
auto&& existing_list_opt = params.get_prefetched_list(m.key(), prefix, column);
int32_t idx = index.value.to_view().deserialize<int32_t>(*int32_type);
int32_t idx = index.view().deserialize<int32_t>(*int32_type);
if (!existing_list_opt) {
throw exceptions::invalid_request_exception("Attempted to delete an element from a list which is null");

View File

@@ -57,69 +57,11 @@ public:
static lw_shared_ptr<column_specification> index_spec_of(const column_specification&);
static lw_shared_ptr<column_specification> value_spec_of(const column_specification&);
static lw_shared_ptr<column_specification> uuid_index_spec_of(const column_specification&);
class value : public terminal, collection_terminal {
public:
utils::chunked_vector<managed_bytes_opt> _elements;
public:
explicit value(utils::chunked_vector<managed_bytes_opt> elements, data_type my_type)
: terminal(std::move(my_type)), _elements(std::move(elements)) {
}
static value from_serialized(const raw_value_view& v, const list_type_impl& type, cql_serialization_format sf);
virtual cql3::raw_value get(const query_options& options) override;
virtual managed_bytes get_with_protocol_version(cql_serialization_format sf) override;
bool equals(const list_type_impl& lt, const value& v);
const utils::chunked_vector<managed_bytes_opt>& get_elements() const;
virtual sstring to_string() const override;
friend class lists;
};
/**
* Basically similar to a Value, but with some non-pure function (that need
* to be evaluated at execution time) in it.
*
* Note: this would also work for a list with bind markers, but we don't support
* that because 1) it's not excessively useful and 2) we wouldn't have a good
* column name to return in the ColumnSpecification for those markers (not a
* blocker per-se but we don't bother due to 1)).
*/
class delayed_value : public non_terminal {
std::vector<shared_ptr<term>> _elements;
data_type _my_type;
public:
explicit delayed_value(std::vector<shared_ptr<term>> elements, data_type my_type)
: _elements(std::move(elements)), _my_type(std::move(my_type)) {
}
virtual bool contains_bind_marker() const override;
virtual void fill_prepare_context(prepare_context& ctx) const override;
virtual shared_ptr<terminal> bind(const query_options& options) override;
// Binds the value, but skips all nulls inside the list
virtual shared_ptr<terminal> bind_ignore_null(const query_options& options);
const std::vector<shared_ptr<term>>& get_elements() const {
return _elements;
}
virtual expr::expression to_expression() override;
};
/**
* A marker for List values and IN relations
*/
class marker : public abstract_marker {
public:
marker(int32_t bind_index, lw_shared_ptr<column_specification> receiver)
: abstract_marker{bind_index, std::move(receiver)}
{ }
virtual ::shared_ptr<terminal> bind(const query_options& options) override;
virtual expr::expression to_expression() override;
};
public:
class setter : public operation {
public:
setter(const column_definition& column, shared_ptr<term> t)
: operation(column, std::move(t)) {
setter(const column_definition& column, expr::expression e)
: operation(column, std::move(e)) {
}
virtual void execute(mutation& m, const clustering_key_prefix& prefix, const update_parameters& params) override;
static void execute(mutation& m, const clustering_key_prefix& prefix, const update_parameters& params, const column_definition& column, const expr::constant& value);
@@ -127,20 +69,20 @@ public:
class setter_by_index : public operation {
protected:
shared_ptr<term> _idx;
expr::expression _idx;
public:
setter_by_index(const column_definition& column, shared_ptr<term> idx, shared_ptr<term> t)
: operation(column, std::move(t)), _idx(std::move(idx)) {
setter_by_index(const column_definition& column, expr::expression idx, expr::expression e)
: operation(column, std::move(e)), _idx(std::move(idx)) {
}
virtual bool requires_read() const override;
virtual void fill_prepare_context(prepare_context& ctx) const override;
virtual void fill_prepare_context(prepare_context& ctx) override;
virtual void execute(mutation& m, const clustering_key_prefix& prefix, const update_parameters& params) override;
};
class setter_by_uuid : public setter_by_index {
public:
setter_by_uuid(const column_definition& column, shared_ptr<term> idx, shared_ptr<term> t)
: setter_by_index(column, std::move(idx), std::move(t)) {
setter_by_uuid(const column_definition& column, expr::expression idx, expr::expression e)
: setter_by_index(column, std::move(idx), std::move(e)) {
}
virtual bool requires_read() const override;
virtual void execute(mutation& m, const clustering_key_prefix& prefix, const update_parameters& params) override;
@@ -166,8 +108,8 @@ public:
class discarder : public operation {
public:
discarder(const column_definition& column, shared_ptr<term> t)
: operation(column, std::move(t)) {
discarder(const column_definition& column, expr::expression e)
: operation(column, std::move(e)) {
}
virtual bool requires_read() const override;
virtual void execute(mutation& m, const clustering_key_prefix& prefix, const update_parameters& params) override;
@@ -175,7 +117,7 @@ public:
class discarder_by_index : public operation {
public:
discarder_by_index(const column_definition& column, shared_ptr<term> idx)
discarder_by_index(const column_definition& column, expr::expression idx)
: operation(column, std::move(idx)) {
}
virtual bool requires_read() const override;

View File

@@ -41,182 +41,17 @@
#include "maps.hh"
#include "cql3/abstract_marker.hh"
#include "cql3/term.hh"
#include "operation.hh"
#include "update_parameters.hh"
#include "exceptions/exceptions.hh"
#include "cql3/cql3_type.hh"
#include "constants.hh"
#include "types/map.hh"
#include "cql3/tuples.hh"
namespace cql3 {
maps::value
maps::value::from_serialized(const raw_value_view& fragmented_value, const map_type_impl& type, cql_serialization_format sf) {
try {
// Collections have this small hack that validate cannot be called on a serialized object,
// but compose does the validation (so we're fine).
// FIXME: deserialize_for_native_protocol?!
auto m = fragmented_value.deserialize<map_type_impl::native_type>(type, sf);
std::map<managed_bytes, managed_bytes, serialized_compare> map(type.get_keys_type()->as_less_comparator());
if (sf.collection_format_unchanged()) {
std::vector<std::pair<managed_bytes, managed_bytes>> tmp = fragmented_value.with_value([sf] (const FragmentedView auto& v) {
return partially_deserialize_map(v, sf);
});
for (auto&& key_value : tmp) {
map.insert(std::move(key_value));
}
} else [[unlikely]] {
auto m = fragmented_value.deserialize<map_type_impl::native_type>(type, sf);
for (auto&& e : m) {
map.emplace(type.get_keys_type()->decompose(e.first),
type.get_values_type()->decompose(e.second));
}
}
return maps::value(std::move(map), type.shared_from_this());
} catch (marshal_exception& e) {
throw exceptions::invalid_request_exception(e.what());
}
}
cql3::raw_value
maps::value::get(const query_options& options) {
return cql3::raw_value::make_value(get_with_protocol_version(cql_serialization_format::internal()));
}
managed_bytes
maps::value::get_with_protocol_version(cql_serialization_format sf) {
//FIXME: share code with serialize_partially_deserialized_form
size_t len = collection_value_len(sf) * map.size() * 2 + collection_size_len(sf);
for (auto&& e : map) {
len += e.first.size() + e.second.size();
}
managed_bytes b(managed_bytes::initialized_later(), len);
managed_bytes_mutable_view out(b);
write_collection_size(out, map.size(), sf);
for (auto&& e : map) {
write_collection_value(out, sf, e.first);
write_collection_value(out, sf, e.second);
}
return b;
}
bool
maps::value::equals(const map_type_impl& mt, const value& v) {
return std::equal(map.begin(), map.end(),
v.map.begin(), v.map.end(),
[&mt] (auto&& e1, auto&& e2) {
return mt.get_keys_type()->compare(e1.first, e2.first) == 0
&& mt.get_values_type()->compare(e1.second, e2.second) == 0;
});
}
sstring
maps::value::to_string() const {
// FIXME:
abort();
}
bool
maps::delayed_value::contains_bind_marker() const {
// False since we don't support them in collection
return false;
}
void
maps::delayed_value::fill_prepare_context(prepare_context& ctx) const {
}
shared_ptr<terminal>
maps::delayed_value::bind(const query_options& options) {
const map_type_impl& my_map_type = dynamic_cast<const map_type_impl&>(_my_type->without_reversed());
serialized_compare comparator = my_map_type.get_keys_type()->as_less_comparator();
std::map<managed_bytes, managed_bytes, serialized_compare> buffers(std::move(comparator));
for (auto&& entry : _elements) {
auto&& key = entry.first;
auto&& value = entry.second;
// We don't support values > 64K because the serialization format encode the length as an unsigned short.
auto key_bytes = expr::evaluate_to_raw_view(key, options);
if (key_bytes.is_null()) {
throw exceptions::invalid_request_exception("null is not supported inside collections");
}
if (key_bytes.is_unset_value()) {
throw exceptions::invalid_request_exception("unset value is not supported inside collections");
}
if (key_bytes.size_bytes() > std::numeric_limits<uint16_t>::max()) {
throw exceptions::invalid_request_exception(format("Map key is too long. Map keys are limited to {:d} bytes but {:d} bytes keys provided",
std::numeric_limits<uint16_t>::max(),
key_bytes.size_bytes()));
}
auto value_bytes = expr::evaluate_to_raw_view(value, options);
if (value_bytes.is_null()) {
throw exceptions::invalid_request_exception("null is not supported inside collections");\
}
if (value_bytes.is_unset_value()) {
return constants::UNSET_VALUE;
}
buffers.emplace(*to_managed_bytes_opt(key_bytes), *to_managed_bytes_opt(value_bytes));
}
return ::make_shared<value>(std::move(buffers), _my_type);
}
expr::expression maps::delayed_value::to_expression() {
std::vector<expr::expression> new_elements;
new_elements.reserve(_elements.size());
const map_type_impl& mtype = dynamic_cast<const map_type_impl&>(_my_type->without_reversed());
data_type ttype = tuple_type_impl::get_instance({mtype.get_keys_type(), mtype.get_values_type()});
for (auto&& [key, value] : _elements) {
expr::expression key_expr = expr::to_expression(key);
expr::expression value_expr = expr::to_expression(value);
new_elements.emplace_back(expr::tuple_constructor{{std::move(key_expr), std::move(value_expr)}, std::move(ttype)});
}
return expr::collection_constructor {
.style = expr::collection_constructor::style_type::map,
.elements = std::move(new_elements),
.type = _my_type
};
}
::shared_ptr<terminal>
maps::marker::bind(const query_options& options) {
auto val = options.get_value_at(_bind_index);
if (val.is_null()) {
return nullptr;
}
if (val.is_unset_value()) {
return constants::UNSET_VALUE;
}
try {
val.validate(*_receiver->type, options.get_cql_serialization_format());
} catch (marshal_exception& e) {
throw exceptions::invalid_request_exception(
format("Exception while binding column {:s}: {:s}", _receiver->name->to_cql_string(), e.what()));
}
return ::make_shared<maps::value>(
maps::value::from_serialized(
val,
dynamic_cast<const map_type_impl&>(_receiver->type->without_reversed()),
options.get_cql_serialization_format()));
}
expr::expression maps::marker::to_expression() {
return expr::bind_variable {
.shape = expr::bind_variable::shape_type::scalar,
.bind_index = _bind_index,
.value_type = _receiver->type
};
}
void
maps::setter::execute(mutation& m, const clustering_key_prefix& row_key, const update_parameters& params) {
expr::constant value = expr::evaluate(_t, params._options);
expr::constant value = expr::evaluate(*_e, params._options);
execute(m, row_key, params, column, value);
}
@@ -235,30 +70,32 @@ maps::setter::execute(mutation& m, const clustering_key_prefix& row_key, const u
}
void
maps::setter_by_key::fill_prepare_context(prepare_context& ctx) const {
maps::setter_by_key::fill_prepare_context(prepare_context& ctx) {
operation::fill_prepare_context(ctx);
_k->fill_prepare_context(ctx);
expr::fill_prepare_context(_k, ctx);
}
void
maps::setter_by_key::execute(mutation& m, const clustering_key_prefix& prefix, const update_parameters& params) {
using exceptions::invalid_request_exception;
assert(column.type->is_multi_cell()); // "Attempted to set a value for a single key on a frozen map"m
auto key = expr::evaluate_to_raw_view(_k, params._options);
auto value = expr::evaluate_to_raw_view(_t, params._options);
auto key = expr::evaluate(_k, params._options);
auto value = expr::evaluate(*_e, params._options);
if (value.is_unset_value()) {
return;
}
if (key.is_unset_value()) {
throw invalid_request_exception("Invalid unset map key");
}
if (!key) {
if (key.is_null()) {
throw invalid_request_exception("Invalid null map key");
}
auto ctype = static_cast<const map_type_impl*>(column.type.get());
auto avalue = value ? params.make_cell(*ctype->get_values_type(), value, atomic_cell::collection_member::yes) : params.make_dead_cell();
auto avalue = !value.is_null() ?
params.make_cell(*ctype->get_values_type(), value.view(), atomic_cell::collection_member::yes)
: params.make_dead_cell();
collection_mutation_description update;
update.cells.emplace_back(to_bytes(key), std::move(avalue));
update.cells.emplace_back(std::move(key.value).to_bytes(), std::move(avalue));
m.set_cell(prefix, column, update.serialize(*ctype));
}
@@ -266,7 +103,7 @@ maps::setter_by_key::execute(mutation& m, const clustering_key_prefix& prefix, c
void
maps::putter::execute(mutation& m, const clustering_key_prefix& prefix, const update_parameters& params) {
assert(column.type->is_multi_cell()); // "Attempted to add items to a frozen map";
expr::constant value = expr::evaluate(_t, params._options);
expr::constant value = expr::evaluate(*_e, params._options);
if (!value.is_unset_value()) {
do_put(m, prefix, params, value, column);
}
@@ -293,7 +130,7 @@ maps::do_put(mutation& m, const clustering_key_prefix& prefix, const update_para
if (map_value.is_null()) {
m.set_cell(prefix, column, params.make_dead_cell());
} else {
m.set_cell(prefix, column, params.make_cell(*column.type, map_value.value.to_view()));
m.set_cell(prefix, column, params.make_cell(*column.type, map_value.view()));
}
}
}
@@ -301,7 +138,7 @@ maps::do_put(mutation& m, const clustering_key_prefix& prefix, const update_para
void
maps::discarder_by_key::execute(mutation& m, const clustering_key_prefix& prefix, const update_parameters& params) {
assert(column.type->is_multi_cell()); // "Attempted to delete a single key in a frozen map";
expr::constant key = expr::evaluate(_t, params._options);
expr::constant key = expr::evaluate(*_e, params._options);
if (key.is_null()) {
throw exceptions::invalid_request_exception("Invalid null map key");
}

View File

@@ -42,7 +42,6 @@
#pragma once
#include "cql3/abstract_marker.hh"
#include "cql3/term.hh"
#include "operation.hh"
#include "update_parameters.hh"
#include "constants.hh"
@@ -59,48 +58,10 @@ public:
static lw_shared_ptr<column_specification> key_spec_of(const column_specification& column);
static lw_shared_ptr<column_specification> value_spec_of(const column_specification& column);
class value : public terminal, collection_terminal {
public:
std::map<managed_bytes, managed_bytes, serialized_compare> map;
value(std::map<managed_bytes, managed_bytes, serialized_compare> map, data_type my_type)
: terminal(std::move(my_type)), map(std::move(map)) {
}
static value from_serialized(const raw_value_view& value, const map_type_impl& type, cql_serialization_format sf);
virtual cql3::raw_value get(const query_options& options) override;
virtual managed_bytes get_with_protocol_version(cql_serialization_format sf) override;
bool equals(const map_type_impl& mt, const value& v);
virtual sstring to_string() const override;
};
// See Lists.DelayedValue
class delayed_value : public non_terminal {
std::unordered_map<shared_ptr<term>, shared_ptr<term>> _elements;
data_type _my_type;
public:
delayed_value(std::unordered_map<shared_ptr<term>, shared_ptr<term>> elements, data_type my_type)
: _elements(std::move(elements)), _my_type(std::move(my_type)) {
}
virtual bool contains_bind_marker() const override;
virtual void fill_prepare_context(prepare_context& ctx) const override;
virtual shared_ptr<terminal> bind(const query_options& options) override;
virtual expr::expression to_expression() override;
};
class marker : public abstract_marker {
public:
marker(int32_t bind_index, lw_shared_ptr<column_specification> receiver)
: abstract_marker{bind_index, std::move(receiver)}
{ }
virtual ::shared_ptr<terminal> bind(const query_options& options) override;
virtual expr::expression to_expression() override;
};
class setter : public operation {
public:
setter(const column_definition& column, shared_ptr<term> t)
: operation(column, std::move(t)) {
setter(const column_definition& column, expr::expression e)
: operation(column, std::move(e)) {
}
virtual void execute(mutation& m, const clustering_key_prefix& row_key, const update_parameters& params) override;
@@ -108,19 +69,19 @@ public:
};
class setter_by_key : public operation {
const shared_ptr<term> _k;
expr::expression _k;
public:
setter_by_key(const column_definition& column, shared_ptr<term> k, shared_ptr<term> t)
: operation(column, std::move(t)), _k(std::move(k)) {
setter_by_key(const column_definition& column, expr::expression k, expr::expression e)
: operation(column, std::move(e)), _k(std::move(k)) {
}
virtual void fill_prepare_context(prepare_context& ctx) const override;
virtual void fill_prepare_context(prepare_context& ctx) override;
virtual void execute(mutation& m, const clustering_key_prefix& prefix, const update_parameters& params) override;
};
class putter : public operation {
public:
putter(const column_definition& column, shared_ptr<term> t)
: operation(column, std::move(t)) {
putter(const column_definition& column, expr::expression e)
: operation(column, std::move(e)) {
}
virtual void execute(mutation& m, const clustering_key_prefix& prefix, const update_parameters& params) override;
};
@@ -130,7 +91,7 @@ public:
class discarder_by_key : public operation {
public:
discarder_by_key(const column_definition& column, shared_ptr<term> k)
discarder_by_key(const column_definition& column, expr::expression k)
: operation(column, std::move(k)) {
}
virtual void execute(mutation& m, const clustering_key_prefix& prefix, const update_parameters& params) override;

View File

@@ -43,9 +43,6 @@
#include "cql3/expr/expression.hh"
#include "cql3/relation.hh"
#include "cql3/term.hh"
#include "cql3/tuples.hh"
#include "cql3/restrictions/multi_column_restriction.hh"
#include <ranges>
@@ -159,8 +156,8 @@ protected:
std::transform(rs.begin(), rs.end(), col_specs.begin(), [] (auto cs) {
return cs->column_specification;
});
auto t = to_term(col_specs, get_value(), db, schema->ks_name(), ctx);
return ::make_shared<restrictions::multi_column_restriction::EQ>(schema, rs, t);
auto e = to_expression(col_specs, get_value(), db, schema->ks_name(), ctx);
return ::make_shared<restrictions::multi_column_restriction::EQ>(schema, rs, std::move(e));
}
virtual shared_ptr<restrictions::restriction> new_IN_restriction(database& db, schema_ptr schema,
@@ -171,18 +168,18 @@ protected:
return cs->column_specification;
});
if (_in_marker) {
auto t = to_term(col_specs, get_value(), db, schema->ks_name(), ctx);
auto as_abstract_marker = static_pointer_cast<abstract_marker>(t);
return ::make_shared<restrictions::multi_column_restriction::IN_with_marker>(schema, rs, as_abstract_marker);
auto e = to_expression(col_specs, get_value(), db, schema->ks_name(), ctx);
auto bound_value_marker = expr::as<expr::bind_variable>(e);
return ::make_shared<restrictions::multi_column_restriction::IN_with_marker>(schema, rs, std::move(bound_value_marker));
} else {
std::vector<expr::expression> raws(_in_values.size());
std::copy(_in_values.begin(), _in_values.end(), raws.begin());
auto ts = to_terms(col_specs, raws, db, schema->ks_name(), ctx);
auto es = to_expressions(col_specs, raws, db, schema->ks_name(), ctx);
// Convert a single-item IN restriction to an EQ restriction
if (ts.size() == 1) {
return ::make_shared<restrictions::multi_column_restriction::EQ>(schema, rs, std::move(ts[0]));
if (es.size() == 1) {
return ::make_shared<restrictions::multi_column_restriction::EQ>(schema, rs, std::move(es[0]));
}
return ::make_shared<restrictions::multi_column_restriction::IN_with_values>(schema, rs, ts);
return ::make_shared<restrictions::multi_column_restriction::IN_with_values>(schema, rs, std::move(es));
}
}
@@ -194,8 +191,8 @@ protected:
std::transform(rs.begin(), rs.end(), col_specs.begin(), [] (auto cs) {
return cs->column_specification;
});
auto t = to_term(col_specs, get_value(), db, schema->ks_name(), ctx);
return ::make_shared<restrictions::multi_column_restriction::slice>(schema, rs, bound, inclusive, t, _mode);
auto e = to_expression(col_specs, get_value(), db, schema->ks_name(), ctx);
return ::make_shared<restrictions::multi_column_restriction::slice>(schema, rs, bound, inclusive, std::move(e), _mode);
}
virtual shared_ptr<restrictions::restriction> new_contains_restriction(database& db, schema_ptr schema,
@@ -215,12 +212,12 @@ protected:
return create_multi_column_relation(std::move(new_entities), _relation_type, _values_or_marker, _in_values, _in_marker);
}
virtual shared_ptr<term> to_term(const std::vector<lw_shared_ptr<column_specification>>& receivers,
const expr::expression& raw, database& db, const sstring& keyspace,
prepare_context& ctx) const override {
auto t = prepare_term_multi_column(raw, db, keyspace, receivers);
t->fill_prepare_context(ctx);
return t;
virtual expr::expression to_expression(const std::vector<lw_shared_ptr<column_specification>>& receivers,
const expr::expression& raw, database& db, const sstring& keyspace,
prepare_context& ctx) const override {
auto e = prepare_expression_multi_column(raw, db, keyspace, receivers);
expr::fill_prepare_context(e, ctx);
return e;
}
std::vector<const column_definition*> receivers(database& db, const schema& schema) {
@@ -245,11 +242,16 @@ protected:
return names;
}
template <typename T>
static sstring tuple_to_string(const std::vector<T>& items) {
return format("({})", join(", ", items));
}
virtual sstring to_string() const override {
sstring str = tuples::tuple_to_string(_entities);
sstring str = tuple_to_string(_entities);
if (is_IN()) {
str += " IN ";
str += !_in_marker ? "?" : tuples::tuple_to_string(_in_values);
str += !_in_marker ? "?" : tuple_to_string(_in_values);
return str;
}
return format("{} {} {}", str, _relation_type, *_values_or_marker);

View File

@@ -68,20 +68,20 @@ operation::set_element::prepare(database& db, const sstring& keyspace, const col
}
if (rtype->get_kind() == abstract_type::kind::list) {
auto&& lval = prepare_term(_value, db, keyspace, lists::value_spec_of(*receiver.column_specification));
auto&& lval = prepare_expression(_value, db, keyspace, lists::value_spec_of(*receiver.column_specification));
if (_by_uuid) {
auto&& idx = prepare_term(_selector, db, keyspace, lists::uuid_index_spec_of(*receiver.column_specification));
return make_shared<lists::setter_by_uuid>(receiver, idx, lval);
auto&& idx = prepare_expression(_selector, db, keyspace, lists::uuid_index_spec_of(*receiver.column_specification));
return make_shared<lists::setter_by_uuid>(receiver, std::move(idx), std::move(lval));
} else {
auto&& idx = prepare_term(_selector, db, keyspace, lists::index_spec_of(*receiver.column_specification));
return make_shared<lists::setter_by_index>(receiver, idx, lval);
auto&& idx = prepare_expression(_selector, db, keyspace, lists::index_spec_of(*receiver.column_specification));
return make_shared<lists::setter_by_index>(receiver, std::move(idx), std::move(lval));
}
} else if (rtype->get_kind() == abstract_type::kind::set) {
throw invalid_request_exception(format("Invalid operation ({}) for set column {}", to_string(receiver), receiver.name()));
} else if (rtype->get_kind() == abstract_type::kind::map) {
auto key = prepare_term(_selector, db, keyspace, maps::key_spec_of(*receiver.column_specification));
auto mval = prepare_term(_value, db, keyspace, maps::value_spec_of(*receiver.column_specification));
return make_shared<maps::setter_by_key>(receiver, key, mval);
auto key = prepare_expression(_selector, db, keyspace, maps::key_spec_of(*receiver.column_specification));
auto mval = prepare_expression(_value, db, keyspace, maps::value_spec_of(*receiver.column_specification));
return make_shared<maps::setter_by_key>(receiver, std::move(key), std::move(mval));
}
abort();
}
@@ -115,7 +115,7 @@ operation::set_field::prepare(database& db, const sstring& keyspace, const colum
format("UDT column {} does not have a field named {}", receiver.name_as_text(), *_field));
}
auto val = prepare_term(_value, db, keyspace, user_types::field_spec_of(*receiver.column_specification, *idx));
auto val = prepare_expression(_value, db, keyspace, user_types::field_spec_of(*receiver.column_specification, *idx));
return make_shared<user_types::setter_by_field>(receiver, *idx, std::move(val));
}
@@ -161,24 +161,24 @@ operation::addition::to_string(const column_definition& receiver) const {
shared_ptr<operation>
operation::addition::prepare(database& db, const sstring& keyspace, const column_definition& receiver) const {
auto v = prepare_term(_value, db, keyspace, receiver.column_specification);
auto v = prepare_expression(_value, db, keyspace, receiver.column_specification);
auto ctype = dynamic_pointer_cast<const collection_type_impl>(receiver.type);
if (!ctype) {
if (!receiver.is_counter()) {
throw exceptions::invalid_request_exception(format("Invalid operation ({}) for non counter column {}", to_string(receiver), receiver.name()));
}
return make_shared<constants::adder>(receiver, v);
return make_shared<constants::adder>(receiver, std::move(v));
} else if (!ctype->is_multi_cell()) {
throw exceptions::invalid_request_exception(format("Invalid operation ({}) for frozen collection column {}", to_string(receiver), receiver.name()));
}
if (ctype->get_kind() == abstract_type::kind::list) {
return make_shared<lists::appender>(receiver, v);
return make_shared<lists::appender>(receiver, std::move(v));
} else if (ctype->get_kind() == abstract_type::kind::set) {
return make_shared<sets::adder>(receiver, v);
return make_shared<sets::adder>(receiver, std::move(v));
} else if (ctype->get_kind() == abstract_type::kind::map) {
return make_shared<maps::putter>(receiver, v);
return make_shared<maps::putter>(receiver, std::move(v));
} else {
abort();
}
@@ -201,8 +201,8 @@ operation::subtraction::prepare(database& db, const sstring& keyspace, const col
if (!receiver.is_counter()) {
throw exceptions::invalid_request_exception(format("Invalid operation ({}) for non counter column {}", to_string(receiver), receiver.name()));
}
auto v = prepare_term(_value, db, keyspace, receiver.column_specification);
return make_shared<constants::subtracter>(receiver, v);
auto v = prepare_expression(_value, db, keyspace, receiver.column_specification);
return make_shared<constants::subtracter>(receiver, std::move(v));
}
if (!ctype->is_multi_cell()) {
throw exceptions::invalid_request_exception(
@@ -210,9 +210,9 @@ operation::subtraction::prepare(database& db, const sstring& keyspace, const col
}
if (ctype->get_kind() == abstract_type::kind::list) {
return make_shared<lists::discarder>(receiver, prepare_term(_value, db, keyspace, receiver.column_specification));
return make_shared<lists::discarder>(receiver, prepare_expression(_value, db, keyspace, receiver.column_specification));
} else if (ctype->get_kind() == abstract_type::kind::set) {
return make_shared<sets::discarder>(receiver, prepare_term(_value, db, keyspace, receiver.column_specification));
return make_shared<sets::discarder>(receiver, prepare_expression(_value, db, keyspace, receiver.column_specification));
} else if (ctype->get_kind() == abstract_type::kind::map) {
auto&& mtype = dynamic_pointer_cast<const map_type_impl>(ctype);
// The value for a map subtraction is actually a set
@@ -221,7 +221,7 @@ operation::subtraction::prepare(database& db, const sstring& keyspace, const col
receiver.column_specification->cf_name,
receiver.column_specification->name,
set_type_impl::get_instance(mtype->get_keys_type(), false));
return ::make_shared<sets::discarder>(receiver, prepare_term(_value, db, keyspace, std::move(vr)));
return ::make_shared<sets::discarder>(receiver, prepare_expression(_value, db, keyspace, std::move(vr)));
}
abort();
}
@@ -238,7 +238,7 @@ operation::prepend::to_string(const column_definition& receiver) const {
shared_ptr<operation>
operation::prepend::prepare(database& db, const sstring& keyspace, const column_definition& receiver) const {
auto v = prepare_term(_value, db, keyspace, receiver.column_specification);
auto v = prepare_expression(_value, db, keyspace, receiver.column_specification);
if (!dynamic_cast<const list_type_impl*>(receiver.type.get())) {
throw exceptions::invalid_request_exception(format("Invalid operation ({}) for non list column {}", to_string(receiver), receiver.name()));
@@ -257,7 +257,7 @@ operation::prepend::is_compatible_with(const std::unique_ptr<raw_update>& other)
::shared_ptr <operation>
operation::set_value::prepare(database& db, const sstring& keyspace, const column_definition& receiver) const {
auto v = prepare_term(_value, db, keyspace, receiver.column_specification);
auto v = prepare_expression(_value, db, keyspace, receiver.column_specification);
if (receiver.type->is_counter()) {
throw exceptions::invalid_request_exception(format("Cannot set the value of counter column {} (counters can only be incremented/decremented, not set)", receiver.name_as_text()));
@@ -266,21 +266,21 @@ operation::set_value::prepare(database& db, const sstring& keyspace, const colum
if (receiver.type->is_collection()) {
auto k = receiver.type->get_kind();
if (k == abstract_type::kind::list) {
return make_shared<lists::setter>(receiver, v);
return make_shared<lists::setter>(receiver, std::move(v));
} else if (k == abstract_type::kind::set) {
return make_shared<sets::setter>(receiver, v);
return make_shared<sets::setter>(receiver, std::move(v));
} else if (k == abstract_type::kind::map) {
return make_shared<maps::setter>(receiver, v);
return make_shared<maps::setter>(receiver, std::move(v));
} else {
abort();
}
}
if (receiver.type->is_user_type()) {
return make_shared<user_types::setter>(receiver, v);
return make_shared<user_types::setter>(receiver, std::move(v));
}
return ::make_shared<constants::setter>(receiver, v);
return ::make_shared<constants::setter>(receiver, std::move(v));
}
::shared_ptr <operation>
@@ -292,10 +292,10 @@ operation::set_counter_value_from_tuple_list::prepare(database& db, const sstrin
throw exceptions::invalid_request_exception(format("Column {} is not a counter", receiver.name_as_text()));
}
// We need to fake a column of list<tuple<...>> to prepare the value term
// We need to fake a column of list<tuple<...>> to prepare the value expression
auto & os = receiver.column_specification;
auto spec = make_lw_shared<cql3::column_specification>(os->ks_name, os->cf_name, os->name, counter_tuple_list_type);
auto v = prepare_term(_value, db, keyspace, spec);
auto v = prepare_expression(_value, db, keyspace, spec);
// Will not be used elsewhere, so make it local.
class counter_setter : public operation {
@@ -306,7 +306,7 @@ operation::set_counter_value_from_tuple_list::prepare(database& db, const sstrin
return true;
}
void execute(mutation& m, const clustering_key_prefix& prefix, const update_parameters& params) override {
expr::constant list_value = expr::evaluate(_t, params._options);
expr::constant list_value = expr::evaluate(*_e, params._options);
if (list_value.is_null()) {
throw std::invalid_argument("Invalid input data to counter set");
}
@@ -356,7 +356,7 @@ operation::set_counter_value_from_tuple_list::prepare(database& db, const sstrin
}
};
return make_shared<counter_setter>(receiver, v);
return make_shared<counter_setter>(receiver, std::move(v));
};
bool
@@ -380,13 +380,13 @@ operation::element_deletion::prepare(database& db, const sstring& keyspace, cons
}
auto ctype = static_pointer_cast<const collection_type_impl>(receiver.type);
if (ctype->get_kind() == abstract_type::kind::list) {
auto&& idx = prepare_term(_element, db, keyspace, lists::index_spec_of(*receiver.column_specification));
auto&& idx = prepare_expression(_element, db, keyspace, lists::index_spec_of(*receiver.column_specification));
return make_shared<lists::discarder_by_index>(receiver, std::move(idx));
} else if (ctype->get_kind() == abstract_type::kind::set) {
auto&& elt = prepare_term(_element, db, keyspace, sets::value_spec_of(*receiver.column_specification));
auto&& elt = prepare_expression(_element, db, keyspace, sets::value_spec_of(*receiver.column_specification));
return make_shared<sets::element_discarder>(receiver, std::move(elt));
} else if (ctype->get_kind() == abstract_type::kind::map) {
auto&& key = prepare_term(_element, db, keyspace, maps::key_spec_of(*receiver.column_specification));
auto&& key = prepare_expression(_element, db, keyspace, maps::key_spec_of(*receiver.column_specification));
return make_shared<maps::discarder_by_key>(receiver, std::move(key));
}
abort();

View File

@@ -44,7 +44,6 @@
#include <seastar/core/shared_ptr.hh>
#include "exceptions/exceptions.hh"
#include "database_fwd.hh"
#include "term.hh"
#include "update_parameters.hh"
#include "cql3/column_identifier.hh"
#include "cql3/expr/expression.hh"
@@ -77,14 +76,14 @@ public:
const column_definition& column;
protected:
// Term involved in the operation. In theory this should not be here since some operation
// may require none of more than one term, but most need 1 so it simplify things a bit.
const ::shared_ptr<term> _t;
// Value involved in the operation. In theory this should not be here since some operation
// may require none of more than one expression, but most need 1 so it simplify things a bit.
std::optional<expr::expression> _e;
public:
operation(const column_definition& column_, ::shared_ptr<term> t)
operation(const column_definition& column_, std::optional<expr::expression> e)
: column{column_}
, _t{t}
, _e(std::move(e))
{ }
virtual ~operation() {}
@@ -107,9 +106,9 @@ public:
* @param meta the list of column specification where to collect the
* bind variables of this term in.
*/
virtual void fill_prepare_context(prepare_context& ctx) const {
if (_t) {
_t->fill_prepare_context(ctx);
virtual void fill_prepare_context(prepare_context& ctx) {
if (_e.has_value()) {
expr::fill_prepare_context(*_e, ctx);
}
}
@@ -140,7 +139,7 @@ public:
* Operation.
*
* @param receiver the "column" this operation applies to. Note that
* contrarly to the method of same name in Term.Raw, the receiver should always
* contrarly to the method of same name in raw expression, the receiver should always
* be a true column.
* @return the prepared update operation.
*/

View File

@@ -42,7 +42,6 @@
#include "cql3/prepare_context.hh"
#include "cql3/column_identifier.hh"
#include "cql3/column_specification.hh"
#include "cql3/functions/function_call.hh"
namespace cql3 {
@@ -98,18 +97,23 @@ void prepare_context::set_bound_variables(const std::vector<shared_ptr<column_id
_target_columns.resize(bn_size);
}
prepare_context::function_calls_t& prepare_context::pk_function_calls() {
return _pk_fn_calls;
void prepare_context::clear_pk_function_calls_cache() {
for (::shared_ptr<std::optional<uint8_t>>& cache_id : _pk_function_calls_cache_ids) {
if (cache_id.get() != nullptr) {
*cache_id = std::nullopt;
}
}
}
void prepare_context::add_pk_function_call(::shared_ptr<cql3::functions::function_call> fn) {
void prepare_context::add_pk_function_call(expr::function_call& fn) {
constexpr auto fn_limit = std::numeric_limits<uint8_t>::max();
if (_pk_fn_calls.size() == fn_limit) {
if (_pk_function_calls_cache_ids.size() == fn_limit) {
throw exceptions::invalid_request_exception(
format("Too many function calls within one statement. Max supported number is {}", fn_limit));
}
fn->set_id(_pk_fn_calls.size());
_pk_fn_calls.emplace_back(std::move(fn));
fn.lwt_cache_id = ::make_shared<std::optional<uint8_t>>(_pk_function_calls_cache_ids.size());
_pk_function_calls_cache_ids.emplace_back(fn.lwt_cache_id);
}

View File

@@ -47,6 +47,7 @@
#include <optional>
#include <vector>
#include <stddef.h>
#include "cql3/expr/expression.hh"
class schema;
@@ -66,10 +67,10 @@ private:
std::vector<shared_ptr<column_identifier>> _variable_names;
std::vector<lw_shared_ptr<column_specification>> _specs;
std::vector<lw_shared_ptr<column_specification>> _target_columns;
// A list of pointers to prepared `function_call` AST nodes, that
// A list of pointers to prepared `function_call` cache ids, that
// participate in partition key ranges computation within an LWT statement.
using function_calls_t = std::vector<::shared_ptr<cql3::functions::function_call>>;
function_calls_t _pk_fn_calls;
std::vector<::shared_ptr<std::optional<uint8_t>>> _pk_function_calls_cache_ids;
// The flag denoting whether the context is currently in partition key
// processing mode (inside query restrictions AST nodes). If set to true,
// then every `function_call` instance will be recorded in the context and
@@ -93,11 +94,11 @@ public:
void set_bound_variables(const std::vector<shared_ptr<column_identifier>>& prepare_meta);
function_calls_t& pk_function_calls();
void clear_pk_function_calls_cache();
// Record a new function call, which evaluates a partition key constraint.
// Also automatically assigns an id to the AST node for caching purposes.
void add_pk_function_call(::shared_ptr<cql3::functions::function_call> fn);
void add_pk_function_call(cql3::expr::function_call& fn);
// Inform the context object that it has started or ended processing the
// partition key part of statement restrictions.

View File

@@ -46,7 +46,6 @@
#include "prepare_context.hh"
#include "restrictions/restriction.hh"
#include "statements/bound.hh"
#include "term.hh"
#include "expr/expression.hh"
namespace cql3 {
@@ -240,41 +239,42 @@ public:
protected:
/**
* Converts the specified <code>Raw</code> into a <code>Term</code>.
* Converts the specified <code>Raw</code> into an <code>Expression</code>.
* @param receivers the columns to which the values must be associated at
* @param raw the raw term to convert
* @param raw the raw expression to convert
* @param keyspace the keyspace name
* @param boundNames the variables specification where to collect the bind variables
*
* @return the <code>Term</code> corresponding to the specified <code>Raw</code>
* @throws InvalidRequestException if the <code>Raw</code> term is not valid
* @return the <code>Expression</code> corresponding to the specified <code>Raw</code>
* @throws InvalidRequestException if the <code>Raw</code> expression is not valid
*/
virtual ::shared_ptr<term> to_term(const std::vector<lw_shared_ptr<column_specification>>& receivers,
const expr::expression& raw,
database& db,
const sstring& keyspace,
prepare_context& ctx) const = 0;
virtual expr::expression to_expression(const std::vector<lw_shared_ptr<column_specification>>& receivers,
const expr::expression& raw,
database& db,
const sstring& keyspace,
prepare_context& ctx) const = 0;
/**
* Converts the specified <code>Raw</code> terms into a <code>Term</code>s.
* Converts the specified <code>Raw</code> expressions into <code>expressions</code>s.
* @param receivers the columns to which the values must be associated at
* @param raws the raw terms to convert
* @param raws the raw expressions to convert
* @param keyspace the keyspace name
* @param boundNames the variables specification where to collect the bind variables
*
* @return the <code>Term</code>s corresponding to the specified <code>Raw</code> terms
* @throws InvalidRequestException if the <code>Raw</code> terms are not valid
* @return the <code>Expression</code>s corresponding to the specified <code>Raw</code> expressions
* @throws InvalidRequestException if the <code>Raw</code> expressions are not valid
*/
std::vector<::shared_ptr<term>> to_terms(const std::vector<lw_shared_ptr<column_specification>>& receivers,
const std::vector<expr::expression>& raws,
database& db,
const sstring& keyspace,
prepare_context& ctx) const {
std::vector<::shared_ptr<term>> terms;
std::vector<expr::expression> to_expressions(const std::vector<lw_shared_ptr<column_specification>>& receivers,
const std::vector<expr::expression>& raws,
database& db,
const sstring& keyspace,
prepare_context& ctx) const {
std::vector<expr::expression> expressions;
expressions.reserve(raws.size());
for (const auto& r : raws) {
terms.emplace_back(to_term(receivers, r, db, keyspace, ctx));
expressions.emplace_back(to_expression(receivers, r, db, keyspace, ctx));
}
return terms;
return expressions;
}
/**

View File

@@ -42,7 +42,6 @@
#pragma once
#include "cql3/restrictions/restriction.hh"
#include "cql3/term.hh"
#include <seastar/core/shared_ptr.hh>
#include "to_string.hh"
#include "exceptions/exceptions.hh"
@@ -52,36 +51,27 @@ namespace cql3 {
namespace restrictions {
class term_slice final {
class bounds_slice final {
private:
struct bound {
bool inclusive;
::shared_ptr<term> t;
std::optional<expr::expression> value;
};
bound _bounds[2];
private:
term_slice(::shared_ptr<term> start, bool include_start, ::shared_ptr<term> end, bool include_end)
bounds_slice(std::optional<expr::expression> start, bool include_start,
std::optional<expr::expression> end, bool include_end)
: _bounds{{include_start, std::move(start)}, {include_end, std::move(end)}}
{ }
public:
static term_slice new_instance(statements::bound bound, bool include, ::shared_ptr<term> term) {
static bounds_slice new_instance(statements::bound bound, bool include, std::optional<expr::expression> value) {
if (is_start(bound)) {
return term_slice(std::move(term), include, {}, false);
return bounds_slice(std::move(value), include, std::nullopt, false);
} else {
return term_slice({}, false, std::move(term), include);
return bounds_slice(std::nullopt, false, std::move(value), include);
}
}
/**
* Returns the boundary value.
*
* @param bound the boundary type
* @return the boundary value
*/
::shared_ptr<term> bound(statements::bound b) const {
return _bounds[get_idx(b)].t;
}
/**
* Checks if this slice has a boundary for the specified type.
*
@@ -89,7 +79,7 @@ public:
* @return <code>true</code> if this slice has a boundary for the specified type, <code>false</code> otherwise.
*/
bool has_bound(statements::bound b) const {
return bool(_bounds[get_idx(b)].t);
return _bounds[get_idx(b)].value.has_value();
}
/**
@@ -100,7 +90,7 @@ public:
* <code>false</code> otherwise.
*/
bool is_inclusive(statements::bound b) const {
return !_bounds[get_idx(b)].t || _bounds[get_idx(b)].inclusive;
return !_bounds[get_idx(b)].value.has_value() || _bounds[get_idx(b)].inclusive;
}
/**
@@ -108,7 +98,7 @@ public:
*
* @param other the slice to merge with
*/
void merge(const term_slice& other) {
void merge(const bounds_slice& other) {
if (has_bound(statements::bound::START)) {
assert(!other.has_bound(statements::bound::START));
_bounds[get_idx(statements::bound::END)] = other._bounds[get_idx(statements::bound::END)];
@@ -134,15 +124,15 @@ public:
}
sstring to_string() const {
static auto print_term = [] (::shared_ptr<term> t) -> sstring {
return t ? t->to_string() : "null";
static auto print_value = [] (const std::optional<expr::expression>& v) -> sstring {
return v.has_value() ? expr::to_string(*v) : "none";
};
return format("({} {}, {} {})",
_bounds[0].inclusive ? ">=" : ">", print_term(_bounds[0].t),
_bounds[1].inclusive ? "<=" : "<", print_term(_bounds[1].t));
_bounds[0].inclusive ? ">=" : ">", print_value(_bounds[0].value),
_bounds[1].inclusive ? "<=" : "<", print_value(_bounds[1].value));
}
friend std::ostream& operator<<(std::ostream& out, const term_slice& slice) {
friend std::ostream& operator<<(std::ostream& out, const bounds_slice& slice) {
return out << slice.to_string();
}

View File

@@ -41,7 +41,6 @@
#pragma once
#include "cql3/tuples.hh"
#include "cql3/statements/request_validations.hh"
#include "cql3/restrictions/primary_key_restrictions.hh"
#include "cql3/statements/request_validations.hh"
@@ -50,6 +49,7 @@
#include "cql3/lists.hh"
#include "cql3/expr/expression.hh"
#include "types/list.hh"
#include "types/tuple.hh"
namespace cql3 {
@@ -196,9 +196,9 @@ public:
class multi_column_restriction::EQ final : public multi_column_restriction {
private:
::shared_ptr<term> _value;
expr::expression _value;
public:
EQ(schema_ptr schema, std::vector<const column_definition*> defs, ::shared_ptr<term> value)
EQ(schema_ptr schema, std::vector<const column_definition*> defs, expr::expression value)
: multi_column_restriction(schema, std::move(defs))
, _value(std::move(value))
{
@@ -309,11 +309,11 @@ public:
*/
class multi_column_restriction::IN_with_values final : public multi_column_restriction::IN {
private:
std::vector<::shared_ptr<term>> _values;
std::vector<expr::expression> _value;
public:
IN_with_values(schema_ptr schema, std::vector<const column_definition*> defs, std::vector<::shared_ptr<term>> value)
IN_with_values(schema_ptr schema, std::vector<const column_definition*> defs, std::vector<expr::expression> value)
: multi_column_restriction::IN(schema, std::move(defs))
, _values(std::move(value))
, _value(std::move(value))
{
std::vector<data_type> column_types;
column_types.reserve(defs.size());
@@ -325,11 +325,17 @@ public:
data_type list_elements_type = tuple_type_impl::get_instance(std::move(column_types));
data_type in_list_type = list_type_impl::get_instance(std::move(list_elements_type), false);
expr::collection_constructor values_list {
.style = expr::collection_constructor::style_type::list,
.elements = _value,
.type = std::move(in_list_type)
};
using namespace expr;
expression = binary_operator{
column_definitions_as_tuple_constructor(_column_defs),
oper_t::IN,
::make_shared<lists::delayed_value>(_values, std::move(in_list_type))};
std::move(values_list)};
}
};
@@ -340,37 +346,37 @@ public:
*/
class multi_column_restriction::IN_with_marker final : public multi_column_restriction::IN {
private:
shared_ptr<abstract_marker> _marker;
expr::bind_variable _marker;
public:
IN_with_marker(schema_ptr schema, std::vector<const column_definition*> defs, shared_ptr<abstract_marker> marker)
IN_with_marker(schema_ptr schema, std::vector<const column_definition*> defs, expr::bind_variable marker)
: IN(schema, std::move(defs)), _marker(marker) {
using namespace expr;
expression = binary_operator{
column_definitions_as_tuple_constructor(_column_defs),
oper_t::IN,
std::move(marker)};
expr::expression(std::move(marker))};
}
};
class multi_column_restriction::slice final : public multi_column_restriction {
using restriction_shared_ptr = ::shared_ptr<clustering_key_restrictions>;
using mode = expr::comparison_order;
term_slice _slice;
bounds_slice _slice;
mode _mode;
slice(schema_ptr schema, std::vector<const column_definition*> defs, term_slice slice, mode m)
slice(schema_ptr schema, std::vector<const column_definition*> defs, bounds_slice slice, mode m)
: multi_column_restriction(schema, std::move(defs))
, _slice(slice)
, _mode(m)
{ }
public:
slice(schema_ptr schema, std::vector<const column_definition*> defs, statements::bound bound, bool inclusive, shared_ptr<term> term, mode m = mode::cql)
: slice(schema, defs, term_slice::new_instance(bound, inclusive, term), m)
slice(schema_ptr schema, std::vector<const column_definition*> defs, statements::bound bound, bool inclusive, expr::expression e, mode m = mode::cql)
: slice(schema, defs, bounds_slice::new_instance(bound, inclusive, e), m)
{
expression = expr::binary_operator{
column_definitions_as_tuple_constructor(defs),
expr::pick_operator(bound, inclusive),
std::move(term),
std::move(e),
m};
}
@@ -472,17 +478,16 @@ private:
*/
::shared_ptr<restriction> make_single_column_restriction(std::optional<cql3::statements::bound> bound, bool inclusive,
std::size_t column_pos, const managed_bytes_opt& value) const {
::shared_ptr<cql3::term> term =
::make_shared<cql3::constants::value>(cql3::raw_value::make_value(value), _column_defs[column_pos]->type);
expr::expression e = expr::constant(cql3::raw_value::make_value(value), _column_defs[column_pos]->type);
using namespace expr;
if (!bound){
auto r = ::make_shared<cql3::restrictions::single_column_restriction>(*_column_defs[column_pos]);
r->expression = binary_operator{column_value{_column_defs[column_pos]}, expr::oper_t::EQ, std::move(term)};
r->expression = binary_operator{column_value{_column_defs[column_pos]}, expr::oper_t::EQ, std::move(e)};
return r;
} else {
auto r = ::make_shared<cql3::restrictions::single_column_restriction>(*_column_defs[column_pos]);
r->expression = binary_operator{
column_value(_column_defs[column_pos]), pick_operator(*bound, inclusive), std::move(term)};
column_value(_column_defs[column_pos]), pick_operator(*bound, inclusive), std::move(e)};
return r;
}
}

View File

@@ -149,7 +149,7 @@ public:
}
virtual void merge_with(::shared_ptr<restriction> restriction) override {
if (find_atom(restriction->expression, [] (const expr::binary_operator& b) {
if (find_binop(restriction->expression, [] (const expr::binary_operator& b) {
return expr::is<expr::tuple_constructor>(b.lhs);
})) {
throw exceptions::invalid_request_exception(

View File

@@ -44,8 +44,7 @@
#include <optional>
#include "cql3/restrictions/restriction.hh"
#include "cql3/restrictions/term_slice.hh"
#include "cql3/term.hh"
#include "cql3/restrictions/bounds_slice.hh"
#include "cql3/abstract_marker.hh"
#include <seastar/core/shared_ptr.hh>
#include "schema_fwd.hh"

View File

@@ -41,7 +41,6 @@
#include "cql3/selection/selection.hh"
#include "cql3/single_column_relation.hh"
#include "cql3/statements/request_validations.hh"
#include "cql3/tuples.hh"
#include "types/list.hh"
#include "types/map.hh"
#include "types/set.hh"
@@ -452,7 +451,7 @@ statement_restrictions::statement_restrictions(database& db,
const expr::allow_local_index allow_local(
!_partition_key_restrictions->has_unrestricted_components(*_schema)
&& _partition_key_restrictions->is_all_eq());
_has_multi_column = find_atom(_clustering_columns_restrictions->expression, expr::is_multi_column);
_has_multi_column = find_binop(_clustering_columns_restrictions->expression, expr::is_multi_column);
_has_queriable_ck_index = _clustering_columns_restrictions->has_supporting_index(sim, allow_local)
&& !type.is_delete();
_has_queriable_pk_index = _partition_key_restrictions->has_supporting_index(sim, allow_local)
@@ -499,7 +498,7 @@ statement_restrictions::statement_restrictions(database& db,
if (_uses_secondary_indexing || _clustering_columns_restrictions->needs_filtering(*_schema)) {
_index_restrictions.push_back(_clustering_columns_restrictions);
} else if (find_atom(_clustering_columns_restrictions->expression, expr::is_on_collection)) {
} else if (find_binop(_clustering_columns_restrictions->expression, expr::is_on_collection)) {
fail(unimplemented::cause::INDEXES);
#if 0
_index_restrictions.push_back(new Forwardingprimary_key_restrictions() {
@@ -689,7 +688,7 @@ void statement_restrictions::process_clustering_columns_restrictions(bool for_vi
return;
}
if (find_atom(_clustering_columns_restrictions->expression, expr::is_on_collection)
if (find_binop(_clustering_columns_restrictions->expression, expr::is_on_collection)
&& !_has_queriable_ck_index && !allow_filtering) {
throw exceptions::invalid_request_exception(
"Cannot restrict clustering columns by a CONTAINS relation without a secondary index or filtering");
@@ -764,7 +763,7 @@ dht::partition_range_vector partition_ranges_from_singles(
std::vector<std::vector<managed_bytes>> column_values(schema.partition_key_size());
size_t product_size = 1;
for (const auto& e : expressions) {
if (const auto arbitrary_binop = find_atom(e, [] (const binary_operator&) { return true; })) {
if (const auto arbitrary_binop = find_binop(e, [] (const binary_operator&) { return true; })) {
if (auto cv = expr::as_if<expr::column_value>(&arbitrary_binop->lhs)) {
const value_set vals = possible_lhs_values(cv->col, e, options);
if (auto lst = std::get_if<value_list>(&vals)) {
@@ -1428,7 +1427,7 @@ std::vector<query::clustering_range> statement_restrictions::get_clustering_boun
if (_clustering_prefix_restrictions.empty()) {
return {query::clustering_range::make_open_ended_both_sides()};
}
if (find_atom(_clustering_prefix_restrictions[0], expr::is_multi_column)) {
if (find_binop(_clustering_prefix_restrictions[0], expr::is_multi_column)) {
bool all_natural = true, all_reverse = true; ///< Whether column types are reversed or natural.
for (auto& r : _clustering_prefix_restrictions) { // TODO: move to constructor, do only once.
using namespace expr;
@@ -1617,7 +1616,7 @@ void statement_restrictions::prepare_indexed_global(const schema& idx_tbl_schema
oper_t::EQ,
// TODO: This should be a unique marker whose value we set at execution time. There is currently no
// handy mechanism for doing that in query_options.
::make_shared<constants::value>(raw_value::make_unset_value(), token_column->type));
expr::constant::make_unset_value(token_column->type));
}
void statement_restrictions::prepare_indexed_local(const schema& idx_tbl_schema) {
@@ -1647,11 +1646,11 @@ void statement_restrictions::prepare_indexed_local(const schema& idx_tbl_schema)
void statement_restrictions::add_clustering_restrictions_to_idx_ck_prefix(const schema& idx_tbl_schema) {
for (const auto& e : _clustering_prefix_restrictions) {
if (find_atom(_clustering_prefix_restrictions[0], expr::is_multi_column)) {
if (find_binop(_clustering_prefix_restrictions[0], expr::is_multi_column)) {
// TODO: We could handle single-element tuples, eg. `(c)>=(123)`.
break;
}
const auto any_binop = find_atom(e, [] (auto&&) { return true; });
const auto any_binop = find_binop(e, [] (auto&&) { return true; });
if (!any_binop) {
break;
}
@@ -1689,8 +1688,8 @@ std::vector<query::clustering_range> statement_restrictions::get_global_index_cl
}
// WARNING: We must not yield to another fiber from here until the function's end, lest this RHS be
// overwritten.
const_cast<::shared_ptr<term>&>(expr::as<binary_operator>((*_idx_tbl_ck_prefix)[0]).rhs) =
::make_shared<constants::value>(raw_value::make_value(*token_bytes), token_column.type);
const_cast<expr::expression&>(expr::as<binary_operator>((*_idx_tbl_ck_prefix)[0]).rhs) =
expr::constant(raw_value::make_value(*token_bytes), token_column.type);
// Multi column restrictions are not added to _idx_tbl_ck_prefix, they are handled later by filtering.
return get_single_column_clustering_bounds(options, idx_tbl_schema, *_idx_tbl_ck_prefix);
@@ -1733,8 +1732,8 @@ sstring statement_restrictions::to_string() const {
}
static bool has_eq_null(const query_options& options, const expression& expr) {
return find_atom(expr, [&] (const binary_operator& binop) {
return binop.op == oper_t::EQ && !evaluate_to_raw_view(binop.rhs, options);
return find_binop(expr, [&] (const binary_operator& binop) {
return binop.op == oper_t::EQ && evaluate(binop.rhs, options).is_null();
});
}
@@ -1742,6 +1741,5 @@ bool statement_restrictions::range_or_slice_eq_null(const query_options& options
return boost::algorithm::any_of(_partition_range_restrictions, std::bind_front(has_eq_null, options))
|| boost::algorithm::any_of(_clustering_prefix_restrictions, std::bind_front(has_eq_null, options));
}
} // namespace restrictions
} // namespace cql3

View File

@@ -43,7 +43,7 @@
#include "restriction.hh"
#include "primary_key_restrictions.hh"
#include "exceptions/exceptions.hh"
#include "term_slice.hh"
#include "bounds_slice.hh"
#include "keys.hh"
class column_definition;

View File

@@ -26,157 +26,9 @@
#include "types/set.hh"
namespace cql3 {
sets::value
sets::value::from_serialized(const raw_value_view& val, const set_type_impl& type, cql_serialization_format sf) {
try {
std::set<managed_bytes, serialized_compare> elements(type.get_elements_type()->as_less_comparator());
if (sf.collection_format_unchanged()) {
utils::chunked_vector<managed_bytes> tmp = val.with_value([sf] (const FragmentedView auto& v) {
return partially_deserialize_listlike(v, sf);
});
for (auto&& element : tmp) {
elements.insert(std::move(element));
}
} else [[unlikely]] {
auto s = val.deserialize<set_type_impl::native_type>(type, sf);
for (auto&& element : s) {
elements.insert(elements.end(), managed_bytes(type.get_elements_type()->decompose(element)));
}
}
return value(std::move(elements), type.shared_from_this());
} catch (marshal_exception& e) {
throw exceptions::invalid_request_exception(e.what());
}
}
cql3::raw_value
sets::value::get(const query_options& options) {
return cql3::raw_value::make_value(get_with_protocol_version(cql_serialization_format::internal()));
}
managed_bytes
sets::value::get_with_protocol_version(cql_serialization_format sf) {
return collection_type_impl::pack_fragmented(_elements.begin(), _elements.end(),
_elements.size(), sf);
}
bool
sets::value::equals(const set_type_impl& st, const value& v) {
if (_elements.size() != v._elements.size()) {
return false;
}
auto&& elements_type = st.get_elements_type();
return std::equal(_elements.begin(), _elements.end(),
v._elements.begin(),
[elements_type] (managed_bytes_view v1, managed_bytes_view v2) {
return elements_type->equal(v1, v2);
});
}
sstring
sets::value::to_string() const {
sstring result = "{";
bool first = true;
for (auto&& e : _elements) {
if (!first) {
result += ", ";
}
first = true;
result += to_hex(e);
}
result += "}";
return result;
}
bool
sets::delayed_value::contains_bind_marker() const {
// False since we don't support them in collection
return false;
}
void
sets::delayed_value::fill_prepare_context(prepare_context& ctx) const {
}
shared_ptr<terminal>
sets::delayed_value::bind(const query_options& options) {
const set_type_impl& my_set_type = dynamic_cast<const set_type_impl&>(_my_type->without_reversed());
std::set<managed_bytes, serialized_compare> buffers(my_set_type.get_elements_type()->as_less_comparator());
for (auto&& t : _elements) {
auto b = expr::evaluate_to_raw_view(t, options);
if (b.is_null()) {
throw exceptions::invalid_request_exception("null is not supported inside collections");
}
if (b.is_unset_value()) {
return constants::UNSET_VALUE;
}
// We don't support value > 64K because the serialization format encode the length as an unsigned short.
if (b.size_bytes() > std::numeric_limits<uint16_t>::max()) {
throw exceptions::invalid_request_exception(format("Set value is too long. Set values are limited to {:d} bytes but {:d} bytes value provided",
std::numeric_limits<uint16_t>::max(),
b.size_bytes()));
}
buffers.insert(buffers.end(), *to_managed_bytes_opt(b));
}
return ::make_shared<value>(std::move(buffers), _my_type);
}
expr::expression sets::delayed_value::to_expression() {
std::vector<expr::expression> new_elements;
new_elements.reserve(_elements.size());
for (shared_ptr<term>& e : _elements) {
new_elements.emplace_back(expr::to_expression(e));
}
return expr::collection_constructor {
.style = expr::collection_constructor::style_type::set,
.elements = std::move(new_elements),
.type = _my_type,
};
}
sets::marker::marker(int32_t bind_index, lw_shared_ptr<column_specification> receiver)
: abstract_marker{bind_index, std::move(receiver)} {
if (!_receiver->type->without_reversed().is_set()) {
throw std::runtime_error(format("Receiver {} for set marker has wrong type: {}",
_receiver->cf_name, _receiver->type->name()));
}
}
::shared_ptr<terminal>
sets::marker::bind(const query_options& options) {
const auto& value = options.get_value_at(_bind_index);
if (value.is_null()) {
return nullptr;
} else if (value.is_unset_value()) {
return constants::UNSET_VALUE;
} else {
auto& type = dynamic_cast<const set_type_impl&>(_receiver->type->without_reversed());
try {
value.validate(type, options.get_cql_serialization_format());
} catch (marshal_exception& e) {
throw exceptions::invalid_request_exception(
format("Exception while binding column {:s}: {:s}", _receiver->name->to_cql_string(), e.what()));
}
return make_shared<cql3::sets::value>(value::from_serialized(value, type, options.get_cql_serialization_format()));
}
}
expr::expression sets::marker::to_expression() {
return expr::bind_variable {
.shape = expr::bind_variable::shape_type::scalar,
.bind_index = _bind_index,
.value_type = _receiver->type
};
}
void
sets::setter::execute(mutation& m, const clustering_key_prefix& row_key, const update_parameters& params) {
expr::constant value = expr::evaluate(_t, params._options);
expr::constant value = expr::evaluate(*_e, params._options);
execute(m, row_key, params, column, std::move(value));
}
@@ -196,7 +48,7 @@ sets::setter::execute(mutation& m, const clustering_key_prefix& row_key, const u
void
sets::adder::execute(mutation& m, const clustering_key_prefix& row_key, const update_parameters& params) {
const expr::constant value = expr::evaluate(_t, params._options);
const expr::constant value = expr::evaluate(*_e, params._options);
if (value.is_unset_value()) {
return;
}
@@ -229,7 +81,7 @@ sets::adder::do_add(mutation& m, const clustering_key_prefix& row_key, const upd
m.set_cell(row_key, column, mut.serialize(set_type));
} else if (!value.is_null()) {
// for frozen sets, we're overwriting the whole cell
m.set_cell(row_key, column, params.make_cell(*column.type, value.value.to_view()));
m.set_cell(row_key, column, params.make_cell(*column.type, value.view()));
} else {
m.set_cell(row_key, column, params.make_dead_cell());
}
@@ -239,7 +91,7 @@ void
sets::discarder::execute(mutation& m, const clustering_key_prefix& row_key, const update_parameters& params) {
assert(column.type->is_multi_cell()); // "Attempted to remove items from a frozen set";
expr::constant svalue = expr::evaluate(_t, params._options);
expr::constant svalue = expr::evaluate(*_e, params._options);
if (svalue.is_null_or_unset()) {
return;
}
@@ -257,7 +109,7 @@ sets::discarder::execute(mutation& m, const clustering_key_prefix& row_key, cons
void sets::element_discarder::execute(mutation& m, const clustering_key_prefix& row_key, const update_parameters& params)
{
assert(column.type->is_multi_cell() && "Attempted to remove items from a frozen set");
expr::constant elt = expr::evaluate(_t, params._options);
expr::constant elt = expr::evaluate(*_e, params._options);
if (elt.is_null()) {
throw exceptions::invalid_request_exception("Invalid null set element");
}

View File

@@ -58,46 +58,10 @@ class sets {
public:
static lw_shared_ptr<column_specification> value_spec_of(const column_specification& column);
class value : public terminal, collection_terminal {
public:
std::set<managed_bytes, serialized_compare> _elements;
public:
value(std::set<managed_bytes, serialized_compare> elements, data_type my_type)
: terminal(std::move(my_type)), _elements(std::move(elements)) {
}
static value from_serialized(const raw_value_view& v, const set_type_impl& type, cql_serialization_format sf);
virtual cql3::raw_value get(const query_options& options) override;
virtual managed_bytes get_with_protocol_version(cql_serialization_format sf) override;
bool equals(const set_type_impl& st, const value& v);
virtual sstring to_string() const override;
};
// See Lists.DelayedValue
class delayed_value : public non_terminal {
std::vector<shared_ptr<term>> _elements;
data_type _my_type;
public:
delayed_value(std::vector<shared_ptr<term>> elements, data_type my_type)
: _elements(std::move(elements)), _my_type(std::move(my_type)) {
}
virtual bool contains_bind_marker() const override;
virtual void fill_prepare_context(prepare_context& ctx) const override;
virtual shared_ptr<terminal> bind(const query_options& options) override;
virtual expr::expression to_expression() override;
};
class marker : public abstract_marker {
public:
marker(int32_t bind_index, lw_shared_ptr<column_specification> receiver);
virtual ::shared_ptr<terminal> bind(const query_options& options) override;
virtual expr::expression to_expression() override;
};
class setter : public operation {
public:
setter(const column_definition& column, shared_ptr<term> t)
: operation(column, std::move(t)) {
setter(const column_definition& column, expr::expression e)
: operation(column, std::move(e)) {
}
virtual void execute(mutation& m, const clustering_key_prefix& row_key, const update_parameters& params) override;
static void execute(mutation& m, const clustering_key_prefix& row_key, const update_parameters& params, const column_definition& column, const expr::constant& value);
@@ -105,8 +69,8 @@ public:
class adder : public operation {
public:
adder(const column_definition& column, shared_ptr<term> t)
: operation(column, std::move(t)) {
adder(const column_definition& column, expr::expression e)
: operation(column, std::move(e)) {
}
virtual void execute(mutation& m, const clustering_key_prefix& row_key, const update_parameters& params) override;
static void do_add(mutation& m, const clustering_key_prefix& row_key, const update_parameters& params,
@@ -116,16 +80,16 @@ public:
// Note that this is reused for Map subtraction too (we subtract a set from a map)
class discarder : public operation {
public:
discarder(const column_definition& column, shared_ptr<term> t)
: operation(column, std::move(t)) {
discarder(const column_definition& column, expr::expression e)
: operation(column, std::move(e)) {
}
virtual void execute(mutation& m, const clustering_key_prefix& row_key, const update_parameters& params) override;
};
class element_discarder : public operation {
public:
element_discarder(const column_definition& column, shared_ptr<term> t)
: operation(column, std::move(t)) { }
element_discarder(const column_definition& column, expr::expression e)
: operation(column, std::move(e)) { }
virtual void execute(mutation& m, const clustering_key_prefix& row_key, const update_parameters& params) override;
};
};

View File

@@ -54,17 +54,17 @@ using namespace cql3::expr;
namespace cql3 {
::shared_ptr<term>
single_column_relation::to_term(const std::vector<lw_shared_ptr<column_specification>>& receivers,
const expr::expression& raw,
database& db,
const sstring& keyspace,
prepare_context& ctx) const {
expression
single_column_relation::to_expression(const std::vector<lw_shared_ptr<column_specification>>& receivers,
const expr::expression& raw,
database& db,
const sstring& keyspace,
prepare_context& ctx) const {
// TODO: optimize vector away, accept single column_specification
assert(receivers.size() == 1);
auto term = prepare_term(raw, db, keyspace, receivers[0]);
term->fill_prepare_context(ctx);
return term;
auto expr = prepare_expression(raw, db, keyspace, receivers[0]);
expr::fill_prepare_context(expr, ctx);
return expr;
}
::shared_ptr<restrictions::restriction>
@@ -76,13 +76,13 @@ single_column_relation::new_EQ_restriction(database& db, schema_ptr schema, prep
}
if (!_map_key) {
auto r = ::make_shared<restrictions::single_column_restriction>(column_def);
auto term = to_term(to_receivers(*schema, column_def), *_value, db, schema->ks_name(), ctx);
r->expression = binary_operator{column_value{&column_def}, expr::oper_t::EQ, std::move(term)};
auto e = to_expression(to_receivers(*schema, column_def), *_value, db, schema->ks_name(), ctx);
r->expression = binary_operator{column_value{&column_def}, expr::oper_t::EQ, std::move(e)};
return r;
}
auto&& receivers = to_receivers(*schema, column_def);
auto&& entry_key = to_term({receivers[0]}, *_map_key, db, schema->ks_name(), ctx);
auto&& entry_value = to_term({receivers[1]}, *_value, db, schema->ks_name(), ctx);
auto&& entry_key = to_expression({receivers[0]}, *_map_key, db, schema->ks_name(), ctx);
auto&& entry_value = to_expression({receivers[1]}, *_value, db, schema->ks_name(), ctx);
auto r = make_shared<restrictions::single_column_restriction>(column_def);
r->expression = binary_operator{
column_value(&column_def, std::move(entry_key)), oper_t::EQ, std::move(entry_value)};
@@ -100,23 +100,28 @@ single_column_relation::new_IN_restriction(database& db, schema_ptr schema, prep
auto receivers = to_receivers(*schema, column_def);
assert(_in_values.empty() || !_value);
if (_value) {
auto term = to_term(receivers, *_value, db, schema->ks_name(), ctx);
auto e = to_expression(receivers, *_value, db, schema->ks_name(), ctx);
auto r = ::make_shared<single_column_restriction>(column_def);
r->expression = binary_operator{column_value{&column_def}, expr::oper_t::IN, std::move(term)};
r->expression = binary_operator{column_value{&column_def}, expr::oper_t::IN, std::move(e)};
return r;
}
auto terms = to_terms(receivers, _in_values, db, schema->ks_name(), ctx);
auto expressions = to_expressions(receivers, _in_values, db, schema->ks_name(), ctx);
// Convert a single-item IN restriction to an EQ restriction
if (terms.size() == 1) {
if (expressions.size() == 1) {
auto r = ::make_shared<single_column_restriction>(column_def);
r->expression = binary_operator{column_value{&column_def}, expr::oper_t::EQ, std::move(terms[0])};
r->expression = binary_operator{column_value{&column_def}, expr::oper_t::EQ, std::move(expressions[0])};
return r;
}
auto r = ::make_shared<single_column_restriction>(column_def);
collection_constructor list_value {
.style = collection_constructor::style_type::list,
.elements = std::move(expressions),
.type = list_type_impl::get_instance(column_def.type, false),
};
r->expression = binary_operator{
column_value{&column_def},
expr::oper_t::IN,
::make_shared<lists::delayed_value>(std::move(terms), list_type_impl::get_instance(column_def.type, false))};
std::move(list_value)};
return r;
}
@@ -128,9 +133,9 @@ single_column_relation::new_LIKE_restriction(
throw exceptions::invalid_request_exception(
format("LIKE is allowed only on string types, which {} is not", column_def.name_as_text()));
}
auto term = to_term(to_receivers(*schema, column_def), *_value, db, schema->ks_name(), ctx);
auto e = to_expression(to_receivers(*schema, column_def), *_value, db, schema->ks_name(), ctx);
auto r = ::make_shared<restrictions::single_column_restriction>(column_def);
r->expression = binary_operator{column_value{&column_def}, expr::oper_t::LIKE, std::move(term)};
r->expression = binary_operator{column_value{&column_def}, expr::oper_t::LIKE, std::move(e)};
return r;
}

View File

@@ -51,7 +51,6 @@
#include "cql3/relation.hh"
#include "cql3/column_identifier.hh"
#include "cql3/expr/expression.hh"
#include "cql3/term.hh"
#include "types/collection.hh"
namespace cql3 {
@@ -115,9 +114,9 @@ public:
}
protected:
virtual ::shared_ptr<term> to_term(const std::vector<lw_shared_ptr<column_specification>>& receivers,
const expr::expression& raw, database& db, const sstring& keyspace,
prepare_context& ctx) const override;
virtual expr::expression to_expression(const std::vector<lw_shared_ptr<column_specification>>& receivers,
const expr::expression& raw, database& db, const sstring& keyspace,
prepare_context& ctx) const override;
#if 0
public SingleColumnRelation withNonStrictOperator()
@@ -169,9 +168,9 @@ protected:
throw exceptions::invalid_request_exception("Slice restrictions are not supported on duration columns");
}
auto term = to_term(to_receivers(*schema, column_def), *_value, db, schema->ks_name(), ctx);
auto e = to_expression(to_receivers(*schema, column_def), *_value, db, schema->ks_name(), ctx);
auto r = ::make_shared<restrictions::single_column_restriction>(column_def);
r->expression = expr::binary_operator{expr::column_value{&column_def}, _relation_type, std::move(term)};
r->expression = expr::binary_operator{expr::column_value{&column_def}, _relation_type, std::move(e)};
return r;
}
@@ -179,10 +178,10 @@ protected:
prepare_context& ctx,
bool is_key) override {
auto&& column_def = to_column_definition(*schema, *_entity);
auto term = to_term(to_receivers(*schema, column_def), *_value, db, schema->ks_name(), ctx);
auto e = to_expression(to_receivers(*schema, column_def), *_value, db, schema->ks_name(), ctx);
auto r = ::make_shared<restrictions::single_column_restriction>(column_def);
r->expression = expr::binary_operator{
expr::column_value{&column_def}, is_key ? expr::oper_t::CONTAINS_KEY : expr::oper_t::CONTAINS, std::move(term)};
expr::column_value{&column_def}, is_key ? expr::oper_t::CONTAINS_KEY : expr::oper_t::CONTAINS, std::move(e)};
return r;
}

View File

@@ -42,6 +42,7 @@
#include "raw/batch_statement.hh"
#include "timestamp.hh"
#include "log.hh"
#include "service_permit.hh"
namespace cql_transport::messages {
class result_message;

View File

@@ -57,8 +57,8 @@ void create_aggregate_statement::create(service::storage_proxy& proxy, functions
auto dummy_ident = ::make_shared<column_identifier>("", true);
auto column_spec = make_lw_shared<column_specification>("", "", dummy_ident, state_type);
auto initcond_term = prepare_term(_ival, db, _name.keyspace, {column_spec});
bytes_opt initcond = to_bytes(*to_managed_bytes_opt(expr::evaluate_to_raw_view(initcond_term, cql3::query_options::DEFAULT)));
auto initcond_term = expr::evaluate(prepare_expression(_ival, db, _name.keyspace, {column_spec}), query_options::DEFAULT);
bytes_opt initcond = std::move(initcond_term.value).to_bytes();
_aggregate = ::make_shared<functions::user_aggregate>(_name, initcond, std::move(state_func), std::move(final_func));
return;

View File

@@ -42,7 +42,6 @@
#include <boost/algorithm/cxx11/all_of.hpp>
#include <boost/range/adaptors.hpp>
#include "cql3/tuples.hh"
#include "database.hh"
#include "delete_statement.hh"
#include "raw/delete_statement.hh"

View File

@@ -499,10 +499,8 @@ modification_statement::prepare(database& db, prepare_context& ctx, cql_stats& s
// Since this cache is only meaningful for LWT queries, just clear the ids
// if it's not a conditional statement so that the AST nodes don't
// participate in the caching mechanism later.
if (!prepared_stmt->has_conditions()) {
for (auto& fn : ctx.pk_function_calls()) {
fn->set_id(std::nullopt);
}
if (!prepared_stmt->has_conditions() && prepared_stmt->_restrictions.has_value()) {
ctx.clear_pk_function_calls_cache();
}
return prepared_stmt;
}

View File

@@ -43,9 +43,7 @@
#include "cql3/statements/raw/modification_statement.hh"
#include "cql3/column_identifier.hh"
#include "cql3/term.hh"
#include "cql3/expr/expression.hh"
#include "database_fwd.hh"
#include <vector>

View File

@@ -133,8 +133,8 @@ private:
bool for_view = false,
bool allow_filtering = false);
/** Returns a ::shared_ptr<term> for the limit or null if no limit is set */
::shared_ptr<term> prepare_limit(database& db, prepare_context& ctx, const std::optional<expr::expression>& limit);
/** Returns an expression for the limit or nullopt if no limit is set */
std::optional<expr::expression> prepare_limit(database& db, prepare_context& ctx, const std::optional<expr::expression>& limit);
static void verify_ordering_is_allowed(const restrictions::statement_restrictions& restrictions);

View File

@@ -67,7 +67,7 @@ private:
public:
/**
* Creates a new UpdateStatement from a column family name, columns map, consistency
* level, and key term.
* level, and key expression.
*
* @param name column family being operated on
* @param attrs additional attributes for statement (timestamp, timeToLive)

View File

@@ -140,8 +140,8 @@ select_statement::select_statement(schema_ptr schema,
::shared_ptr<std::vector<size_t>> group_by_cell_indices,
bool is_reversed,
ordering_comparator_type ordering_comparator,
::shared_ptr<term> limit,
::shared_ptr<term> per_partition_limit,
std::optional<expr::expression> limit,
std::optional<expr::expression> per_partition_limit,
cql_stats& stats,
std::unique_ptr<attributes> attrs)
: cql_statement(select_timeout(*restrictions))
@@ -251,12 +251,14 @@ select_statement::make_partition_slice(const query_options& options) const
std::move(static_columns), std::move(regular_columns), _opts, nullptr, options.get_cql_serialization_format(), get_per_partition_limit(options));
}
uint64_t select_statement::do_get_limit(const query_options& options, ::shared_ptr<term> limit, uint64_t default_limit) const {
if (!limit || _selection->is_aggregate()) {
uint64_t select_statement::do_get_limit(const query_options& options,
const std::optional<expr::expression>& limit,
uint64_t default_limit) const {
if (!limit.has_value() || _selection->is_aggregate()) {
return default_limit;
}
auto val = expr::evaluate_to_raw_view(limit, options);
auto val = expr::evaluate(*limit, options);
if (val.is_null()) {
throw exceptions::invalid_request_exception("Invalid null value of limit");
}
@@ -264,7 +266,7 @@ uint64_t select_statement::do_get_limit(const query_options& options, ::shared_p
return default_limit;
}
try {
auto l = val.validate_and_deserialize<int32_t>(*int32_type, options.get_cql_serialization_format());
auto l = val.view().validate_and_deserialize<int32_t>(*int32_type, cql_serialization_format::internal());
if (l <= 0) {
throw exceptions::invalid_request_exception("LIMIT must be strictly positive");
}
@@ -820,11 +822,11 @@ primary_key_select_statement::primary_key_select_statement(schema_ptr schema, ui
::shared_ptr<std::vector<size_t>> group_by_cell_indices,
bool is_reversed,
ordering_comparator_type ordering_comparator,
::shared_ptr<term> limit,
::shared_ptr<term> per_partition_limit,
std::optional<expr::expression> limit,
std::optional<expr::expression> per_partition_limit,
cql_stats &stats,
std::unique_ptr<attributes> attrs)
: select_statement{schema, bound_terms, parameters, selection, restrictions, group_by_cell_indices, is_reversed, ordering_comparator, limit, per_partition_limit, stats, std::move(attrs)}
: select_statement{schema, bound_terms, parameters, selection, restrictions, group_by_cell_indices, is_reversed, ordering_comparator, std::move(limit), std::move(per_partition_limit), stats, std::move(attrs)}
{
if (_ks_sel == ks_selector::NONSYSTEM) {
if (_restrictions->need_filtering() ||
@@ -848,8 +850,8 @@ indexed_table_select_statement::prepare(database& db,
::shared_ptr<std::vector<size_t>> group_by_cell_indices,
bool is_reversed,
ordering_comparator_type ordering_comparator,
::shared_ptr<term> limit,
::shared_ptr<term> per_partition_limit,
std::optional<expr::expression> limit,
std::optional<expr::expression> per_partition_limit,
cql_stats &stats,
std::unique_ptr<attributes> attrs)
{
@@ -878,8 +880,8 @@ indexed_table_select_statement::prepare(database& db,
std::move(group_by_cell_indices),
is_reversed,
std::move(ordering_comparator),
limit,
per_partition_limit,
std::move(limit),
std::move(per_partition_limit),
stats,
*index_opt,
std::move(used_index_restrictions),
@@ -895,8 +897,8 @@ indexed_table_select_statement::indexed_table_select_statement(schema_ptr schema
::shared_ptr<std::vector<size_t>> group_by_cell_indices,
bool is_reversed,
ordering_comparator_type ordering_comparator,
::shared_ptr<term> limit,
::shared_ptr<term> per_partition_limit,
std::optional<expr::expression> limit,
std::optional<expr::expression> per_partition_limit,
cql_stats &stats,
const secondary_index::index& index,
::shared_ptr<restrictions::restrictions> used_index_restrictions,
@@ -1477,16 +1479,16 @@ select_statement::prepare_restrictions(database& db,
}
}
/** Returns a ::shared_ptr<term> for the limit or null if no limit is set */
::shared_ptr<term>
/** Returns a expr::expression for the limit or nullopt if no limit is set */
std::optional<expr::expression>
select_statement::prepare_limit(database& db, prepare_context& ctx, const std::optional<expr::expression>& limit)
{
if (!limit) {
return {};
if (!limit.has_value()) {
return std::nullopt;
}
auto prep_limit = prepare_term(*limit, db, keyspace(), limit_receiver());
prep_limit->fill_prepare_context(ctx);
expr::expression prep_limit = prepare_expression(*limit, db, keyspace(), limit_receiver());
expr::fill_prepare_context(prep_limit, ctx);
return prep_limit;
}

View File

@@ -86,8 +86,8 @@ protected:
::shared_ptr<restrictions::statement_restrictions> _restrictions;
::shared_ptr<std::vector<size_t>> _group_by_cell_indices; ///< Indices in result row of cells holding GROUP BY values.
bool _is_reversed;
::shared_ptr<term> _limit;
::shared_ptr<term> _per_partition_limit;
std::optional<expr::expression> _limit;
std::optional<expr::expression> _per_partition_limit;
template<typename T>
using compare_fn = raw::select_statement::compare_fn<T>;
@@ -118,8 +118,8 @@ public:
::shared_ptr<std::vector<size_t>> group_by_cell_indices,
bool is_reversed,
ordering_comparator_type ordering_comparator,
::shared_ptr<term> limit,
::shared_ptr<term> per_partition_limit,
std::optional<expr::expression> limit,
std::optional<expr::expression> per_partition_limit,
cql_stats& stats,
std::unique_ptr<cql3::attributes> attrs);
@@ -158,7 +158,7 @@ public:
db::timeout_clock::duration get_timeout(const service::client_state& state, const query_options& options) const;
protected:
uint64_t do_get_limit(const query_options& options, ::shared_ptr<term> limit, uint64_t default_limit) const;
uint64_t do_get_limit(const query_options& options, const std::optional<expr::expression>& limit, uint64_t default_limit) const;
uint64_t get_limit(const query_options& options) const {
return do_get_limit(options, _limit, query::max_rows);
}
@@ -181,8 +181,8 @@ public:
::shared_ptr<std::vector<size_t>> group_by_cell_indices,
bool is_reversed,
ordering_comparator_type ordering_comparator,
::shared_ptr<term> limit,
::shared_ptr<term> per_partition_limit,
std::optional<expr::expression> limit,
std::optional<expr::expression> per_partition_limit,
cql_stats &stats,
std::unique_ptr<cql3::attributes> attrs);
};
@@ -205,8 +205,8 @@ public:
::shared_ptr<std::vector<size_t>> group_by_cell_indices,
bool is_reversed,
ordering_comparator_type ordering_comparator,
::shared_ptr<term> limit,
::shared_ptr<term> per_partition_limit,
std::optional<expr::expression> limit,
std::optional<expr::expression> per_partition_limit,
cql_stats &stats,
std::unique_ptr<cql3::attributes> attrs);
@@ -218,8 +218,8 @@ public:
::shared_ptr<std::vector<size_t>> group_by_cell_indices,
bool is_reversed,
ordering_comparator_type ordering_comparator,
::shared_ptr<term> limit,
::shared_ptr<term> per_partition_limit,
std::optional<expr::expression> limit,
std::optional<expr::expression> per_partition_limit,
cql_stats &stats,
const secondary_index::index& index,
::shared_ptr<restrictions::restrictions> used_index_restrictions,

View File

@@ -140,7 +140,7 @@ void update_statement::add_update_for_key(mutation& m, const query::clustering_r
if (rb->name().empty() || rb->type == empty_type) {
// There is no column outside the PK. So no operation could have passed through validation
assert(_column_operations.empty());
constants::setter(*s->regular_begin(), make_shared<constants::value>(cql3::raw_value::make_value(bytes()), empty_type)).execute(m, prefix, params);
constants::setter(*s->regular_begin(), expr::constant(cql3::raw_value::make_value(bytes()), empty_type)).execute(m, prefix, params);
} else {
// dense means we don't have a row marker, so don't accept to set only the PK. See CASSANDRA-5648.
if (_column_operations.empty()) {
@@ -180,8 +180,9 @@ void update_statement::add_update_for_key(mutation& m, const query::clustering_r
}
modification_statement::json_cache_opt insert_prepared_json_statement::maybe_prepare_json_cache(const query_options& options) const {
sstring json_string = utf8_type->to_string(to_bytes(expr::evaluate_to_raw_view(_term, options)));
return json_helpers::parse(std::move(json_string), s->all_columns(), options.get_cql_serialization_format());
expr::constant c = expr::evaluate(_value, options);
sstring json_string = utf8_type->to_string(to_bytes(c.view()));
return json_helpers::parse(std::move(json_string), s->all_columns(), cql_serialization_format::internal());
}
void
@@ -212,29 +213,26 @@ insert_prepared_json_statement::execute_set_value(mutation& m, const clustering_
return;
}
cql_serialization_format sf = params._options.get_cql_serialization_format();
expr::constant val(raw_value::make_value(*value), column.type);
visit(*column.type, make_visitor(
[&] (const list_type_impl& ltype) {
auto val = ::make_shared<lists::value>(lists::value::from_serialized(raw_value_view::make_value(*value), ltype, sf));
lists::setter::execute(m, prefix, params, column, expr::evaluate(val, query_options::DEFAULT));
lists::setter::execute(m, prefix, params, column, val);
},
[&] (const set_type_impl& stype) {
auto val = ::make_shared<sets::value>(sets::value::from_serialized(raw_value_view::make_value(*value), stype, sf));
sets::setter::execute(m, prefix, params, column, expr::evaluate(val, query_options::DEFAULT));
sets::setter::execute(m, prefix, params, column, val);
},
[&] (const map_type_impl& mtype) {
auto val = ::make_shared<maps::value>(maps::value::from_serialized(raw_value_view::make_value(*value), mtype, sf));
maps::setter::execute(m, prefix, params, column, expr::evaluate(val, query_options::DEFAULT));
maps::setter::execute(m, prefix, params, column, val);
},
[&] (const user_type_impl& utype) {
auto val = ::make_shared<user_types::value>(user_types::value::from_serialized(raw_value_view::make_value(*value), utype));
user_types::setter::execute(m, prefix, params, column, expr::evaluate(val, query_options::DEFAULT));
user_types::setter::execute(m, prefix, params, column, val);
},
[&] (const abstract_type& type) {
if (type.is_collection()) {
throw std::runtime_error(format("insert_prepared_json_statement::execute_set_value: unhandled collection type {}", type.name()));
}
constants::setter::execute(m, prefix, params, column, raw_value_view::make_value(*value));
constants::setter::execute(m, prefix, params, column, val.view());
}
));
}
@@ -365,8 +363,8 @@ insert_json_statement::prepare_internal(database& db, schema_ptr schema,
(void)_if_not_exists;
assert(expr::is<cql3::expr::untyped_constant>(_json_value) || expr::is<cql3::expr::bind_variable>(_json_value));
auto json_column_placeholder = ::make_shared<column_identifier>("", true);
auto prepared_json_value = prepare_term(_json_value, db, "", make_lw_shared<column_specification>("", "", json_column_placeholder, utf8_type));
prepared_json_value->fill_prepare_context(ctx);
auto prepared_json_value = prepare_expression(_json_value, db, "", make_lw_shared<column_specification>("", "", json_column_placeholder, utf8_type));
expr::fill_prepare_context(prepared_json_value, ctx);
auto stmt = ::make_shared<cql3::statements::insert_prepared_json_statement>(ctx.bound_variables_size(), schema, std::move(attrs), stats, std::move(prepared_json_value), _default_unset);
prepare_conditions(db, *schema, ctx, *stmt);
return stmt;

View File

@@ -48,8 +48,6 @@
namespace cql3 {
class term;
namespace statements {
/**
@@ -82,7 +80,7 @@ private:
* Overridden add_update_for_key uses this parsed JSON to look up values for columns.
*/
class insert_prepared_json_statement : public update_statement {
::shared_ptr<term> _term;
expr::expression _value;
bool _default_unset;
public:
insert_prepared_json_statement(
@@ -90,9 +88,9 @@ public:
schema_ptr s,
std::unique_ptr<attributes> attrs,
cql_stats& stats,
::shared_ptr<term> t, bool default_unset)
expr::expression v, bool default_unset)
: update_statement(statement_type::INSERT, bound_terms, s, std::move(attrs), stats)
, _term(t)
, _value(std::move(v))
, _default_unset(default_unset) {
_restrictions = restrictions::statement_restrictions(s, false);
}

View File

@@ -1,179 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Copyright (C) 2015-present ScyllaDB
*
* Modified by ScyllaDB
*/
/*
* This file is part of Scylla.
*
* Scylla is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Scylla is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Scylla. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "cql3/assignment_testable.hh"
#include "cql3/query_options.hh"
#include "cql3/values.hh"
#include "cql3/expr/expression.hh"
#include <variant>
#include <vector>
namespace cql3 {
class term;
class terminal;
class prepare_context;
/**
* A CQL3 term, i.e. a column value with or without bind variables.
*
* A Term can be either terminal or non terminal. A term object is one that is typed and is obtained
* from a raw term (Term.Raw) by poviding the actual receiver to which the term is supposed to be a
* value of.
*/
class term : public ::enable_shared_from_this<term> {
public:
virtual ~term() {}
/**
* Collects the column specification for the bind variables in this Term.
* This is obviously a no-op if the term is Terminal.
*
* @param boundNames the variables specification where to collect the
* bind variables of this term in.
*/
virtual void fill_prepare_context(prepare_context& ctx) const = 0;
/**
* Bind the values in this term to the values contained in {@code values}.
* This is obviously a no-op if the term is Terminal.
*
* @param options the values to bind markers to.
* @return the result of binding all the variables of this NonTerminal (or
* 'this' if the term is terminal).
*/
virtual ::shared_ptr<terminal> bind(const query_options& options) = 0;
/**
* Whether or not that term contains at least one bind marker.
*
* Note that this is slightly different from being or not a NonTerminal,
* because calls to non pure functions will be NonTerminal (see #5616)
* even if they don't have bind markers.
*/
virtual bool contains_bind_marker() const = 0;
virtual sstring to_string() const {
return format("term@{:p}", static_cast<const void*>(this));
}
friend std::ostream& operator<<(std::ostream& out, const term& t) {
return out << t.to_string();
}
virtual expr::expression to_expression() = 0;
};
/**
* A terminal term, one that can be reduced to a byte buffer directly.
*
* This includes most terms that don't have a bind marker (an exception
* being delayed call for non pure function that are NonTerminal even
* if they don't have bind markers).
*
* This can be only one of:
* - a constant value
* - a collection value
*
* Note that a terminal term will always have been type checked, and thus
* consumer can (and should) assume so.
*/
class terminal : public term {
data_type _my_type;
public:
terminal(data_type my_type) : _my_type(std::move(my_type)) {
}
virtual void fill_prepare_context(prepare_context& ctx) const override {
}
virtual ::shared_ptr<terminal> bind(const query_options& options) override {
return static_pointer_cast<terminal>(this->shared_from_this());
}
// While some NonTerminal may not have bind markers, no Term can be Terminal
// with a bind marker
virtual bool contains_bind_marker() const override {
return false;
}
/**
* @return the serialized value of this terminal.
*/
virtual cql3::raw_value get(const query_options& options) = 0;
virtual sstring to_string() const override = 0;
data_type get_value_type() const {
return _my_type;
}
virtual expr::expression to_expression() override {
cql3::raw_value raw_val = get(query_options::DEFAULT);
return expr::constant(std::move(raw_val), get_value_type());
}
};
class collection_terminal {
public:
virtual ~collection_terminal() {}
/** Gets the value of the collection when serialized with the given protocol version format */
virtual managed_bytes get_with_protocol_version(cql_serialization_format sf) = 0;
};
/**
* A non terminal term, i.e. a term that can only be reduce to a byte buffer
* at execution time.
*
* We have the following type of NonTerminal:
* - marker for a constant value
* - marker for a collection value (list, set, map)
* - a function having bind marker
* - a non pure function (even if it doesn't have bind marker - see #5616)
*/
class non_terminal : public term {
};
}

View File

@@ -41,7 +41,6 @@
#include "restrictions/token_restriction.hh"
#include "token_relation.hh"
#include "column_identifier.hh"
#include "term.hh"
#include "to_string.hh"
std::vector<const column_definition*> cql3::token_relation::get_column_definitions(const schema& s) {
@@ -83,11 +82,11 @@ std::vector<lw_shared_ptr<cql3::column_specification>> cql3::token_relation::to_
database& db, schema_ptr schema,
prepare_context& ctx) {
auto column_defs = get_column_definitions(*schema);
auto term = to_term(to_receivers(*schema, column_defs), _value, db,
auto e = to_expression(to_receivers(*schema, column_defs), _value, db,
schema->ks_name(), ctx);
auto r = ::make_shared<restrictions::token_restriction>(column_defs);
using namespace expr;
r->expression = binary_operator{token{}, oper_t::EQ, std::move(term)};
r->expression = binary_operator{token{}, oper_t::EQ, std::move(e)};
return r;
}
@@ -105,11 +104,11 @@ std::vector<lw_shared_ptr<cql3::column_specification>> cql3::token_relation::to_
statements::bound bound,
bool inclusive) {
auto column_defs = get_column_definitions(*schema);
auto term = to_term(to_receivers(*schema, column_defs), _value, db,
auto e = to_expression(to_receivers(*schema, column_defs), _value, db,
schema->ks_name(), ctx);
auto r = ::make_shared<restrictions::token_restriction>(column_defs);
using namespace expr;
r->expression = binary_operator{token{}, pick_operator(bound, inclusive), std::move(term)};
r->expression = binary_operator{token{}, pick_operator(bound, inclusive), std::move(e)};
return r;
}
@@ -130,13 +129,13 @@ sstring cql3::token_relation::to_string() const {
return format("token({}) {} {}", join(", ", _entities), get_operator(), _value);
}
::shared_ptr<cql3::term> cql3::token_relation::to_term(
cql3::expr::expression cql3::token_relation::to_expression(
const std::vector<lw_shared_ptr<column_specification>>& receivers,
const expr::expression& raw, database& db, const sstring& keyspace,
prepare_context& ctx) const {
auto term = expr::prepare_term(raw, db, keyspace, receivers.front());
term->fill_prepare_context(ctx);
return term;
auto e = expr::prepare_expression(raw, db, keyspace, receivers.front());
expr::fill_prepare_context(e, ctx);
return e;
}
::shared_ptr<cql3::relation> cql3::token_relation::maybe_rename_identifier(const cql3::column_identifier::raw& from, cql3::column_identifier::raw to) {

View File

@@ -47,7 +47,6 @@
#include "relation.hh"
#include "column_identifier.hh"
#include "term.hh"
#include "restrictions/restriction.hh"
#include "expr/expression.hh"
@@ -123,11 +122,11 @@ public:
sstring to_string() const override;
protected:
::shared_ptr<term> to_term(const std::vector<lw_shared_ptr<column_specification>>& receivers,
const expr::expression& raw,
database& db,
const sstring& keyspace,
prepare_context& ctx) const override;
expr::expression to_expression(const std::vector<lw_shared_ptr<column_specification>>& receivers,
const expr::expression& raw,
database& db,
const sstring& keyspace,
prepare_context& ctx) const override;
};
}

View File

@@ -1,126 +0,0 @@
/*
* Copyright (C) 2015-present ScyllaDB
*/
/*
* This file is part of Scylla.
*
* Scylla is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Scylla is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Scylla. If not, see <http://www.gnu.org/licenses/>.
*/
#include <seastar/core/shared_ptr.hh>
#include "tuples.hh"
#include "types/list.hh"
#include "cql3/lists.hh"
namespace cql3 {
expr::expression tuples::delayed_value::to_expression() {
std::vector<expr::expression> new_elements;
new_elements.reserve(_elements.size());
for (shared_ptr<term>& e : _elements) {
new_elements.emplace_back(expr::to_expression(e));
}
return expr::tuple_constructor {
.elements = std::move(new_elements),
.type = _type,
};
}
tuples::in_value
tuples::in_value::from_serialized(const raw_value_view& value_view, const list_type_impl& type, const query_options& options) {
try {
// Collections have this small hack that validate cannot be called on a serialized object,
// but the deserialization does the validation (so we're fine).
auto l = value_view.deserialize<list_type_impl::native_type>(type, options.get_cql_serialization_format());
auto ttype = dynamic_pointer_cast<const tuple_type_impl>(type.get_elements_type());
assert(ttype);
utils::chunked_vector<std::vector<managed_bytes_opt>> elements;
elements.reserve(l.size());
for (auto&& e : l) {
// FIXME: Avoid useless copies.
elements.emplace_back(ttype->split_fragmented(single_fragmented_view(ttype->decompose(e))));
}
return tuples::in_value(elements, type.shared_from_this());
} catch (marshal_exception& e) {
throw exceptions::invalid_request_exception(e.what());
}
}
cql3::raw_value tuples::in_value::get(const query_options& options) {
const list_type_impl& my_list_type = dynamic_cast<const list_type_impl&>(get_value_type()->without_reversed());
data_type element_tuple_type = my_list_type.get_elements_type();
utils::chunked_vector<managed_bytes_opt> list_elements;
list_elements.reserve(_elements.size());
for (const std::vector<managed_bytes_opt>& tuple_elements : _elements) {
::shared_ptr<tuples::value> tvalue =
::make_shared<tuples::value>(tuples::value(tuple_elements, element_tuple_type));
expr::constant tuple_val = expr::evaluate(tvalue, options);
list_elements.emplace_back(std::move(tuple_val.value).to_managed_bytes());
}
::shared_ptr<lists::value> list_value = ::make_shared<lists::value>(std::move(list_elements), get_value_type());
return list_value->get(options);
}
expr::expression tuples::marker::to_expression() {
return expr::bind_variable {
.shape = expr::bind_variable::shape_type::tuple,
.bind_index = _bind_index,
.value_type = _receiver->type
};
}
expr::expression tuples::in_marker::to_expression() {
return expr::bind_variable {
.shape = expr::bind_variable::shape_type::tuple_in,
.bind_index = _bind_index,
.value_type = _receiver->type
};
}
tuples::in_marker::in_marker(int32_t bind_index, lw_shared_ptr<column_specification> receiver)
: abstract_marker(bind_index, std::move(receiver))
{
assert(dynamic_pointer_cast<const list_type_impl>(_receiver->type));
}
shared_ptr<terminal> tuples::in_marker::bind(const query_options& options) {
const auto& value = options.get_value_at(_bind_index);
if (value.is_null()) {
return nullptr;
} else if (value.is_unset_value()) {
throw exceptions::invalid_request_exception(format("Invalid unset value for tuple {}", _receiver->name->text()));
} else {
auto& type = static_cast<const list_type_impl&>(*_receiver->type);
auto& elem_type = static_cast<const tuple_type_impl&>(*type.get_elements_type());
try {
auto l = value.validate_and_deserialize<list_type_impl::native_type>(type, options.get_cql_serialization_format());
for (auto&& element : l) {
elem_type.validate(elem_type.decompose(element), options.get_cql_serialization_format());
}
} catch (marshal_exception& e) {
throw exceptions::invalid_request_exception(e.what());
}
return make_shared<tuples::in_value>(tuples::in_value::from_serialized(value, type, options));
}
}
}

View File

@@ -1,211 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Modified by ScyllaDB
* Copyright (C) 2015-present ScyllaDB
*/
/*
* This file is part of Scylla.
*
* Scylla is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Scylla is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Scylla. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "term.hh"
#include "abstract_marker.hh"
#include "types/tuple.hh"
#include "types/collection.hh"
#include "utils/chunked_vector.hh"
#include "cql3/column_identifier.hh"
#include "cql3/expr/expression.hh"
class list_type_impl;
namespace cql3 {
/**
* Static helper methods and classes for tuples.
*/
class tuples {
public:
/**
* A tuple of terminal values (e.g (123, 'abc')).
*/
class value : public terminal {
public:
std::vector<managed_bytes_opt> _elements;
public:
value(std::vector<managed_bytes_opt> elements, data_type my_type)
: terminal(std::move(my_type)), _elements(std::move(elements)) {
}
static value from_serialized(const raw_value_view& buffer, const tuple_type_impl& type) {
return buffer.with_value([&] (const FragmentedView auto& view) {
return value(type.split_fragmented(view), type.shared_from_this());
});
}
virtual cql3::raw_value get(const query_options& options) override {
return cql3::raw_value::make_value(tuple_type_impl::build_value_fragmented(_elements));
}
size_t size() const {
return _elements.size();
}
virtual sstring to_string() const override {
return format("({})", join(", ", _elements));
}
};
/**
* Similar to Value, but contains at least one NonTerminal, such as a non-pure functions or bind marker.
*/
class delayed_value : public non_terminal {
tuple_type _type;
std::vector<shared_ptr<term>> _elements;
public:
delayed_value(tuple_type type, std::vector<shared_ptr<term>> elements)
: _type(std::move(type)), _elements(std::move(elements)) {
}
virtual bool contains_bind_marker() const override {
return std::any_of(_elements.begin(), _elements.end(), std::mem_fn(&term::contains_bind_marker));
}
virtual void fill_prepare_context(prepare_context& ctx) const override {
for (auto&& term : _elements) {
term->fill_prepare_context(ctx);
}
}
private:
std::vector<managed_bytes_opt> bind_internal(const query_options& options) {
std::vector<managed_bytes_opt> buffers;
buffers.resize(_elements.size());
for (size_t i = 0; i < _elements.size(); ++i) {
const auto& value = expr::evaluate_to_raw_view(_elements[i], options);
if (value.is_unset_value()) {
throw exceptions::invalid_request_exception(format("Invalid unset value for tuple field number {:d}", i));
}
buffers[i] = to_managed_bytes_opt(value);
// Inside tuples, we must force the serialization of collections to v3 whatever protocol
// version is in use since we're going to store directly that serialized value.
if (options.get_cql_serialization_format() != cql_serialization_format::internal()
&& _type->type(i)->is_collection()) {
if (buffers[i]) {
buffers[i] = static_pointer_cast<const collection_type_impl>(_type->type(i))->reserialize(
options.get_cql_serialization_format(),
cql_serialization_format::internal(),
managed_bytes_view(*buffers[i]));
}
}
}
return buffers;
}
public:
virtual shared_ptr<terminal> bind(const query_options& options) override {
return ::make_shared<value>(bind_internal(options), _type);
}
virtual expr::expression to_expression() override;
};
/**
* A terminal value for a list of IN values that are tuples. For example: "SELECT ... WHERE (a, b, c) IN ?"
* This is similar to Lists.Value, but allows us to keep components of the tuples in the list separate.
*/
class in_value : public terminal {
private:
utils::chunked_vector<std::vector<managed_bytes_opt>> _elements;
public:
in_value(utils::chunked_vector<std::vector<managed_bytes_opt>> items, data_type my_type)
: terminal(std::move(my_type)), _elements(std::move(items)) { }
static in_value from_serialized(const raw_value_view& value_view, const list_type_impl& type, const query_options& options);
virtual cql3::raw_value get(const query_options& options) override;
utils::chunked_vector<std::vector<managed_bytes_opt>> get_split_values() const {
return _elements;
}
virtual sstring to_string() const override {
std::vector<sstring> tuples(_elements.size());
std::transform(_elements.begin(), _elements.end(), tuples.begin(), &tuples::tuple_to_string<managed_bytes_opt>);
return tuple_to_string(tuples);
}
};
/**
* Represents a marker for a single tuple, like "SELECT ... WHERE (a, b, c) > ?"
*/
class marker : public abstract_marker {
public:
marker(int32_t bind_index, lw_shared_ptr<column_specification> receiver)
: abstract_marker(bind_index, std::move(receiver))
{ }
virtual shared_ptr<terminal> bind(const query_options& options) override {
const auto& value = options.get_value_at(_bind_index);
if (value.is_null()) {
return nullptr;
} else if (value.is_unset_value()) {
throw exceptions::invalid_request_exception(format("Invalid unset value for tuple {}", _receiver->name->text()));
} else {
auto& type = static_cast<const tuple_type_impl&>(*_receiver->type);
try {
value.validate(type, options.get_cql_serialization_format());
} catch (marshal_exception& e) {
throw exceptions::invalid_request_exception(
format("Exception while binding column {:s}: {:s}", _receiver->name->to_cql_string(), e.what()));
}
return make_shared<tuples::value>(value::from_serialized(value, type));
}
}
virtual expr::expression to_expression() override;
};
/**
* Represents a marker for a set of IN values that are tuples, like "SELECT ... WHERE (a, b, c) IN ?"
*/
class in_marker : public abstract_marker {
public:
in_marker(int32_t bind_index, lw_shared_ptr<column_specification> receiver);
virtual shared_ptr<terminal> bind(const query_options& options) override;
virtual expr::expression to_expression() override;
};
template <typename T>
static sstring tuple_to_string(const std::vector<T>& items) {
return format("({})", join(", ", items));
}
};
}

View File

@@ -101,31 +101,29 @@ static bytes from_json_object_aux(const map_type_impl& t, const rjson::value& va
if (!value.IsObject()) {
throw marshal_exception("map_type must be represented as JSON Object");
}
std::vector<bytes> raw_map;
raw_map.reserve(value.MemberCount());
std::map<bytes, bytes, serialized_compare> raw_map(t.get_keys_type()->as_less_comparator());
for (auto it = value.MemberBegin(); it != value.MemberEnd(); ++it) {
bytes value = from_json_object(*t.get_values_type(), it->value, sf);
if (!t.get_keys_type()->is_compatible_with(*utf8_type)) {
// Keys in maps can only be strings in JSON, but they can also be a string representation
// of another JSON type, which needs to be reparsed. Example - map<frozen<list<int>>, int>
// will be represented like this: { "[1, 3, 6]": 3, "[]": 0, "[1, 2]": 2 }
rjson::value map_key = rjson::parse(rjson::to_string_view(it->name));
raw_map.emplace_back(from_json_object(*t.get_keys_type(), map_key, sf));
raw_map.emplace(from_json_object(*t.get_keys_type(), map_key, sf), std::move(value));
} else {
raw_map.emplace_back(from_json_object(*t.get_keys_type(), it->name, sf));
raw_map.emplace(from_json_object(*t.get_keys_type(), it->name, sf), std::move(value));
}
raw_map.emplace_back(from_json_object(*t.get_values_type(), it->value, sf));
}
return collection_type_impl::pack(raw_map.begin(), raw_map.end(), raw_map.size() / 2, sf);
return map_type_impl::serialize_to_bytes(raw_map);
}
static bytes from_json_object_aux(const set_type_impl& t, const rjson::value& value, cql_serialization_format sf) {
if (!value.IsArray()) {
throw marshal_exception("set_type must be represented as JSON Array");
}
std::vector<bytes> raw_set;
raw_set.reserve(value.Size());
std::set<bytes, serialized_compare> raw_set(t.get_elements_type()->as_less_comparator());
for (const rjson::value& v : value.GetArray()) {
raw_set.emplace_back(from_json_object(*t.get_elements_type(), v, sf));
raw_set.emplace(from_json_object(*t.get_elements_type(), v, sf));
}
return collection_type_impl::pack(raw_set.begin(), raw_set.end(), raw_set.size(), sf);
}

View File

@@ -49,110 +49,8 @@
#include "types/user.hh"
namespace cql3 {
user_types::value::value(std::vector<managed_bytes_opt> elements, data_type my_type)
: terminal(std::move(my_type)), _elements(std::move(elements)) {
}
user_types::value user_types::value::from_serialized(const raw_value_view& v, const user_type_impl& type) {
return v.with_value([&] (const FragmentedView auto& val) {
std::vector<managed_bytes_opt> elements = type.split_fragmented(val);
if (elements.size() > type.size()) {
throw exceptions::invalid_request_exception(
format("User Defined Type value contained too many fields (expected {}, got {})", type.size(), elements.size()));
}
return value(std::move(elements), type.shared_from_this());
});
}
cql3::raw_value user_types::value::get(const query_options&) {
return cql3::raw_value::make_value(tuple_type_impl::build_value_fragmented(_elements));
}
sstring user_types::value::to_string() const {
return format("({})", join(", ", _elements));
}
user_types::delayed_value::delayed_value(user_type type, std::vector<shared_ptr<term>> values)
: _type(std::move(type)), _values(std::move(values)) {
}
bool user_types::delayed_value::contains_bind_marker() const {
return boost::algorithm::any_of(_values, std::mem_fn(&term::contains_bind_marker));
}
void user_types::delayed_value::fill_prepare_context(prepare_context& ctx) const {
for (auto&& v : _values) {
v->fill_prepare_context(ctx);
}
}
std::vector<managed_bytes_opt> user_types::delayed_value::bind_internal(const query_options& options) {
auto sf = options.get_cql_serialization_format();
// user_types::literal::prepare makes sure that every field gets a corresponding value.
// For missing fields the values become nullopts.
assert(_type->size() == _values.size());
std::vector<managed_bytes_opt> buffers;
for (size_t i = 0; i < _type->size(); ++i) {
const auto& value = expr::evaluate_to_raw_view(_values[i], options);
if (!_type->is_multi_cell() && value.is_unset_value()) {
throw exceptions::invalid_request_exception(format("Invalid unset value for field '{}' of user defined type {}",
_type->field_name_as_string(i), _type->get_name_as_string()));
}
buffers.push_back(to_managed_bytes_opt(value));
// Inside UDT values, we must force the serialization of collections to v3 whatever protocol
// version is in use since we're going to store directly that serialized value.
if (!sf.collection_format_unchanged() && _type->field_type(i)->is_collection() && buffers.back()) {
auto&& ctype = static_pointer_cast<const collection_type_impl>(_type->field_type(i));
buffers.back() = ctype->reserialize(sf, cql_serialization_format::latest(), managed_bytes_view(*buffers.back()));
}
}
return buffers;
}
shared_ptr<terminal> user_types::delayed_value::bind(const query_options& options) {
return ::make_shared<user_types::value>(bind_internal(options), _type);
}
expr::expression user_types::delayed_value::to_expression() {
expr::usertype_constructor::elements_map_type new_elements;
for (size_t i = 0; i < _values.size(); i++) {
column_identifier field_name(_type->field_names().at(i), _type->string_field_names().at(i));
expr::expression field_value(expr::to_expression(_values[i]));
new_elements.emplace(std::move(field_name), std::move(field_value));
}
return expr::usertype_constructor {
.elements = std::move(new_elements),
.type = _type
};
}
shared_ptr<terminal> user_types::marker::bind(const query_options& options) {
auto value = options.get_value_at(_bind_index);
if (value.is_null()) {
return nullptr;
}
if (value.is_unset_value()) {
return constants::UNSET_VALUE;
}
return make_shared<user_types::value>(value::from_serialized(value, static_cast<const user_type_impl&>(*_receiver->type)));
}
expr::expression user_types::marker::to_expression() {
return expr::bind_variable {
.shape = expr::bind_variable::shape_type::scalar,
.bind_index = _bind_index,
.value_type = _receiver->type
};
}
void user_types::setter::execute(mutation& m, const clustering_key_prefix& row_key, const update_parameters& params) {
const expr::constant value = expr::evaluate(_t, params._options);
const expr::constant value = expr::evaluate(*_e, params._options);
execute(m, row_key, params, column, value);
}
@@ -200,7 +98,7 @@ void user_types::setter::execute(mutation& m, const clustering_key_prefix& row_k
m.set_cell(row_key, column, mut.serialize(type));
} else {
if (!ut_value.is_null()) {
m.set_cell(row_key, column, params.make_cell(type, ut_value.value.to_view()));
m.set_cell(row_key, column, params.make_cell(type, ut_value.view()));
} else {
m.set_cell(row_key, column, params.make_dead_cell());
}
@@ -210,7 +108,7 @@ void user_types::setter::execute(mutation& m, const clustering_key_prefix& row_k
void user_types::setter_by_field::execute(mutation& m, const clustering_key_prefix& row_key, const update_parameters& params) {
assert(column.type->is_user_type() && column.type->is_multi_cell());
auto value = expr::evaluate_to_raw_view(_t, params._options);
auto value = expr::evaluate(*_e, params._options);
if (value.is_unset_value()) {
return;
}
@@ -218,8 +116,8 @@ void user_types::setter_by_field::execute(mutation& m, const clustering_key_pref
auto& type = static_cast<const user_type_impl&>(*column.type);
collection_mutation_description mut;
mut.cells.emplace_back(serialize_field_index(_field_idx), value
? params.make_cell(*type.type(_field_idx), value, atomic_cell::collection_member::yes)
mut.cells.emplace_back(serialize_field_index(_field_idx), !value.is_null()
? params.make_cell(*type.type(_field_idx), value.view(), atomic_cell::collection_member::yes)
: params.make_dead_cell());
m.set_cell(row_key, column, mut.serialize(type));

View File

@@ -42,7 +42,6 @@
#include "cql3/abstract_marker.hh"
#include "column_specification.hh"
#include "term.hh"
#include "column_identifier.hh"
#include "operation.hh"
#include "to_string.hh"
@@ -57,45 +56,6 @@ class user_types {
public:
static lw_shared_ptr<column_specification> field_spec_of(const column_specification& column, size_t field);
class value : public terminal {
std::vector<managed_bytes_opt> _elements;
public:
explicit value(std::vector<managed_bytes_opt>, data_type my_type);
static value from_serialized(const raw_value_view&, const user_type_impl&);
virtual cql3::raw_value get(const query_options&) override;
virtual sstring to_string() const override;
};
// Same purpose than Lists.DelayedValue, except we do handle bind marker in that case
class delayed_value : public non_terminal {
user_type _type;
std::vector<shared_ptr<term>> _values;
public:
delayed_value(user_type type, std::vector<shared_ptr<term>> values);
virtual bool contains_bind_marker() const override;
virtual void fill_prepare_context(prepare_context& ctx) const override;
private:
std::vector<managed_bytes_opt> bind_internal(const query_options& options);
public:
virtual shared_ptr<terminal> bind(const query_options& options) override;
virtual expr::expression to_expression() override;
};
class marker : public abstract_marker {
public:
marker(int32_t bind_index, lw_shared_ptr<column_specification> receiver)
: abstract_marker{bind_index, std::move(receiver)}
{
assert(_receiver->type->is_user_type());
}
virtual shared_ptr<terminal> bind(const query_options& options) override;
virtual expr::expression to_expression() override;
};
class setter : public operation {
public:
using operation::operation;
@@ -107,8 +67,8 @@ public:
class setter_by_field : public operation {
size_t _field_idx;
public:
setter_by_field(const column_definition& column, size_t field_idx, shared_ptr<term> t)
: operation(column, std::move(t)), _field_idx(field_idx) {
setter_by_field(const column_definition& column, size_t field_idx, expr::expression e)
: operation(column, std::move(e)), _field_idx(field_idx) {
}
virtual void execute(mutation& m, const clustering_key_prefix& row_key, const update_parameters& params) override;
@@ -118,7 +78,7 @@ public:
size_t _field_idx;
public:
deleter_by_field(const column_definition& column, size_t field_idx)
: operation(column, nullptr), _field_idx(field_idx) {
: operation(column, std::nullopt), _field_idx(field_idx) {
}
virtual void execute(mutation& m, const clustering_key_prefix& row_key, const update_parameters& params) override;

View File

@@ -420,8 +420,7 @@ BOOST_AUTO_TEST_CASE(expression_extract_column_restrictions) {
// AND pk1 AND pk2
// AND (pk1, pk2)
std::vector<expression> big_where;
::shared_ptr<constants::value> zero_value =
::make_shared<constants::value>(raw_value::make_value(I(0)), int32_type);
expr::constant zero_value = constant(raw_value::make_value(I(0)), int32_type);
expression pk1_restriction(binary_operator(column_value(&col_pk1), oper_t::EQ, zero_value));
expression pk2_restriction(binary_operator(column_value(&col_pk2), oper_t::EQ, zero_value));
@@ -438,10 +437,10 @@ BOOST_AUTO_TEST_CASE(expression_extract_column_restrictions) {
std::vector<managed_bytes_opt> zeros_tuple_elems(columns.size(), managed_bytes_opt(I(0)));
data_type tup_type = tuple_type_impl::get_instance(std::vector<data_type>(columns.size(), int32_type));
::shared_ptr<tuples::value> zeros_tuple =
::make_shared<tuples::value>(std::move(zeros_tuple_elems), std::move(tup_type));
managed_bytes tup_bytes = tuple_type_impl::build_value_fragmented(std::move(zeros_tuple_elems));
constant zeros_tuple(raw_value::make_value(std::move(tup_bytes)), std::move(tup_type));
return binary_operator(column_tuple, oper, zeros_tuple);
return binary_operator(column_tuple, oper, std::move(zeros_tuple));
};
expression pk1_pk2_restriction = make_multi_column_restriction({&col_pk1, &col_pk2}, oper_t::LT);

View File

@@ -31,6 +31,7 @@
#include "schema_fwd.hh"
#include "log.hh"
#include "cql_serialization_format.hh"
#include "exceptions/exceptions.hh"
namespace cql3 {

View File

@@ -61,6 +61,92 @@ public:
cql_serialization_format sf);
static managed_bytes serialize_partially_deserialized_form_fragmented(const std::vector<std::pair<managed_bytes_view, managed_bytes_view>>& v,
cql_serialization_format sf);
// Serializes a map using the internal cql serialization format
// Takes a range of pair<const bytes, bytes>
template <std::ranges::range Range>
requires std::convertible_to<std::ranges::range_value_t<Range>, std::pair<const bytes, bytes>>
static bytes serialize_to_bytes(const Range& map_range);
// Serializes a map using the internal cql serialization format
// Takes a range of pair<const managed_bytes, managed_bytes>
template <std::ranges::range Range>
requires std::convertible_to<std::ranges::range_value_t<Range>, std::pair<const managed_bytes, managed_bytes>>
static managed_bytes serialize_to_managed_bytes(const Range& map_range);
};
data_value make_map_value(data_type tuple_type, map_type_impl::native_type value);
template <std::ranges::range Range>
requires std::convertible_to<std::ranges::range_value_t<Range>, std::pair<const bytes, bytes>>
bytes map_type_impl::serialize_to_bytes(const Range& map_range) {
size_t serialized_len = 4;
size_t map_size = 0;
for (const std::pair<bytes, bytes>& elem : map_range) {
serialized_len += 4 + elem.first.size() + 4 + elem.second.size();
map_size += 1;
}
if (map_size > std::numeric_limits<int32_t>::max()) {
throw exceptions::invalid_request_exception(
fmt::format("Map size too large: {} > {}", map_size, std::numeric_limits<int32_t>::max()));
}
bytes result(bytes::initialized_later(), serialized_len);
bytes::iterator out = result.begin();
write_collection_size(out, map_size, cql_serialization_format::internal());
for (const std::pair<bytes, bytes>& elem : map_range) {
if (elem.first.size() > std::numeric_limits<int32_t>::max()) {
throw exceptions::invalid_request_exception(
fmt::format("Map key size too large: {} bytes > {}", map_size, std::numeric_limits<int32_t>::max()));
}
if (elem.second.size() > std::numeric_limits<int32_t>::max()) {
throw exceptions::invalid_request_exception(
fmt::format("Map value size too large: {} bytes > {}", map_size, std::numeric_limits<int32_t>::max()));
}
write_collection_value(out, cql_serialization_format::internal(), elem.first);
write_collection_value(out, cql_serialization_format::internal(), elem.second);
}
return result;
}
template <std::ranges::range Range>
requires std::convertible_to<std::ranges::range_value_t<Range>, std::pair<const managed_bytes, managed_bytes>>
managed_bytes map_type_impl::serialize_to_managed_bytes(const Range& map_range) {
size_t serialized_len = 4;
size_t map_size = 0;
for (const std::pair<managed_bytes, managed_bytes>& elem : map_range) {
serialized_len += 4 + elem.first.size() + 4 + elem.second.size();
map_size += 1;
}
if (map_size > std::numeric_limits<int32_t>::max()) {
throw exceptions::invalid_request_exception(
fmt::format("Map size too large: {} > {}", map_size, std::numeric_limits<int32_t>::max()));
}
managed_bytes result(managed_bytes::initialized_later(), serialized_len);
managed_bytes_mutable_view out(result);
write_collection_size(out, map_size, cql_serialization_format::internal());
for (const std::pair<managed_bytes, managed_bytes>& elem : map_range) {
if (elem.first.size() > std::numeric_limits<int32_t>::max()) {
throw exceptions::invalid_request_exception(
fmt::format("Map key size too large: {} bytes > {}", map_size, std::numeric_limits<int32_t>::max()));
}
if (elem.second.size() > std::numeric_limits<int32_t>::max()) {
throw exceptions::invalid_request_exception(
fmt::format("Map value size too large: {} bytes > {}", map_size, std::numeric_limits<int32_t>::max()));
}
write_collection_value(out, cql_serialization_format::internal(), elem.first);
write_collection_value(out, cql_serialization_format::internal(), elem.second);
}
return result;
}