userver: userver/storages/clickhouse/io/columns/nullable_column.hpp Source File
Loading...
Searching...
No Matches
nullable_column.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/storages/clickhouse/io/columns/nullable_column.hpp
4/// @brief Nullable column support
5/// @ingroup userver_clickhouse_types
6
7#include <optional>
8#include <utility>
9
10#include <userver/utils/assert.hpp>
11
12#include <userver/storages/clickhouse/io/columns/column_includes.hpp>
13#include <userver/storages/clickhouse/io/columns/common_columns.hpp>
14
15USERVER_NAMESPACE_BEGIN
16
17namespace storages::clickhouse::io::columns {
18
19struct NullableColumnMeta final {
20 ColumnRef nulls;
21 ColumnRef inner;
22};
23
24NullableColumnMeta ExtractNullableMeta(const ColumnRef& column);
25
26ColumnRef ConvertMetaToColumn(NullableColumnMeta&& meta);
27
28/// @brief Represents ClickHouse Nullable(T) column,
29/// where T is a ClickhouseColumn as well
30template <typename T>
31class NullableColumn final : public ClickhouseColumn<NullableColumn<T>> {
32public:
33 using cpp_type = std::optional<typename T::cpp_type>;
34 using container_type = std::vector<cpp_type>;
35
36 class NullableDataHolder final {
37 public:
38 NullableDataHolder() = default;
39 NullableDataHolder(
40 typename ColumnIterator<NullableColumn<T>>::IteratorPosition iter_position,
41 ColumnRef&& column
42 );
43
44 NullableDataHolder operator++(int);
45 NullableDataHolder& operator++();
46 void Next();
47 cpp_type& UpdateValue();
48
49 bool operator==(const NullableDataHolder& other) const;
50
51 private:
52 NullableDataHolder(
53 typename ColumnIterator<NullableColumn<T>>::IteratorPosition iter_position,
54 NullableColumnMeta&& meta
55 );
56
57 UInt8Column::iterator nulls_;
58 typename T::iterator inner_;
59 cpp_type current_value_ = std::nullopt;
60 bool has_value_ = false;
61 };
62 using iterator_data = NullableDataHolder;
63
64 NullableColumn(ColumnRef column);
65
66 static ColumnRef Serialize(const container_type& from);
67};
68
69template <typename T>
70NullableColumn<T>::NullableColumn(ColumnRef column) : ClickhouseColumn<NullableColumn>{column} {}
71
72template <typename T>
73NullableColumn<T>::NullableDataHolder::NullableDataHolder(
74 typename ColumnIterator<NullableColumn<T>>::IteratorPosition iter_position,
75 ColumnRef&& column
76)
77 : NullableDataHolder(iter_position, ExtractNullableMeta(column)) {}
78
79template <typename T>
80NullableColumn<T>::NullableDataHolder::NullableDataHolder(
81 typename ColumnIterator<NullableColumn<T>>::IteratorPosition iter_position,
82 NullableColumnMeta&& meta
83)
84 : nulls_{iter_position == decltype(iter_position)::kEnd ? UInt8Column{meta.nulls}.end() : UInt8Column{meta.nulls}.begin()},
85 inner_{iter_position == decltype(iter_position)::kEnd ? T{meta.inner}.end() : T{meta.inner}.begin()} {}
86
87template <typename T>
88typename NullableColumn<T>::NullableDataHolder NullableColumn<T>::NullableDataHolder::operator++(int) {
89 NullableDataHolder old{};
90 old.nulls_ = nulls_++;
91 old.inner_ = inner_++;
92 old.current_value_ = std::move_if_noexcept(current_value_);
93 old.has_value_ = std::exchange(has_value_, false);
94
95 return old;
96}
97
98template <typename T>
99typename NullableColumn<T>::NullableDataHolder& NullableColumn<T>::NullableDataHolder::operator++() {
100 ++nulls_;
101 ++inner_;
102 current_value_.reset();
103 has_value_ = false;
104
105 return *this;
106}
107
108template <typename T>
109typename NullableColumn<T>::cpp_type& NullableColumn<T>::NullableDataHolder::UpdateValue() {
110 if (!has_value_) {
111 if (*nulls_) {
112 current_value_.reset();
113 } else {
114 current_value_.emplace(std::move_if_noexcept(*inner_));
115 }
116 has_value_ = true;
117 }
118
119 return current_value_;
120}
121
122template <typename T>
123bool NullableColumn<T>::NullableDataHolder::operator==(const NullableDataHolder& other) const {
124 return nulls_ == other.nulls_ && inner_ == other.inner_;
125}
126
127template <typename T>
128ColumnRef NullableColumn<T>::Serialize(const container_type& from) {
129 typename T::container_type values;
130 values.reserve(from.size());
131 std::vector<uint8_t> nulls;
132 nulls.reserve(from.size());
133
134 for (auto& opt_v : from) {
135 nulls.push_back(static_cast<uint8_t>(opt_v.has_value() ? 0 : 1));
136
137 if (opt_v.has_value()) {
138 values.push_back(*opt_v);
139 } else {
140 values.push_back(typename T::cpp_type{});
141 }
142 }
143
144 NullableColumnMeta nullable_meta;
145 nullable_meta.nulls = UInt8Column::Serialize(nulls);
146 nullable_meta.inner = T::Serialize(values);
147 return ConvertMetaToColumn(std::move(nullable_meta));
148}
149
150} // namespace storages::clickhouse::io::columns
151
152USERVER_NAMESPACE_END