view: Add may_be_affected_by function
This patch adds the may_be_affected_by() function to the view class, which is responsible to determine whether an update to a base class affects one of its views. Signed-off-by: Duarte Nunes <duarte@scylladb.com>
This commit is contained in:
129
db/view/view.cc
Normal file
129
db/view/view.cc
Normal file
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2017 ScyllaDB
|
||||
*
|
||||
* Modified by ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is part of Scylla.
|
||||
*
|
||||
* Scylla is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Scylla is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Scylla. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <boost/range/algorithm/transform.hpp>
|
||||
|
||||
#include "clustering_bounds_comparator.hh"
|
||||
#include "cql3/statements/select_statement.hh"
|
||||
#include "cql3/util.hh"
|
||||
#include "db/view/view.hh"
|
||||
|
||||
namespace db {
|
||||
|
||||
namespace view {
|
||||
|
||||
cql3::statements::select_statement& view::select_statement() const {
|
||||
if (!_select_statement) {
|
||||
std::vector<sstring_view> included;
|
||||
if (!_schema->view_info()->include_all_columns()) {
|
||||
included.reserve(_schema->all_columns_in_select_order().size());
|
||||
boost::transform(_schema->all_columns_in_select_order(), std::back_inserter(included), std::mem_fn(&column_definition::name_as_text));
|
||||
}
|
||||
auto raw = cql3::util::build_select_statement(_schema->view_info()->base_name(), _schema->view_info()->where_clause(), std::move(included));
|
||||
raw->prepare_keyspace(_schema->ks_name());
|
||||
raw->set_bound_variables({});
|
||||
cql3::cql_stats ignored;
|
||||
auto prepared = raw->prepare(service::get_local_storage_proxy().get_db().local(), ignored, true);
|
||||
_select_statement = static_pointer_cast<cql3::statements::select_statement>(prepared->statement);
|
||||
}
|
||||
return *_select_statement;
|
||||
}
|
||||
|
||||
const query::partition_slice& view::partition_slice() const {
|
||||
if (!_partition_slice) {
|
||||
_partition_slice = select_statement().make_partition_slice(cql3::query_options({ }));
|
||||
}
|
||||
return *_partition_slice;
|
||||
}
|
||||
|
||||
const dht::partition_range_vector& view::partition_ranges() const {
|
||||
if (!_partition_ranges) {
|
||||
_partition_ranges = select_statement().get_restrictions()->get_partition_key_ranges(cql3::query_options({ }));
|
||||
}
|
||||
return *_partition_ranges;
|
||||
}
|
||||
|
||||
bool view::partition_key_matches(const ::schema& base, const dht::decorated_key& key) const {
|
||||
dht::ring_position rp(key);
|
||||
auto& ranges = partition_ranges();
|
||||
return std::any_of(ranges.begin(), ranges.end(), [&] (auto&& range) {
|
||||
return range.contains(rp, dht::ring_position_comparator(base));
|
||||
});
|
||||
}
|
||||
|
||||
bool view::clustering_prefix_matches(const ::schema& base, const partition_key& key, const clustering_key_prefix& ck) const {
|
||||
bound_view::compare less(base);
|
||||
auto& ranges = partition_slice().row_ranges(base, key);
|
||||
return std::any_of(ranges.begin(), ranges.end(), [&] (auto&& range) {
|
||||
auto bounds = bound_view::from_range(range);
|
||||
return !less(ck, bounds.first) && !less(bounds.second, ck);
|
||||
});
|
||||
}
|
||||
|
||||
bool view::may_be_affected_by(const ::schema& base, const dht::decorated_key& key, const rows_entry& update) const {
|
||||
// We can guarantee that the view won't be affected if:
|
||||
// - the primary key is excluded by the view filter (note that this isn't true of the filter on regular columns:
|
||||
// even if an update don't match a view condition on a regular column, that update can still invalidate a
|
||||
// pre-existing entry);
|
||||
// - the update doesn't modify any of the columns impacting the view (where "impacting" the view means that column
|
||||
// is neither included in the view, nor used by the view filter).
|
||||
if (!partition_key_matches(base, key) && !clustering_prefix_matches(base, key.key(), update.key())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We want to check if the update modifies any of the columns that are part of the view (in which case the view is
|
||||
// affected). But iff the view includes all the base table columns, or the update has either a row deletion or a
|
||||
// row marker, we know the view is affected right away.
|
||||
if (_schema->view_info()->include_all_columns() || update.row().deleted_at() || update.row().marker().is_live()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool affected = false;
|
||||
update.row().cells().for_each_cell_until([&] (column_id id, const atomic_cell_or_collection& cell) {
|
||||
affected = _schema->get_column_definition(base.column_at(column_kind::regular_column, id).name());
|
||||
return stop_iteration(affected);
|
||||
});
|
||||
return affected;
|
||||
}
|
||||
|
||||
} // namespace view
|
||||
} // namespace db
|
||||
|
||||
@@ -21,7 +21,16 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "dht/i_partitioner.hh"
|
||||
#include "query-request.hh"
|
||||
#include "schema.hh"
|
||||
#include "stdx.hh"
|
||||
|
||||
namespace cql3 {
|
||||
namespace statements {
|
||||
class select_statement;
|
||||
}
|
||||
}
|
||||
|
||||
namespace db {
|
||||
|
||||
@@ -29,10 +38,13 @@ namespace view {
|
||||
|
||||
class view final {
|
||||
view_ptr _schema;
|
||||
mutable shared_ptr<cql3::statements::select_statement> _select_statement;
|
||||
mutable stdx::optional<query::partition_slice> _partition_slice;
|
||||
mutable stdx::optional<dht::partition_range_vector> _partition_ranges;
|
||||
public:
|
||||
view(view_ptr schema)
|
||||
: _schema(schema)
|
||||
{ }
|
||||
explicit view(view_ptr schema)
|
||||
: _schema(std::move(schema)) {
|
||||
}
|
||||
|
||||
view_ptr schema() const {
|
||||
return _schema;
|
||||
@@ -40,7 +52,40 @@ public:
|
||||
|
||||
void update(view_ptr new_schema) {
|
||||
_schema = new_schema;
|
||||
_select_statement = nullptr;
|
||||
_partition_slice = { };
|
||||
_partition_ranges = { };
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the view filter considers the specified partition key.
|
||||
*
|
||||
* @param base the base table schema.
|
||||
* @param key the partition key that is updated.
|
||||
* @return false if we can guarantee that inserting an update for specified key
|
||||
* won't affect the view in any way, true otherwise.
|
||||
*/
|
||||
bool partition_key_matches(const ::schema& base, const dht::decorated_key& key) const;
|
||||
|
||||
/**
|
||||
* Whether the view might be affected by the provided update.
|
||||
*
|
||||
* Note that having this method return true is not an absolute guarantee that the view will be
|
||||
* updated, just that it most likely will, but a false return guarantees it won't be affected.
|
||||
*
|
||||
* @param base the base table schema.
|
||||
* @param key the partition key that is updated.
|
||||
* @param update the base table update being applied.
|
||||
* @return false if we can guarantee that inserting update for key
|
||||
* won't affect the view in any way, true otherwise.
|
||||
*/
|
||||
bool may_be_affected_by(const ::schema& base, const dht::decorated_key& key, const rows_entry& update) const;
|
||||
|
||||
private:
|
||||
cql3::statements::select_statement& select_statement() const;
|
||||
const query::partition_slice& partition_slice() const;
|
||||
const dht::partition_range_vector& partition_ranges() const;
|
||||
bool clustering_prefix_matches(const ::schema& base, const partition_key& key, const clustering_key_prefix& ck) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user