Skip to content
Open
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -278,3 +278,6 @@ BUCKAROO_DEPS
# Vim
*.swp
*.swo

# clangd cache
/.cache/clangd
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
7 changes: 5 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 (mapped to UInt8 by default; use `-DCH_MAP_BOOL_TO_UINT8=OFF` for a distinct `clickhouse::Bool`/`ColumnBool` API)
* Date
* DateTime, DateTime64
* DateTime([timezone]), DateTime64(N, [timezone])
Expand All @@ -24,6 +25,10 @@ C++ client for [ClickHouse](https://clickhouse.com/).
* Map
* Point, Ring, Polygon, MultiPolygon

The distinct `Bool` type will become the default in some future version. The
current mapping to `UInt8` is provided only for compatibility and controlled via
`DCH_MAP_BOOL_TO_UINT8`.

## Dependencies
In the most basic case one needs only:
- a C++-17-complaint compiler,
Expand Down Expand Up @@ -256,5 +261,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
```


5 changes: 5 additions & 0 deletions clickhouse/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,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
4 changes: 4 additions & 0 deletions clickhouse/columns/factory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ static ColumnRef CreateTerminalColumn(const TypeAst& ast) {
case Type::Void:
return std::make_shared<ColumnNothing>();

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

case Type::Code::Int16:
Expand Down
14 changes: 12 additions & 2 deletions clickhouse/columns/itemview.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,12 @@ 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>
#if !CH_MAP_BOOL_TO_UINT8
|| std::is_same_v<Bool, std::decay_t<T>>
#endif
|| 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 +70,12 @@ 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>
#if !CH_MAP_BOOL_TO_UINT8
|| std::is_same_v<Bool, ValueType>
#endif
|| 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
3 changes: 3 additions & 0 deletions clickhouse/columns/numeric.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,9 @@ template class ColumnVector<int16_t>;
template class ColumnVector<int32_t>;
template class ColumnVector<int64_t>;

#if !CH_MAP_BOOL_TO_UINT8
template class ColumnVector<Bool>;
#endif
template class ColumnVector<uint8_t>;
template class ColumnVector<uint16_t>;
template class ColumnVector<uint32_t>;
Expand Down
3 changes: 3 additions & 0 deletions clickhouse/columns/numeric.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ using Int128 = absl::int128;
using UInt128 = absl::uint128;
using Int64 = int64_t;

#if !CH_MAP_BOOL_TO_UINT8
using ColumnBool = ColumnVector<Bool>;
#endif
using ColumnUInt8 = ColumnVector<uint8_t>;
using ColumnUInt16 = ColumnVector<uint16_t>;
using ColumnUInt32 = ColumnVector<uint32_t>;
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 @@ -32,7 +32,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
9 changes: 9 additions & 0 deletions clickhouse/types/types.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ const char* Type::TypeName(Type::Code code) {
case Type::Code::MultiPolygon: return "MultiPolygon";
case Type::Code::Time: return "Time";
case Type::Code::Time64: return "Time64";
#if !CH_MAP_BOOL_TO_UINT8
case Type::Code::Bool: return "Bool";
#endif
}

return "Unknown type";
Expand Down Expand Up @@ -85,6 +88,9 @@ std::string Type::GetName() const {
case Ring:
case Polygon:
case MultiPolygon:
#if !CH_MAP_BOOL_TO_UINT8
case Bool:
#endif
return TypeName(code_);
case Time64:
return As<Time64Type>()->GetName();
Expand Down Expand Up @@ -146,6 +152,9 @@ uint64_t Type::GetTypeUniqueId() const {
case Ring:
case Polygon:
case MultiPolygon:
#if !CH_MAP_BOOL_TO_UINT8
case Bool:
#endif
// For simple types, unique ID is the same as Type::Code
return code_;

Expand Down
24 changes: 24 additions & 0 deletions clickhouse/types/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,26 @@
#include <vector>
#include <stdexcept>

#ifndef CH_MAP_BOOL_TO_UINT8
#define CH_MAP_BOOL_TO_UINT8 1
#endif

namespace clickhouse {

using Int128 = absl::int128;
using UInt128 = absl::uint128;
using Int64 = int64_t;

#if !CH_MAP_BOOL_TO_UINT8
/// Distinct type for the ClickHouse Bool type. Backed by `bool` so it has the
/// same single-byte layout as `uint8_t` without std::vector<bool>'s
/// bit-packing, while remaining a type distinct from all integer types.
enum Bool : bool {
false_ = false,
true_ = true,
};
#endif

using TypeRef = std::shared_ptr<class Type>;

class Type {
Expand Down Expand Up @@ -59,6 +73,9 @@ class Type {
MultiPolygon,
Time,
Time64,
#if !CH_MAP_BOOL_TO_UINT8
Bool,
#endif
};

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

#if !CH_MAP_BOOL_TO_UINT8
template <>
inline TypeRef Type::CreateSimple<Bool>() {
return TypeRef(new Type(Bool));
}
#endif

template <>
inline TypeRef Type::CreateSimple<float>() {
return TypeRef(new Type(Float32));
Expand Down
11 changes: 11 additions & 0 deletions ut/CreateColumnByType_ut.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,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 @@ -75,6 +83,9 @@ TEST_P(CreateColumnByTypeWithName, CreateColumnByType)
INSTANTIATE_TEST_SUITE_P(Basic, CreateColumnByTypeWithName, ::testing::Values(
"Int8", "Int16", "Int32", "Int64",
"UInt8", "UInt16", "UInt32", "UInt64",
#if !CH_MAP_BOOL_TO_UINT8
"Bool",
#endif
"String", "Date", "DateTime",
"UUID", "Int128", "UInt128"
));
Expand Down
32 changes: 24 additions & 8 deletions ut/client_ut.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,22 @@

using namespace clickhouse;

#if CH_MAP_BOOL_TO_UINT8
using ClientBoolColumn = ColumnUInt8;
using ClientBoolValue = uint8_t;
#else
using ClientBoolColumn = ColumnBool;
using ClientBoolValue = Bool;
#endif

ClientBoolValue MakeClientBoolValue(bool value) {
#if CH_MAP_BOOL_TO_UINT8
return static_cast<uint8_t>(value);
#else
return static_cast<Bool>(value);
#endif
}

template <typename T>
std::shared_ptr<T> createTableWithOneColumn(Client & client, const std::string & table_name, const std::string & column_name)
{
Expand Down Expand Up @@ -400,11 +416,11 @@ TEST_P(ClientCase, Generic) {

auto id = std::make_shared<ColumnUInt64>();
auto name = std::make_shared<ColumnString>();
auto f = std::make_shared<ColumnUInt8> ();
auto f = std::make_shared<ClientBoolColumn>();
for (auto const& td : TEST_DATA) {
id->Append(td.id);
name->Append(td.name);
f->Append(td.f);
f->Append(MakeClientBoolValue(td.f));
}

block.AppendColumn("id" , id);
Expand All @@ -426,7 +442,7 @@ TEST_P(ClientCase, Generic) {
for (size_t c = 0; c < block.GetRowCount(); ++c, ++row) {
EXPECT_EQ(TEST_DATA[row].id, (*block[0]->As<ColumnUInt64>())[c]);
EXPECT_EQ(TEST_DATA[row].name, (*block[1]->As<ColumnString>())[c]);
EXPECT_EQ(TEST_DATA[row].f, (*block[2]->As<ColumnUInt8>())[c]);
EXPECT_EQ(MakeClientBoolValue(TEST_DATA[row].f), (*block[2]->As<ClientBoolColumn>())[c]);
}
}
);
Expand Down Expand Up @@ -468,13 +484,13 @@ TEST_P(ClientCase, InsertData) {
// Fetch the derived columns.
auto id = block[0]->As<ColumnUInt64>();
auto name = block[1]->As<ColumnString>();
auto f = block[2]->As<ColumnUInt8>();
auto f = block[2]->As<ClientBoolColumn>();

// Insert some values.
for (auto const& td : TEST_DATA) {
id->Append(td.id);
name->Append(td.name);
f->Append(td.f);
f->Append(MakeClientBoolValue(td.f));
}
block.RefreshRowCount();
client_->SendInsertBlock(block);
Expand All @@ -484,7 +500,7 @@ TEST_P(ClientCase, InsertData) {
for (auto const& td : TEST_DATA2) {
id->Append(td.id);
name->Append(td.name);
f->Append(td.f);
f->Append(MakeClientBoolValue(td.f));
}
block.RefreshRowCount();
client_->SendInsertBlock(block);
Expand All @@ -509,13 +525,13 @@ TEST_P(ClientCase, InsertData) {
for (size_t c = 0; c < block.GetRowCount(); ++c, ++row) {
EXPECT_EQ(TEST_DATA[row].id, (*block[0]->As<ColumnUInt64>())[c]);
EXPECT_EQ(TEST_DATA[row].name, (*block[1]->As<ColumnString>())[c]);
EXPECT_EQ(TEST_DATA[row].f, (*block[2]->As<ColumnUInt8>())[c]);
EXPECT_EQ(MakeClientBoolValue(TEST_DATA[row].f), (*block[2]->As<ClientBoolColumn>())[c]);
}
} else {
for (size_t c = 0; c < block.GetRowCount(); ++c, ++row) {
EXPECT_EQ(TEST_DATA2[row-block_two_row_num].id, (*block[0]->As<ColumnUInt64>())[c]);
EXPECT_EQ(TEST_DATA2[row-block_two_row_num].name, (*block[1]->As<ColumnString>())[c]);
EXPECT_EQ(TEST_DATA2[row-block_two_row_num].f, (*block[2]->As<ColumnUInt8>())[c]);
EXPECT_EQ(MakeClientBoolValue(TEST_DATA2[row-block_two_row_num].f), (*block[2]->As<ClientBoolColumn>())[c]);
}
}
}
Expand Down
21 changes: 21 additions & 0 deletions ut/types_ut.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include <clickhouse/types/types.h>
#include <clickhouse/columns/factory.h>
#include <clickhouse/columns/numeric.h>
#include <ut/utils.h>

#include <gtest/gtest.h>
Expand Down Expand Up @@ -34,7 +35,27 @@ TEST(TypesCase, TypeName) {
);

ASSERT_EQ(Type::CreateMap(Type::CreateSimple<int32_t>(), Type::CreateString())->GetName(), "Map(Int32, String)");

#if !CH_MAP_BOOL_TO_UINT8
ASSERT_EQ(Type::CreateSimple<Bool>()->GetName(), "Bool");
#endif
}

#if !CH_MAP_BOOL_TO_UINT8
TEST(TypesCase, ColumnBool) {
auto col = std::make_shared<ColumnBool>();
col->Append(true_);
col->Append(false_);
col->Append(true_);

ASSERT_EQ(col->Size(), 3u);
ASSERT_EQ(col->At(0), true_);
ASSERT_EQ(col->At(1), false_);
ASSERT_EQ(col->At(2), true_);
ASSERT_EQ(col->GetType().GetName(), "Bool");
ASSERT_EQ(col->GetType().GetCode(), Type::Bool);
}
#endif

TEST(TypesCase, NullableType) {
TypeRef nested = Type::CreateSimple<int32_t>();
Expand Down
5 changes: 5 additions & 0 deletions ut/utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,11 @@ std::ostream& operator<<(std::ostream& ostr, const ItemView& item_view) {
case Type::UInt8:
ostr << static_cast<unsigned int>(item_view.get<uint8_t>());
break;
#if !CH_MAP_BOOL_TO_UINT8
case Type::Bool:
ostr << (item_view.get<Bool>() ? "true" : "false");
break;
#endif
case Type::UInt16:
ostr << static_cast<unsigned int>(item_view.get<uint16_t>());
break;
Expand Down