cql3: Remove term

term isn't used anywhere now. We can remove it and all classes that derive from it.

Signed-off-by: Jan Ciolek <jan.ciolek@scylladb.com>
This commit is contained in:
Jan Ciolek
2021-10-08 23:47:38 +02:00
parent dcd3199037
commit e458340821
38 changed files with 103 additions and 1792 deletions

View File

@@ -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,7 +756,6 @@ 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',

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

@@ -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,
.receiver = _receiver
};
}
}

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,63 +59,6 @@ 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;

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,6 @@
#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 {
@@ -1312,40 +1310,6 @@ std::optional<bool> get_bool_value(const constant& constant_val) {
return constant_val.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));
}
constant evaluate(const expression& e, const query_options& options) {
return expr::visit(overloaded_functor {
[](const binary_operator&) -> constant {
@@ -1781,16 +1745,6 @@ 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();
return evaluate_IN_list(e, options);
}
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) {
@@ -1968,13 +1922,5 @@ 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;
}
expression to_expression(const ::shared_ptr<term>& term_ptr) {
if (term_ptr.get() == nullptr) {
return constant::make_null();
}
return term_ptr->to_expression();
}
} // namespace expr
} // namespace cql3

View File

@@ -52,7 +52,6 @@ namespace query {
} // namespace query
namespace cql3 {
struct term;
struct prepare_context;
class column_identifier_raw;
@@ -706,23 +705,10 @@ 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&);
constant evaluate_IN_list(const expression&, 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&);
// Takes a prepared expression and calculates its value.
// Evaluates bound values, calls functions and returns just the bytes and type.
constant evaluate(const expression& e, const query_options&);
@@ -755,8 +741,6 @@ void fill_prepare_context(expression&, cql3::prepare_context&);
// 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);
expression to_expression(const ::shared_ptr<term>&);
} // 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"
@@ -814,6 +813,95 @@ cast_prepare_expression(const cast& c, database& db, const sstring& keyspace, lw
return prepare_expression(c.arg, db, keyspace, 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{
@@ -839,7 +927,7 @@ prepare_expression(const expression& expr, database& db, const sstring& keyspace
on_internal_error(expr_logger, "column_mutation_attributes are not yet reachable via term_raw_expr::prepare()");
},
[&] (const function_call& fc) -> expression {
return functions::prepare_function_call(fc, db, keyspace, std::move(receiver));
return prepare_function_call(fc, db, keyspace, std::move(receiver));
},
[&] (const cast& c) -> expression {
return cast_prepare_expression(c, db, keyspace, receiver);
@@ -927,7 +1015,7 @@ test_assignment(const expression& expr, database& db, const sstring& keyspace, c
on_internal_error(expr_logger, "column_mutation_attributes are not yet reachable via term_raw_expr::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);

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);
};
expr::expression 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,9 +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 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();
}
@@ -446,217 +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 = ::make_shared<std::optional<uint8_t>>(_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());
}
));
}
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<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<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::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 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,164 +42,6 @@ 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,
.receiver = _receiver
};
}
void
lists::setter::execute(mutation& m, const clustering_key_prefix& prefix, const update_parameters& params) {
auto value = expr::evaluate(*_e, params._options);

View File

@@ -57,64 +57,6 @@ 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:

View File

@@ -41,179 +41,14 @@
#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,
.receiver = _receiver
};
}
void
maps::setter::execute(mutation& m, const clustering_key_prefix& row_key, const update_parameters& params) {
expr::constant value = expr::evaluate(*_e, params._options);

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,44 +58,6 @@ 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, expr::expression e)

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>
@@ -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

@@ -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"

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 {
@@ -99,10 +98,6 @@ void prepare_context::set_bound_variables(const std::vector<shared_ptr<column_id
}
void prepare_context::clear_pk_function_calls_cache() {
for (::shared_ptr<cql3::functions::function_call>& fun_call : _pk_fn_calls) {
fun_call->set_id(std::nullopt);
}
for (::shared_ptr<std::optional<uint8_t>>& cache_id : _pk_function_calls_cache_ids) {
if (cache_id.get() != nullptr) {
*cache_id = std::nullopt;
@@ -110,20 +105,6 @@ void prepare_context::clear_pk_function_calls_cache() {
}
}
void prepare_context::add_pk_function_call(::shared_ptr<cql3::functions::function_call> fn) {
constexpr auto fn_limit = std::numeric_limits<uint8_t>::max();
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_function_calls_cache_ids.size());
_pk_fn_calls.emplace_back(std::move(fn));
// Workaround for now, this will be removed later along with this method
_pk_function_calls_cache_ids.push_back({});
}
void prepare_context::add_pk_function_call(expr::function_call& fn) {
constexpr auto fn_limit = std::numeric_limits<uint8_t>::max();
if (_pk_function_calls_cache_ids.size() == fn_limit) {

View File

@@ -67,12 +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
@@ -100,7 +98,6 @@ public:
// 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

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 {

View File

@@ -45,7 +45,6 @@
#include "cql3/restrictions/restriction.hh"
#include "cql3/restrictions/bounds_slice.hh"
#include "cql3/term.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"

View File

@@ -26,154 +26,6 @@
#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,
.receiver = _receiver
};
}
void
sets::setter::execute(mutation& m, const clustering_key_prefix& row_key, const update_parameters& params) {
expr::constant value = expr::evaluate(*_e, params._options);

View File

@@ -58,42 +58,6 @@ 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, expr::expression e)

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 {

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

@@ -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

@@ -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

@@ -48,8 +48,6 @@
namespace cql3 {
class term;
namespace statements {
/**

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) {

View File

@@ -47,7 +47,6 @@
#include "relation.hh"
#include "column_identifier.hh"
#include "term.hh"
#include "restrictions/restriction.hh"
#include "expr/expression.hh"

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,
.receiver = _receiver
};
}
expr::expression tuples::in_marker::to_expression() {
return expr::bind_variable {
.shape = expr::bind_variable::shape_type::tuple_in,
.bind_index = _bind_index,
.receiver = _receiver
};
}
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

@@ -49,108 +49,6 @@
#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,
.receiver = _receiver
};
}
void user_types::setter::execute(mutation& m, const clustering_key_prefix& row_key, const update_parameters& params) {
const expr::constant value = expr::evaluate(*_e, params._options);
execute(m, row_key, params, column, value);

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;