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;
 
   39    NullableDataHolder(
typename ColumnIterator<
 
   40                           NullableColumn<T>>::IteratorPosition iter_position,
 
   43    NullableDataHolder operator++(
int);
 
   44    NullableDataHolder& operator++();
 
   46    cpp_type& UpdateValue();
 
   48    bool operator==(
const NullableDataHolder& other) 
const;
 
   51    NullableDataHolder(
typename ColumnIterator<
 
   52                           NullableColumn<T>>::IteratorPosition iter_position,
 
   53                       NullableColumnMeta&& meta);
 
   55    UInt8Column::iterator nulls_;
 
   56    typename T::iterator inner_;
 
   57    cpp_type current_value_ = std::nullopt;
 
   58    bool has_value_ = 
false;
 
   60  using iterator_data = NullableDataHolder;
 
   62  NullableColumn(ColumnRef column);
 
   64  static ColumnRef Serialize(
const container_type& from);
 
   68NullableColumn<T>::NullableColumn(ColumnRef column)
 
   69    : ClickhouseColumn<NullableColumn>{column} {}
 
   72NullableColumn<T>::NullableDataHolder::NullableDataHolder(
 
   73    typename ColumnIterator<NullableColumn<T>>::IteratorPosition iter_position,
 
   75    : NullableDataHolder(iter_position, ExtractNullableMeta(column)) {}
 
   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
 
   86                 : T{meta.inner}.begin()} {}
 
   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);
 
  101typename NullableColumn<T>::NullableDataHolder&
 
  102NullableColumn<T>::NullableDataHolder::operator++() {
 
  105  current_value_.reset();
 
  112typename NullableColumn<T>::cpp_type&
 
  113NullableColumn<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==(
 
  128    const NullableDataHolder& other) 
const {
 
  129  return nulls_ == other.nulls_ && inner_ == other.inner_;
 
  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());
 
  139  for (
auto& opt_v : from) {
 
  140    nulls.push_back(
static_cast<uint8_t>(opt_v.has_value() ? 0 : 1));
 
  142    if (opt_v.has_value()) {
 
  143      values.push_back(*opt_v);
 
  145      values.push_back(
typename T::cpp_type{});
 
  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));