10#include <userver/utils/assert.hpp>
12#include <userver/storages/clickhouse/io/columns/column_includes.hpp>
13#include <userver/storages/clickhouse/io/columns/common_columns.hpp>
15USERVER_NAMESPACE_BEGIN
19struct NullableColumnMeta
final {
24NullableColumnMeta ExtractNullableMeta(
const ColumnRef& column);
26ColumnRef ConvertMetaToColumn(NullableColumnMeta&& meta);
33 using cpp_type = std::optional<
typename T::cpp_type>;
34 using container_type = std::vector<cpp_type>;
36 class NullableDataHolder
final {
38 NullableDataHolder() =
default;
40 typename ColumnIterator<NullableColumn<T>>::IteratorPosition iter_position,
44 NullableDataHolder operator++(
int);
45 NullableDataHolder& operator++();
47 cpp_type& UpdateValue();
49 bool operator==(
const NullableDataHolder& other)
const;
53 typename ColumnIterator<NullableColumn<T>>::IteratorPosition iter_position,
54 NullableColumnMeta&& meta
57 UInt8Column::iterator nulls_;
58 typename T::iterator inner_;
59 cpp_type current_value_ = std::nullopt;
60 bool has_value_ =
false;
62 using iterator_data = NullableDataHolder;
64 NullableColumn(ColumnRef column);
66 static ColumnRef Serialize(
const container_type& from);
70NullableColumn<T>::NullableColumn(ColumnRef column)
75NullableColumn<T>::NullableDataHolder::NullableDataHolder(
76 typename ColumnIterator<NullableColumn<T>>::IteratorPosition iter_position,
79 : NullableDataHolder(iter_position, ExtractNullableMeta(column))
83NullableColumn<T>::NullableDataHolder::NullableDataHolder(
84 typename ColumnIterator<NullableColumn<T>>::IteratorPosition iter_position,
85 NullableColumnMeta&& meta
87 : nulls_{iter_position ==
decltype(iter_position)::kEnd ? UInt8Column{meta.nulls}.end() : UInt8Column{meta.nulls}.begin()},
88 inner_{iter_position ==
decltype(iter_position)::kEnd ? T{meta.inner}.end() : T{meta.inner}.begin()}
92typename NullableColumn<T>::NullableDataHolder NullableColumn<T>::NullableDataHolder::operator++(
int) {
93 NullableDataHolder old{};
94 old.nulls_ = nulls_++;
95 old.inner_ = inner_++;
96 old.current_value_ = std::move_if_noexcept(current_value_);
97 old.has_value_ = std::exchange(has_value_,
false);
103typename NullableColumn<T>::NullableDataHolder& NullableColumn<T>::NullableDataHolder::operator++() {
106 current_value_.reset();
113typename NullableColumn<T>::cpp_type& NullableColumn<T>::NullableDataHolder::UpdateValue() {
116 current_value_.reset();
118 current_value_.emplace(std::move_if_noexcept(*inner_));
123 return current_value_;
127bool NullableColumn<T>::NullableDataHolder::operator==(
const NullableDataHolder& other)
const {
128 return nulls_ == other.nulls_ && inner_ == other.inner_;
132ColumnRef NullableColumn<T>::Serialize(
const container_type& from) {
133 typename T::container_type values;
134 values.reserve(from.size());
135 std::vector<uint8_t> nulls;
136 nulls.reserve(from.size());
138 for (
auto& opt_v : from) {
139 nulls.push_back(
static_cast<uint8_t>(opt_v.has_value() ? 0 : 1));
141 if (opt_v.has_value()) {
142 values.push_back(*opt_v);
144 values.push_back(
typename T::cpp_type{});
148 NullableColumnMeta nullable_meta;
149 nullable_meta.nulls = UInt8Column::Serialize(nulls);
150 nullable_meta.inner = T::Serialize(values);
151 return ConvertMetaToColumn(std::move(nullable_meta));