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>> {
32 public:
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(typename ColumnIterator<
40 NullableColumn<T>>::IteratorPosition iter_position,
41 ColumnRef&& column);
42
43 NullableDataHolder operator++(int);
44 NullableDataHolder& operator++();
45 void Next();
46 cpp_type& UpdateValue();
47
48 bool operator==(const NullableDataHolder& other) const;
49
50 private:
51 NullableDataHolder(typename ColumnIterator<
52 NullableColumn<T>>::IteratorPosition iter_position,
53 NullableColumnMeta&& meta);
54
55 UInt8Column::iterator nulls_;
56 typename T::iterator inner_;
57 cpp_type current_value_ = std::nullopt;
58 bool has_value_ = false;
59 };
60 using iterator_data = NullableDataHolder;
61
62 NullableColumn(ColumnRef column);
63
64 static ColumnRef Serialize(const container_type& from);
65};
66
67template <typename T>
68NullableColumn<T>::NullableColumn(ColumnRef column)
69 : ClickhouseColumn<NullableColumn>{column} {}
70
71template <typename T>
72NullableColumn<T>::NullableDataHolder::NullableDataHolder(
73 typename ColumnIterator<NullableColumn<T>>::IteratorPosition iter_position,
74 ColumnRef&& column)
75 : NullableDataHolder(iter_position, ExtractNullableMeta(column)) {}
76
77template <typename T>
78NullableColumn<T>::NullableDataHolder::NullableDataHolder(
79 typename ColumnIterator<NullableColumn<T>>::IteratorPosition iter_position,
80 NullableColumnMeta&& meta)
81 : nulls_{iter_position == decltype(iter_position)::kEnd
82 ? UInt8Column{meta.nulls}.end()
83 : UInt8Column{meta.nulls}.begin()},
84 inner_{iter_position == decltype(iter_position)::kEnd
85 ? T{meta.inner}.end()
86 : T{meta.inner}.begin()} {}
87
88template <typename T>
89typename NullableColumn<T>::NullableDataHolder
90NullableColumn<T>::NullableDataHolder::operator++(int) {
91 NullableDataHolder old{};
92 old.nulls_ = nulls_++;
93 old.inner_ = inner_++;
94 old.current_value_ = std::move_if_noexcept(current_value_);
95 old.has_value_ = std::exchange(has_value_, false);
96
97 return old;
98}
99
100template <typename T>
101typename NullableColumn<T>::NullableDataHolder&
102NullableColumn<T>::NullableDataHolder::operator++() {
103 ++nulls_;
104 ++inner_;
105 current_value_.reset();
106 has_value_ = false;
107
108 return *this;
109}
110
111template <typename T>
112typename NullableColumn<T>::cpp_type&
113NullableColumn<T>::NullableDataHolder::UpdateValue() {
114 if (!has_value_) {
115 if (*nulls_) {
116 current_value_.reset();
117 } else {
118 current_value_.emplace(std::move_if_noexcept(*inner_));
119 }
120 has_value_ = true;
121 }
122
123 return current_value_;
124}
125
126template <typename T>
127bool NullableColumn<T>::NullableDataHolder::operator==(
128 const NullableDataHolder& other) const {
129 return nulls_ == other.nulls_ && inner_ == other.inner_;
130}
131
132template <typename T>
133ColumnRef NullableColumn<T>::Serialize(const container_type& from) {
134 typename T::container_type values;
135 values.reserve(from.size());
136 std::vector<uint8_t> nulls;
137 nulls.reserve(from.size());
138
139 for (auto& opt_v : from) {
140 nulls.push_back(static_cast<uint8_t>(opt_v.has_value() ? 0 : 1));
141
142 if (opt_v.has_value()) {
143 values.push_back(*opt_v);
144 } else {
145 values.push_back(typename T::cpp_type{});
146 }
147 }
148
149 NullableColumnMeta nullable_meta;
150 nullable_meta.nulls = UInt8Column::Serialize(nulls);
151 nullable_meta.inner = T::Serialize(values);
152 return ConvertMetaToColumn(std::move(nullable_meta));
153}
154
155} // namespace storages::clickhouse::io::columns
156
157USERVER_NAMESPACE_END