Files
scylladb/cql3/values.hh
Kefu Chai df63e2ba27 types: move types.{cc,hh} into types
they are part of the CQL type system, and are "closer" to types.
let's move them into "types" directory.

the building systems are updated accordingly.

the source files referencing `types.hh` were updated using following
command:

```
find . -name "*.{cc,hh}" -exec sed -i 's/\"types.hh\"/\"types\/types.hh\"/' {} +
```

the source files under sstables include "types.hh", which is
indeed the one located under "sstables", so include "sstables/types.hh"
instea, so it's more explicit.

Signed-off-by: Kefu Chai <kefu.chai@scylladb.com>

Closes #12926
2023-02-19 21:05:45 +02:00

295 lines
9.7 KiB
C++

/*
* Copyright (C) 2017-present ScyllaDB
*/
/*
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#pragma once
#include "types/types.hh"
#include "types/collection.hh"
#include "bytes.hh"
#include <optional>
#include <variant>
#include <seastar/util/variant_utils.hh>
#include "utils/fragmented_temporary_buffer.hh"
#include "utils/overloaded_functor.hh"
namespace cql3 {
struct null_value {
friend bool operator==(const null_value&, const null_value) { return true; }
};
class raw_value;
/// \brief View to a raw CQL protocol value.
///
/// \see raw_value
class raw_value_view {
std::variant<fragmented_temporary_buffer::view, managed_bytes_view, null_value> _data;
// Temporary storage is only useful if a raw_value_view needs to be instantiated
// with a value which lifetime is bounded only to the view itself.
// This hack is introduced in order to avoid storing temporary storage
// in an external container, which may cause memory leaking problems.
// This pointer is disengaged for regular raw_value_view instances.
// Data is stored in a shared pointer for two reasons:
// - pointers are cheap to copy
// - it makes the view keep its semantics - it's safe to copy a view multiple times
// and all copies still refer to the same underlying data.
lw_shared_ptr<managed_bytes> _temporary_storage = nullptr;
raw_value_view(null_value data)
: _data{std::move(data)}
{}
raw_value_view(fragmented_temporary_buffer::view data)
: _data{data}
{}
raw_value_view(managed_bytes_view data)
: _data{data}
{}
// This constructor is only used by make_temporary() and it acquires ownership
// of the given buffer. The view created that way refers to its own temporary storage.
explicit raw_value_view(managed_bytes&& temporary_storage);
public:
static raw_value_view make_null() {
return raw_value_view{null_value{}};
}
static raw_value_view make_value(fragmented_temporary_buffer::view view) {
return raw_value_view{view};
}
static raw_value_view make_value(managed_bytes_view view) {
return raw_value_view{view};
}
static raw_value_view make_value(bytes_view view) {
return raw_value_view{managed_bytes_view(view)};
}
static raw_value_view make_temporary(raw_value&& value);
bool is_null() const {
return std::holds_alternative<null_value>(_data);
}
// An empty value is not null, but it has 0 bytes of data.
// An empty int value can be created in CQL using blobasint(0x).
bool is_empty_value() const {
if (is_null()) {
return false;
}
return size_bytes() == 0;
}
bool is_value() const {
return _data.index() <= 1;
}
explicit operator bool() const {
return is_value();
}
template <typename Func>
requires std::invocable<Func, const managed_bytes_view&> && std::invocable<Func, const fragmented_temporary_buffer::view&>
decltype(auto) with_value(Func f) const {
switch (_data.index()) {
case 0: return f(std::get<fragmented_temporary_buffer::view>(_data));
default: return f(std::get<managed_bytes_view>(_data));
}
}
template <typename Func>
requires std::invocable<Func, bytes_view>
decltype(auto) with_linearized(Func f) const {
return with_value([&] (const FragmentedView auto& v) {
return ::with_linearized(v, std::forward<Func>(f));
});
}
size_t size_bytes() const {
return with_value([&] (const FragmentedView auto& v) {
return v.size_bytes();
});
}
template <typename ValueType>
ValueType deserialize(const abstract_type& t) const {
return value_cast<ValueType>(with_value([&] (const FragmentedView auto& v) { return t.deserialize(v); }));
}
template <typename ValueType>
ValueType deserialize(const collection_type_impl& t) const {
return value_cast<ValueType>(with_value([&] (const FragmentedView auto& v) { return t.deserialize(v); }));
}
void validate(const abstract_type& t) const {
return with_value([&] (const FragmentedView auto& v) { return t.validate(v); });
}
template <typename ValueType>
ValueType validate_and_deserialize(const collection_type_impl& t) const {
return with_value([&] (const FragmentedView auto& v) {
t.validate(v);
return value_cast<ValueType>(t.deserialize(v));
});
}
template <typename ValueType>
ValueType validate_and_deserialize(const abstract_type& t) const {
return with_value([&] (const FragmentedView auto& v) {
t.validate(v);
return value_cast<ValueType>(t.deserialize(v));
});
}
friend managed_bytes_opt to_managed_bytes_opt(const cql3::raw_value_view& view) {
if (view.is_value()) {
return view.with_value([] (const FragmentedView auto& v) { return managed_bytes(v); });
}
return managed_bytes_opt();
}
friend managed_bytes_opt to_managed_bytes_opt(cql3::raw_value_view&& view) {
if (view._temporary_storage) {
return std::move(*view._temporary_storage);
}
return to_managed_bytes_opt(view);
}
friend std::ostream& operator<<(std::ostream& os, const raw_value_view& value);
friend class raw_value;
};
/// \brief Raw CQL protocol value.
///
/// The `raw_value` type represents an uninterpreted value from the CQL wire
/// protocol. A raw value can hold either a null value, or a byte
/// blob that represents the value.
class raw_value {
std::variant<bytes, managed_bytes, null_value> _data;
raw_value(null_value&& data)
: _data{std::move(data)}
{}
raw_value(bytes&& data)
: _data{std::move(data)}
{}
raw_value(const bytes& data)
: _data{data}
{}
raw_value(managed_bytes&& data)
: _data{std::move(data)}
{}
raw_value(const managed_bytes& data)
: _data{data}
{}
public:
static raw_value make_null() {
return raw_value{null_value{}};
}
static raw_value make_value(const raw_value_view& view);
static raw_value make_value(managed_bytes&& mb) {
return raw_value{std::move(mb)};
}
static raw_value make_value(managed_bytes_opt&& mbo) {
return mbo ? make_value(std::move(*mbo)) : make_null();
}
static raw_value make_value(const managed_bytes& mb) {
return raw_value{mb};
}
static raw_value make_value(const managed_bytes_opt& mbo) {
if (mbo) {
return make_value(*mbo);
}
return make_null();
}
static raw_value make_value(bytes&& bytes) {
return raw_value{std::move(bytes)};
}
static raw_value make_value(const bytes& bytes) {
return raw_value{bytes};
}
static raw_value make_value(const bytes_opt& bytes) {
if (bytes) {
return make_value(*bytes);
}
return make_null();
}
bool is_null() const {
return std::holds_alternative<null_value>(_data);
}
// An empty value is not null, but it has 0 bytes of data.
// An empty int value can be created in CQL using blobasint(0x).
bool is_empty_value() const {
if (is_null()) {
return false;
}
return view().size_bytes() == 0;
}
bool is_value() const {
return _data.index() <= 1;
}
explicit operator bool() const {
return is_value();
}
bytes to_bytes() && {
return std::visit(overloaded_functor{
[](bytes&& bytes_val) { return std::move(bytes_val); },
[](managed_bytes&& managed_bytes_val) { return ::to_bytes(managed_bytes_val); },
[](null_value&&) -> bytes {
throw std::runtime_error("to_bytes() called on raw value that is null");
},
}, std::move(_data));
}
bytes_opt to_bytes_opt() && {
return std::visit(overloaded_functor{
[](bytes&& bytes_val) { return bytes_opt(bytes_val); },
[](managed_bytes&& managed_bytes_val) { return bytes_opt(::to_bytes(managed_bytes_val)); },
[](null_value&&) -> bytes_opt {
return std::nullopt;
},
}, std::move(_data));
}
managed_bytes to_managed_bytes() && {
return std::visit(overloaded_functor{
[](bytes&& bytes_val) { return managed_bytes(bytes_val); },
[](managed_bytes&& managed_bytes_val) { return std::move(managed_bytes_val); },
[](null_value&&) -> managed_bytes {
throw std::runtime_error("to_managed_bytes() called on raw value that is null");
},
}, std::move(_data));
}
managed_bytes_opt to_managed_bytes_opt() && {
return std::visit(overloaded_functor{
[](bytes&& bytes_val) { return managed_bytes_opt(bytes_val); },
[](managed_bytes&& managed_bytes_val) { return managed_bytes_opt(std::move(managed_bytes_val)); },
[](null_value&&) -> managed_bytes_opt {
return std::nullopt;
},
}, std::move(_data));
}
raw_value_view view() const;
friend class raw_value_view;
friend bool operator==(const raw_value& v1, const raw_value& v2);
friend std::ostream& operator<<(std::ostream& os, const raw_value& value);
};
}
inline bytes to_bytes(const cql3::raw_value_view& view)
{
return view.with_value([] (const FragmentedView auto& v) {
return linearized(v);
});
}
inline bytes_opt to_bytes_opt(const cql3::raw_value_view& view) {
if (view.is_value()) {
return to_bytes(view);
}
return bytes_opt();
}
inline bytes_opt to_bytes_opt(const cql3::raw_value& value) {
return to_bytes_opt(value.view());
}