Files
scylladb/mutation/position_in_partition.hh
Ernest Zaslavsky d624413ddd treewide: Move query related files to a new query directory
As requested in #22120, moved the files and fixed other includes and build system.

Moved files:
- query.cc
- query-request.hh
- query-result.hh
- query-result-reader.hh
- query-result-set.cc
- query-result-set.hh
- query-result-writer.hh
- query_id.hh
- query_result_merger.hh

Fixes: #22120

This is a cleanup, no need to backport

Closes scylladb/scylladb#25105
2025-09-16 23:40:47 +03:00

825 lines
33 KiB
C++

/*
* Copyright (C) 2017-present ScyllaDB
*/
/*
* SPDX-License-Identifier: LicenseRef-ScyllaDB-Source-Available-1.0
*/
#pragma once
#include "utils/assert.hh"
#include "types/types.hh"
#include "keys/keys.hh"
#include "keys/clustering_bounds_comparator.hh"
#include "query/query-request.hh"
#include <optional>
#include <cstdlib>
inline
lexicographical_relation relation_for_lower_bound(composite_view v) {
switch (v.last_eoc()) {
case composite::eoc::start:
case composite::eoc::none:
return lexicographical_relation::before_all_prefixed;
case composite::eoc::end:
return lexicographical_relation::after_all_prefixed;
}
abort();
}
inline
lexicographical_relation relation_for_upper_bound(composite_view v) {
switch (v.last_eoc()) {
case composite::eoc::start:
return lexicographical_relation::before_all_prefixed;
case composite::eoc::none:
return lexicographical_relation::before_all_strictly_prefixed;
case composite::eoc::end:
return lexicographical_relation::after_all_prefixed;
}
abort();
}
enum class bound_weight : int8_t {
before_all_prefixed = -1,
equal = 0,
after_all_prefixed = 1,
};
inline
bound_weight reversed(bound_weight w) {
switch (w) {
case bound_weight::equal:
return w;
case bound_weight::before_all_prefixed:
return bound_weight::after_all_prefixed;
case bound_weight::after_all_prefixed:
return bound_weight::before_all_prefixed;
}
std::abort();
}
inline
bound_weight position_weight(bound_kind k) {
switch (k) {
case bound_kind::excl_end:
case bound_kind::incl_start:
return bound_weight::before_all_prefixed;
case bound_kind::incl_end:
case bound_kind::excl_start:
return bound_weight::after_all_prefixed;
}
abort();
}
enum class partition_region : uint8_t {
partition_start,
static_row,
clustered,
partition_end,
};
struct view_and_holder;
template <>
struct fmt::formatter<partition_region> : fmt::formatter<string_view> {
template <typename FormatContext>
auto format(const ::partition_region& r, FormatContext& ctx) const {
switch (r) {
case partition_region::partition_start:
return formatter<string_view>::format("partition_start", ctx);
case partition_region::static_row:
return formatter<string_view>::format("static_row", ctx);
case partition_region::clustered:
return formatter<string_view>::format("clustered", ctx);
case partition_region::partition_end:
return formatter<string_view>::format("partition_end", ctx);
}
std::abort(); // compiler will error before we reach here
}
};
partition_region parse_partition_region(std::string_view);
class position_in_partition_view {
friend class position_in_partition;
partition_region _type;
bound_weight _bound_weight = bound_weight::equal;
const clustering_key_prefix* _ck; // nullptr when _type != clustered
public:
position_in_partition_view(partition_region type, bound_weight weight, const clustering_key_prefix* ck)
: _type(type)
, _bound_weight(weight)
, _ck(ck)
{ }
bool is_before_key() const {
return _bound_weight == bound_weight::before_all_prefixed;
}
bool is_after_key() const {
return _bound_weight == bound_weight::after_all_prefixed;
}
private:
// Returns placement of this position_in_partition relative to *_ck,
// or lexicographical_relation::at_prefix if !_ck.
lexicographical_relation relation() const {
// FIXME: Currently position_range cannot represent a range end bound which
// includes just the prefix key or a range start which excludes just a prefix key.
// In both cases we should return lexicographical_relation::before_all_strictly_prefixed here.
// Refs #1446.
if (_bound_weight == bound_weight::after_all_prefixed) {
return lexicographical_relation::after_all_prefixed;
} else {
return lexicographical_relation::before_all_prefixed;
}
}
public:
struct partition_start_tag_t { };
struct end_of_partition_tag_t { };
struct static_row_tag_t { };
struct clustering_row_tag_t { };
struct range_tag_t { };
using range_tombstone_tag_t = range_tag_t;
explicit position_in_partition_view(partition_start_tag_t) : _type(partition_region::partition_start), _ck(nullptr) { }
explicit position_in_partition_view(end_of_partition_tag_t) : _type(partition_region::partition_end), _ck(nullptr) { }
explicit position_in_partition_view(static_row_tag_t) : _type(partition_region::static_row), _ck(nullptr) { }
position_in_partition_view(clustering_row_tag_t, const clustering_key_prefix& ck)
: _type(partition_region::clustered), _ck(&ck) { }
position_in_partition_view(const clustering_key_prefix& ck)
: _type(partition_region::clustered), _ck(&ck) { }
position_in_partition_view(range_tag_t, bound_view bv)
: _type(partition_region::clustered), _bound_weight(position_weight(bv.kind())), _ck(&bv.prefix()) { }
position_in_partition_view(const clustering_key_prefix& ck, bound_weight w)
: _type(partition_region::clustered), _bound_weight(w), _ck(&ck) { }
static position_in_partition_view for_range_start(const query::clustering_range& r) {
return {position_in_partition_view::range_tag_t(), bound_view::from_range_start(r)};
}
static position_in_partition_view for_range_end(const query::clustering_range& r) {
return {position_in_partition_view::range_tag_t(), bound_view::from_range_end(r)};
}
static position_in_partition_view before_all_clustered_rows() {
return {range_tag_t(), bound_view::bottom()};
}
static position_in_partition_view after_all_clustered_rows() {
return {position_in_partition_view::range_tag_t(), bound_view::top()};
}
static position_in_partition_view for_partition_start() {
return position_in_partition_view(partition_start_tag_t());
}
static position_in_partition_view for_partition_end() {
return position_in_partition_view(end_of_partition_tag_t());
}
static position_in_partition_view for_static_row() {
return position_in_partition_view(static_row_tag_t());
}
static position_in_partition_view for_key(const clustering_key& ck) {
return {clustering_row_tag_t(), ck};
}
// Returns a view, as the first element of the returned pair, to before_key(pos._ck)
// if pos.is_clustering_row() else returns pos as-is.
// The second element of the pair needs to be kept alive as long as the first element is used.
// The returned view is valid as long as the view passed to this method is valid.
static view_and_holder after_key(const schema&, position_in_partition_view);
static position_in_partition_view after_all_prefixed(const clustering_key& ck) {
return {partition_region::clustered, bound_weight::after_all_prefixed, &ck};
}
// Returns a view to after_all_prefixed(pos._ck) if pos.is_clustering_row() else returns pos as-is.
static position_in_partition_view after_all_prefixed(position_in_partition_view pos) {
return {partition_region::clustered, pos._bound_weight == bound_weight::equal ? bound_weight::after_all_prefixed : pos._bound_weight, pos._ck};
}
static position_in_partition_view before_key(const clustering_key& ck) {
return {partition_region::clustered, bound_weight::before_all_prefixed, &ck};
}
// Returns a view to before_key(pos._ck) if pos.is_clustering_row() else returns pos as-is.
static position_in_partition_view before_key(position_in_partition_view pos) {
return {partition_region::clustered, pos._bound_weight == bound_weight::equal ? bound_weight::before_all_prefixed : pos._bound_weight, pos._ck};
}
partition_region region() const { return _type; }
bound_weight get_bound_weight() const { return _bound_weight; }
bool is_partition_start() const { return _type == partition_region::partition_start; }
bool is_partition_end() const { return _type == partition_region::partition_end; }
bool is_static_row() const { return _type == partition_region::static_row; }
bool is_clustering_row() const { return has_clustering_key() && _bound_weight == bound_weight::equal; }
bool has_clustering_key() const { return _type == partition_region::clustered; }
// Returns true if all fragments that can be seen for given schema have
// positions >= than this. partition_start is ignored.
bool is_before_all_fragments(const schema& s) const {
return _type == partition_region::partition_start || _type == partition_region::static_row
|| (_type == partition_region::clustered && !s.has_static_columns() && _bound_weight == bound_weight::before_all_prefixed && key().is_empty(s));
}
bool is_after_all_clustered_rows(const schema& s) const {
return is_partition_end() || (_ck && _ck->is_empty(s) && _bound_weight == bound_weight::after_all_prefixed);
}
bool has_key() const { return bool(_ck); }
// Valid when has_key() == true
const clustering_key_prefix& key() const {
return *_ck;
}
// Can be called only when !is_static_row && !is_clustering_row().
bound_view as_start_bound_view() const {
SCYLLA_ASSERT(_bound_weight != bound_weight::equal);
return bound_view(*_ck, _bound_weight == bound_weight::before_all_prefixed ? bound_kind::incl_start : bound_kind::excl_start);
}
bound_view as_end_bound_view() const {
SCYLLA_ASSERT(_bound_weight != bound_weight::equal);
return bound_view(*_ck, _bound_weight == bound_weight::before_all_prefixed ? bound_kind::excl_end : bound_kind::incl_end);
}
class printer {
const schema& _schema;
const position_in_partition_view& _pipv;
public:
printer(const schema& schema, const position_in_partition_view& pipv) : _schema(schema), _pipv(pipv) {}
friend fmt::formatter<printer>;
};
// Create a position which is the same as this one but governed by a schema with reversed clustering key order.
position_in_partition_view reversed() const {
return position_in_partition_view(_type, ::reversed(_bound_weight), _ck);
}
friend fmt::formatter<printer>;
friend fmt::formatter<position_in_partition_view>;
friend bool no_clustering_row_between(const schema&, position_in_partition_view, position_in_partition_view);
};
template <>
struct fmt::formatter<position_in_partition_view> : fmt::formatter<string_view> {
template <typename FormatContext>
auto format(const ::position_in_partition_view& pos, FormatContext& ctx) const {
fmt::format_to(ctx.out(), "{{position: {}, ", pos._type);
if (pos._ck) {
fmt::format_to(ctx.out(), "{}, ", *pos._ck);
} else {
fmt::format_to(ctx.out(), "null, ");
}
return fmt::format_to(ctx.out(), "{}}}", int32_t(pos._bound_weight));
}
};
template <>
struct fmt::formatter<position_in_partition_view::printer> : fmt::formatter<string_view> {
template <typename FormatContext>
auto format(const ::position_in_partition_view::printer& p, FormatContext& ctx) const {
auto& pos = p._pipv;
fmt::format_to(ctx.out(), "{{position: {},", pos._type);
if (pos._ck) {
fmt::format_to(ctx.out(), "{}", clustering_key_prefix::with_schema_wrapper(p._schema, *pos._ck));
} else {
fmt::format_to(ctx.out(), "null");
}
return fmt::format_to(ctx.out(), ", {}}}", int32_t(pos._bound_weight));
}
};
class position_in_partition {
partition_region _type;
bound_weight _bound_weight = bound_weight::equal;
std::optional<clustering_key_prefix> _ck;
public:
friend class clustering_interval_set;
struct partition_start_tag_t { };
struct end_of_partition_tag_t { };
struct static_row_tag_t { };
struct after_static_row_tag_t { };
struct clustering_row_tag_t { };
struct after_clustering_row_tag_t { };
struct before_clustering_row_tag_t { };
struct range_tag_t { };
using range_tombstone_tag_t = range_tag_t;
partition_region get_type() const { return _type; }
bound_weight get_bound_weight() const { return _bound_weight; }
const std::optional<clustering_key_prefix>& get_clustering_key_prefix() const { return _ck; }
position_in_partition(partition_region type, bound_weight weight, std::optional<clustering_key_prefix> ck)
: _type(type), _bound_weight(weight), _ck(std::move(ck)) { }
explicit position_in_partition(partition_start_tag_t) : _type(partition_region::partition_start) { }
explicit position_in_partition(end_of_partition_tag_t) : _type(partition_region::partition_end) { }
explicit position_in_partition(static_row_tag_t) : _type(partition_region::static_row) { }
position_in_partition(clustering_row_tag_t, clustering_key_prefix ck)
: _type(partition_region::clustered), _ck(std::move(ck)) { }
position_in_partition(after_clustering_row_tag_t, const schema& s, clustering_key_prefix ck)
: _type(partition_region::clustered)
, _bound_weight(bound_weight::after_all_prefixed)
, _ck(std::move(ck))
{
if (clustering_key::make_full(s, *_ck)) { // Refs #1446
_bound_weight = bound_weight::before_all_prefixed;
}
}
position_in_partition(after_clustering_row_tag_t, const schema& s, position_in_partition_view pos)
: position_in_partition(after_clustering_row_tag_t(), s, position_in_partition(pos))
{ }
position_in_partition(after_clustering_row_tag_t, const schema& s, position_in_partition&& pos)
: _type(partition_region::clustered)
, _bound_weight(pos._bound_weight != bound_weight::equal ? pos._bound_weight : bound_weight::after_all_prefixed)
, _ck(std::move(pos._ck))
{
if (pos._bound_weight == bound_weight::equal && _ck && clustering_key::make_full(s, *_ck)) { // Refs #1446
_bound_weight = bound_weight::before_all_prefixed;
}
}
position_in_partition(before_clustering_row_tag_t, clustering_key_prefix ck)
: _type(partition_region::clustered), _bound_weight(bound_weight::before_all_prefixed), _ck(std::move(ck)) { }
position_in_partition(before_clustering_row_tag_t, position_in_partition_view pos)
: _type(partition_region::clustered)
, _bound_weight(pos._bound_weight != bound_weight::equal ? pos._bound_weight : bound_weight::before_all_prefixed)
, _ck(*pos._ck) { }
position_in_partition(range_tag_t, bound_view bv)
: _type(partition_region::clustered), _bound_weight(position_weight(bv.kind())), _ck(bv.prefix()) { }
position_in_partition(range_tag_t, bound_kind kind, clustering_key_prefix&& prefix)
: _type(partition_region::clustered), _bound_weight(position_weight(kind)), _ck(std::move(prefix)) { }
position_in_partition(after_static_row_tag_t) :
position_in_partition(range_tag_t(), bound_view::bottom()) { }
explicit position_in_partition(position_in_partition_view view)
: _type(view._type), _bound_weight(view._bound_weight)
{
if (view._ck) {
_ck = *view._ck;
}
}
// Strong exception guarantees.
position_in_partition& operator=(position_in_partition_view view) {
// The copy assignment to _ck can throw (because it allocates),
// but assignments to _type and _bound_weight can't throw.
// Thus, to achieve strong exception guarantees,
// we only need to perform the _ck assignment before others.
if (view._ck) {
_ck = *view._ck;
} else {
_ck.reset();
}
_type = view._type;
_bound_weight = view._bound_weight;
return *this;
}
static position_in_partition before_all_clustered_rows() {
return {position_in_partition::range_tag_t(), bound_view::bottom()};
}
static position_in_partition after_all_clustered_rows() {
return {position_in_partition::range_tag_t(), bound_view::top()};
}
// If given position is a clustering row position, returns a position
// right before it. Otherwise, returns it unchanged.
// The position "pos" must be a clustering position.
static position_in_partition before_key(position_in_partition_view pos) {
return {before_clustering_row_tag_t(), pos};
}
static position_in_partition before_key(clustering_key ck) {
return {before_clustering_row_tag_t(), std::move(ck)};
}
static position_in_partition after_key(const schema& s, clustering_key ck) {
return {after_clustering_row_tag_t(), s, std::move(ck)};
}
// If given position is a clustering row position, returns a position
// right after it. Otherwise returns it unchanged.
// The position "pos" must be a clustering position.
static position_in_partition after_key(const schema& s, position_in_partition_view pos) {
return {after_clustering_row_tag_t(), s, pos};
}
static position_in_partition after_key(const schema& s, position_in_partition&& pos) noexcept {
return {after_clustering_row_tag_t(), s, std::move(pos)};
}
static position_in_partition for_key(clustering_key ck) {
return {clustering_row_tag_t(), std::move(ck)};
}
static position_in_partition for_partition_start() {
return position_in_partition{partition_start_tag_t()};
}
static position_in_partition for_partition_end() {
return position_in_partition(end_of_partition_tag_t());
}
static position_in_partition for_static_row() {
return position_in_partition{static_row_tag_t()};
}
static position_in_partition min() {
return for_static_row();
}
static position_in_partition for_range_start(const query::clustering_range&);
static position_in_partition for_range_end(const query::clustering_range&);
partition_region region() const { return _type; }
bool is_partition_start() const { return _type == partition_region::partition_start; }
bool is_partition_end() const { return _type == partition_region::partition_end; }
bool is_static_row() const { return _type == partition_region::static_row; }
bool is_clustering_row() const { return has_clustering_key() && _bound_weight == bound_weight::equal; }
bool has_clustering_key() const { return _type == partition_region::clustered; }
bool is_after_all_clustered_rows(const schema& s) const {
return is_partition_end() || (_ck && _ck->is_empty(s) && _bound_weight == bound_weight::after_all_prefixed);
}
bool is_before_all_clustered_rows(const schema& s) const {
return _type < partition_region::clustered
|| (_type == partition_region::clustered && _ck->is_empty(s) && _bound_weight == bound_weight::before_all_prefixed);
}
template<typename Hasher>
void feed_hash(Hasher& hasher, const schema& s) const {
::feed_hash(hasher, _bound_weight);
if (_ck) {
::feed_hash(hasher, true);
::feed_hash(hasher, *_ck, s);
} else {
::feed_hash(hasher, false);
}
}
bool has_key() const { return bool(_ck); }
const clustering_key_prefix& key() const {
return *_ck;
}
operator position_in_partition_view() const {
return { _type, _bound_weight, _ck ? &*_ck : nullptr };
}
size_t external_memory_usage() const {
return _ck ? _ck->external_memory_usage() : 0;
}
// Defines total order on the union of position_and_partition and composite objects.
//
// The ordering is compatible with position_range (r). The following is satisfied for
// all cells with name c included by the range:
//
// r.start() <= c < r.end()
//
// The ordering on composites given by this is compatible with but weaker than the cell name order.
//
// The ordering on position_in_partition given by this is compatible but weaker than the ordering
// given by position_in_partition::tri_compare.
//
class composite_tri_compare {
const schema& _s;
public:
static int rank(partition_region t) {
return static_cast<int>(t);
}
composite_tri_compare(const schema& s) : _s(s) {}
std::strong_ordering operator()(position_in_partition_view a, position_in_partition_view b) const {
if (a._type != b._type) {
return rank(a._type) <=> rank(b._type);
}
if (!a._ck) {
return std::strong_ordering::equal;
}
auto&& types = _s.clustering_key_type()->types();
auto cmp = [&] (const data_type& t, managed_bytes_view c1, managed_bytes_view c2) { return t->compare(c1, c2); };
return lexicographical_tri_compare(types.begin(), types.end(),
a._ck->begin(_s), a._ck->end(_s),
b._ck->begin(_s), b._ck->end(_s),
cmp, a.relation(), b.relation());
}
std::strong_ordering operator()(position_in_partition_view a, composite_view b) const {
if (b.empty()) {
return std::strong_ordering::greater; // a cannot be empty.
}
partition_region b_type = b.is_static() ? partition_region::static_row : partition_region::clustered;
if (a._type != b_type) {
return rank(a._type) <=> rank(b_type);
}
if (!a._ck) {
return std::strong_ordering::equal;
}
auto&& types = _s.clustering_key_type()->types();
auto b_values = b.values();
auto cmp = [&] (const data_type& t, managed_bytes_view c1, managed_bytes_view c2) { return t->compare(c1, c2); };
return lexicographical_tri_compare(types.begin(), types.end(),
a._ck->begin(_s), a._ck->end(_s),
b_values.begin(), b_values.end(),
cmp, a.relation(), relation_for_lower_bound(b));
}
std::strong_ordering operator()(composite_view a, position_in_partition_view b) const {
return 0 <=> (*this)(b, a);
}
std::strong_ordering operator()(composite_view a, composite_view b) const {
if (a.is_static() != b.is_static()) {
return a.is_static() ? std::strong_ordering::less : std::strong_ordering::greater;
}
auto&& types = _s.clustering_key_type()->types();
auto a_values = a.values();
auto b_values = b.values();
auto cmp = [&] (const data_type& t, bytes_view c1, bytes_view c2) { return t->compare(c1, c2); };
return lexicographical_tri_compare(types.begin(), types.end(),
a_values.begin(), a_values.end(),
b_values.begin(), b_values.end(),
cmp,
relation_for_lower_bound(a),
relation_for_lower_bound(b));
}
};
// Less comparator giving the same order as composite_tri_compare.
class composite_less_compare {
composite_tri_compare _cmp;
public:
composite_less_compare(const schema& s) : _cmp(s) {}
template<typename T, typename U>
bool operator()(const T& a, const U& b) const {
return _cmp(a, b) < 0;
}
};
class tri_compare {
bound_view::tri_compare _cmp;
private:
template<typename T, typename U>
std::strong_ordering compare(const T& a, const U& b) const {
if (a._type != b._type) {
return composite_tri_compare::rank(a._type) <=> composite_tri_compare::rank(b._type);
}
if (!a._ck) {
return std::strong_ordering::equal;
}
return _cmp(*a._ck, int8_t(a._bound_weight), *b._ck, int8_t(b._bound_weight));
}
public:
tri_compare(const schema& s) : _cmp(s) { }
std::strong_ordering operator()(const position_in_partition& a, const position_in_partition& b) const {
return compare(a, b);
}
std::strong_ordering operator()(const position_in_partition_view& a, const position_in_partition_view& b) const {
return compare(a, b);
}
std::strong_ordering operator()(const position_in_partition& a, const position_in_partition_view& b) const {
return compare(a, b);
}
std::strong_ordering operator()(const position_in_partition_view& a, const position_in_partition& b) const {
return compare(a, b);
}
};
class less_compare {
tri_compare _cmp;
public:
less_compare(const schema& s) : _cmp(s) { }
bool operator()(const position_in_partition& a, const position_in_partition& b) const {
return _cmp(a, b) < 0;
}
bool operator()(const position_in_partition_view& a, const position_in_partition_view& b) const {
return _cmp(a, b) < 0;
}
bool operator()(const position_in_partition& a, const position_in_partition_view& b) const {
return _cmp(a, b) < 0;
}
bool operator()(const position_in_partition_view& a, const position_in_partition& b) const {
return _cmp(a, b) < 0;
}
};
class equal_compare {
clustering_key_prefix::equality _equal;
template<typename T, typename U>
bool compare(const T& a, const U& b) const {
if (a._type != b._type) {
return false;
}
bool a_rt_weight = bool(a._ck);
bool b_rt_weight = bool(b._ck);
return a_rt_weight == b_rt_weight
&& (!a_rt_weight || (a._bound_weight == b._bound_weight && _equal(*a._ck, *b._ck)));
}
public:
equal_compare(const schema& s) : _equal(s) { }
bool operator()(const position_in_partition& a, const position_in_partition& b) const {
return compare(a, b);
}
bool operator()(const position_in_partition_view& a, const position_in_partition_view& b) const {
return compare(a, b);
}
bool operator()(const position_in_partition_view& a, const position_in_partition& b) const {
return compare(a, b);
}
bool operator()(const position_in_partition& a, const position_in_partition_view& b) const {
return compare(a, b);
}
};
// Create a position which is the same as this one but governed by a schema with reversed clustering key order.
position_in_partition reversed() const& {
return position_in_partition(_type, ::reversed(_bound_weight), _ck);
}
// Create a position which is the same as this one but governed by a schema with reversed clustering key order.
position_in_partition reversed() && {
return position_in_partition(_type, ::reversed(_bound_weight), std::move(_ck));
}
};
template <>
struct fmt::formatter<position_in_partition> : fmt::formatter<string_view> {
template <typename FormatContext>
auto format(const ::position_in_partition& pos, FormatContext& ctx) const {
return fmt::format_to(ctx.out(), "{}", position_in_partition_view(pos));
}
};
struct view_and_holder {
std::optional<position_in_partition> holder;
position_in_partition_view view;
view_and_holder(position_in_partition pos)
: holder(std::move(pos))
, view(*holder)
{ }
explicit view_and_holder(position_in_partition_view pos)
: view(pos)
{ }
view_and_holder(view_and_holder&& other) noexcept
: holder(std::move(other.holder))
, view(holder ? *holder : other.view)
{ }
view_and_holder& operator=(view_and_holder&& other) noexcept {
holder = std::move(other.holder);
view = holder ? *holder: other.view;
return *this;
}
};
inline
view_and_holder position_in_partition_view::after_key(const schema& s, position_in_partition_view pos) {
if (!pos.is_clustering_row()) {
return view_and_holder(pos);
}
if (pos.key().is_full(s)) {
return view_and_holder(position_in_partition_view::after_all_prefixed(pos.key()));
}
// FIXME: This wouldn't be needed if we had a bound weight to represent this.
return view_and_holder(position_in_partition::after_key(s, clustering_key(pos.key())));
}
inline
position_in_partition position_in_partition::for_range_start(const query::clustering_range& r) {
return {position_in_partition::range_tag_t(), bound_view::from_range_start(r)};
}
inline
position_in_partition position_in_partition::for_range_end(const query::clustering_range& r) {
return {position_in_partition::range_tag_t(), bound_view::from_range_end(r)};
}
// Returns true if and only if there can't be any clustering_row with position > a and < b.
// It is assumed that a <= b.
inline
bool no_clustering_row_between(const schema& s, position_in_partition_view a, position_in_partition_view b) {
clustering_key_prefix::equality eq(s);
if (a._ck && b._ck) {
return eq(*a._ck, *b._ck) && (a._bound_weight != bound_weight::before_all_prefixed || b._bound_weight != bound_weight::after_all_prefixed);
} else {
return !a._ck && !b._ck;
}
}
// Returns true if and only if there can't be any clustering_row with position >= a and < b.
// It is assumed that a <= b.
inline
bool no_clustering_row_between_weak(const schema& s, position_in_partition_view a, position_in_partition_view b) {
clustering_key_prefix::equality eq(s);
if (a.has_key() && b.has_key()) {
return eq(a.key(), b.key())
&& (a.get_bound_weight() == bound_weight::after_all_prefixed
|| b.get_bound_weight() != bound_weight::after_all_prefixed);
} else {
return !a.has_key() && !b.has_key();
}
}
// Includes all position_in_partition objects "p" for which: start <= p < end
// And only those.
class position_range {
private:
position_in_partition _start;
position_in_partition _end;
public:
static position_range from_range(const query::clustering_range&);
static position_range for_static_row() {
return {
position_in_partition(position_in_partition::static_row_tag_t()),
position_in_partition(position_in_partition::after_static_row_tag_t())
};
}
static position_range full() {
return {
position_in_partition(position_in_partition::static_row_tag_t()),
position_in_partition::after_all_clustered_rows()
};
}
static position_range all_clustered_rows() {
return {
position_in_partition::before_all_clustered_rows(),
position_in_partition::after_all_clustered_rows()
};
}
position_range(position_range&&) = default;
position_range& operator=(position_range&&) = default;
position_range(const position_range&) = default;
position_range& operator=(const position_range&) = default;
// Constructs position_range which covers the same rows as given clustering_range.
// position_range includes a fragment if it includes position of that fragment.
position_range(const query::clustering_range&);
position_range(query::clustering_range&&);
position_range(position_in_partition start, position_in_partition end)
: _start(std::move(start))
, _end(std::move(end))
{ }
void set_start(position_in_partition pos) { _start = std::move(pos); }
void set_end(position_in_partition pos) { _end = std::move(pos); }
const position_in_partition& start() const& { return _start; }
position_in_partition&& start() && { return std::move(_start); }
const position_in_partition& end() const& { return _end; }
position_in_partition&& end() && { return std::move(_end); }
bool contains(const schema& s, position_in_partition_view pos) const;
bool overlaps(const schema& s, position_in_partition_view start, position_in_partition_view end) const;
// Returns true iff this range contains all keys contained by position_range(start, end).
bool contains(const schema& s, position_in_partition_view start, position_in_partition_view end) const;
bool is_all_clustered_rows(const schema&) const;
};
class clustering_interval_set;
inline
bool position_range::contains(const schema& s, position_in_partition_view pos) const {
position_in_partition::less_compare less(s);
return !less(pos, _start) && less(pos, _end);
}
inline
bool position_range::contains(const schema& s, position_in_partition_view start, position_in_partition_view end) const {
position_in_partition::less_compare less(s);
return !less(start, _start) && !less(_end, end);
}
inline
bool position_range::overlaps(const schema& s, position_in_partition_view start, position_in_partition_view end) const {
position_in_partition::less_compare less(s);
return !less(end, _start) && less(start, _end);
}
inline
bool position_range::is_all_clustered_rows(const schema& s) const {
return _start.is_before_all_clustered_rows(s) && _end.is_after_all_clustered_rows(s);
}
// Assumes that the bounds of `r` are of 'clustered' type
// and that `r` is non-empty (the left bound is smaller than the right bound).
//
// If `r` does not contain any keys, returns nullopt.
std::optional<query::clustering_range> position_range_to_clustering_range(const position_range& r, const schema&);
template <> struct fmt::formatter<position_range> : fmt::formatter<string_view> {
auto format(const position_range& range, fmt::format_context& ctx) const {
return fmt::format_to(ctx.out(), "{{{}, {}}}", range.start(), range.end());
}
};