Merge 'Remove all remaining restrictions classes' from Jan Ciołek
This PR removes all code that used classes `restriction`, `restrictions` and their children. There were two fields in `statement_restrictions` that needed to be dealt with: `_clustering_columns_restrictions` and `_nonprimary_key_restrictions`. Each function was reimplemented to operate on the new expression representaiion and eventually these fields weren't needed anymore. After that the restriction classes weren't used anymore and could be deleted as well. Now all of the code responsible for analyzing WHERE clause and planning a query works on expressions. Closes #11069 * github.com:scylladb/scylla: cql3: Remove all remaining restrictions code cql3: Move a function from restrictions class to the test cql3: Remove initial_key_restrictions cql3: expr: Remove convert_to_restriction cql3: Remove _new from _new_nonprimary_key_restrictions cql3: Remove _nonprimary_key_restrictions field cql3: Reimplement uses of _nonprimary_key_restrictions using expression cql3: Keep a map of single column nonprimary key restrictions cql3: Remove _new from _new_clustering_columns_restrictions cql3: Remove _clustering_columns_restrictions from statement_restrictions cql3: Use a variable instead of dynamic cast cql3: Use the new map of single column clustering restrictions cql3: Keep a map of single column clustering key restrictions cql3: Return an expression in get_clustering_columns_restrctions() cql3: Reimplement _clustering_columns_restrictions->has_supporting_index() cql3: Don't create single element conjunction cql3: Add expr::index_supports_some_column cql3: Reimplement has_unrestricted_components() cql3: Reimplement _clustering_columns_restrictions->need_filtering() cql3: Reimplement num_prefix_columns_that_need_not_be_filtered cql3: Use the new clustering restrictions field instead of ->expression cql3: Reimplement _clustering_columns_restrictions->size() using expressions cql3: Reimplement _clustering_columns_restrictions->get_column_defs() using expressions cql3: Reimplement _clustering_columns_restrictions->is_all_eq() using expressions cql3: expr: Add has_only_eq_binops function cql3: Reimplement _clustering_columns_restrictions->empty() using expressions
This commit is contained in:
@@ -1028,6 +1028,21 @@ bool has_supporting_index(
|
||||
support);
|
||||
}
|
||||
|
||||
bool index_supports_some_column(
|
||||
const expression& e,
|
||||
const secondary_index::secondary_index_manager& index_manager,
|
||||
allow_local_index allow_local) {
|
||||
single_column_restrictions_map single_col_restrictions = get_single_column_restrictions_map(e);
|
||||
|
||||
for (auto&& [col, col_restrictions] : single_col_restrictions) {
|
||||
if (has_supporting_index(col_restrictions, index_manager, allow_local)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const column_value& cv) {
|
||||
os << cv.col->name_as_text();
|
||||
return os;
|
||||
@@ -2461,5 +2476,15 @@ bool contains_multi_column_restriction(const expression& e) {
|
||||
});
|
||||
return find_res != nullptr;
|
||||
}
|
||||
|
||||
bool has_only_eq_binops(const expression& e) {
|
||||
const expr::binary_operator* non_eq_binop = find_in_expression<expr::binary_operator>(e,
|
||||
[](const expr::binary_operator& binop) {
|
||||
return binop.op != expr::oper_t::EQ;
|
||||
}
|
||||
);
|
||||
|
||||
return non_eq_binop == nullptr;
|
||||
}
|
||||
} // namespace expr
|
||||
} // namespace cql3
|
||||
|
||||
@@ -499,6 +499,13 @@ extern bool is_supported_by(const expression&, const secondary_index::index&);
|
||||
extern bool has_supporting_index(
|
||||
const expression&, const secondary_index::secondary_index_manager&, allow_local_index allow_local);
|
||||
|
||||
// Looks at each column indivudually and checks whether some index can support restrictions on this single column.
|
||||
// Expression has to consist only of single column restrictions.
|
||||
extern bool index_supports_some_column(
|
||||
const expression&,
|
||||
const secondary_index::secondary_index_manager&,
|
||||
allow_local_index allow_local);
|
||||
|
||||
extern sstring to_string(const expression&);
|
||||
|
||||
extern std::ostream& operator<<(std::ostream&, const column_value&);
|
||||
@@ -763,6 +770,8 @@ sstring get_columns_in_commons(const expression& a, const expression& b);
|
||||
bytes_opt value_for(const column_definition&, const expression&, const query_options&);
|
||||
|
||||
bool contains_multi_column_restriction(const expression&);
|
||||
|
||||
bool has_only_eq_binops(const expression&);
|
||||
} // namespace expr
|
||||
|
||||
} // namespace cql3
|
||||
|
||||
@@ -6,12 +6,10 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#include "restrictions.hh"
|
||||
#include "cql3/statements/request_validations.hh"
|
||||
#include "seastar/util/defer.hh"
|
||||
#include "cql3/prepare_context.hh"
|
||||
#include "cql3/restrictions/multi_column_restriction.hh"
|
||||
#include "cql3/restrictions/token_restriction.hh"
|
||||
#include "types/list.hh"
|
||||
|
||||
namespace cql3 {
|
||||
namespace expr {
|
||||
@@ -273,111 +271,5 @@ binary_operator validate_and_prepare_new_restriction(const binary_operator& rest
|
||||
return prepared_binop;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
::shared_ptr<restrictions::restriction> new_multi_column_restriction(const tuple_constructor& prepared_lhs_tuple,
|
||||
oper_t oper,
|
||||
expression prepared_rhs,
|
||||
comparison_order order,
|
||||
const schema_ptr& schema) {
|
||||
std::vector<const column_definition*> lhs_cols = to_column_definitions(prepared_lhs_tuple.elements);
|
||||
|
||||
if (oper == oper_t::EQ) {
|
||||
return ::make_shared<restrictions::multi_column_restriction::EQ>(schema,
|
||||
std::move(lhs_cols),
|
||||
std::move(prepared_rhs));
|
||||
}
|
||||
|
||||
if (oper == oper_t::LT) {
|
||||
return ::make_shared<restrictions::multi_column_restriction::slice>(schema,
|
||||
std::move(lhs_cols),
|
||||
statements::bound::END,
|
||||
false,
|
||||
std::move(prepared_rhs),
|
||||
order);
|
||||
}
|
||||
|
||||
if (oper == oper_t::LTE) {
|
||||
return ::make_shared<restrictions::multi_column_restriction::slice>(schema,
|
||||
std::move(lhs_cols),
|
||||
statements::bound::END,
|
||||
true,
|
||||
std::move(prepared_rhs),
|
||||
order);
|
||||
}
|
||||
|
||||
if (oper == oper_t::GT) {
|
||||
return ::make_shared<restrictions::multi_column_restriction::slice>(schema,
|
||||
std::move(lhs_cols),
|
||||
statements::bound::START,
|
||||
false,
|
||||
std::move(prepared_rhs),
|
||||
order);
|
||||
}
|
||||
|
||||
if (oper == oper_t::GTE) {
|
||||
return ::make_shared<restrictions::multi_column_restriction::slice>(schema,
|
||||
std::move(lhs_cols),
|
||||
statements::bound::START,
|
||||
true,
|
||||
std::move(prepared_rhs),
|
||||
order);
|
||||
}
|
||||
|
||||
if (oper == oper_t::IN) {
|
||||
if (auto rhs_marker = as_if<bind_variable>(&prepared_rhs)) {
|
||||
return ::make_shared<restrictions::multi_column_restriction::IN_with_marker>(schema,
|
||||
std::move(lhs_cols),
|
||||
std::move(*rhs_marker));
|
||||
}
|
||||
|
||||
if (auto rhs_list = as_if<collection_constructor>(&prepared_rhs)) {
|
||||
return ::make_shared<restrictions::multi_column_restriction::IN_with_values>(schema,
|
||||
std::move(lhs_cols),
|
||||
std::move(rhs_list->elements));
|
||||
}
|
||||
|
||||
if (auto rhs_list = as_if<constant>(&prepared_rhs)) {
|
||||
const list_type_impl* list_type = dynamic_cast<const list_type_impl*>(&rhs_list->type->without_reversed());
|
||||
const data_type& elements_type = list_type->get_elements_type();
|
||||
|
||||
utils::chunked_vector<managed_bytes> raw_elems = get_list_elements(rhs_list->value);
|
||||
std::vector<expression> list_elems;
|
||||
list_elems.reserve(raw_elems.size());
|
||||
|
||||
for (managed_bytes elem : std::move(raw_elems)) {
|
||||
raw_value elem_val = raw_value::make_value(std::move(elem));
|
||||
list_elems.emplace_back(constant(elem_val, elements_type));
|
||||
}
|
||||
|
||||
return ::make_shared<restrictions::multi_column_restriction::IN_with_values>(schema,
|
||||
std::move(lhs_cols),
|
||||
std::move(list_elems));
|
||||
}
|
||||
}
|
||||
|
||||
on_internal_error(expr_logger,
|
||||
fmt::format("new_multi_column_restriction operation type: {} not handled", oper));
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
::shared_ptr<restrictions::restriction> convert_to_restriction(const binary_operator& prepared_binop, const schema_ptr& schema) {
|
||||
if (is<column_value>(prepared_binop.lhs) || is<subscript>(prepared_binop.lhs)) {
|
||||
::shared_ptr<restrictions::restriction> r = ::make_shared<restrictions::restriction>();
|
||||
r->expression = prepared_binop;
|
||||
return r;
|
||||
} else if (auto lhs_tuple = as_if<tuple_constructor>(&prepared_binop.lhs)) {
|
||||
return new_multi_column_restriction(*lhs_tuple, prepared_binop.op, prepared_binop.rhs, prepared_binop.order, schema);
|
||||
} else if (auto lhs_token = as_if<token>(&prepared_binop.lhs)) {
|
||||
std::vector<const column_definition*> column_defs = to_column_definitions(lhs_token->args);
|
||||
::shared_ptr<restrictions::restriction> r = ::make_shared<restrictions::token_restriction>(std::move(column_defs));
|
||||
r->expression = prepared_binop;
|
||||
return r;
|
||||
} else {
|
||||
throw exceptions::invalid_request_exception(
|
||||
format("expr::validate_and_prepare_new_restriction unhandled restriction: {}", prepared_binop));
|
||||
}
|
||||
}
|
||||
} // namespace expr
|
||||
} // namespace cql3
|
||||
@@ -17,12 +17,5 @@ binary_operator validate_and_prepare_new_restriction(const binary_operator& rest
|
||||
data_dictionary::database db,
|
||||
schema_ptr schema,
|
||||
prepare_context& ctx);
|
||||
|
||||
|
||||
// Converts a prepared binary operator to an instance of the restriction class.
|
||||
// Doesn't perform any any validation checks.
|
||||
::shared_ptr<restrictions::restriction> convert_to_restriction(const binary_operator& prepared_binop,
|
||||
const schema_ptr& schema);
|
||||
|
||||
} // namespace expr
|
||||
} // namespace cql3
|
||||
|
||||
@@ -10,11 +10,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cql3/restrictions/restriction.hh"
|
||||
#include <seastar/core/shared_ptr.hh>
|
||||
#include "to_string.hh"
|
||||
#include "exceptions/exceptions.hh"
|
||||
#include "index/secondary_index_manager.hh"
|
||||
#include "cql3/expr/expression.hh"
|
||||
|
||||
namespace cql3 {
|
||||
|
||||
@@ -41,6 +41,26 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
static bounds_slice from_binary_operator(const expr::binary_operator& binop) {
|
||||
if (binop.op == expr::oper_t::LT) {
|
||||
return new_instance(statements::bound::END, false, binop.rhs);
|
||||
}
|
||||
|
||||
if (binop.op == expr::oper_t::LTE) {
|
||||
return new_instance(statements::bound::END, true, binop.rhs);
|
||||
}
|
||||
|
||||
if (binop.op == expr::oper_t::GT) {
|
||||
return new_instance(statements::bound::START, false, binop.rhs);
|
||||
}
|
||||
|
||||
if (binop.op == expr::oper_t::GTE) {
|
||||
return new_instance(statements::bound::START, true, binop.rhs);
|
||||
}
|
||||
|
||||
throw std::runtime_error(format("bounds_slice::from_binary_operator - invalid operator: {}", binop.op));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this slice has a boundary for the specified type.
|
||||
*
|
||||
|
||||
@@ -1,443 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015-present ScyllaDB
|
||||
*
|
||||
* Modified by ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
* SPDX-License-Identifier: (AGPL-3.0-or-later and Apache-2.0)
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cql3/statements/request_validations.hh"
|
||||
#include "cql3/restrictions/primary_key_restrictions.hh"
|
||||
#include "cql3/statements/request_validations.hh"
|
||||
#include "cql3/restrictions/single_column_primary_key_restrictions.hh"
|
||||
#include "cql3/restrictions/bounds_slice.hh"
|
||||
#include "cql3/constants.hh"
|
||||
#include "cql3/lists.hh"
|
||||
#include "cql3/expr/expression.hh"
|
||||
#include "types/list.hh"
|
||||
#include "types/tuple.hh"
|
||||
|
||||
namespace cql3 {
|
||||
|
||||
namespace restrictions {
|
||||
|
||||
inline
|
||||
expr::tuple_constructor
|
||||
column_definitions_as_tuple_constructor(const std::vector<const column_definition*>& defs) {
|
||||
std::vector<expr::expression> columns;
|
||||
std::vector<data_type> column_types;
|
||||
columns.reserve(defs.size());
|
||||
for (auto& def : defs) {
|
||||
columns.push_back(expr::column_value{def});
|
||||
column_types.push_back(def->type);
|
||||
}
|
||||
data_type ttype = tuple_type_impl::get_instance(std::move(column_types));
|
||||
return expr::tuple_constructor{std::move(columns), std::move(ttype)};
|
||||
}
|
||||
|
||||
class multi_column_restriction : public clustering_key_restrictions {
|
||||
private:
|
||||
bool _has_only_asc_columns;
|
||||
bool _has_only_desc_columns;
|
||||
protected:
|
||||
schema_ptr _schema;
|
||||
std::vector<const column_definition*> _column_defs;
|
||||
public:
|
||||
multi_column_restriction(schema_ptr schema, std::vector<const column_definition*>&& defs)
|
||||
: _schema(schema)
|
||||
, _column_defs(std::move(defs))
|
||||
{
|
||||
update_asc_desc_existence();
|
||||
}
|
||||
|
||||
virtual std::vector<const column_definition*> get_column_defs() const override {
|
||||
return _column_defs;
|
||||
}
|
||||
|
||||
virtual void merge_with(::shared_ptr<restriction> other) override {
|
||||
const auto as_pkr = dynamic_pointer_cast<clustering_key_restrictions>(other);
|
||||
statements::request_validations::check_true(bool(as_pkr),
|
||||
"Mixing single column relations and multi column relations on clustering columns is not allowed");
|
||||
do_merge_with(as_pkr);
|
||||
update_asc_desc_existence();
|
||||
expression = make_conjunction(std::move(expression), other->expression);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void do_merge_with(::shared_ptr<clustering_key_restrictions> other) = 0;
|
||||
|
||||
/**
|
||||
* Returns the names of the columns that are specified within this <code>Restrictions</code> and the other one
|
||||
* as a comma separated <code>String</code>.
|
||||
*
|
||||
* @param otherRestrictions the other restrictions
|
||||
* @return the names of the columns that are specified within this <code>Restrictions</code> and the other one
|
||||
* as a comma separated <code>String</code>.
|
||||
*/
|
||||
sstring get_columns_in_commons(::shared_ptr<restrictions> other) const {
|
||||
auto ours = get_column_defs();
|
||||
auto theirs = other->get_column_defs();
|
||||
|
||||
std::sort(ours.begin(), ours.end());
|
||||
std::sort(theirs.begin(), theirs.end());
|
||||
std::vector<const column_definition*> common;
|
||||
std::set_intersection(ours.begin(), ours.end(), theirs.begin(), theirs.end(), std::back_inserter(common));
|
||||
|
||||
sstring str;
|
||||
for (auto&& c : common) {
|
||||
if (!str.empty()) {
|
||||
str += " ,";
|
||||
}
|
||||
str += c->name_as_text();
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
virtual bool has_supporting_index(const secondary_index::secondary_index_manager& index_manager,
|
||||
expr::allow_local_index allow_local) const override {
|
||||
for (const auto& index : index_manager.list_indexes()) {
|
||||
if (!allow_local && index.metadata().local()) {
|
||||
continue;
|
||||
}
|
||||
if (is_supported_by(index))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool is_supported_by(const secondary_index::index& index) const = 0;
|
||||
|
||||
/**
|
||||
* @return true if the restriction contains at least one column of each
|
||||
* ordering, false otherwise.
|
||||
*/
|
||||
bool is_mixed_order() const {
|
||||
return !is_desc_order() && !is_asc_order();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if all the restricted columns ordered in descending
|
||||
* order, false otherwise
|
||||
*/
|
||||
bool is_desc_order() const {
|
||||
return _has_only_desc_columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if all the restricted columns ordered in ascending
|
||||
* order, false otherwise
|
||||
*/
|
||||
bool is_asc_order() const {
|
||||
return _has_only_asc_columns;
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* Updates the _has_only_asc_columns and _has_only_desc_columns fields.
|
||||
*/
|
||||
void update_asc_desc_existence() {
|
||||
std::size_t num_of_desc =
|
||||
std::count_if(_column_defs.begin(), _column_defs.end(), [] (const column_definition* cd) { return cd->type->is_reversed(); });
|
||||
_has_only_asc_columns = num_of_desc == 0;
|
||||
_has_only_desc_columns = num_of_desc == _column_defs.size();
|
||||
}
|
||||
#if 0
|
||||
/**
|
||||
* Check if this type of restriction is supported for the specified column by the specified index.
|
||||
* @param index the Secondary index
|
||||
*
|
||||
* @return <code>true</code> this type of restriction is supported by the specified index,
|
||||
* <code>false</code> otherwise.
|
||||
*/
|
||||
protected abstract boolean isSupportedBy(SecondaryIndex index);
|
||||
#endif
|
||||
public:
|
||||
class EQ;
|
||||
class IN;
|
||||
class IN_with_values;
|
||||
class IN_with_marker;
|
||||
|
||||
class slice;
|
||||
};
|
||||
|
||||
class multi_column_restriction::EQ final : public multi_column_restriction {
|
||||
private:
|
||||
expr::expression _value;
|
||||
public:
|
||||
EQ(schema_ptr schema, std::vector<const column_definition*> defs, expr::expression value)
|
||||
: multi_column_restriction(schema, std::move(defs))
|
||||
, _value(std::move(value))
|
||||
{
|
||||
using namespace expr;
|
||||
expression = binary_operator{
|
||||
column_definitions_as_tuple_constructor(_column_defs), oper_t::EQ, _value};
|
||||
}
|
||||
|
||||
virtual bool is_supported_by(const secondary_index::index& index) const override {
|
||||
for (auto* cdef : _column_defs) {
|
||||
if (index.supports_expression(*cdef, expr::oper_t::EQ)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual void do_merge_with(::shared_ptr<clustering_key_restrictions> other) override {
|
||||
throw exceptions::invalid_request_exception(format("{} cannot be restricted by more than one relation if it includes an Equal",
|
||||
get_columns_in_commons(other)));
|
||||
}
|
||||
#if 0
|
||||
@Override
|
||||
protected boolean isSupportedBy(SecondaryIndex index)
|
||||
{
|
||||
return index.supportsOperator(Operator.EQ);
|
||||
}
|
||||
#endif
|
||||
|
||||
clustering_key_prefix composite_value(const query_options& options) const {
|
||||
cql3::raw_value t = expr::evaluate(_value, options);
|
||||
auto values = expr::get_tuple_elements(t, *type_of(_value));
|
||||
std::vector<managed_bytes> components;
|
||||
for (unsigned i = 0; i < values.size(); i++) {
|
||||
auto component = statements::request_validations::check_not_null(values[i],
|
||||
"Invalid null value in condition for column {}",
|
||||
_column_defs.at(i)->name_as_text());
|
||||
components.emplace_back(*component);
|
||||
}
|
||||
return clustering_key_prefix::from_exploded(*_schema, std::move(components));
|
||||
}
|
||||
|
||||
#if 0
|
||||
@Override
|
||||
public final void addIndexExpressionTo(List<IndexExpression> expressions,
|
||||
QueryOptions options) throws InvalidRequestException
|
||||
{
|
||||
Tuples.Value t = ((Tuples.Value) value.bind(options));
|
||||
List<ByteBuffer> values = t.getElements();
|
||||
for (int i = 0; i < values.size(); i++)
|
||||
{
|
||||
ColumnDefinition columnDef = columnDefs.get(i);
|
||||
ByteBuffer component = validateIndexedValue(columnDef, values.get(i));
|
||||
expressions.add(new IndexExpression(columnDef.name.bytes, Operator.EQ, component));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
class multi_column_restriction::IN : public multi_column_restriction {
|
||||
public:
|
||||
IN(schema_ptr schema, std::vector<const column_definition*> defs)
|
||||
: multi_column_restriction(schema, std::move(defs))
|
||||
{ }
|
||||
|
||||
virtual bool is_supported_by(const secondary_index::index& index) const override {
|
||||
for (auto* cdef : _column_defs) {
|
||||
if (index.supports_expression(*cdef, expr::oper_t::IN)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#if 0
|
||||
@Override
|
||||
public void addIndexExpressionTo(List<IndexExpression> expressions,
|
||||
QueryOptions options) throws InvalidRequestException
|
||||
{
|
||||
List<List<ByteBuffer>> splitInValues = splitValues(options);
|
||||
checkTrue(splitInValues.size() == 1, "IN restrictions are not supported on indexed columns");
|
||||
|
||||
List<ByteBuffer> values = splitInValues.get(0);
|
||||
checkTrue(values.size() == 1, "IN restrictions are not supported on indexed columns");
|
||||
|
||||
ColumnDefinition columnDef = columnDefs.get(0);
|
||||
ByteBuffer component = validateIndexedValue(columnDef, values.get(0));
|
||||
expressions.add(new IndexExpression(columnDef.name.bytes, Operator.EQ, component));
|
||||
}
|
||||
#endif
|
||||
|
||||
virtual void do_merge_with(::shared_ptr<clustering_key_restrictions> other) override {
|
||||
throw exceptions::invalid_request_exception(format("{} cannot be restricted by more than one relation if it includes a IN",
|
||||
get_columns_in_commons(other)));
|
||||
}
|
||||
|
||||
#if 0
|
||||
@Override
|
||||
protected boolean isSupportedBy(SecondaryIndex index)
|
||||
{
|
||||
return index.supportsOperator(Operator.IN);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
* An IN restriction that has a set of terms for in values.
|
||||
* For example: "SELECT ... WHERE (a, b, c) IN ((1, 2, 3), (4, 5, 6))" or "WHERE (a, b, c) IN (?, ?)"
|
||||
*/
|
||||
class multi_column_restriction::IN_with_values final : public multi_column_restriction::IN {
|
||||
private:
|
||||
std::vector<expr::expression> _value;
|
||||
public:
|
||||
IN_with_values(schema_ptr schema, std::vector<const column_definition*> defs, std::vector<expr::expression> value)
|
||||
: multi_column_restriction::IN(schema, std::move(defs))
|
||||
, _value(std::move(value))
|
||||
{
|
||||
std::vector<data_type> column_types;
|
||||
column_types.reserve(defs.size());
|
||||
for (const column_definition* cdef : defs) {
|
||||
column_types.push_back(cdef->type);
|
||||
}
|
||||
|
||||
// type of the delayed_value is frozen list of tuples
|
||||
data_type list_elements_type = tuple_type_impl::get_instance(std::move(column_types));
|
||||
data_type in_list_type = list_type_impl::get_instance(std::move(list_elements_type), false);
|
||||
|
||||
expr::collection_constructor values_list {
|
||||
.style = expr::collection_constructor::style_type::list,
|
||||
.elements = _value,
|
||||
.type = std::move(in_list_type)
|
||||
};
|
||||
|
||||
using namespace expr;
|
||||
expression = binary_operator{
|
||||
column_definitions_as_tuple_constructor(_column_defs),
|
||||
oper_t::IN,
|
||||
std::move(values_list)};
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* An IN restriction that uses a single marker for a set of IN values that are tuples.
|
||||
* For example: "SELECT ... WHERE (a, b, c) IN ?"
|
||||
*/
|
||||
class multi_column_restriction::IN_with_marker final : public multi_column_restriction::IN {
|
||||
private:
|
||||
expr::bind_variable _marker;
|
||||
public:
|
||||
IN_with_marker(schema_ptr schema, std::vector<const column_definition*> defs, expr::bind_variable marker)
|
||||
: IN(schema, std::move(defs)), _marker(marker) {
|
||||
using namespace expr;
|
||||
expression = binary_operator{
|
||||
column_definitions_as_tuple_constructor(_column_defs),
|
||||
oper_t::IN,
|
||||
expr::expression(std::move(marker))};
|
||||
}
|
||||
};
|
||||
|
||||
class multi_column_restriction::slice final : public multi_column_restriction {
|
||||
using restriction_shared_ptr = ::shared_ptr<clustering_key_restrictions>;
|
||||
using mode = expr::comparison_order;
|
||||
bounds_slice _slice;
|
||||
mode _mode;
|
||||
|
||||
slice(schema_ptr schema, std::vector<const column_definition*> defs, bounds_slice slice, mode m)
|
||||
: multi_column_restriction(schema, std::move(defs))
|
||||
, _slice(slice)
|
||||
, _mode(m)
|
||||
{ }
|
||||
public:
|
||||
slice(schema_ptr schema, std::vector<const column_definition*> defs, statements::bound bound, bool inclusive, expr::expression e, mode m = mode::cql)
|
||||
: slice(schema, defs, bounds_slice::new_instance(bound, inclusive, e), m)
|
||||
{
|
||||
expression = expr::binary_operator{
|
||||
column_definitions_as_tuple_constructor(defs),
|
||||
expr::pick_operator(bound, inclusive),
|
||||
std::move(e),
|
||||
m};
|
||||
}
|
||||
|
||||
virtual bool is_supported_by(const secondary_index::index& index) const override {
|
||||
for (auto* cdef : _column_defs) {
|
||||
if (_slice.is_supported_by(*cdef, index)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#if 0
|
||||
@Override
|
||||
public void addIndexExpressionTo(List<IndexExpression> expressions,
|
||||
QueryOptions options) throws InvalidRequestException
|
||||
{
|
||||
throw invalidRequest("Slice restrictions are not supported on indexed columns which are part of a multi column relation");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isSupportedBy(SecondaryIndex index)
|
||||
{
|
||||
return slice.isSupportedBy(index);
|
||||
}
|
||||
|
||||
private static Composite.EOC eocFor(Restriction r, Bound eocBound, Bound inclusiveBound)
|
||||
{
|
||||
if (eocBound.isStart())
|
||||
return r.isInclusive(inclusiveBound) ? Composite.EOC.NONE : Composite.EOC.END;
|
||||
|
||||
return r.isInclusive(inclusiveBound) ? Composite.EOC.END : Composite.EOC.START;
|
||||
}
|
||||
#endif
|
||||
public:
|
||||
virtual void do_merge_with(::shared_ptr<clustering_key_restrictions> other) override {
|
||||
using namespace statements::request_validations;
|
||||
check_true(has_slice(other->expression),
|
||||
"Column \"{}\" cannot be restricted by both an equality and an inequality relation",
|
||||
get_columns_in_commons(other));
|
||||
auto other_slice = static_pointer_cast<slice>(other);
|
||||
|
||||
static auto mode2str = [](auto m) { return m == mode::cql ? "plain" : "SCYLLA_CLUSTERING_BOUND"; };
|
||||
check_true(other_slice->_mode == this->_mode,
|
||||
"Invalid combination of restrictions ({} / {})",
|
||||
mode2str(this->_mode), mode2str(other_slice->_mode)
|
||||
);
|
||||
check_false(_slice.has_bound(statements::bound::START) && other_slice->_slice.has_bound(statements::bound::START),
|
||||
"More than one restriction was found for the start bound on {}",
|
||||
get_columns_in_commons(other));
|
||||
check_false(_slice.has_bound(statements::bound::END) && other_slice->_slice.has_bound(statements::bound::END),
|
||||
"More than one restriction was found for the end bound on {}",
|
||||
get_columns_in_commons(other));
|
||||
|
||||
if (_column_defs.size() < other_slice->_column_defs.size()) {
|
||||
_column_defs = other_slice->_column_defs;
|
||||
}
|
||||
_slice.merge(other_slice->_slice);
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* The function returns the first real inequality component.
|
||||
* The first real inequality is the index of the first component in the
|
||||
* tuple that will turn into a slice single column restriction.
|
||||
* For example: (a, b, c) > (0, 1, 2) and (a, b, c) < (0, 1, 5) will be
|
||||
* broken into one single column restriction set of the form:
|
||||
* a = 0 and b = 1 and c > 2 and c < 5 , c is the first element that has
|
||||
* inequality so for this case the function will return 2.
|
||||
* @param start_components - the components of the starts tuple range.
|
||||
* @param end_components - the components of the end tuple range.
|
||||
* @return an empty value if not found and the index of the first index that
|
||||
* will yield inequality
|
||||
*/
|
||||
std::optional<std::size_t> find_first_neq_component(std::vector<bytes_opt>& start_components,
|
||||
std::vector<bytes_opt>& end_components) const {
|
||||
size_t common_components_count = std::min(start_components.size(), end_components.size());
|
||||
for (size_t i = 0; i < common_components_count ; i++) {
|
||||
if (start_components[i].value() != end_components[i].value()) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
size_t max_components_count = std::max(start_components.size(), end_components.size());
|
||||
if (common_components_count < max_components_count) {
|
||||
return common_components_count;
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,144 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015-present ScyllaDB
|
||||
*
|
||||
* Modified by ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
* SPDX-License-Identifier: (AGPL-3.0-or-later and Apache-2.0)
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "cql3/query_options.hh"
|
||||
#include "cql3/statements/bound.hh"
|
||||
#include "cql3/restrictions/restrictions.hh"
|
||||
#include "cql3/restrictions/restriction.hh"
|
||||
#include "cql3/restrictions/restriction.hh"
|
||||
#include "types.hh"
|
||||
#include "query-request.hh"
|
||||
#include <seastar/core/shared_ptr.hh>
|
||||
|
||||
namespace cql3 {
|
||||
namespace restrictions {
|
||||
|
||||
/**
|
||||
* A set of restrictions on a primary key part (partition key or clustering key).
|
||||
*
|
||||
* What was in AbstractPrimaryKeyRestrictions was moved here (In pre 1.8 Java interfaces could not have default
|
||||
* implementations of methods).
|
||||
*/
|
||||
|
||||
class partition_key_restrictions: public restriction, public restrictions, public enable_shared_from_this<partition_key_restrictions> {
|
||||
public:
|
||||
partition_key_restrictions() = default;
|
||||
|
||||
virtual void merge_with(::shared_ptr<restriction> other) = 0;
|
||||
|
||||
virtual ::shared_ptr<partition_key_restrictions> merge_to(schema_ptr, ::shared_ptr<restriction> restriction) {
|
||||
merge_with(restriction);
|
||||
return this->shared_from_this();
|
||||
}
|
||||
|
||||
using restrictions::has_supporting_index;
|
||||
|
||||
bool empty() const override {
|
||||
return get_column_defs().empty();
|
||||
}
|
||||
uint32_t size() const override {
|
||||
return uint32_t(get_column_defs().size());
|
||||
}
|
||||
|
||||
bool has_unrestricted_components(const schema& schema) const {
|
||||
return size() < schema.partition_key_size();
|
||||
}
|
||||
|
||||
virtual bool needs_filtering(const schema& schema) const {
|
||||
return !empty() && !has_token(expression) &&
|
||||
(has_unrestricted_components(schema) || has_slice_or_needs_filtering(expression));
|
||||
}
|
||||
|
||||
// NOTICE(sarna): This function is useless for partition key restrictions,
|
||||
// but it should remain here until single_column_primary_key_restrictions class is detemplatized.
|
||||
virtual unsigned int num_prefix_columns_that_need_not_be_filtered() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual bool is_all_eq() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t prefix_size(const schema&) const {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
class clustering_key_restrictions : public restriction, public restrictions, public enable_shared_from_this<clustering_key_restrictions> {
|
||||
public:
|
||||
clustering_key_restrictions() = default;
|
||||
|
||||
virtual void merge_with(::shared_ptr<restriction> other) = 0;
|
||||
|
||||
virtual ::shared_ptr<clustering_key_restrictions> merge_to(schema_ptr, ::shared_ptr<restriction> restriction) {
|
||||
merge_with(restriction);
|
||||
return this->shared_from_this();
|
||||
}
|
||||
|
||||
using restrictions::has_supporting_index;
|
||||
|
||||
bool empty() const override {
|
||||
return get_column_defs().empty();
|
||||
}
|
||||
uint32_t size() const override {
|
||||
return uint32_t(get_column_defs().size());
|
||||
}
|
||||
|
||||
bool has_unrestricted_components(const schema& schema) const {
|
||||
return size() < schema.clustering_key_size();
|
||||
}
|
||||
|
||||
virtual bool needs_filtering(const schema& schema) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
// How long a prefix of the restrictions could have resulted in
|
||||
// need_filtering() == false. These restrictions do not need to be
|
||||
// applied during filtering.
|
||||
// For example, if we have the filter "c1 < 3 and c2 > 3", c1 does
|
||||
// not need filtering (just a read stopping at c1=3) but c2 does,
|
||||
// so num_prefix_columns_that_need_not_be_filtered() will be 1.
|
||||
virtual unsigned int num_prefix_columns_that_need_not_be_filtered() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual bool is_all_eq() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t prefix_size(const schema& schema) const {
|
||||
size_t count = 0;
|
||||
if (schema.clustering_key_columns().empty()) {
|
||||
return count;
|
||||
}
|
||||
auto column_defs = get_column_defs();
|
||||
column_id expected_column_id = schema.clustering_key_columns().begin()->id;
|
||||
for (auto&& cdef : column_defs) {
|
||||
if (schema.position(*cdef) != expected_column_id) {
|
||||
return count;
|
||||
}
|
||||
expected_column_id++;
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
};
|
||||
|
||||
// FIXME(sarna): transitive hack only, do not judge. Should be dropped after all primary_key_restrictions<T> uses are removed from code.
|
||||
template<typename ValueType>
|
||||
using primary_key_restrictions = std::conditional_t<std::is_same_v<ValueType, partition_key>, partition_key_restrictions, clustering_key_restrictions>;
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2019-present ScyllaDB
|
||||
*
|
||||
* Modified by ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
* SPDX-License-Identifier: (AGPL-3.0-or-later and Apache-2.0)
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cql3/expr/expression.hh"
|
||||
|
||||
namespace cql3 {
|
||||
|
||||
namespace restrictions {
|
||||
|
||||
/**
|
||||
* Result of relation::to_restriction(). TODO: remove this class and rewrite to_restriction to return
|
||||
* expression.
|
||||
*/
|
||||
class restriction {
|
||||
public:
|
||||
// Init to false for now, to easily detect errors. This whole class is going away.
|
||||
cql3::expr::expression expression = expr::constant::make_bool(false);
|
||||
virtual ~restriction() {}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015-present ScyllaDB
|
||||
*
|
||||
* Modified by ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
* SPDX-License-Identifier: (AGPL-3.0-or-later and Apache-2.0)
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "cql3/query_options.hh"
|
||||
#include "types.hh"
|
||||
#include "schema_fwd.hh"
|
||||
#include "index/secondary_index_manager.hh"
|
||||
#include "restriction.hh"
|
||||
|
||||
namespace cql3 {
|
||||
|
||||
namespace restrictions {
|
||||
|
||||
/**
|
||||
* Sets of restrictions
|
||||
*/
|
||||
class restrictions {
|
||||
public:
|
||||
virtual ~restrictions() {}
|
||||
|
||||
/**
|
||||
* Returns the column definitions in position order.
|
||||
* @return the column definitions in position order.
|
||||
*/
|
||||
virtual std::vector<const column_definition*> get_column_defs() const = 0;
|
||||
|
||||
virtual bytes_opt value_for(const column_definition& cdef, const query_options& options) const {
|
||||
throw exceptions::invalid_request_exception("Single value can be obtained from single-column restrictions only");
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the restriction is on indexed columns.
|
||||
*
|
||||
* @param index_manager the index manager
|
||||
* @return <code>true</code> if the restriction is on indexed columns, <code>false</code>
|
||||
*/
|
||||
virtual bool has_supporting_index(const secondary_index::secondary_index_manager& index_manager,
|
||||
expr::allow_local_index allow_local) const = 0;
|
||||
|
||||
#if 0
|
||||
/**
|
||||
* Adds to the specified list the <code>index_expression</code>s corresponding to this <code>Restriction</code>.
|
||||
*
|
||||
* @param expressions the list to add the <code>index_expression</code>s to
|
||||
* @param options the query options
|
||||
* @throws InvalidRequestException if this <code>Restriction</code> cannot be converted into
|
||||
* <code>index_expression</code>s
|
||||
*/
|
||||
virtual void add_index_expression_to(std::vector<::shared_ptr<index_expression>>& expressions,
|
||||
const query_options& options) = 0;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Checks if this <code>SingleColumnprimary_key_restrictions</code> is empty or not.
|
||||
*
|
||||
* @return <code>true</code> if this <code>SingleColumnprimary_key_restrictions</code> is empty, <code>false</code> otherwise.
|
||||
*/
|
||||
virtual bool empty() const = 0;
|
||||
|
||||
/**
|
||||
* Returns the number of columns that have a restriction.
|
||||
*
|
||||
* @return the number of columns that have a restriction.
|
||||
*/
|
||||
virtual uint32_t size() const = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,270 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015-present ScyllaDB
|
||||
*
|
||||
* Modified by ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
* SPDX-License-Identifier: (AGPL-3.0-or-later and Apache-2.0)
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
#include "schema_fwd.hh"
|
||||
#include "cartesian_product.hh"
|
||||
#include "cql3/restrictions/primary_key_restrictions.hh"
|
||||
#include "cql3/restrictions/single_column_restrictions.hh"
|
||||
#include "cql3/cql_config.hh"
|
||||
#include "clustering_bounds_comparator.hh"
|
||||
#include <boost/range/adaptor/transformed.hpp>
|
||||
#include <boost/range/adaptor/filtered.hpp>
|
||||
#include <boost/range/adaptor/map.hpp>
|
||||
|
||||
namespace cql3 {
|
||||
|
||||
namespace restrictions {
|
||||
|
||||
namespace {
|
||||
|
||||
template <typename ValueType>
|
||||
const char*
|
||||
restricted_component_name_v;
|
||||
|
||||
template <>
|
||||
const char* restricted_component_name_v<partition_key> = "partition key";
|
||||
|
||||
template <>
|
||||
const char* restricted_component_name_v<clustering_key> = "clustering key";
|
||||
|
||||
|
||||
inline
|
||||
void check_cartesian_product_size(size_t size, size_t max, const char* component_name) {
|
||||
if (size > max) {
|
||||
throw std::runtime_error(fmt::format("{} cartesian product size {} is greater than maximum {}",
|
||||
component_name, size, max));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A set of single column restrictions on a primary key part (partition key or clustering key).
|
||||
*/
|
||||
template<typename ValueType>
|
||||
class single_column_primary_key_restrictions : public primary_key_restrictions<ValueType> {
|
||||
using range_type = query::range<ValueType>;
|
||||
using range_bound = typename range_type::bound;
|
||||
template<typename OtherValueType>
|
||||
friend class single_column_primary_key_restrictions;
|
||||
private:
|
||||
schema_ptr _schema;
|
||||
bool _allow_filtering;
|
||||
::shared_ptr<single_column_restrictions> _restrictions;
|
||||
private:
|
||||
static uint32_t max_cartesian_product_size(const restrictions_config& config);
|
||||
public:
|
||||
single_column_primary_key_restrictions(schema_ptr schema, bool allow_filtering)
|
||||
: _schema(schema)
|
||||
, _allow_filtering(allow_filtering)
|
||||
, _restrictions(::make_shared<single_column_restrictions>(schema))
|
||||
{
|
||||
this->expression = expr::conjunction{}; // This will track _restrictions, which is a conjunction.
|
||||
}
|
||||
|
||||
// Convert another primary key restrictions type into this type, possibly using different schema
|
||||
template<typename OtherValueType>
|
||||
explicit single_column_primary_key_restrictions(schema_ptr schema, const single_column_primary_key_restrictions<OtherValueType>& other)
|
||||
: _schema(schema)
|
||||
, _allow_filtering(other._allow_filtering)
|
||||
, _restrictions(::make_shared<single_column_restrictions>(schema))
|
||||
{
|
||||
for (const auto& entry : other.restrictions()) {
|
||||
const column_definition* other_cdef = entry.first;
|
||||
const column_definition* this_cdef = _schema->get_column_definition(other_cdef->name());
|
||||
if (!this_cdef) {
|
||||
throw exceptions::invalid_request_exception(format("Base column {} not found in view index schema", other_cdef->name_as_text()));
|
||||
}
|
||||
auto r = ::make_shared<restriction>(*this_cdef);
|
||||
r->expression = replace_column_def(entry.second->expression, this_cdef);
|
||||
_restrictions->add_restriction(r);
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool is_all_eq() const override {
|
||||
return _restrictions->is_all_eq();
|
||||
}
|
||||
|
||||
void do_merge_with(const ::shared_ptr<restriction>& single_column_restriction) {
|
||||
if (!_restrictions->empty() && !_allow_filtering) {
|
||||
auto last_column = *_restrictions->last_column();
|
||||
auto new_column = *get_the_only_column(single_column_restriction->expression).col;
|
||||
|
||||
if (has_slice(this->expression) && _schema->position(new_column) > _schema->position(last_column)) {
|
||||
throw exceptions::invalid_request_exception(format("Clustering column \"{}\" cannot be restricted (preceding column \"{}\" is restricted by a non-EQ relation)",
|
||||
new_column.name_as_text(), last_column.name_as_text()));
|
||||
}
|
||||
|
||||
if (_schema->position(new_column) < _schema->position(last_column)) {
|
||||
if (has_slice(single_column_restriction->expression)) {
|
||||
throw exceptions::invalid_request_exception(format("PRIMARY KEY column \"{}\" cannot be restricted (preceding column \"{}\" is restricted by a non-EQ relation)",
|
||||
last_column.name_as_text(), new_column.name_as_text()));
|
||||
}
|
||||
}
|
||||
}
|
||||
_restrictions->add_restriction(single_column_restriction);
|
||||
this->expression = make_conjunction(std::move(this->expression), single_column_restriction->expression);
|
||||
}
|
||||
|
||||
virtual void merge_with(::shared_ptr<restriction> restriction) override {
|
||||
if (find_binop(restriction->expression, [] (const expr::binary_operator& b) {
|
||||
return expr::is<expr::tuple_constructor>(b.lhs);
|
||||
})) {
|
||||
throw exceptions::invalid_request_exception(
|
||||
"Mixing single column relations and multi column relations on clustering columns is not allowed");
|
||||
}
|
||||
if (has_token(restriction->expression)) {
|
||||
throw exceptions::invalid_request_exception(
|
||||
format("Columns \"{}\" cannot be restricted by both a normal relation and a token relation",
|
||||
join(", ", get_column_defs())));
|
||||
}
|
||||
do_merge_with(restriction);
|
||||
}
|
||||
|
||||
std::vector<ValueType> values_as_keys(const query_options& options) const {
|
||||
std::vector<std::vector<managed_bytes_opt>> value_vector;
|
||||
value_vector.reserve(_restrictions->size());
|
||||
for (auto&& e : restrictions()) {
|
||||
auto&& r = e.second;
|
||||
assert(!has_slice(r->expression));
|
||||
auto values = expr::as<expr::value_list>(possible_lhs_values(e.first, r->expression, options));
|
||||
if (values.empty()) {
|
||||
return {};
|
||||
}
|
||||
value_vector.emplace_back(std::make_move_iterator(values.begin()), std::make_move_iterator(values.end()));
|
||||
}
|
||||
|
||||
std::vector<ValueType> result;
|
||||
auto size = cartesian_product_size(value_vector);
|
||||
check_cartesian_product_size(size, max_cartesian_product_size(options.get_cql_config().restrictions),
|
||||
restricted_component_name_v<ValueType>);
|
||||
result.reserve(size);
|
||||
for (auto&& v : make_cartesian_product(value_vector)) {
|
||||
result.emplace_back(ValueType::from_optional_exploded(*_schema, std::move(v)));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public:
|
||||
std::vector<bytes_opt> values(const query_options& options) const {
|
||||
auto src = values_as_keys(options);
|
||||
std::vector<bytes_opt> res;
|
||||
for (const ValueType& r : src) {
|
||||
for (const auto& component : r.components()) {
|
||||
res.emplace_back(to_bytes(component));
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
virtual bytes_opt value_for(const column_definition& cdef, const query_options& options) const override {
|
||||
return _restrictions->value_for(cdef, options);
|
||||
}
|
||||
|
||||
const single_column_restrictions::restrictions_map& restrictions() const {
|
||||
return _restrictions->restrictions();
|
||||
}
|
||||
|
||||
virtual bool has_supporting_index(const secondary_index::secondary_index_manager& index_manager,
|
||||
expr::allow_local_index allow_local) const override {
|
||||
return _restrictions->has_supporting_index(index_manager, allow_local);
|
||||
}
|
||||
|
||||
#if 0
|
||||
virtual void addIndexExpressionTo(List<IndexExpression> expressions, QueryOptions options) override {
|
||||
restrictions.addIndexExpressionTo(expressions, options);
|
||||
}
|
||||
#endif
|
||||
|
||||
virtual std::vector<const column_definition*> get_column_defs() const override {
|
||||
return _restrictions->get_column_defs();
|
||||
}
|
||||
|
||||
virtual bool empty() const override {
|
||||
return _restrictions->empty();
|
||||
}
|
||||
|
||||
virtual uint32_t size() const override {
|
||||
return _restrictions->size();
|
||||
}
|
||||
|
||||
virtual bool needs_filtering(const schema& schema) const override;
|
||||
virtual unsigned int num_prefix_columns_that_need_not_be_filtered() const override;
|
||||
};
|
||||
|
||||
template<>
|
||||
inline bool single_column_primary_key_restrictions<partition_key>::needs_filtering(const schema& schema) const {
|
||||
return primary_key_restrictions<partition_key>::needs_filtering(schema);
|
||||
}
|
||||
|
||||
// How many of the restrictions (in column order) do not need filtering
|
||||
// because they are implemented as a slice (potentially, a contiguous disk
|
||||
// read). For example, if we have the filter "c1 < 3 and c2 > 3", c1 does not
|
||||
// need filtering but c2 does so num_prefix_columns_that_need_not_be_filtered
|
||||
// will be 1.
|
||||
template<>
|
||||
inline unsigned single_column_primary_key_restrictions<clustering_key>::num_prefix_columns_that_need_not_be_filtered() const {
|
||||
// Restrictions currently need filtering in three cases:
|
||||
// 1. any of them is a CONTAINS restriction
|
||||
// 2. restrictions do not form a contiguous prefix (i.e. there are gaps in it)
|
||||
// 3. a SLICE restriction isn't on a last place
|
||||
column_id position = 0;
|
||||
unsigned int count = 0;
|
||||
for (const auto& restriction : restrictions() | boost::adaptors::map_values) {
|
||||
if (find_needs_filtering(restriction->expression)
|
||||
|| position != get_the_only_column(restriction->expression).col->id) {
|
||||
return count;
|
||||
}
|
||||
if (!has_slice(restriction->expression)) {
|
||||
position = get_the_only_column(restriction->expression).col->id + 1;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline bool single_column_primary_key_restrictions<clustering_key>::needs_filtering(const schema&) const {
|
||||
return num_prefix_columns_that_need_not_be_filtered() < size();
|
||||
}
|
||||
|
||||
template<>
|
||||
inline unsigned single_column_primary_key_restrictions<partition_key>::num_prefix_columns_that_need_not_be_filtered() const {
|
||||
// skip_filtering() is currently called only for clustering key
|
||||
// restrictions, so it doesn't matter what we return here.
|
||||
return 0;
|
||||
}
|
||||
|
||||
//TODO(sarna): These should be transformed into actual class definitions after detemplatizing single_column_primary_key_restrictions<T>
|
||||
using single_column_partition_key_restrictions = single_column_primary_key_restrictions<partition_key>;
|
||||
using single_column_clustering_key_restrictions = single_column_primary_key_restrictions<clustering_key>;
|
||||
|
||||
template <>
|
||||
inline
|
||||
uint32_t single_column_primary_key_restrictions<partition_key>::max_cartesian_product_size(const restrictions_config& config) {
|
||||
return config.partition_key_restrictions_max_cartesian_product_size;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline
|
||||
uint32_t single_column_primary_key_restrictions<clustering_key>::max_cartesian_product_size(const restrictions_config& config) {
|
||||
return config.clustering_key_restrictions_max_cartesian_product_size;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,212 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015-present ScyllaDB
|
||||
*
|
||||
* Modified by ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
* SPDX-License-Identifier: (AGPL-3.0-or-later and Apache-2.0)
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cql3/restrictions/restrictions.hh"
|
||||
#include "schema_fwd.hh"
|
||||
#include "types.hh"
|
||||
|
||||
namespace cql3 {
|
||||
|
||||
namespace restrictions {
|
||||
|
||||
/**
|
||||
* Sets of single column _restrictions.
|
||||
*/
|
||||
class single_column_restrictions : public restrictions {
|
||||
private:
|
||||
/**
|
||||
* The comparator used to sort the <code>restriction</code>s.
|
||||
*/
|
||||
struct column_definition_comparator {
|
||||
schema_ptr _schema;
|
||||
bool operator()(const column_definition* def1, const column_definition* def2) const {
|
||||
auto pos1 = _schema->position(*def1);
|
||||
auto pos2 = _schema->position(*def2);
|
||||
if (pos1 != pos2) {
|
||||
return pos1 < pos2;
|
||||
}
|
||||
// FIXME: shouldn't we use regular column name comparator here? Origin does not...
|
||||
return less_unsigned(def1->name(), def2->name());
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The _restrictions per column.
|
||||
*/
|
||||
public:
|
||||
using restrictions_map = std::map<const column_definition*, ::shared_ptr<restriction>, column_definition_comparator>;
|
||||
private:
|
||||
restrictions_map _restrictions;
|
||||
bool _is_all_eq = true;
|
||||
public:
|
||||
single_column_restrictions(schema_ptr schema)
|
||||
: _restrictions(column_definition_comparator{std::move(schema)})
|
||||
{ }
|
||||
|
||||
#if 0
|
||||
@Override
|
||||
public final void addIndexExpressionTo(List<IndexExpression> expressions,
|
||||
QueryOptions options) throws InvalidRequestException
|
||||
{
|
||||
for (Restriction restriction : _restrictions.values())
|
||||
restriction.addIndexExpressionTo(expressions, options);
|
||||
}
|
||||
#endif
|
||||
|
||||
virtual std::vector<const column_definition*> get_column_defs() const override {
|
||||
std::vector<const column_definition*> r;
|
||||
for (auto&& e : _restrictions) {
|
||||
r.push_back(e.first);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
virtual bytes_opt value_for(const column_definition& cdef, const query_options& options) const override {
|
||||
auto it = _restrictions.find(std::addressof(cdef));
|
||||
if (it == _restrictions.end()) {
|
||||
return bytes_opt{};
|
||||
} else {
|
||||
const auto values = std::get<expr::value_list>(possible_lhs_values(&cdef, it->second->expression, options));
|
||||
if (values.empty()) {
|
||||
return bytes_opt{};
|
||||
}
|
||||
assert(values.size() == 1);
|
||||
return to_bytes(values.front());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the restriction associated to the specified column.
|
||||
*
|
||||
* @param column_def the column definition
|
||||
* @return the restriction associated to the specified column
|
||||
*/
|
||||
::shared_ptr<restriction> get_restriction(const column_definition& column_def) const {
|
||||
auto i = _restrictions.find(&column_def);
|
||||
if (i == _restrictions.end()) {
|
||||
return {};
|
||||
}
|
||||
return i->second;
|
||||
}
|
||||
|
||||
virtual bool empty() const override {
|
||||
return _restrictions.empty();
|
||||
}
|
||||
|
||||
virtual uint32_t size() const override {
|
||||
return _restrictions.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the specified restriction to this set of _restrictions.
|
||||
*
|
||||
* @param restriction the restriction to add
|
||||
* @throws InvalidRequestException if the new restriction cannot be added
|
||||
*/
|
||||
void add_restriction(::shared_ptr<restriction> restriction) {
|
||||
if (!find(restriction->expression, expr::oper_t::EQ)) {
|
||||
_is_all_eq = false;
|
||||
}
|
||||
|
||||
auto i = _restrictions.find(get_the_only_column(restriction->expression).col);
|
||||
if (i == _restrictions.end()) {
|
||||
_restrictions.emplace_hint(i, get_the_only_column(restriction->expression).col, std::move(restriction));
|
||||
} else {
|
||||
auto& e = i->second->expression;
|
||||
e = make_conjunction(std::move(e), restriction->expression);
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool has_supporting_index(const secondary_index::secondary_index_manager& index_manager,
|
||||
expr::allow_local_index allow_local) const override {
|
||||
for (auto&& e : _restrictions) {
|
||||
if (expr::has_supporting_index(e.second->expression, index_manager, allow_local)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the column after the specified one.
|
||||
*
|
||||
* @param column_def the column for which the next one need to be found
|
||||
* @return the column after the specified one.
|
||||
*/
|
||||
const column_definition* next_column(const column_definition& column_def) const {
|
||||
auto i = _restrictions.find(&column_def);
|
||||
if (i == _restrictions.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
++i;
|
||||
if (i == _restrictions.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
return i->first;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the definition of the last column.
|
||||
*
|
||||
* @return the definition of the last column.
|
||||
*/
|
||||
const column_definition* last_column() const {
|
||||
if (_restrictions.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
auto i = _restrictions.end();
|
||||
--i;
|
||||
return i->first;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last restriction.
|
||||
*
|
||||
* @return the last restriction.
|
||||
*/
|
||||
::shared_ptr<restriction> last_restriction() const {
|
||||
if (_restrictions.empty()) {
|
||||
return {};
|
||||
}
|
||||
auto i = _restrictions.end();
|
||||
--i;
|
||||
return i->second;
|
||||
}
|
||||
|
||||
const restrictions_map& restrictions() const {
|
||||
return _restrictions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the _restrictions contains multiple contains, contains key, or map[key] = value.
|
||||
*
|
||||
* @return <code>true</code> if the _restrictions contains multiple contains, contains key, or ,
|
||||
* map[key] = value; <code>false</code> otherwise
|
||||
*/
|
||||
bool has_multiple_contains() const {
|
||||
uint32_t number_of_contains = 0;
|
||||
for (auto&& e : _restrictions) {
|
||||
number_of_contains += count_if(e.second->expression, expr::is_on_collection);
|
||||
if (number_of_contains > 1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return number_of_contains > 1;
|
||||
}
|
||||
|
||||
bool is_all_eq() const {
|
||||
return _is_all_eq;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
@@ -19,10 +19,9 @@
|
||||
#include "cql3/expr/expression.hh"
|
||||
#include "query-result-reader.hh"
|
||||
#include "statement_restrictions.hh"
|
||||
#include "multi_column_restriction.hh"
|
||||
#include "token_restriction.hh"
|
||||
#include "data_dictionary/data_dictionary.hh"
|
||||
#include "cartesian_product.hh"
|
||||
#include "cql3/cql_config.hh"
|
||||
|
||||
#include "cql3/constants.hh"
|
||||
#include "cql3/lists.hh"
|
||||
@@ -31,7 +30,6 @@
|
||||
#include "types/list.hh"
|
||||
#include "types/map.hh"
|
||||
#include "types/set.hh"
|
||||
#include "cql3/expr/restrictions.hh"
|
||||
|
||||
namespace cql3 {
|
||||
namespace restrictions {
|
||||
@@ -42,80 +40,8 @@ using boost::adaptors::filtered;
|
||||
using boost::adaptors::transformed;
|
||||
using statements::request_validations::invalid_request;
|
||||
|
||||
template<typename T>
|
||||
class statement_restrictions::initial_key_restrictions : public primary_key_restrictions<T> {
|
||||
bool _allow_filtering;
|
||||
public:
|
||||
initial_key_restrictions(bool allow_filtering)
|
||||
: _allow_filtering(allow_filtering) {
|
||||
this->expression = expr::constant::make_bool(true);
|
||||
}
|
||||
|
||||
::shared_ptr<primary_key_restrictions<T>> do_merge_to(schema_ptr schema, ::shared_ptr<restriction> restriction) const {
|
||||
return ::make_shared<single_column_primary_key_restrictions<T>>(schema, _allow_filtering)->merge_to(schema, restriction);
|
||||
}
|
||||
::shared_ptr<primary_key_restrictions<T>> merge_to(schema_ptr schema, ::shared_ptr<restriction> restriction) override {
|
||||
if (has_token(restriction->expression)) {
|
||||
return static_pointer_cast<token_restriction>(restriction);
|
||||
}
|
||||
return ::make_shared<single_column_primary_key_restrictions<T>>(schema, _allow_filtering)->merge_to(restriction);
|
||||
}
|
||||
void merge_with(::shared_ptr<restriction> restriction) override {
|
||||
throw exceptions::unsupported_operation_exception();
|
||||
}
|
||||
bytes_opt value_for(const column_definition& cdef, const query_options& options) const override {
|
||||
return {};
|
||||
}
|
||||
std::vector<const column_definition*> get_column_defs() const override {
|
||||
// throw? should not reach?
|
||||
return {};
|
||||
}
|
||||
bool empty() const override {
|
||||
return true;
|
||||
}
|
||||
uint32_t size() const override {
|
||||
return 0;
|
||||
}
|
||||
virtual bool has_supporting_index(const secondary_index::secondary_index_manager& index_manager,
|
||||
expr::allow_local_index allow_local) const override {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
::shared_ptr<primary_key_restrictions<partition_key>>
|
||||
statement_restrictions::initial_key_restrictions<partition_key>::merge_to(schema_ptr schema, ::shared_ptr<restriction> restriction) {
|
||||
if (has_token(restriction->expression)) {
|
||||
return static_pointer_cast<token_restriction>(restriction);
|
||||
}
|
||||
return do_merge_to(std::move(schema), std::move(restriction));
|
||||
}
|
||||
|
||||
template<>
|
||||
::shared_ptr<primary_key_restrictions<clustering_key_prefix>>
|
||||
statement_restrictions::initial_key_restrictions<clustering_key_prefix>::merge_to(schema_ptr schema, ::shared_ptr<restriction> restriction) {
|
||||
if (auto p = dynamic_pointer_cast<multi_column_restriction>(restriction)) {
|
||||
return p;
|
||||
}
|
||||
return do_merge_to(std::move(schema), std::move(restriction));
|
||||
}
|
||||
|
||||
::shared_ptr<partition_key_restrictions> statement_restrictions::get_initial_partition_key_restrictions(bool allow_filtering) {
|
||||
static thread_local ::shared_ptr<partition_key_restrictions> initial_kr_true = ::make_shared<initial_key_restrictions<partition_key>>(true);
|
||||
static thread_local ::shared_ptr<partition_key_restrictions> initial_kr_false = ::make_shared<initial_key_restrictions<partition_key>>(false);
|
||||
return allow_filtering ? initial_kr_true : initial_kr_false;
|
||||
}
|
||||
|
||||
::shared_ptr<clustering_key_restrictions> statement_restrictions::get_initial_clustering_key_restrictions(bool allow_filtering) {
|
||||
static thread_local ::shared_ptr<clustering_key_restrictions> initial_kr_true = ::make_shared<initial_key_restrictions<clustering_key>>(true);
|
||||
static thread_local ::shared_ptr<clustering_key_restrictions> initial_kr_false = ::make_shared<initial_key_restrictions<clustering_key>>(false);
|
||||
return allow_filtering ? initial_kr_true : initial_kr_false;
|
||||
}
|
||||
|
||||
statement_restrictions::statement_restrictions(schema_ptr schema, bool allow_filtering)
|
||||
: _schema(schema)
|
||||
, _clustering_columns_restrictions(get_initial_clustering_key_restrictions(allow_filtering))
|
||||
, _nonprimary_key_restrictions(::make_shared<single_column_restrictions>(schema))
|
||||
, _partition_range_is_simple(true)
|
||||
{ }
|
||||
#if 0
|
||||
@@ -419,21 +345,6 @@ statement_restrictions::statement_restrictions(data_dictionary::database db,
|
||||
add_restriction(prepared_restriction, schema, allow_filtering, for_view);
|
||||
|
||||
if (prepared_restriction.op != expr::oper_t::IS_NOT) {
|
||||
const auto restriction = expr::convert_to_restriction(prepared_restriction, schema);
|
||||
if (dynamic_pointer_cast<multi_column_restriction>(restriction)) {
|
||||
_clustering_columns_restrictions = _clustering_columns_restrictions->merge_to(_schema, restriction);
|
||||
} else if (has_token(restriction->expression)) {
|
||||
} else {
|
||||
auto single = restriction;
|
||||
auto& def = *get_the_only_column(single->expression).col;
|
||||
if (def.is_partition_key()) {
|
||||
} else if (def.is_clustering_key()) {
|
||||
_clustering_columns_restrictions = _clustering_columns_restrictions->merge_to(_schema, restriction);
|
||||
} else {
|
||||
_nonprimary_key_restrictions->add_restriction(single);
|
||||
}
|
||||
}
|
||||
|
||||
_where = _where.has_value() ? make_conjunction(std::move(*_where), prepared_restriction) : prepared_restriction;
|
||||
}
|
||||
}
|
||||
@@ -442,6 +353,10 @@ statement_restrictions::statement_restrictions(data_dictionary::database db,
|
||||
if (!has_token(_partition_key_restrictions)) {
|
||||
_single_column_partition_key_restrictions = expr::get_single_column_restrictions_map(_partition_key_restrictions);
|
||||
}
|
||||
if (!expr::contains_multi_column_restriction(_clustering_columns_restrictions)) {
|
||||
_single_column_clustering_key_restrictions = expr::get_single_column_restrictions_map(_clustering_columns_restrictions);
|
||||
}
|
||||
_single_column_nonprimary_key_restrictions = expr::get_single_column_restrictions_map(_nonprimary_key_restrictions);
|
||||
_clustering_prefix_restrictions = extract_clustering_prefix_restrictions(*_where, _schema);
|
||||
_partition_range_restrictions = extract_partition_range(*_where, _schema);
|
||||
}
|
||||
@@ -450,12 +365,12 @@ statement_restrictions::statement_restrictions(data_dictionary::database db,
|
||||
const expr::allow_local_index allow_local(
|
||||
!has_partition_key_unrestricted_components()
|
||||
&& partition_key_restrictions_is_all_eq());
|
||||
_has_multi_column = find_binop(_clustering_columns_restrictions->expression, expr::is_multi_column);
|
||||
_has_queriable_ck_index = _clustering_columns_restrictions->has_supporting_index(sim, allow_local)
|
||||
_has_multi_column = find_binop(_clustering_columns_restrictions, expr::is_multi_column);
|
||||
_has_queriable_ck_index = clustering_columns_restrictions_have_supporting_index(sim, allow_local)
|
||||
&& !type.is_delete();
|
||||
_has_queriable_pk_index = parition_key_restrictions_have_supporting_index(sim, allow_local)
|
||||
&& !type.is_delete();
|
||||
_has_queriable_regular_index = _nonprimary_key_restrictions->has_supporting_index(sim, allow_local)
|
||||
_has_queriable_regular_index = expr::index_supports_some_column(_nonprimary_key_restrictions, sim, allow_local)
|
||||
&& !type.is_delete();
|
||||
|
||||
// At this point, the select statement if fully constructed, but we still have a few things to validate
|
||||
@@ -495,9 +410,9 @@ statement_restrictions::statement_restrictions(data_dictionary::database db,
|
||||
_uses_secondary_indexing = true;
|
||||
}
|
||||
|
||||
if (_uses_secondary_indexing || _clustering_columns_restrictions->needs_filtering(*_schema)) {
|
||||
_index_restrictions.push_back(_new_clustering_columns_restrictions);
|
||||
} else if (find_binop(_clustering_columns_restrictions->expression, expr::is_on_collection)) {
|
||||
if (_uses_secondary_indexing || clustering_key_restrictions_need_filtering()) {
|
||||
_index_restrictions.push_back(_clustering_columns_restrictions);
|
||||
} else if (find_binop(_clustering_columns_restrictions, expr::is_on_collection)) {
|
||||
fail(unimplemented::cause::INDEXES);
|
||||
#if 0
|
||||
_index_restrictions.push_back(new Forwardingprimary_key_restrictions() {
|
||||
@@ -525,7 +440,7 @@ statement_restrictions::statement_restrictions(data_dictionary::database db,
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!_nonprimary_key_restrictions->empty()) {
|
||||
if (!expr::is_empty_restriction(_nonprimary_key_restrictions)) {
|
||||
if (_has_queriable_regular_index && _partition_range_is_simple) {
|
||||
_uses_secondary_indexing = true;
|
||||
} else if (!allow_filtering) {
|
||||
@@ -533,7 +448,7 @@ statement_restrictions::statement_restrictions(data_dictionary::database db,
|
||||
"thus may have unpredictable performance. If you want to execute "
|
||||
"this query despite the performance unpredictability, use ALLOW FILTERING");
|
||||
}
|
||||
_index_restrictions.push_back(_new_nonprimary_key_restrictions);
|
||||
_index_restrictions.push_back(_nonprimary_key_restrictions);
|
||||
}
|
||||
|
||||
if (_uses_secondary_indexing && !(for_view || allow_filtering)) {
|
||||
@@ -612,33 +527,23 @@ std::vector<const column_definition*> statement_restrictions::get_column_defs_fo
|
||||
}
|
||||
}
|
||||
}
|
||||
auto single_ck_restrs = dynamic_pointer_cast<single_column_clustering_key_restrictions>(_clustering_columns_restrictions);
|
||||
const bool pk_has_unrestricted_components = has_partition_key_unrestricted_components();
|
||||
if (pk_has_unrestricted_components || _clustering_columns_restrictions->needs_filtering(*_schema)) {
|
||||
if (pk_has_unrestricted_components || clustering_key_restrictions_need_filtering()) {
|
||||
column_id first_filtering_id = pk_has_unrestricted_components ? 0 : _schema->clustering_key_columns().begin()->id +
|
||||
_clustering_columns_restrictions->num_prefix_columns_that_need_not_be_filtered();
|
||||
for (auto&& cdef : _clustering_columns_restrictions->get_column_defs()) {
|
||||
num_clustering_prefix_columns_that_need_not_be_filtered();
|
||||
for (auto&& cdef : expr::get_sorted_column_defs(_clustering_columns_restrictions)) {
|
||||
const expr::expression* single_col_restr = nullptr;
|
||||
if (single_ck_restrs) {
|
||||
auto it = single_ck_restrs->restrictions().find(cdef);
|
||||
if (it != single_ck_restrs->restrictions().end()) {
|
||||
if (is_single_column_restriction(it->second->expression)) {
|
||||
single_col_restr = &it->second->expression;
|
||||
}
|
||||
}
|
||||
auto it = _single_column_partition_key_restrictions.find(cdef);
|
||||
if (it != _single_column_partition_key_restrictions.end()) {
|
||||
single_col_restr = &it->second;
|
||||
}
|
||||
if (cdef->id >= first_filtering_id && !column_uses_indexing(cdef, single_col_restr)) {
|
||||
column_defs_for_filtering.emplace_back(cdef);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto&& cdef : _nonprimary_key_restrictions->get_column_defs()) {
|
||||
::shared_ptr<restriction> cur_restr = _nonprimary_key_restrictions->get_restriction(*cdef);
|
||||
const expr::expression* single_col_restr = nullptr;
|
||||
if (is_single_column_restriction(cur_restr->expression)) {
|
||||
single_col_restr = &cur_restr->expression;
|
||||
}
|
||||
if (!column_uses_indexing(cdef, single_col_restr)) {
|
||||
for (auto&& [cdef, cur_restr] : _single_column_nonprimary_key_restrictions) {
|
||||
if (!column_uses_indexing(cdef, &cur_restr)) {
|
||||
column_defs_for_filtering.emplace_back(cdef);
|
||||
}
|
||||
}
|
||||
@@ -718,7 +623,7 @@ void statement_restrictions::add_token_partition_key_restriction(const expr::bin
|
||||
}
|
||||
|
||||
void statement_restrictions::add_single_column_clustering_key_restriction(const expr::binary_operator& restr, schema_ptr schema, bool allow_filtering) {
|
||||
if (find_binop(_new_clustering_columns_restrictions, [] (const expr::binary_operator& b) {
|
||||
if (find_binop(_clustering_columns_restrictions, [] (const expr::binary_operator& b) {
|
||||
return expr::is<expr::tuple_constructor>(b.lhs);
|
||||
})) {
|
||||
throw exceptions::invalid_request_exception(
|
||||
@@ -726,10 +631,10 @@ void statement_restrictions::add_single_column_clustering_key_restriction(const
|
||||
}
|
||||
|
||||
const column_definition* new_column = expr::get_the_only_column(restr).col;
|
||||
const column_definition* last_column = expr::get_last_column_def(_new_clustering_columns_restrictions);
|
||||
const column_definition* last_column = expr::get_last_column_def(_clustering_columns_restrictions);
|
||||
|
||||
if (last_column != nullptr && !allow_filtering) {
|
||||
if (has_slice(_new_clustering_columns_restrictions) && schema->position(*new_column) > schema->position(*last_column)) {
|
||||
if (has_slice(_clustering_columns_restrictions) && schema->position(*new_column) > schema->position(*last_column)) {
|
||||
throw exceptions::invalid_request_exception(format("Clustering column \"{}\" cannot be restricted (preceding column \"{}\" is restricted by a non-EQ relation)",
|
||||
new_column->name_as_text(), last_column->name_as_text()));
|
||||
}
|
||||
@@ -742,16 +647,16 @@ void statement_restrictions::add_single_column_clustering_key_restriction(const
|
||||
}
|
||||
}
|
||||
|
||||
_new_clustering_columns_restrictions = expr::make_conjunction(_new_clustering_columns_restrictions, restr);
|
||||
_clustering_columns_restrictions = expr::make_conjunction(_clustering_columns_restrictions, restr);
|
||||
}
|
||||
|
||||
void statement_restrictions::add_multi_column_clustering_key_restriction(const expr::binary_operator& restr) {
|
||||
if (expr::is_empty_restriction(_new_clustering_columns_restrictions)) {
|
||||
_new_clustering_columns_restrictions = expr::make_conjunction(_new_clustering_columns_restrictions, restr);
|
||||
if (expr::is_empty_restriction(_clustering_columns_restrictions)) {
|
||||
_clustering_columns_restrictions = restr;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!find_binop(_new_clustering_columns_restrictions, [] (const expr::binary_operator& b) {
|
||||
if (!find_binop(_clustering_columns_restrictions, [] (const expr::binary_operator& b) {
|
||||
return expr::is<expr::tuple_constructor>(b.lhs);
|
||||
})) {
|
||||
throw exceptions::invalid_request_exception("Mixing single column relations and multi column relations on clustering columns is not allowed");
|
||||
@@ -759,19 +664,19 @@ void statement_restrictions::add_multi_column_clustering_key_restriction(const e
|
||||
|
||||
if (restr.op == expr::oper_t::EQ) {
|
||||
throw exceptions::invalid_request_exception(format("{} cannot be restricted by more than one relation if it includes an Equal",
|
||||
expr::get_columns_in_commons(_new_clustering_columns_restrictions, restr)));
|
||||
expr::get_columns_in_commons(_clustering_columns_restrictions, restr)));
|
||||
} else if (restr.op == expr::oper_t::IN) {
|
||||
throw exceptions::invalid_request_exception(format("{} cannot be restricted by more than one relation if it includes a IN",
|
||||
expr::get_columns_in_commons(_new_clustering_columns_restrictions, restr)));
|
||||
expr::get_columns_in_commons(_clustering_columns_restrictions, restr)));
|
||||
} else if (is_slice(restr.op)) {
|
||||
if (!expr::has_slice(_new_clustering_columns_restrictions)) {
|
||||
if (!expr::has_slice(_clustering_columns_restrictions)) {
|
||||
throw exceptions::invalid_request_exception(format("Column \"{}\" cannot be restricted by both an equality and an inequality relation",
|
||||
expr::get_columns_in_commons(_new_clustering_columns_restrictions, restr)));
|
||||
expr::get_columns_in_commons(_clustering_columns_restrictions, restr)));
|
||||
}
|
||||
|
||||
const expr::binary_operator* other_slice = expr::find_in_expression<expr::binary_operator>(_new_clustering_columns_restrictions, [](const expr::binary_operator){return true;});
|
||||
const expr::binary_operator* other_slice = expr::find_in_expression<expr::binary_operator>(_clustering_columns_restrictions, [](const expr::binary_operator){return true;});
|
||||
if (other_slice == nullptr) {
|
||||
on_internal_error(rlogger, "add_multi_column_clustering_key_restriction: _new_clustering_columns_restrictions is empty!");
|
||||
on_internal_error(rlogger, "add_multi_column_clustering_key_restriction: _clustering_columns_restrictions is empty!");
|
||||
}
|
||||
|
||||
// Don't allow to mix plain and SCYLLA_CLUSTERING_BOUND bounds
|
||||
@@ -798,14 +703,14 @@ void statement_restrictions::add_multi_column_clustering_key_restriction(const e
|
||||
expr::get_columns_in_commons(restr, *other_slice)));
|
||||
}
|
||||
|
||||
_new_clustering_columns_restrictions = expr::make_conjunction(_new_clustering_columns_restrictions, restr);
|
||||
_clustering_columns_restrictions = expr::make_conjunction(_clustering_columns_restrictions, restr);
|
||||
} else {
|
||||
throw exceptions::invalid_request_exception(format("Unsupported multi-column relation: ", restr));
|
||||
}
|
||||
}
|
||||
|
||||
void statement_restrictions::add_single_column_nonprimary_key_restriction(const expr::binary_operator& restr) {
|
||||
_new_nonprimary_key_restrictions = expr::make_conjunction(_new_nonprimary_key_restrictions, restr);
|
||||
_nonprimary_key_restrictions = expr::make_conjunction(_nonprimary_key_restrictions, restr);
|
||||
}
|
||||
|
||||
void statement_restrictions::process_partition_key_restrictions(bool for_view, bool allow_filtering) {
|
||||
@@ -845,13 +750,7 @@ bool statement_restrictions::partition_key_restrictions_is_empty() const {
|
||||
}
|
||||
|
||||
bool statement_restrictions::partition_key_restrictions_is_all_eq() const {
|
||||
const expr::binary_operator* non_eq_binop = find_in_expression<expr::binary_operator>(_partition_key_restrictions,
|
||||
[](const expr::binary_operator& binop) {
|
||||
return binop.op != expr::oper_t::EQ;
|
||||
}
|
||||
);
|
||||
|
||||
return non_eq_binop == nullptr;
|
||||
return expr::has_only_eq_binops(_partition_key_restrictions);
|
||||
}
|
||||
|
||||
size_t statement_restrictions::partition_key_restrictions_size() const {
|
||||
@@ -864,8 +763,95 @@ bool statement_restrictions::pk_restrictions_need_filtering() const {
|
||||
&& (has_partition_key_unrestricted_components() || expr::has_slice_or_needs_filtering(_partition_key_restrictions));
|
||||
}
|
||||
|
||||
size_t statement_restrictions::clustering_columns_restrictions_size() const {
|
||||
return expr::get_sorted_column_defs(_clustering_columns_restrictions).size();
|
||||
}
|
||||
|
||||
bool statement_restrictions::clustering_key_restrictions_need_filtering() const {
|
||||
if (expr::contains_multi_column_restriction(_clustering_columns_restrictions)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return num_clustering_prefix_columns_that_need_not_be_filtered() < clustering_columns_restrictions_size();
|
||||
}
|
||||
|
||||
bool statement_restrictions::has_unrestricted_clustering_columns() const {
|
||||
return _clustering_columns_restrictions->has_unrestricted_components(*_schema);
|
||||
return clustering_columns_restrictions_size() < _schema->clustering_key_size();
|
||||
}
|
||||
|
||||
bool statement_restrictions::clustering_columns_restrictions_have_supporting_index(
|
||||
const secondary_index::secondary_index_manager& index_manager,
|
||||
expr::allow_local_index allow_local) const {
|
||||
// Single column restrictions can be handled by the existing code
|
||||
if (!expr::contains_multi_column_restriction(_clustering_columns_restrictions)) {
|
||||
return expr::index_supports_some_column(_clustering_columns_restrictions, index_manager, allow_local);
|
||||
}
|
||||
|
||||
// Multi column restrictions have to be handled separately
|
||||
for (const auto& index : index_manager.list_indexes()) {
|
||||
if (!allow_local && index.metadata().local()) {
|
||||
continue;
|
||||
}
|
||||
if (multi_column_clustering_restrictions_are_supported_by(index)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool statement_restrictions::multi_column_clustering_restrictions_are_supported_by(
|
||||
const secondary_index::index& index) const {
|
||||
// Slice restrictions have to be checked depending on the clustering slice
|
||||
if (has_slice(_clustering_columns_restrictions)) {
|
||||
bounds_slice clustering_slice = get_clustering_slice();
|
||||
|
||||
const expr::column_value* supported_column =
|
||||
find_in_expression<expr::column_value>(_clustering_columns_restrictions,
|
||||
[&](const expr::column_value& cval) -> bool {
|
||||
return clustering_slice.is_supported_by(*cval.col, index);
|
||||
}
|
||||
);
|
||||
return supported_column != nullptr;
|
||||
}
|
||||
|
||||
// Otherwise it has to be a singe binary operator with EQ or IN.
|
||||
// This is checked earlier during add_restriction.
|
||||
const expr::binary_operator* single_binop =
|
||||
expr::as_if<expr::binary_operator>(&_clustering_columns_restrictions);
|
||||
if (single_binop == nullptr) {
|
||||
on_internal_error(rlogger, format(
|
||||
"multi_column_clustering_restrictions_are_supported_by more than one non-slice restriction: {}",
|
||||
_clustering_columns_restrictions));
|
||||
}
|
||||
|
||||
if (single_binop->op != expr::oper_t::IN && single_binop->op != expr::oper_t::EQ) {
|
||||
on_internal_error(rlogger, format("Disallowed multi column restriction: {}", *single_binop));
|
||||
}
|
||||
|
||||
const expr::column_value* supported_column =
|
||||
find_in_expression<expr::column_value>(_clustering_columns_restrictions,
|
||||
[&](const expr::column_value& cval) -> bool {
|
||||
return index.supports_expression(*cval.col, single_binop->op);
|
||||
}
|
||||
);
|
||||
return supported_column != nullptr;
|
||||
}
|
||||
|
||||
bounds_slice statement_restrictions::get_clustering_slice() const {
|
||||
std::optional<bounds_slice> result;
|
||||
|
||||
expr::for_each_expression<expr::binary_operator>(_clustering_columns_restrictions,
|
||||
[&](const expr::binary_operator& binop) {
|
||||
bounds_slice cur_slice = bounds_slice::from_binary_operator(binop);
|
||||
if (!result.has_value()) {
|
||||
result = cur_slice;
|
||||
} else {
|
||||
result->merge(cur_slice);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return *result;
|
||||
}
|
||||
|
||||
bool statement_restrictions::parition_key_restrictions_have_supporting_index(const secondary_index::secondary_index_manager& index_manager,
|
||||
@@ -875,20 +861,7 @@ bool statement_restrictions::parition_key_restrictions_have_supporting_index(con
|
||||
return false;
|
||||
}
|
||||
|
||||
// Otherwise those are single column restrictions
|
||||
std::vector<const column_definition*> restricted_pk_columns = expr::get_sorted_column_defs(_partition_key_restrictions);
|
||||
|
||||
for (const column_definition* cdef : restricted_pk_columns) {
|
||||
expr::expression col_restrictions = expr::conjunction {
|
||||
.children = expr::extract_single_column_restrictions_for_column(_partition_key_restrictions, *cdef)
|
||||
};
|
||||
|
||||
if (expr::has_supporting_index(col_restrictions, index_manager, allow_local)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return expr::index_supports_some_column(_partition_key_restrictions, index_manager, allow_local);
|
||||
}
|
||||
|
||||
void statement_restrictions::process_clustering_columns_restrictions(bool for_view, bool allow_filtering) {
|
||||
@@ -896,18 +869,18 @@ void statement_restrictions::process_clustering_columns_restrictions(bool for_vi
|
||||
return;
|
||||
}
|
||||
|
||||
if (find_binop(_clustering_columns_restrictions->expression, expr::is_on_collection)
|
||||
if (find_binop(_clustering_columns_restrictions, expr::is_on_collection)
|
||||
&& !_has_queriable_ck_index && !allow_filtering) {
|
||||
throw exceptions::invalid_request_exception(
|
||||
"Cannot restrict clustering columns by a CONTAINS relation without a secondary index or filtering");
|
||||
}
|
||||
|
||||
if (has_clustering_columns_restriction() && _clustering_columns_restrictions->needs_filtering(*_schema)) {
|
||||
if (has_clustering_columns_restriction() && clustering_key_restrictions_need_filtering()) {
|
||||
if (_has_queriable_ck_index) {
|
||||
_uses_secondary_indexing = true;
|
||||
} else if (!allow_filtering && !for_view) {
|
||||
auto clustering_columns_iter = _schema->clustering_key_columns().begin();
|
||||
for (auto&& restricted_column : _clustering_columns_restrictions->get_column_defs()) {
|
||||
for (auto&& restricted_column : expr::get_sorted_column_defs(_clustering_columns_restrictions)) {
|
||||
const column_definition* clustering_column = &(*clustering_columns_iter);
|
||||
++clustering_columns_iter;
|
||||
if (clustering_column != restricted_column) {
|
||||
@@ -1692,8 +1665,8 @@ bool statement_restrictions::need_filtering() const {
|
||||
if (_uses_secondary_indexing && has_token(_partition_key_restrictions)) {
|
||||
// If there is a token(p1, p2) restriction, no p1, p2 restrictions are allowed in the query.
|
||||
// All other restrictions must be on clustering or regular columns.
|
||||
int64_t non_pk_restrictions_count = _clustering_columns_restrictions->size();
|
||||
non_pk_restrictions_count += _nonprimary_key_restrictions->size();
|
||||
int64_t non_pk_restrictions_count = clustering_columns_restrictions_size();
|
||||
non_pk_restrictions_count += expr::get_sorted_column_defs(_nonprimary_key_restrictions).size();
|
||||
|
||||
// We are querying using an index, one restriction goes to the index restriction.
|
||||
// If there are some restrictions other than token() and index column then we need to do filtering.
|
||||
@@ -1706,7 +1679,8 @@ bool statement_restrictions::need_filtering() const {
|
||||
// Can't calculate the token value, so a naive base-table query must be filtered. Same for any index tables,
|
||||
// except if there's only one restriction supported by an index.
|
||||
return !(npart == 1 && _has_queriable_pk_index &&
|
||||
_clustering_columns_restrictions->empty() && _nonprimary_key_restrictions->empty());
|
||||
expr::is_empty_restriction(_clustering_columns_restrictions) &&
|
||||
expr::is_empty_restriction(_nonprimary_key_restrictions));
|
||||
}
|
||||
if (pk_restrictions_need_filtering()) {
|
||||
// We most likely cannot calculate token(s). Neither base-table nor index-table queries can avoid filtering.
|
||||
@@ -1714,24 +1688,24 @@ bool statement_restrictions::need_filtering() const {
|
||||
}
|
||||
// Now we know the partition key is either unrestricted or fully restricted.
|
||||
|
||||
const auto nreg = _nonprimary_key_restrictions->size();
|
||||
const auto nreg = expr::get_sorted_column_defs(_nonprimary_key_restrictions).size();
|
||||
if (nreg > 1 || (nreg == 1 && !_has_queriable_regular_index)) {
|
||||
return true; // Regular columns are unsorted in storage and no single index suffices.
|
||||
}
|
||||
if (nreg == 1) { // Single non-key restriction supported by an index.
|
||||
// Will the index-table query require filtering? That depends on whether its clustering key is restricted to a
|
||||
// continuous range. Recall that this clustering key is (token, pk, ck) of the base table.
|
||||
if (npart == 0 && _clustering_columns_restrictions->empty()) {
|
||||
if (npart == 0 && expr::is_empty_restriction(_clustering_columns_restrictions)) {
|
||||
return false; // No clustering key restrictions => whole partitions.
|
||||
}
|
||||
return !token_known(*this) || _clustering_columns_restrictions->needs_filtering(*_schema)
|
||||
return !token_known(*this) || clustering_key_restrictions_need_filtering()
|
||||
// Multi-column restrictions don't require filtering when querying the base table, but the index
|
||||
// table has a different clustering key and may require filtering.
|
||||
|| _has_multi_column;
|
||||
}
|
||||
// Now we know there are no nonkey restrictions.
|
||||
|
||||
if (dynamic_pointer_cast<multi_column_restriction>(_clustering_columns_restrictions)) {
|
||||
if (_has_multi_column) {
|
||||
// Multicolumn bounds mean lexicographic order, implying a continuous clustering range. Multicolumn IN means a
|
||||
// finite set of continuous ranges. Multicolumn restrictions cannot currently be combined with single-column
|
||||
// clustering restrictions. Therefore, a continuous clustering range is guaranteed.
|
||||
@@ -1752,12 +1726,12 @@ bool statement_restrictions::need_filtering() const {
|
||||
// WHERE p = ? AND c1 = ? AND c2 LIKE ? AND c3 = ? - requires filtering
|
||||
// WHERE p = ? AND c1 = ? AND c2 = ? AND c3 = ? - doesn't use an index
|
||||
// WHERE p = ? AND c1 = ? AND c2 < ? AND c3 = ? - doesn't require filtering, but we report it does
|
||||
return _clustering_columns_restrictions->size() > 1;
|
||||
return clustering_columns_restrictions_size() > 1;
|
||||
}
|
||||
// Now we know that the query doesn't use an index.
|
||||
|
||||
// The only thing that can cause filtering now are the clustering columns.
|
||||
return _clustering_columns_restrictions->needs_filtering(*_schema);
|
||||
return clustering_key_restrictions_need_filtering();
|
||||
}
|
||||
|
||||
void statement_restrictions::validate_secondary_index_selections(bool selects_only_static_columns) {
|
||||
@@ -1774,16 +1748,8 @@ const expr::single_column_restrictions_map& statement_restrictions::get_single_c
|
||||
/**
|
||||
* @return clustering key restrictions split into single column restrictions (e.g. for filtering support).
|
||||
*/
|
||||
const single_column_restrictions::restrictions_map& statement_restrictions::get_single_column_clustering_key_restrictions() const {
|
||||
static single_column_restrictions::restrictions_map empty;
|
||||
auto single_restrictions = dynamic_pointer_cast<single_column_clustering_key_restrictions>(_clustering_columns_restrictions);
|
||||
if (!single_restrictions) {
|
||||
if (dynamic_pointer_cast<initial_key_restrictions<clustering_key>>(_clustering_columns_restrictions)) {
|
||||
return empty;
|
||||
}
|
||||
throw std::runtime_error("statement restrictions for multi-column partition key restrictions are not implemented yet");
|
||||
}
|
||||
return single_restrictions->restrictions();
|
||||
const expr::single_column_restrictions_map& statement_restrictions::get_single_column_clustering_key_restrictions() const {
|
||||
return _single_column_clustering_key_restrictions;
|
||||
}
|
||||
|
||||
void statement_restrictions::prepare_indexed_global(const schema& idx_tbl_schema) {
|
||||
@@ -1863,6 +1829,38 @@ void statement_restrictions::add_clustering_restrictions_to_idx_ck_prefix(const
|
||||
}
|
||||
}
|
||||
|
||||
// How many of the restrictions (in column order) do not need filtering
|
||||
// because they are implemented as a slice (potentially, a contiguous disk
|
||||
// read). For example, if we have the filter "c1 < 3 and c2 > 3", c1 does not
|
||||
// need filtering but c2 does so num_prefix_columns_that_need_not_be_filtered
|
||||
// will be 1.
|
||||
unsigned int statement_restrictions::num_clustering_prefix_columns_that_need_not_be_filtered() const {
|
||||
if (expr::contains_multi_column_restriction(_clustering_columns_restrictions)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
expr::single_column_restrictions_map column_restrictions =
|
||||
expr::get_single_column_restrictions_map(_clustering_columns_restrictions);
|
||||
|
||||
// Restrictions currently need filtering in three cases:
|
||||
// 1. any of them is a CONTAINS restriction
|
||||
// 2. restrictions do not form a contiguous prefix (i.e. there are gaps in it)
|
||||
// 3. a SLICE restriction isn't on a last place
|
||||
column_id position = 0;
|
||||
unsigned int count = 0;
|
||||
for (const auto& restriction : column_restrictions | boost::adaptors::map_values) {
|
||||
if (find_needs_filtering(restriction)
|
||||
|| position != get_the_only_column(restriction).col->id) {
|
||||
return count;
|
||||
}
|
||||
if (!has_slice(restriction)) {
|
||||
position = get_the_only_column(restriction).col->id + 1;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
std::vector<query::clustering_range> statement_restrictions::get_global_index_clustering_ranges(
|
||||
const query_options& options,
|
||||
const schema& idx_tbl_schema) const {
|
||||
|
||||
@@ -12,14 +12,14 @@
|
||||
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include "bounds_slice.hh"
|
||||
#include "cql3/expr/expression.hh"
|
||||
#include "cql3/expr/restrictions.hh"
|
||||
#include "to_string.hh"
|
||||
#include "schema_fwd.hh"
|
||||
#include "cql3/restrictions/restrictions.hh"
|
||||
#include "cql3/restrictions/primary_key_restrictions.hh"
|
||||
#include "cql3/restrictions/single_column_restrictions.hh"
|
||||
#include "cql3/prepare_context.hh"
|
||||
#include "cql3/statements/statement_type.hh"
|
||||
#include "query-request.hh"
|
||||
|
||||
namespace cql3 {
|
||||
|
||||
@@ -33,12 +33,6 @@ class statement_restrictions {
|
||||
private:
|
||||
schema_ptr _schema;
|
||||
|
||||
template<typename>
|
||||
class initial_key_restrictions;
|
||||
|
||||
static ::shared_ptr<partition_key_restrictions> get_initial_partition_key_restrictions(bool allow_filtering);
|
||||
static ::shared_ptr<clustering_key_restrictions> get_initial_clustering_key_restrictions(bool allow_filtering);
|
||||
|
||||
/**
|
||||
* Restrictions on partitioning columns
|
||||
*/
|
||||
@@ -49,16 +43,16 @@ private:
|
||||
/**
|
||||
* Restrictions on clustering columns
|
||||
*/
|
||||
::shared_ptr<clustering_key_restrictions> _clustering_columns_restrictions;
|
||||
expr::expression _clustering_columns_restrictions;
|
||||
|
||||
expr::expression _new_clustering_columns_restrictions;
|
||||
expr::single_column_restrictions_map _single_column_clustering_key_restrictions;
|
||||
|
||||
/**
|
||||
* Restriction on non-primary key columns (i.e. secondary index restrictions)
|
||||
*/
|
||||
::shared_ptr<single_column_restrictions> _nonprimary_key_restrictions;
|
||||
expr::expression _nonprimary_key_restrictions;
|
||||
|
||||
expr::expression _new_nonprimary_key_restrictions;
|
||||
expr::single_column_restrictions_map _single_column_nonprimary_key_restrictions;
|
||||
|
||||
std::unordered_set<const column_definition*> _not_null_columns;
|
||||
|
||||
@@ -153,11 +147,11 @@ public:
|
||||
* otherwise.
|
||||
*/
|
||||
bool clustering_key_restrictions_has_IN() const {
|
||||
return find(_clustering_columns_restrictions->expression, expr::oper_t::IN);
|
||||
return find(_clustering_columns_restrictions, expr::oper_t::IN);
|
||||
}
|
||||
|
||||
bool clustering_key_restrictions_has_only_eq() const {
|
||||
return _clustering_columns_restrictions->empty() || _clustering_columns_restrictions->is_all_eq();
|
||||
return expr::has_only_eq_binops(_clustering_columns_restrictions);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -182,7 +176,7 @@ public:
|
||||
return _partition_key_restrictions;
|
||||
}
|
||||
|
||||
::shared_ptr<clustering_key_restrictions> get_clustering_columns_restrictions() const {
|
||||
const expr::expression& get_clustering_columns_restrictions() const {
|
||||
return _clustering_columns_restrictions;
|
||||
}
|
||||
|
||||
@@ -231,6 +225,16 @@ public:
|
||||
|
||||
bool parition_key_restrictions_have_supporting_index(const secondary_index::secondary_index_manager& index_manager, expr::allow_local_index allow_local) const;
|
||||
|
||||
size_t clustering_columns_restrictions_size() const;
|
||||
|
||||
bool clustering_columns_restrictions_have_supporting_index(
|
||||
const secondary_index::secondary_index_manager& index_manager,
|
||||
expr::allow_local_index allow_local) const;
|
||||
|
||||
bool multi_column_clustering_restrictions_are_supported_by(const secondary_index::index& index) const;
|
||||
|
||||
bounds_slice get_clustering_slice() const;
|
||||
|
||||
/**
|
||||
* Checks if the clustering key has some unrestricted components.
|
||||
* @return <code>true</code> if the clustering key has some unrestricted components, <code>false</code> otherwise.
|
||||
@@ -264,8 +268,8 @@ private:
|
||||
const expr::expression& get_restrictions(column_kind kind) const {
|
||||
switch (kind) {
|
||||
case column_kind::partition_key: return _partition_key_restrictions;
|
||||
case column_kind::clustering_key: return _new_clustering_columns_restrictions;
|
||||
default: return _new_nonprimary_key_restrictions;
|
||||
case column_kind::clustering_key: return _clustering_columns_restrictions;
|
||||
default: return _nonprimary_key_restrictions;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -277,6 +281,7 @@ private:
|
||||
*/
|
||||
void add_clustering_restrictions_to_idx_ck_prefix(const schema& idx_tbl_schema);
|
||||
|
||||
unsigned int num_clustering_prefix_columns_that_need_not_be_filtered() const;
|
||||
#if 0
|
||||
std::vector<::shared_ptr<index_expression>> get_index_expressions(const query_options& options) {
|
||||
if (!_uses_secondary_indexing || _index_restrictions.empty()) {
|
||||
@@ -440,7 +445,7 @@ public:
|
||||
* <code>false</code> otherwise.
|
||||
*/
|
||||
bool has_clustering_columns_restriction() const {
|
||||
return !_clustering_columns_restrictions->empty();
|
||||
return !expr::is_empty_restriction(_clustering_columns_restrictions);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -449,24 +454,26 @@ public:
|
||||
* @return <code>true</code> if the restrictions contain any non-primary key restrictions, <code>false</code> otherwise.
|
||||
*/
|
||||
bool has_non_primary_key_restriction() const {
|
||||
return !_nonprimary_key_restrictions->empty();
|
||||
return !expr::is_empty_restriction(_nonprimary_key_restrictions);
|
||||
}
|
||||
|
||||
bool pk_restrictions_need_filtering() const;
|
||||
|
||||
bool ck_restrictions_need_filtering() const {
|
||||
if (_clustering_columns_restrictions->empty()) {
|
||||
if (expr::is_empty_restriction(_clustering_columns_restrictions)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return has_partition_key_unrestricted_components()
|
||||
|| _clustering_columns_restrictions->needs_filtering(*_schema)
|
||||
|| clustering_key_restrictions_need_filtering()
|
||||
// If token restrictions are present in an indexed query, then all other restrictions need to be filtered.
|
||||
// A single token restriction can have multiple matching partition key values.
|
||||
// Because of this we can't create a clustering prefix with more than token restriction.
|
||||
|| (_uses_secondary_indexing && has_token(_partition_key_restrictions));
|
||||
}
|
||||
|
||||
bool clustering_key_restrictions_need_filtering() const;
|
||||
|
||||
/**
|
||||
* @return true if column is restricted by some restriction, false otherwise
|
||||
*/
|
||||
@@ -482,8 +489,8 @@ public:
|
||||
/**
|
||||
* @return the non-primary key restrictions.
|
||||
*/
|
||||
const single_column_restrictions::restrictions_map& get_non_pk_restriction() const {
|
||||
return _nonprimary_key_restrictions->restrictions();
|
||||
const expr::single_column_restrictions_map& get_non_pk_restriction() const {
|
||||
return _single_column_nonprimary_key_restrictions;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -494,7 +501,7 @@ public:
|
||||
/**
|
||||
* @return clustering key restrictions split into single column restrictions (e.g. for filtering support).
|
||||
*/
|
||||
const single_column_restrictions::restrictions_map& get_single_column_clustering_key_restrictions() const;
|
||||
const expr::single_column_restrictions_map& get_single_column_clustering_key_restrictions() const;
|
||||
|
||||
/// Prepares internal data for evaluating index-table queries. Must be called before
|
||||
/// get_local_index_clustering_ranges().
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015-present ScyllaDB
|
||||
*
|
||||
* Modified by ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
* SPDX-License-Identifier: (AGPL-3.0-or-later and Apache-2.0)
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "restriction.hh"
|
||||
#include "primary_key_restrictions.hh"
|
||||
#include "exceptions/exceptions.hh"
|
||||
#include "bounds_slice.hh"
|
||||
#include "keys.hh"
|
||||
|
||||
class column_definition;
|
||||
|
||||
namespace cql3 {
|
||||
|
||||
namespace restrictions {
|
||||
|
||||
/**
|
||||
* <code>Restriction</code> using the token function.
|
||||
*/
|
||||
class token_restriction: public partition_key_restrictions {
|
||||
private:
|
||||
/**
|
||||
* The definition of the columns to which apply the token restriction.
|
||||
*/
|
||||
std::vector<const column_definition *> _column_definitions;
|
||||
public:
|
||||
token_restriction(std::vector<const column_definition *> c)
|
||||
: _column_definitions(std::move(c)) {
|
||||
}
|
||||
|
||||
std::vector<const column_definition*> get_column_defs() const override {
|
||||
return _column_definitions;
|
||||
}
|
||||
|
||||
void merge_with(::shared_ptr<restriction> restriction) override {
|
||||
if (!has_token(restriction->expression)) {
|
||||
throw exceptions::invalid_request_exception(
|
||||
format("Columns \"{}\" cannot be restricted by both a normal relation and a token relation",
|
||||
join(", ", get_column_defs())));
|
||||
}
|
||||
expression = make_conjunction(std::move(expression), restriction->expression);
|
||||
}
|
||||
|
||||
virtual bool has_supporting_index(const secondary_index::secondary_index_manager& index_manager,
|
||||
expr::allow_local_index allow_local) const override {
|
||||
return false;
|
||||
}
|
||||
|
||||
#if 0
|
||||
void add_index_expression_to(std::vector<::shared_ptr<index_expression>>& expressions,
|
||||
const query_options& options) override {
|
||||
throw exceptions::unsupported_operation_exception();
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,7 +18,6 @@
|
||||
#include "cql3/selection/selector_factories.hh"
|
||||
#include "cql3/result_set.hh"
|
||||
#include "cql3/query_options.hh"
|
||||
#include "cql3/restrictions/multi_column_restriction.hh"
|
||||
#include "cql3/restrictions/statement_restrictions.hh"
|
||||
|
||||
namespace cql3 {
|
||||
@@ -431,13 +430,13 @@ bool result_set_builder::restrictions_filter::do_filter(const selection& selecti
|
||||
return false;
|
||||
}
|
||||
|
||||
auto clustering_columns_restrictions = _restrictions->get_clustering_columns_restrictions();
|
||||
if (dynamic_pointer_cast<cql3::restrictions::multi_column_restriction>(clustering_columns_restrictions)) {
|
||||
const expr::expression& clustering_columns_restrictions = _restrictions->get_clustering_columns_restrictions();
|
||||
if (expr::contains_multi_column_restriction(clustering_columns_restrictions)) {
|
||||
clustering_key_prefix ckey = clustering_key_prefix::from_exploded(clustering_key);
|
||||
// FIXME: push to upper layer so it happens once per row
|
||||
auto static_and_regular_columns = expr::get_non_pk_values(selection, static_row, row);
|
||||
return expr::is_satisfied_by(
|
||||
clustering_columns_restrictions->expression,
|
||||
clustering_columns_restrictions,
|
||||
expr::evaluation_inputs{
|
||||
.partition_key = &partition_key,
|
||||
.clustering_key = &clustering_key,
|
||||
@@ -449,7 +448,7 @@ bool result_set_builder::restrictions_filter::do_filter(const selection& selecti
|
||||
|
||||
auto static_row_iterator = static_row.iterator();
|
||||
auto row_iterator = row ? std::optional<query::result_row_view::iterator_type>(row->iterator()) : std::nullopt;
|
||||
auto non_pk_restrictions_map = _restrictions->get_non_pk_restriction();
|
||||
const expr::single_column_restrictions_map& non_pk_restrictions_map = _restrictions->get_non_pk_restriction();
|
||||
for (auto&& cdef : selection.get_columns()) {
|
||||
switch (cdef->kind) {
|
||||
case column_kind::static_column:
|
||||
@@ -462,11 +461,11 @@ bool result_set_builder::restrictions_filter::do_filter(const selection& selecti
|
||||
if (restr_it == non_pk_restrictions_map.end()) {
|
||||
continue;
|
||||
}
|
||||
restrictions::restriction& single_col_restriction = *restr_it->second;
|
||||
const expr::expression& single_col_restriction = restr_it->second;
|
||||
// FIXME: push to upper layer so it happens once per row
|
||||
auto static_and_regular_columns = expr::get_non_pk_values(selection, static_row, row);
|
||||
bool regular_restriction_matches = expr::is_satisfied_by(
|
||||
single_col_restriction.expression,
|
||||
single_col_restriction,
|
||||
expr::evaluation_inputs{
|
||||
.partition_key = &partition_key,
|
||||
.clustering_key = &clustering_key,
|
||||
@@ -508,7 +507,8 @@ bool result_set_builder::restrictions_filter::do_filter(const selection& selecti
|
||||
if (_skip_ck_restrictions) {
|
||||
continue;
|
||||
}
|
||||
auto clustering_key_restrictions_map = _restrictions->get_single_column_clustering_key_restrictions();
|
||||
const expr::single_column_restrictions_map& clustering_key_restrictions_map =
|
||||
_restrictions->get_single_column_clustering_key_restrictions();
|
||||
auto restr_it = clustering_key_restrictions_map.find(cdef);
|
||||
if (restr_it == clustering_key_restrictions_map.end()) {
|
||||
continue;
|
||||
@@ -516,9 +516,9 @@ bool result_set_builder::restrictions_filter::do_filter(const selection& selecti
|
||||
if (clustering_key.empty()) {
|
||||
return false;
|
||||
}
|
||||
restrictions::restriction& single_col_restriction = *restr_it->second;
|
||||
const expr::expression& single_col_restriction = restr_it->second;
|
||||
if (!expr::is_satisfied_by(
|
||||
single_col_restriction.expression,
|
||||
single_col_restriction,
|
||||
expr::evaluation_inputs{
|
||||
.partition_key = &partition_key,
|
||||
.clustering_key = &clustering_key,
|
||||
|
||||
@@ -270,7 +270,7 @@ view_ptr create_view_statement::prepare_view(data_dictionary::database db) const
|
||||
// non-pk base column + base column used in view pk)". When the filtered
|
||||
// column *is* the base column added to the view pk, we don't have this
|
||||
// problem. And this case actually works correctly.
|
||||
auto non_pk_restrictions = restrictions->get_non_pk_restriction();
|
||||
const expr::single_column_restrictions_map& non_pk_restrictions = restrictions->get_non_pk_restriction();
|
||||
if (non_pk_restrictions.size() == 1 && has_non_pk_column &&
|
||||
target_primary_keys.contains(non_pk_restrictions.cbegin()->first)) {
|
||||
// This case (filter by new PK column of the view) works, as explained above
|
||||
|
||||
@@ -78,7 +78,7 @@ delete_statement::prepare_internal(data_dictionary::database db, schema_ptr sche
|
||||
}
|
||||
prepare_conditions(db, *schema, ctx, *stmt);
|
||||
stmt->process_where_clause(db, _where_clause, ctx);
|
||||
if (has_slice(stmt->restrictions().get_clustering_columns_restrictions()->expression)) {
|
||||
if (has_slice(stmt->restrictions().get_clustering_columns_restrictions())) {
|
||||
if (!schema->is_compound()) {
|
||||
throw exceptions::invalid_request_exception("Range deletions on \"compact storage\" schemas are not supported");
|
||||
}
|
||||
|
||||
@@ -399,10 +399,10 @@ modification_statement::process_where_clause(data_dictionary::database db, std::
|
||||
| boost::adaptors::transformed(std::mem_fn(&column_definition::name_as_text)));
|
||||
throw exceptions::invalid_request_exception(format("Invalid where clause contains non PRIMARY KEY columns: {}", column_names));
|
||||
}
|
||||
auto ck_restrictions = _restrictions->get_clustering_columns_restrictions();
|
||||
if (has_slice(ck_restrictions->expression) && !allow_clustering_key_slices()) {
|
||||
const expr::expression& ck_restrictions = _restrictions->get_clustering_columns_restrictions();
|
||||
if (has_slice(ck_restrictions) && !allow_clustering_key_slices()) {
|
||||
throw exceptions::invalid_request_exception(
|
||||
format("Invalid operator in where clause {}", to_string(ck_restrictions->expression)));
|
||||
format("Invalid operator in where clause {}", to_string(ck_restrictions)));
|
||||
}
|
||||
if (_restrictions->has_unrestricted_clustering_columns() && !applies_only_to_static_columns() && !s->is_dense()) {
|
||||
// Tomek: Origin had "&& s->comparator->is_composite()" in the condition below.
|
||||
@@ -415,13 +415,13 @@ modification_statement::process_where_clause(data_dictionary::database db, std::
|
||||
// Those tables don't have clustering columns so we wouldn't reach this code, thus
|
||||
// the check seems redundant.
|
||||
if (require_full_clustering_key()) {
|
||||
auto& col = s->column_at(column_kind::clustering_key, ck_restrictions->size());
|
||||
auto& col = s->column_at(column_kind::clustering_key, _restrictions->clustering_columns_restrictions_size());
|
||||
throw exceptions::invalid_request_exception(format("Missing mandatory PRIMARY KEY part {}", col.name_as_text()));
|
||||
}
|
||||
// In general, we can't modify specific columns if not all clustering columns have been specified.
|
||||
// However, if we modify only static columns, it's fine since we won't really use the prefix anyway.
|
||||
if (!has_slice(ck_restrictions->expression)) {
|
||||
auto& col = s->column_at(column_kind::clustering_key, ck_restrictions->size());
|
||||
if (!has_slice(ck_restrictions)) {
|
||||
auto& col = s->column_at(column_kind::clustering_key, _restrictions->clustering_columns_restrictions_size());
|
||||
for (auto&& op : _column_operations) {
|
||||
if (!op->column.is_static()) {
|
||||
throw exceptions::invalid_request_exception(format("Primary key column '{}' must be specified in order to modify column '{}'",
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
#include "cql3/functions/as_json_function.hh"
|
||||
#include "cql3/selection/selection.hh"
|
||||
#include "cql3/util.hh"
|
||||
#include "cql3/restrictions/single_column_primary_key_restrictions.hh"
|
||||
#include "cql3/restrictions/statement_restrictions.hh"
|
||||
#include "cql3/selection/selector_factories.hh"
|
||||
#include "validation.hh"
|
||||
@@ -1948,12 +1947,12 @@ static bool needs_allow_filtering_anyway(
|
||||
if (strict_allow_filtering == flag_t::FALSE) {
|
||||
return false;
|
||||
}
|
||||
const auto& ck_restrictions = *restrictions.get_clustering_columns_restrictions();
|
||||
const auto& ck_restrictions = restrictions.get_clustering_columns_restrictions();
|
||||
const auto& pk_restrictions = restrictions.get_partition_key_restrictions();
|
||||
// Even if no filtering happens on the coordinator, we still warn about poor performance when partition
|
||||
// slice is defined but in potentially unlimited number of partitions (see #7608).
|
||||
if ((expr::is_empty_restriction(pk_restrictions) || has_token(pk_restrictions)) // Potentially unlimited partitions.
|
||||
&& !ck_restrictions.empty() // Slice defined.
|
||||
&& !expr::is_empty_restriction(ck_restrictions) // Slice defined.
|
||||
&& !restrictions.uses_secondary_indexing()) { // Base-table is used. (Index-table use always limits partitions.)
|
||||
if (strict_allow_filtering == flag_t::WARN) {
|
||||
warnings.emplace_back("This query should use ALLOW FILTERING and will be rejected in future versions.");
|
||||
|
||||
@@ -256,7 +256,7 @@ bool partition_key_matches(const schema& base, const view_info& view, const dht:
|
||||
}
|
||||
|
||||
bool clustering_prefix_matches(const schema& base, const view_info& view, const partition_key& key, const clustering_key_prefix& ck) {
|
||||
const auto r = view.select_statement().get_restrictions()->get_clustering_columns_restrictions();
|
||||
const cql3::expr::expression& r = view.select_statement().get_restrictions()->get_clustering_columns_restrictions();
|
||||
std::vector<bytes> exploded_pk = key.explode();
|
||||
std::vector<bytes> exploded_ck = ck.explode();
|
||||
std::vector<const column_definition*> ck_columns;
|
||||
@@ -269,7 +269,7 @@ bool clustering_prefix_matches(const schema& base, const view_info& view, const
|
||||
auto dummy_options = cql3::query_options({ });
|
||||
// FIXME: pass nullptrs for some of theses dummies
|
||||
return cql3::expr::is_satisfied_by(
|
||||
r->expression,
|
||||
r,
|
||||
cql3::expr::evaluation_inputs{
|
||||
.partition_key = &exploded_pk,
|
||||
.clustering_key = &exploded_ck,
|
||||
@@ -346,7 +346,7 @@ public:
|
||||
// FIXME: pass dummy_options as nullptr
|
||||
auto dummy_options = cql3::query_options({});
|
||||
return cql3::expr::is_satisfied_by(
|
||||
r->expression,
|
||||
r,
|
||||
cql3::expr::evaluation_inputs{
|
||||
.partition_key = &_pk,
|
||||
.clustering_key = &ck,
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
#include "cql3/util.hh"
|
||||
#include "test/lib/cql_assertions.hh"
|
||||
#include "test/lib/cql_test_env.hh"
|
||||
#include "cql3/restrictions/multi_column_restriction.hh"
|
||||
|
||||
using namespace cql3;
|
||||
|
||||
@@ -95,6 +94,18 @@ auto both_closed(std::vector<bytes> lb, std::vector<bytes> ub) {
|
||||
return query::clustering_range({{cklb, inclusive}}, {{ckub, inclusive}});
|
||||
}
|
||||
|
||||
expr::tuple_constructor
|
||||
column_definitions_as_tuple_constructor(const std::vector<const column_definition*>& defs) {
|
||||
std::vector<expr::expression> columns;
|
||||
std::vector<data_type> column_types;
|
||||
columns.reserve(defs.size());
|
||||
for (auto& def : defs) {
|
||||
columns.push_back(expr::column_value{def});
|
||||
column_types.push_back(def->type);
|
||||
}
|
||||
data_type ttype = tuple_type_impl::get_instance(std::move(column_types));
|
||||
return expr::tuple_constructor{std::move(columns), std::move(ttype)};
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
SEASTAR_TEST_CASE(slice_empty_restriction) {
|
||||
@@ -420,7 +431,7 @@ BOOST_AUTO_TEST_CASE(expression_extract_column_restrictions) {
|
||||
expression r2_restriction(binary_operator(column_value(&col_r2), oper_t::EQ, zero_value));
|
||||
|
||||
auto make_multi_column_restriction = [](std::vector<const column_definition*> columns, oper_t oper) -> expression {
|
||||
tuple_constructor column_tuple(cql3::restrictions::column_definitions_as_tuple_constructor(columns));
|
||||
tuple_constructor column_tuple(column_definitions_as_tuple_constructor(columns));
|
||||
|
||||
std::vector<managed_bytes_opt> zeros_tuple_elems(columns.size(), managed_bytes_opt(I(0)));
|
||||
data_type tup_type = tuple_type_impl::get_instance(std::vector<data_type>(columns.size(), int32_type));
|
||||
|
||||
Reference in New Issue
Block a user