Skip to content

Commit

Permalink
ColumnNulableT
Browse files Browse the repository at this point in the history
  • Loading branch information
den818 committed Dec 18, 2022
1 parent 4251535 commit 7716110
Show file tree
Hide file tree
Showing 8 changed files with 511 additions and 194 deletions.
2 changes: 1 addition & 1 deletion clickhouse/columns/lowcardinality.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ auto Load(ColumnRef new_dictionary_column, InputStream& input, size_t rows) {

if (auto nullable = new_dictionary_column->As<ColumnNullable>()) {
nullable->Append(true);
for(std::size_t i = 1; i < new_index_column->Size(); i++) {
for(std::size_t i = 1; i < dataColumn->Size(); i++) {
nullable->Append(false);
}
}
Expand Down
65 changes: 61 additions & 4 deletions clickhouse/columns/lowcardinality.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,16 @@ class ColumnLowCardinality : public Column {
UniqueItems unique_items_map_;

public:
ColumnLowCardinality(ColumnLowCardinality&& col) = default;
// c-tor makes a deep copy of the dictionary_column.
explicit ColumnLowCardinality(ColumnRef dictionary_column);
explicit ColumnLowCardinality(std::shared_ptr<ColumnNullable> dictionary_column);

template <typename T>
explicit ColumnLowCardinality(std::shared_ptr<ColumnNullableT<T>> dictionary_column)
: ColumnLowCardinality(dictionary_column->template As<ColumnNullable>())
{}

~ColumnLowCardinality();

/// Appends another LowCardinality column to the end of this one, updating dictionary.
Expand Down Expand Up @@ -117,16 +124,23 @@ class ColumnLowCardinalityT : public ColumnLowCardinality {
// Type this column takes as argument of Append and returns with At() and operator[]
using ValueType = typename DictionaryColumnType::ValueType;

explicit ColumnLowCardinalityT(ColumnLowCardinality&& col)
: ColumnLowCardinality(std::move(col))
, typed_dictionary_(dynamic_cast<DictionaryColumnType &>(*GetDictionary()))
, type_(GetTypeCode(typed_dictionary_))
{
}

template <typename ...Args>
explicit ColumnLowCardinalityT(Args &&... args)
: ColumnLowCardinalityT(std::make_shared<DictionaryColumnType>(std::forward<Args>(args)...))
{}

// Create LC<T> column from existing T-column, making a deep copy of all contents.
explicit ColumnLowCardinalityT(std::shared_ptr<DictionaryColumnType> dictionary_col)
: ColumnLowCardinality(dictionary_col),
typed_dictionary_(dynamic_cast<DictionaryColumnType &>(*GetDictionary())),
type_(typed_dictionary_.Type()->GetCode())
: ColumnLowCardinality(dictionary_col)
, typed_dictionary_(dynamic_cast<DictionaryColumnType &>(*GetDictionary()))
, type_(GetTypeCode(typed_dictionary_))
{}

/// Extended interface to simplify reading/adding individual items.
Expand All @@ -145,7 +159,15 @@ class ColumnLowCardinalityT : public ColumnLowCardinality {
using ColumnLowCardinality::Append;

inline void Append(const ValueType & value) {
AppendUnsafe(ItemView{type_, value});
if constexpr (IsNullable<WrappedColumnType>) {
if (value.has_value()) {
AppendUnsafe(ItemView{type_, *value});
} else {
AppendUnsafe(ItemView{});
}
} else {
AppendUnsafe(ItemView{type_, value});
}
}

template <typename T>
Expand All @@ -154,6 +176,41 @@ class ColumnLowCardinalityT : public ColumnLowCardinality {
Append(item);
}
}

/** Create a ColumnLowCardinalityT from a ColumnLowCardinality, without copying data and offsets, but by
* 'stealing' those from `col`.
*
* Ownership of column internals is transferred to returned object, original (argument) object
* MUST NOT BE USED IN ANY WAY, it is only safe to dispose it.
*
* Throws an exception if `col` is of wrong type, it is safe to use original col in this case.
* This is a static method to make such conversion verbose.
*/
static auto Wrap(ColumnLowCardinality&& col) {
return std::make_shared<ColumnLowCardinalityT<WrappedColumnType>>(std::move(col));
}

static auto Wrap(Column&& col) { return Wrap(std::move(dynamic_cast<ColumnLowCardinality&&>(col))); }

// Helper to simplify integration with other APIs
static auto Wrap(ColumnRef&& col) { return Wrap(std::move(*col->AsStrict<ColumnLowCardinality>())); }

ColumnRef Slice(size_t begin, size_t size) const override {
return Wrap(ColumnLowCardinality::Slice(begin, size));
}

ColumnRef CloneEmpty() const override { return Wrap(ColumnLowCardinality::CloneEmpty()); }

private:

template <typename T>
static auto GetTypeCode(T& column) {
if constexpr (IsNullable<T>) {
return GetTypeCode(*column.Nested()->template AsStrict<typename T::NestedColumnType>());
} else {
return column.Type()->GetCode();
}
}
};

}
91 changes: 90 additions & 1 deletion clickhouse/columns/nullable.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
#include "column.h"
#include "numeric.h"

#include <optional>

namespace clickhouse {

/**
Expand Down Expand Up @@ -42,7 +44,7 @@ class ColumnNullable : public Column {

/// Clear column data .
void Clear() override;

/// Returns count of rows in the column.
size_t Size() const override;

Expand All @@ -58,4 +60,91 @@ class ColumnNullable : public Column {
std::shared_ptr<ColumnUInt8> nulls_;
};

template <typename ColumnType>
class ColumnNullableT : public ColumnNullable {
public:
using NestedColumnType = ColumnType;
using ValueType = std::optional<std::decay_t<decltype(std::declval<NestedColumnType>().At(0))>>;

ColumnNullableT(std::shared_ptr<NestedColumnType> data, std::shared_ptr<ColumnUInt8> nulls)
: ColumnNullable(data, nulls)
, typed_nested_data_(data)
{}

explicit ColumnNullableT(std::shared_ptr<NestedColumnType> data)
: ColumnNullableT(data, FillNulls(data->Size()))
{}

template <typename ...Args>
explicit ColumnNullableT(Args &&... args)
: ColumnNullableT(std::make_shared<NestedColumnType>(std::forward<Args>(args)...))
{}

inline ValueType At(size_t index) const {
return IsNull(index) ? ValueType{} : ValueType{typed_nested_data_->At(index)};
}

inline ValueType operator[](size_t index) const { return At(index); }

/// Appends content of given column to the end of current one.
void Append(ColumnRef column) override {
ColumnNullable::Append(std::move(column));
}

inline void Append(ValueType value) {
ColumnNullable::Append(!value.has_value());
if (value.has_value()) {
typed_nested_data_->Append(std::move(*value));
} else {
typed_nested_data_->Append(typename ValueType::value_type{});
}
}

/** Create a ColumnNullableT from a ColumnNullable, without copying data and offsets, but by
* 'stealing' those from `col`.
*
* Ownership of column internals is transferred to returned object, original (argument) object
* MUST NOT BE USED IN ANY WAY, it is only safe to dispose it.
*
* Throws an exception if `col` is of wrong type, it is safe to use original col in this case.
* This is a static method to make such conversion verbose.
*/
static auto Wrap(ColumnNullable&& col) {
return std::make_shared<ColumnNullableT<NestedColumnType>>(
col.Nested()->AsStrict<NestedColumnType>(),
col.Nulls()->AsStrict<ColumnUInt8>()) ;
}

static auto Wrap(Column&& col) { return Wrap(std::move(dynamic_cast<ColumnNullable&&>(col))); }

// Helper to simplify integration with other APIs
static auto Wrap(ColumnRef&& col) { return Wrap(std::move(*col->AsStrict<ColumnNullable>())); }

ColumnRef Slice(size_t begin, size_t size) const override {
return Wrap(ColumnNullable::Slice(begin, size));
}

ColumnRef CloneEmpty() const override { return Wrap(ColumnNullable::CloneEmpty()); }

void Swap(Column& other) override {
auto& col = dynamic_cast<ColumnNullableT<NestedColumnType>&>(other);
typed_nested_data_.swap(col.typed_nested_data_);
ColumnNullable::Swap(other);
}

private:
static inline auto FillNulls(size_t n){
auto result = std::make_shared<ColumnUInt8>();
for (size_t i = 0; i < n; ++i) {
result->Append(0);
}
return result;
}

std::shared_ptr<NestedColumnType> typed_nested_data_;
};

template <typename T>
constexpr bool IsNullable = std::is_base_of_v<ColumnNullable, T>;

}
1 change: 1 addition & 0 deletions ut/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ SET ( clickhouse-cpp-ut-src
CreateColumnByType_ut.cpp
Column_ut.cpp
roundtrip_column.cpp
roundtrip_tests.cpp

utils.cpp
value_generators.cpp
Expand Down
56 changes: 45 additions & 11 deletions ut/Column_ut.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,28 @@ class GenericColumnTest : public testing::Test {

return std::tuple{column, values};
}

static std::optional<std::string> SkipTest(clickhouse::Client& client) {
if constexpr (std::is_same_v<ColumnType, ColumnDate32>) {
// Date32 first appeared in v21.9.2.17-stable
const auto server_info = client.GetServerInfo();
if (versionNumber(server_info) < versionNumber(21, 9)) {
std::stringstream buffer;
buffer << "Date32 is available since v21.9.2.17-stable and can't be tested against server: " << server_info;
return buffer.str();
}
}

if constexpr (std::is_same_v<ColumnType, ColumnInt128>) {
const auto server_info = client.GetServerInfo();
if (versionNumber(server_info) < versionNumber(21, 7)) {
std::stringstream buffer;
buffer << "ColumnInt128 is available since v21.7.2.7-stable and can't be tested against server: " << server_info;
return buffer.str();
}
}
return std::nullopt;
}
};

using ValueColumns = ::testing::Types<
Expand Down Expand Up @@ -279,21 +301,33 @@ TYPED_TEST(GenericColumnTest, RoundTrip) {

clickhouse::Client client(LocalHostEndpoint);

if constexpr (std::is_same_v<typename TestFixture::ColumnType, ColumnDate32>) {
// Date32 first appeared in v21.9.2.17-stable
const auto server_info = client.GetServerInfo();
if (versionNumber(server_info) < versionNumber(21, 9)) {
GTEST_SKIP() << "Date32 is available since v21.9.2.17-stable and can't be tested against server: " << server_info;
}
if (auto message = this->SkipTest(client)) {
GTEST_SKIP() << *message;
}

if constexpr (std::is_same_v<typename TestFixture::ColumnType, ColumnInt128>) {
const auto server_info = client.GetServerInfo();
if (versionNumber(server_info) < versionNumber(21, 7)) {
GTEST_SKIP() << "ColumnInt128 is available since v21.7.2.7-stable and can't be tested against server: " << server_info;
auto result_typed = RoundtripColumnValues(client, column)->template AsStrict<typename TestFixture::ColumnType>();
EXPECT_TRUE(CompareRecursive(*column, *result_typed));
}

TYPED_TEST(GenericColumnTest, NulableT_RoundTrip) {
using NullableType = ColumnNullableT<TypeParam>;
auto column = std::make_shared<NullableType>(this->MakeColumn());
auto values = this->GenerateValues(100);
FromVectorGenerator<bool> is_null({true, false});
for (size_t i = 0; i < values.size(); ++i) {
if (is_null(i)) {
column->Append(std::nullopt);
} else {
column->Append(values[i]);
}
}

auto result_typed = RoundtripColumnValues(client, column)->template AsStrict<typename TestFixture::ColumnType>();
clickhouse::Client client(LocalHostEndpoint);

if (auto message = this->SkipTest(client)) {
GTEST_SKIP() << *message;
}

auto result_typed = WrapColumn<NullableType>(RoundtripColumnValues(client, column));
EXPECT_TRUE(CompareRecursive(*column, *result_typed));
}
Loading

0 comments on commit 7716110

Please sign in to comment.