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:
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
158
cql3/lists.cc
158
cql3/lists.cc
@@ -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);
|
||||
|
||||
@@ -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:
|
||||
|
||||
165
cql3/maps.cc
165
cql3/maps.cc
@@ -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);
|
||||
|
||||
39
cql3/maps.hh
39
cql3/maps.hh
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
148
cql3/sets.cc
148
cql3/sets.cc
@@ -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);
|
||||
|
||||
36
cql3/sets.hh
36
cql3/sets.hh
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -48,8 +48,6 @@
|
||||
|
||||
namespace cql3 {
|
||||
|
||||
class term;
|
||||
|
||||
namespace statements {
|
||||
|
||||
/**
|
||||
|
||||
179
cql3/term.hh
179
cql3/term.hh
@@ -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 {
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -47,7 +47,6 @@
|
||||
|
||||
#include "relation.hh"
|
||||
#include "column_identifier.hh"
|
||||
#include "term.hh"
|
||||
#include "restrictions/restriction.hh"
|
||||
#include "expr/expression.hh"
|
||||
|
||||
|
||||
126
cql3/tuples.cc
126
cql3/tuples.cc
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
211
cql3/tuples.hh
211
cql3/tuples.hh
@@ -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));
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user