Skip to content

Support geo column types #258

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ C++ client for [ClickHouse](https://clickhouse.com/).
* Int128
* UUID
* Map
* Point, Ring, Polygon, MultiPolygon

## Building

Expand Down
2 changes: 2 additions & 0 deletions clickhouse/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ SET ( clickhouse-cpp-lib-src
columns/decimal.cpp
columns/enum.cpp
columns/factory.cpp
columns/geo.cpp
columns/ip4.cpp
columns/ip6.cpp
columns/lowcardinality.cpp
Expand Down Expand Up @@ -116,6 +117,7 @@ INSTALL(FILES columns/date.h DESTINATION include/clickhouse/columns/)
INSTALL(FILES columns/decimal.h DESTINATION include/clickhouse/columns/)
INSTALL(FILES columns/enum.h DESTINATION include/clickhouse/columns/)
INSTALL(FILES columns/factory.h DESTINATION include/clickhouse/columns/)
INSTALL(FILES columns/geo.h DESTINATION include/clickhouse/columns/)
INSTALL(FILES columns/ip4.h DESTINATION include/clickhouse/columns/)
INSTALL(FILES columns/ip6.h DESTINATION include/clickhouse/columns/)
INSTALL(FILES columns/itemview.h DESTINATION include/clickhouse/columns/)
Expand Down
1 change: 1 addition & 0 deletions clickhouse/client.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "columns/date.h"
#include "columns/decimal.h"
#include "columns/enum.h"
#include "columns/geo.h"
#include "columns/ip4.h"
#include "columns/ip6.h"
#include "columns/lowcardinality.h"
Expand Down
16 changes: 15 additions & 1 deletion clickhouse/columns/factory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,19 @@
#include "date.h"
#include "decimal.h"
#include "enum.h"
#include "geo.h"
#include "ip4.h"
#include "ip6.h"
#include "lowcardinality.h"
#include "lowcardinalityadaptor.h"
#include "map.h"
#include "nothing.h"
#include "nullable.h"
#include "numeric.h"
#include "string.h"
#include "tuple.h"
#include "uuid.h"
#include "map.h"


#include "../types/type_parser.h"

Expand Down Expand Up @@ -97,6 +99,18 @@ static ColumnRef CreateTerminalColumn(const TypeAst& ast) {
case Type::UUID:
return std::make_shared<ColumnUUID>();

case Type::Point:
return std::make_shared<ColumnPoint>();

case Type::Ring:
return std::make_shared<ColumnRing>();

case Type::Polygon:
return std::make_shared<ColumnPolygon>();

case Type::MultiPolygon:
return std::make_shared<ColumnMultiPolygon>();

default:
return nullptr;
}
Expand Down
108 changes: 108 additions & 0 deletions clickhouse/columns/geo.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
#include "geo.h"

#include "utils.h"

namespace {
using namespace ::clickhouse;

template <Type::Code type_code>
TypeRef CreateGeoType() {
if constexpr (type_code == Type::Code::Point) {
return Type::CreatePoint();
} else if constexpr (type_code == Type::Code::Ring) {
return Type::CreateRing();
} else if constexpr (type_code == Type::Code::Polygon) {
return Type::CreatePolygon();
} else if constexpr (type_code == Type::Code::MultiPolygon) {
return Type::CreateMultiPolygon();
}
}

template <typename ColumnType>
std::shared_ptr<ColumnType> CreateColumn() {
if constexpr (std::is_same_v<ColumnType, ColumnTupleT<ColumnFloat64, ColumnFloat64>>) {
return std::make_shared<ColumnTupleT<ColumnFloat64, ColumnFloat64>>(
std::make_tuple(std::make_shared<ColumnFloat64>(), std::make_shared<ColumnFloat64>()));
} else {
return std::make_shared<ColumnType>();
}
}

} // namespace

namespace clickhouse {

template <typename NestedColumnType, Type::Code type_code>
ColumnGeo<NestedColumnType, type_code>::ColumnGeo()
: Column(std::move(CreateGeoType<type_code>())),
data_(std::move(CreateColumn<NestedColumnType>())) {
}

template <typename NestedColumnType, Type::Code type_code>
ColumnGeo<NestedColumnType, type_code>::ColumnGeo(ColumnRef data)
: Column(std::move(CreateGeoType<type_code>()))
, data_(std::move(WrapColumn<NestedColumnType>(std::move(data)))) {
}

template <typename NestedColumnType, Type::Code type_code>
void ColumnGeo<NestedColumnType, type_code>::Clear() {
data_->Clear();
}

template <typename NestedColumnType, Type::Code type_code>
const typename ColumnGeo<NestedColumnType, type_code>::ValueType ColumnGeo<NestedColumnType, type_code>::At(size_t n) const {
return data_->At(n);
}

template <typename NestedColumnType, Type::Code type_code>
const typename ColumnGeo<NestedColumnType, type_code>::ValueType ColumnGeo<NestedColumnType, type_code>::operator[](size_t n) const {
return data_->At(n);
}

template <typename NestedColumnType, Type::Code type_code>
void ColumnGeo<NestedColumnType, type_code>::Append(ColumnRef column) {
if (auto col = column->template As<ColumnGeo>()) {
data_->Append(col->data_->template As<Column>());
}
}

template <typename NestedColumnType, Type::Code type_code>
bool ColumnGeo<NestedColumnType, type_code>::LoadBody(InputStream* input, size_t rows) {
return data_->LoadBody(input, rows);
}

template <typename NestedColumnType, Type::Code type_code>
void ColumnGeo<NestedColumnType, type_code>::SaveBody(OutputStream* output) {
data_->SaveBody(output);
}

template <typename NestedColumnType, Type::Code type_code>
size_t ColumnGeo<NestedColumnType, type_code>::Size() const {
return data_->Size();
}

template <typename NestedColumnType, Type::Code type_code>
ColumnRef ColumnGeo<NestedColumnType, type_code>::Slice(size_t begin, size_t len) const {
return std::make_shared<ColumnGeo>(data_->Slice(begin, len));
}

template <typename NestedColumnType, Type::Code type_code>
ColumnRef ColumnGeo<NestedColumnType, type_code>::CloneEmpty() const {
return std::make_shared<ColumnGeo>();
}

template <typename NestedColumnType, Type::Code type_code>
void ColumnGeo<NestedColumnType, type_code>::Swap(Column& other) {
auto& col = dynamic_cast<ColumnGeo&>(other);
data_.swap(col.data_);
}

template class ColumnGeo<ColumnTupleT<ColumnFloat64, ColumnFloat64>, Type::Code::Point>;

template class ColumnGeo<ColumnArrayT<ColumnPoint>, Type::Code::Ring>;

template class ColumnGeo<ColumnArrayT<ColumnRing>, Type::Code::Polygon>;

template class ColumnGeo<ColumnArrayT<ColumnPolygon>, Type::Code::MultiPolygon>;

} // namespace clickhouse
76 changes: 76 additions & 0 deletions clickhouse/columns/geo.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#pragma once

#include "array.h"
#include "column.h"
#include "numeric.h"
#include "tuple.h"

namespace clickhouse {

template <typename NestedColumnType, Type::Code type_code>
class ColumnGeo : public Column {
public:
using ValueType = typename NestedColumnType::ValueType;

ColumnGeo();

explicit ColumnGeo(ColumnRef data);

/// Appends one element to the end of column.
template <typename T = ValueType>
void Append(const T& value) {
data_->Append(value);
}

/// Returns element at given row number.
const ValueType At(size_t n) const;

/// Returns element at given row number.
const ValueType operator[](size_t n) const;

public:
/// Appends content of given column to the end of current one.
void Append(ColumnRef column) override;

/// Loads column data from input stream.
bool LoadBody(InputStream* input, size_t rows) override;

/// Saves column data to output stream.
void SaveBody(OutputStream* output) override;

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

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

/// Makes slice of the current column.
ColumnRef Slice(size_t begin, size_t len) const override;
ColumnRef CloneEmpty() const override;
void Swap(Column& other) override;

private:
std::shared_ptr<NestedColumnType> data_;
};

// /**
// * Represents a Point column.
// */
using ColumnPoint = ColumnGeo<ColumnTupleT<ColumnFloat64, ColumnFloat64>, Type::Code::Point>;

/**
* Represents a Ring column.
*/
using ColumnRing = ColumnGeo<ColumnArrayT<ColumnPoint>, Type::Code::Ring>;

/**
* Represents a Polygon column.
*/
using ColumnPolygon = ColumnGeo<ColumnArrayT<ColumnRing>, Type::Code::Polygon>;

/**
* Represents a MultiPolygon column.
*/
using ColumnMultiPolygon = ColumnGeo<ColumnArrayT<ColumnPolygon>, Type::Code::MultiPolygon>;

} // namespace clickhouse
27 changes: 17 additions & 10 deletions clickhouse/columns/tuple.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,16 +72,11 @@ class ColumnTupleT : public ColumnTuple {

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

template <typename T, size_t index = std::tuple_size_v<T>>
inline void Append([[maybe_unused]] T value) {
static_assert(index <= std::tuple_size_v<T>);
static_assert(std::tuple_size_v<TupleOfColumns> == std::tuple_size_v<T>);
if constexpr (index == 0) {
return;
} else {
std::get<index - 1>(typed_columns_)->Append(std::move(std::get<index - 1>(value)));
Append<T, index - 1>(std::move(value));
}
using ColumnTuple::Append;

template <typename... T>
inline void Append(std::tuple<T...> value) {
AppendTuple(std::move(value));
}

/** Create a ColumnTupleT from a ColumnTuple, without copying data and offsets, but by
Expand Down Expand Up @@ -118,6 +113,18 @@ class ColumnTupleT : public ColumnTuple {
}

private:
template <typename T, size_t index = std::tuple_size_v<T>>
inline void AppendTuple([[maybe_unused]] T value) {
static_assert(index <= std::tuple_size_v<T>);
static_assert(std::tuple_size_v<TupleOfColumns> == std::tuple_size_v<T>);
if constexpr (index == 0) {
return;
} else {
std::get<index - 1>(typed_columns_)->Append(std::move(std::get<index - 1>(value)));
AppendTuple<T, index - 1>(std::move(value));
}
}

template <typename T, size_t index = std::tuple_size_v<T>>
inline static std::vector<ColumnRef> TupleToVector([[maybe_unused]] const T& value) {
static_assert(index <= std::tuple_size_v<T>);
Expand Down
6 changes: 5 additions & 1 deletion clickhouse/types/type_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,11 @@ static const std::unordered_map<std::string, Type::Code> kTypeCode = {
{ "Decimal64", Type::Decimal64 },
{ "Decimal128", Type::Decimal128 },
{ "LowCardinality", Type::LowCardinality },
{ "Map", Type::Map},
{ "Map", Type::Map},
{ "Point", Type::Point},
{ "Ring", Type::Ring},
{ "Polygon", Type::Polygon},
{ "MultiPolygon", Type::MultiPolygon},
};

static Type::Code GetTypeCode(const std::string& name) {
Expand Down
28 changes: 28 additions & 0 deletions clickhouse/types/types.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ const char* Type::TypeName(Type::Code code) {
case Type::Code::DateTime64: return "DateTime64";
case Type::Code::Date32: return "Date32";
case Type::Code::Map: return "Map";
case Type::Code::Point: return "Point";
case Type::Code::Ring: return "Ring";
case Type::Code::Polygon: return "Polygon";
case Type::Code::MultiPolygon: return "MultiPolygon";
}

return "Unknown type";
Expand All @@ -72,6 +76,10 @@ std::string Type::GetName() const {
case IPv6:
case Date:
case Date32:
case Point:
case Ring:
case Polygon:
case MultiPolygon:
return TypeName(code_);
case FixedString:
return As<FixedStringType>()->GetName();
Expand Down Expand Up @@ -126,6 +134,10 @@ uint64_t Type::GetTypeUniqueId() const {
case IPv6:
case Date:
case Date32:
case Point:
case Ring:
case Polygon:
case MultiPolygon:
// For simple types, unique ID is the same as Type::Code
return code_;

Expand Down Expand Up @@ -233,6 +245,22 @@ TypeRef Type::CreateMap(TypeRef key_type, TypeRef value_type) {
return std::make_shared<MapType>(key_type, value_type);
}

TypeRef Type::CreatePoint() {
return TypeRef(new Type(Type::Point));
}

TypeRef Type::CreateRing() {
return TypeRef(new Type(Type::Ring));
}

TypeRef Type::CreatePolygon() {
return TypeRef(new Type(Type::Polygon));
}

TypeRef Type::CreateMultiPolygon() {
return TypeRef(new Type(Type::MultiPolygon));
}

/// class ArrayType

ArrayType::ArrayType(TypeRef item_type) : Type(Array), item_type_(item_type) {
Expand Down
14 changes: 13 additions & 1 deletion clickhouse/types/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,11 @@ class Type {
LowCardinality,
DateTime64,
Date32,
Map
Map,
Point,
Ring,
Polygon,
MultiPolygon
};

using EnumItem = std::pair<std::string /* name */, int16_t /* value */>;
Expand Down Expand Up @@ -128,6 +132,14 @@ class Type {

static TypeRef CreateMap(TypeRef key_type, TypeRef value_type);

static TypeRef CreatePoint();

static TypeRef CreateRing();

static TypeRef CreatePolygon();

static TypeRef CreateMultiPolygon();

private:
uint64_t GetTypeUniqueId() const;

Expand Down
Loading