As requested in #22104, moved the files and fixed other includes and build system. Moved files: - combine.hh - collection_mutation.hh - collection_mutation.cc - converting_mutation_partition_applier.hh - converting_mutation_partition_applier.cc - counters.hh - counters.cc - timestamp.hh Fixes: #22104 This is a cleanup, no need to backport Closes scylladb/scylladb#25085
128 lines
5.6 KiB
C++
128 lines
5.6 KiB
C++
/*
|
|
* Copyright (C) 2024-present ScyllaDB
|
|
*/
|
|
|
|
/*
|
|
* SPDX-License-Identifier: LicenseRef-ScyllaDB-Source-Available-1.0
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include "schema/column_computation.hh"
|
|
#include "mutation/atomic_cell.hh"
|
|
#include "mutation/timestamp.hh"
|
|
#include <type_traits>
|
|
|
|
class row_marker;
|
|
|
|
// In a basic column_computation defined in column_computation.hh, the
|
|
// compute_value() method is only based on the partition key, and it must
|
|
// return a value. That API has very limited applications - basically the
|
|
// only thing we can implement with it is token_column_computation which
|
|
// we used to create the token column in secondary indexes.
|
|
// The regular_column_transformation base class here is more powerful, but
|
|
// still is not a completely general computation: Its compute_value() virtual
|
|
// method can transform the value read from a single cell of a regular column
|
|
// into a new cell stored in a structure regular_column_transformation::result.
|
|
//
|
|
// In more details, the assumptions of regular_column_transformation is:
|
|
// 1. compute_value() computes the value based on a *single* column in a
|
|
// row passed to compute_value().
|
|
// This assumption means that the value or deletion of the value always
|
|
// has a single known timestamp (and the value can't be half-missing)
|
|
// and single TTL information. That would not have been possible if we
|
|
// allowed the computation to depend on multiple columns.
|
|
// 2. compute_value() computes the value based on a *regular* column in the
|
|
// base table. This means that an update can modify this value (unlike a
|
|
// base-table key column that can't change in an update), so the view
|
|
// update code needs to compute the value before and after the update,
|
|
// and potentially delete and create view rows.
|
|
// 3. compute_value() returns a column_computation::result which includes
|
|
// a value and its liveness information (timestamp and ttl/expiry) or
|
|
// is missing a value.
|
|
|
|
class regular_column_transformation : public column_computation {
|
|
public:
|
|
struct result {
|
|
// We can use "bytes" instead of "managed_bytes" here because we know
|
|
// that a column_computation is only used for generating a key value,
|
|
// and that is limited to 64K. This limitation is enforced below -
|
|
// we never linearize a cell's value if its size is more than 64K.
|
|
std::optional<bytes> _value;
|
|
|
|
// _ttl and _expiry are only defined if _value is set.
|
|
// The default values below are used when the source cell does not
|
|
// expire, and are the same values that row_marker uses for a non-
|
|
// expiring marker. This is useful when creating a row_marker from
|
|
// get_ttl() and get_expiry().
|
|
gc_clock::duration _ttl { 0 };
|
|
gc_clock::time_point _expiry { gc_clock::duration(0) };
|
|
|
|
// _ts may be set even if _value is missing, which can remember the
|
|
// timestamp of a tombstone. Note that the current view-update code
|
|
// that uses this class doesn't use _ts when _value is missing.
|
|
api::timestamp_type _ts = api::missing_timestamp;
|
|
|
|
api::timestamp_type get_ts() const {
|
|
return _ts;
|
|
}
|
|
|
|
bool has_value() const {
|
|
return _value.has_value();
|
|
}
|
|
|
|
// Should only be called if has_value() is true:
|
|
const bytes& get_value() const {
|
|
return *_value;
|
|
}
|
|
gc_clock::duration get_ttl() const {
|
|
return _ttl;
|
|
}
|
|
gc_clock::time_point get_expiry() const {
|
|
return _expiry;
|
|
}
|
|
|
|
// A missing computation result
|
|
result() { }
|
|
|
|
// Construct a computation result by copying a given atomic_cell -
|
|
// including its value, timestamp, and ttl - or deletion timestamp.
|
|
// The second parameter is an optional transformation function f -
|
|
// taking a bytes and returning an optional<bytes> - that transforms
|
|
// the value of the cell but keeps its other liveness information.
|
|
// If f returns a nullopt, it causes the view row should be deleted.
|
|
template<typename Func=std::identity>
|
|
requires std::invocable<Func, bytes> && std::convertible_to<std::invoke_result_t<Func, bytes>, std::optional<bytes>>
|
|
result(atomic_cell_view cell, Func f = {}) {
|
|
_ts = cell.timestamp();
|
|
if (cell.is_live()) {
|
|
// If the cell is larger than what a key can hold (64KB),
|
|
// return a missing value. This lets us skip this item during
|
|
// view building and avoid hanging the view build as described
|
|
// in #8627. But it doesn't prevent later inserting such a item
|
|
// to the base table, nor does it implement front-end specific
|
|
// limits (such as Alternator's 1K or 2K limits - see #10347).
|
|
// Those stricter limits should be validated in the base-table
|
|
// write code, not here - deep inside the view update code.
|
|
// Note also we assume that f() doesn't grow the value further.
|
|
if (cell.value().size() >= 65536) {
|
|
return;
|
|
}
|
|
_value = f(to_bytes(cell.value()));
|
|
if (_value) {
|
|
if (cell.is_live_and_has_ttl()) {
|
|
_ttl = cell.ttl();
|
|
_expiry = cell.expiry();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
virtual ~regular_column_transformation() = default;
|
|
virtual result compute_value(
|
|
const schema& schema,
|
|
const partition_key& key,
|
|
const db::view::clustering_or_static_row& row) const = 0;
|
|
};
|