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:
Duarte Nunes
2016-12-20 17:08:03 +00:00
parent c35d14e285
commit 21d1bbb527
5 changed files with 182 additions and 3 deletions

129
db/view/view.cc Normal file
View 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

View File

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