Skip to content
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 .github/workflows/linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ jobs:
-D CMAKE_C_COMPILER=${{matrix.C_COMPILER}} \
-D CMAKE_CXX_COMPILER=${{matrix.CXX_COMPILER}} \
-D CMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} \
-D CH_MAP_BOOL_TO_UINT8=OFF \
-D BUILD_TESTS=ON \
${{matrix.SSL_CMAKE_OPTION}} \
${{matrix.DEPENDENCIES_CMAKE_OPTIONS}} \
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/macos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ jobs:
run: |
cmake \
-D CMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} \
-D CH_MAP_BOOL_TO_UINT8=OFF \
-D BUILD_TESTS=ON \
${{matrix.SSL_CMAKE_OPTION}} \
-S ${{github.workspace}} \
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/windows_mingw.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ jobs:
tar

- name: Configure CMake
run: cmake -B build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DBUILD_TESTS=ON -DCHECK_VERSION=OFF
run: cmake -B build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCH_MAP_BOOL_TO_UINT8=OFF -DBUILD_TESTS=ON -DCHECK_VERSION=OFF
# -DWITH_OPENSSL=ON was not able to make it work (some strange issues with CA paths, need debug)
# -DCHECK_VERSION=OFF since it requires git, which can't be found from within cmake for some reason.

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/windows_msvc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ jobs:
- uses: ilammy/msvc-dev-cmd@v1

- name: Configure CMake
run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DBUILD_TESTS=ON
run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCH_MAP_BOOL_TO_UINT8=OFF -DBUILD_TESTS=ON

- name: Build
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
Expand Down
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ OPTION (WITH_SYSTEM_ZSTD "Use system ZSTD" OFF)
OPTION (DEBUG_DEPENDENCIES "Print debug info about dependencies duting build" ON)
OPTION (CHECK_VERSION "Check that version number corresponds to git tag, usefull in CI/CD to validate that new version published on GitHub has same version in sources" OFF)
OPTION (DISABLE_CLANG_LIBC_WORKAROUND "Disable linking compiler-rt & gcc_s if using clang & libstdc++" OFF)
OPTION (CH_MAP_BOOL_TO_UINT8 "Map ClickHouse Bool type to UInt8 instead of exposing a distinct Bool API." ON)

PROJECT (CLICKHOUSE-CLIENT
VERSION "${CLICKHOUSE_CPP_VERSION}"
Expand Down
10 changes: 8 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ C++ client for [ClickHouse](https://clickhouse.com/).
## Supported data types

* Array(T)
* Bool \* (by default, mapped to UInt8 when receiving data)
* Date
* DateTime, DateTime64
* DateTime([timezone]), DateTime64(N, [timezone])
Expand All @@ -25,6 +26,13 @@ C++ client for [ClickHouse](https://clickhouse.com/).
* Point, Ring, Polygon, MultiPolygon
* JSON - experimental support; requires output_format_native_write_json_as_string=1; data is passed as strings

\*: There exists a distinct `ColumnBool` and entry in the `Type` enumeration that
can be used.
By default, data received from the server will map Bool columns to UInt8. This is
a backwards compatibility feature.
If you want the library to produce ColumnBool, set the CMake variable `DCH_MAP_BOOL_TO_UINT8=OFF`.
The default for this variable will switch, with it being subsequently removed, in
a future release.

## Dependencies
In the most basic case one needs only:
Expand Down Expand Up @@ -258,5 +266,3 @@ client.Insert("default.test", block);
```sql
ALTER USER insert_account SETTINGS async_insert=1,wait_for_async_insert=1,async_insert_use_adaptive_busy_timeout=0,async_insert_busy_timeout_ms=5000,async_insert_max_data_size=104857600
```


7 changes: 7 additions & 0 deletions clickhouse/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ SET ( clickhouse-cpp-lib-src
base/endpoints_iterator.cpp

columns/array.cpp
columns/bool.cpp
columns/column.cpp
columns/date.cpp
columns/decimal.cpp
Expand Down Expand Up @@ -53,6 +54,7 @@ SET ( clickhouse-cpp-lib-src
base/wire_format.h

columns/array.h
columns/bool.h
columns/column.h
columns/date.h
columns/decimal.h
Expand Down Expand Up @@ -122,6 +124,11 @@ TARGET_LINK_LIBRARIES (clickhouse-cpp-lib
TARGET_INCLUDE_DIRECTORIES (clickhouse-cpp-lib
PUBLIC ${PROJECT_SOURCE_DIR}
)
IF (CH_MAP_BOOL_TO_UINT8)
TARGET_COMPILE_DEFINITIONS (clickhouse-cpp-lib PUBLIC CH_MAP_BOOL_TO_UINT8=1)
ELSE ()
TARGET_COMPILE_DEFINITIONS (clickhouse-cpp-lib PUBLIC CH_MAP_BOOL_TO_UINT8=0)
ENDIF ()

IF (NOT BUILD_SHARED_LIBS)
ADD_LIBRARY (clickhouse-cpp-lib-static ALIAS clickhouse-cpp-lib)
Expand Down
1 change: 1 addition & 0 deletions clickhouse/client.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "columns/tuple.h"
#include "columns/time.h"
#include "columns/uuid.h"
#include "columns/bool.h"

#include <chrono>
#include <cstdint>
Expand Down
79 changes: 79 additions & 0 deletions clickhouse/columns/bool.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
#include "bool.h"

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

namespace clickhouse {

ColumnBool::ColumnBool()
: Column(Type::CreateSimple<bool>())
, data_()
{
}

ColumnBool::ColumnBool(std::vector<uint8_t> data)
: Column(Type::CreateSimple<bool>())
, data_(std::move(data))
{
}

void ColumnBool::Reserve(size_t new_cap) {
data_.Reserve(new_cap);
}

size_t ColumnBool::Capacity() const {
return data_.Capacity();
}

void ColumnBool::Append(bool value) {
data_.Append(static_cast<uint8_t>(value));
}

bool ColumnBool::At(size_t n) const {
return static_cast<bool>(data_.At(n));
}

void ColumnBool::Append(ColumnRef column) {
if (auto col = column->As<ColumnBool>()) {
auto& src = col->data_.GetWritableData();
data_.GetWritableData().insert(data_.GetWritableData().end(), src.begin(), src.end());
} else if (auto col = column->As<ColumnUInt8>()) {
auto& src = col->GetWritableData();
data_.GetWritableData().insert(data_.GetWritableData().end(), src.begin(), src.end());
}
}

bool ColumnBool::LoadBody(InputStream* input, size_t rows) {
return data_.LoadBody(input, rows);
}

void ColumnBool::SaveBody(OutputStream* output) {
data_.SaveBody(output);
}

void ColumnBool::Clear() {
data_.Clear();
}

size_t ColumnBool::Size() const {
return data_.Size();
}

ColumnRef ColumnBool::Slice(size_t begin, size_t len) const {
auto sliced = std::static_pointer_cast<ColumnUInt8>(data_.Slice(begin, len));
return std::make_shared<ColumnBool>(std::move(sliced->GetWritableData()));
}

ColumnRef ColumnBool::CloneEmpty() const {
return std::make_shared<ColumnBool>();
}

void ColumnBool::Swap(Column& other) {
auto& col = dynamic_cast<ColumnBool&>(other);
data_.Swap(col.data_);
}

ItemView ColumnBool::GetItem(size_t index) const {
return ItemView{Type::Bool, data_.At(index)};
}

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

#include "column.h"
#include "numeric.h"

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

#include <vector>

namespace clickhouse {

class ColumnBool : public Column {
public:
using ValueType = bool;

ColumnBool();
explicit ColumnBool(std::vector<uint8_t> data);

/// Increase the capacity of the column for large block insertion.
void Reserve(size_t new_cap) override;

/// Appends one element to the end of column.
void Append(bool value);

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

/// Returns element at given row number.
bool operator[](size_t n) const { return At(n); }

/// Returns the capacity of the column
size_t Capacity() const;

public:
/// Appends content of given column to the end of current one.
/// Accepts ColumnBool or ColumnUInt8.
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;

ItemView GetItem(size_t index) const override;

private:
ColumnUInt8 data_;
};

} // namespace clickhouse
3 changes: 3 additions & 0 deletions clickhouse/columns/factory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "map.h"
#include "nothing.h"
#include "nullable.h"
#include "bool.h"
#include "numeric.h"
#include "string.h"
#include "./time.h" // `./` avoids possible conflicts with standard C time.h
Expand Down Expand Up @@ -50,6 +51,8 @@ static ColumnRef CreateTerminalColumn(const TypeAst& ast) {
case Type::Void:
return std::make_shared<ColumnNothing>();

case Type::Bool:
return std::make_shared<ColumnBool>();
case Type::UInt8:
return std::make_shared<ColumnUInt8>();
case Type::UInt16:
Expand Down
1 change: 1 addition & 0 deletions clickhouse/columns/itemview.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ void ItemView::ValidateData(Type::Code type, DataType data) {
case Type::Code::Int8:
case Type::Code::UInt8:
case Type::Code::Enum8:
case Type::Code::Bool:
return AssertSize({1});

case Type::Code::Int16:
Expand Down
8 changes: 6 additions & 2 deletions clickhouse/columns/itemview.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ struct ItemView {
inline auto ConvertToStorageValue(const T& t) {
if constexpr (std::is_same_v<std::string_view, T> || std::is_same_v<std::string, T>) {
return std::string_view{t};
} else if constexpr (std::is_fundamental_v<T> || std::is_same_v<Int128, std::decay_t<T>> || std::is_same_v<UInt128, std::decay_t<T>>) {
} else if constexpr (std::is_fundamental_v<T>
|| std::is_same_v<Int128, std::decay_t<T>>
|| std::is_same_v<UInt128, std::decay_t<T>>) {
return std::string_view{reinterpret_cast<const char*>(&t), sizeof(T)};
} else {
static_assert(!std::is_same_v<T, T>, "Unknown type, which can't be stored in ItemView");
Expand Down Expand Up @@ -65,7 +67,9 @@ struct ItemView {
using ValueType = std::remove_cv_t<std::decay_t<T>>;
if constexpr (std::is_same_v<std::string_view, ValueType> || std::is_same_v<std::string, ValueType>) {
return data;
} else if constexpr (std::is_fundamental_v<ValueType> || std::is_same_v<Int128, ValueType> || std::is_same_v<UInt128, ValueType>) {
} else if constexpr (std::is_fundamental_v<ValueType>
|| std::is_same_v<Int128, ValueType>
|| std::is_same_v<UInt128, ValueType>) {
if (sizeof(ValueType) == data.size()) {
return *reinterpret_cast<const T*>(data.data());
} else {
Expand Down
4 changes: 4 additions & 0 deletions clickhouse/types/type_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,11 @@ static const std::unordered_map<std::string, Type::Code> kTypeCode = {
{ "Int16", Type::Int16 },
{ "Int32", Type::Int32 },
{ "Int64", Type::Int64 },
#if CH_MAP_BOOL_TO_UINT8
{ "Bool", Type::UInt8 },
#else
{ "Bool", Type::Bool },
#endif
{ "UInt8", Type::UInt8 },
{ "UInt16", Type::UInt16 },
{ "UInt32", Type::UInt32 },
Expand Down
3 changes: 3 additions & 0 deletions clickhouse/types/types.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ const char* Type::TypeName(Type::Code code) {
case Type::Code::Time: return "Time";
case Type::Code::Time64: return "Time64";
case Type::Code::JSON: return "JSON";
case Type::Code::Bool: return "Bool";
}

return "Unknown type";
Expand Down Expand Up @@ -87,6 +88,7 @@ std::string Type::GetName() const {
case Polygon:
case MultiPolygon:
case JSON:
case Bool:
return TypeName(code_);
case Time64:
return As<Time64Type>()->GetName();
Expand Down Expand Up @@ -149,6 +151,7 @@ uint64_t Type::GetTypeUniqueId() const {
case Ring:
case Polygon:
case MultiPolygon:
case Bool:
// For simple types, unique ID is the same as Type::Code
return code_;

Expand Down
6 changes: 6 additions & 0 deletions clickhouse/types/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ class Type {
Time,
Time64,
JSON,
Bool,
};

using EnumItem = std::pair<std::string /* name */, int16_t /* value */>;
Expand Down Expand Up @@ -394,6 +395,11 @@ inline TypeRef Type::CreateSimple<uint64_t>() {
return TypeRef(new Type(UInt64));
}

template <>
inline TypeRef Type::CreateSimple<bool>() {
return TypeRef(new Type(Bool));
}

template <>
inline TypeRef Type::CreateSimple<float>() {
return TypeRef(new Type(Float32));
Expand Down
4 changes: 4 additions & 0 deletions ut/Column_ut.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,10 @@ using TestCases = ::testing::Types<
NumberColumnTestCase<ColumnFloat32>,
NumberColumnTestCase<ColumnFloat64>,

#if !CH_MAP_BOOL_TO_UINT8
GenericColumnTestCase<ColumnBool, &makeColumn<ColumnBool>, uint8_t, &MakeBools>,
#endif

GenericColumnTestCase<ColumnString, &makeColumn<ColumnString>, std::string, &MakeStrings>,
GenericColumnTestCase<ColumnFixedString, &makeColumn<ColumnFixedString, 12>, std::string, &MakeFixedStrings<12>>,
GenericColumnTestCase<ColumnJSON, &makeColumn<ColumnJSON>, std::string, &MakeJSONs>,
Expand Down
12 changes: 12 additions & 0 deletions ut/CreateColumnByType_ut.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#include <clickhouse/columns/bool.h>
#include <clickhouse/columns/factory.h>
#include <clickhouse/columns/date.h>
#include <clickhouse/columns/numeric.h>
Expand Down Expand Up @@ -63,7 +64,15 @@ class CreateColumnByTypeWithName : public ::testing::TestWithParam<const char* /
TEST(CreateColumnByType, Bool) {
const auto col = CreateColumnByType("Bool");
ASSERT_NE(nullptr, col);
#if CH_MAP_BOOL_TO_UINT8
EXPECT_EQ(col->GetType().GetName(), "UInt8");
EXPECT_EQ(col->GetType().GetCode(), Type::UInt8);
EXPECT_NE(nullptr, col->As<ColumnUInt8>());
#else
EXPECT_EQ(col->GetType().GetName(), "Bool");
EXPECT_EQ(col->GetType().GetCode(), Type::Bool);
EXPECT_NE(nullptr, col->As<ColumnBool>());
#endif
}

TEST_P(CreateColumnByTypeWithName, CreateColumnByType)
Expand All @@ -79,6 +88,9 @@ INSTANTIATE_TEST_SUITE_P(Basic, CreateColumnByTypeWithName, ::testing::Values(
"String", "Date", "DateTime",
"UUID", "Int128", "UInt128"
));
#if !CH_MAP_BOOL_TO_UINT8
INSTANTIATE_TEST_SUITE_P(BasicBool, CreateColumnByTypeWithName, ::testing::Values("Bool"));
#endif

INSTANTIATE_TEST_SUITE_P(Parametrized, CreateColumnByTypeWithName, ::testing::Values(
"FixedString(0)", "FixedString(10000)",
Expand Down
Loading
Loading