userver: userver/storages/clickhouse/io/columns/nullable_column.hpp Source File
⚠️ This is the documentation for an old userver version. Click here to switch to the latest version.
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
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