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
3 changes: 1 addition & 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
* Date
* DateTime, DateTime64
* DateTime([timezone]), DateTime64(N, [timezone])
Expand Down Expand Up @@ -256,5 +257,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
```


12 changes: 12 additions & 0 deletions clickhouse/columns/factory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,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 Expand Up @@ -162,16 +164,26 @@ static ColumnRef CreateColumnFromAst(const TypeAst& ast, CreateColumnByTypeSetti

case TypeAst::Tuple: {
std::vector<ColumnRef> columns;
std::vector<std::string> names;

columns.reserve(ast.elements.size());
names.reserve(ast.elements.size());
bool any_named = false;
for (const auto& elem : ast.elements) {
if (auto col = CreateColumnFromAst(elem, settings)) {
columns.push_back(col);
names.push_back(elem.element_name);
if (!elem.element_name.empty()) {
any_named = true;
}
} else {
return nullptr;
}
}

if (any_named) {
return std::make_shared<ColumnTuple>(columns, std::move(names));
}
return std::make_shared<ColumnTuple>(columns);
}

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
10 changes: 8 additions & 2 deletions clickhouse/columns/itemview.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@ 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<Bool, std::decay_t<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 +68,10 @@ 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<Bool, 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
1 change: 1 addition & 0 deletions clickhouse/columns/numeric.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ template class ColumnVector<int16_t>;
template class ColumnVector<int32_t>;
template class ColumnVector<int64_t>;

template class ColumnVector<Bool>;
template class ColumnVector<uint8_t>;
template class ColumnVector<uint16_t>;
template class ColumnVector<uint32_t>;
Expand Down
1 change: 1 addition & 0 deletions clickhouse/columns/numeric.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ using Int128 = absl::int128;
using UInt128 = absl::uint128;
using Int64 = int64_t;

using ColumnBool = ColumnVector<Bool>;
using ColumnUInt8 = ColumnVector<uint8_t>;
using ColumnUInt16 = ColumnVector<uint16_t>;
using ColumnUInt32 = ColumnVector<uint32_t>;
Expand Down
19 changes: 17 additions & 2 deletions clickhouse/columns/tuple.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ ColumnTuple::ColumnTuple(const std::vector<ColumnRef>& columns)
{
}

ColumnTuple::ColumnTuple(const std::vector<ColumnRef>& columns,
std::vector<std::string> names)
: Column(Type::CreateTuple(CollectTypes(columns), std::move(names)))
, columns_(columns)
{
}

size_t ColumnTuple::TupleSize() const {
return columns_.size();
}
Expand Down Expand Up @@ -48,7 +55,11 @@ ColumnRef ColumnTuple::Slice(size_t begin, size_t len) const {
sliced_columns.push_back(column->Slice(begin, len));
}

return std::make_shared<ColumnTuple>(sliced_columns);
const auto& names = this->Type()->As<TupleType>()->GetItemNames();
if (names.empty()) {
return std::make_shared<ColumnTuple>(sliced_columns);
}
return std::make_shared<ColumnTuple>(sliced_columns, names);
}

ColumnRef ColumnTuple::CloneEmpty() const {
Expand All @@ -59,7 +70,11 @@ ColumnRef ColumnTuple::CloneEmpty() const {
result_columns.push_back(column->CloneEmpty());
}

return std::make_shared<ColumnTuple>(result_columns);
const auto& names = this->Type()->As<TupleType>()->GetItemNames();
if (names.empty()) {
return std::make_shared<ColumnTuple>(result_columns);
}
return std::make_shared<ColumnTuple>(result_columns, names);
}

bool ColumnTuple::LoadPrefix(InputStream* input, size_t rows) {
Expand Down
2 changes: 2 additions & 0 deletions clickhouse/columns/tuple.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ namespace clickhouse {
class ColumnTuple : public Column {
public:
ColumnTuple(const std::vector<ColumnRef>& columns);
ColumnTuple(const std::vector<ColumnRef>& columns,
std::vector<std::string> names);

/// Returns count of columns in the tuple.
size_t TupleSize() const;
Expand Down
10 changes: 9 additions & 1 deletion clickhouse/types/type_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ bool TypeAst::operator==(const TypeAst & other) const {
return meta == other.meta
&& code == other.code
&& name == other.name
&& element_name == other.element_name
&& value == other.value
Comment thread
IyeOnline marked this conversation as resolved.
&& value_string == other.value_string
&& std::equal(elements.begin(), elements.end(), other.elements.begin(), other.elements.end());
}

Expand All @@ -32,7 +34,7 @@ static const std::unordered_map<std::string, Type::Code> kTypeCode = {
{ "Int16", Type::Int16 },
{ "Int32", Type::Int32 },
{ "Int64", Type::Int64 },
{ "Bool", Type::UInt8 },
{ "Bool", Type::Bool },
{ "UInt8", Type::UInt8 },
{ "UInt16", Type::UInt16 },
{ "UInt32", Type::UInt32 },
Expand Down Expand Up @@ -167,6 +169,12 @@ bool TypeParser::Parse(TypeAst* type) {
break;
}
case Token::Name:
if (!type_->name.empty()) {
// A second Name token on the same element means the
// previous one was a field name in a named-tuple element
// (e.g. "a" in "Tuple(a Int32, …)").
type_->element_name = std::move(type_->name);
}
type_->meta = GetTypeMeta(token.value);
type_->name = token.value.to_string();
type_->code = GetTypeCode(type_->name);
Expand Down
3 changes: 3 additions & 0 deletions clickhouse/types/type_parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ struct TypeAst {
/// Type's name.
/// Need to cache TypeAst, so can't use StringView for name.
std::string name;
/// Name of this element inside its parent (e.g. field name inside a named
/// Tuple). Empty for unnamed elements.
std::string element_name;
/// Value associated with the node,
/// used for fixed-width types and enum values.
int64_t value = 0;
Expand Down
34 changes: 32 additions & 2 deletions clickhouse/types/types.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ 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";
case Type::Code::Bool: return "Bool";
}

return "Unknown type";
Expand Down Expand Up @@ -85,6 +86,7 @@ std::string Type::GetName() const {
case Ring:
case Polygon:
case MultiPolygon:
case Bool:
return TypeName(code_);
case Time64:
return As<Time64Type>()->GetName();
Expand Down Expand Up @@ -146,6 +148,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 Expand Up @@ -243,6 +246,11 @@ TypeRef Type::CreateTuple(const std::vector<TypeRef>& item_types) {
return TypeRef(new TupleType(item_types));
}

TypeRef Type::CreateTuple(const std::vector<TypeRef>& item_types,
std::vector<std::string> item_names) {
return TypeRef(new TupleType(item_types, std::move(item_names)));
}
Comment thread
IyeOnline marked this conversation as resolved.

TypeRef Type::CreateEnum8(const std::vector<EnumItem>& enum_items) {
return TypeRef(new EnumType(Type::Enum8, enum_items));
}
Expand Down Expand Up @@ -447,6 +455,11 @@ NullableType::NullableType(TypeRef nested_type) : Type(Nullable), nested_type_(n
TupleType::TupleType(const std::vector<TypeRef>& item_types) : Type(Tuple), item_types_(item_types) {
}

TupleType::TupleType(const std::vector<TypeRef>& item_types,
std::vector<std::string> item_names)
: Type(Tuple), item_types_(item_types), item_names_(std::move(item_names)) {
}

/// class LowCardinalityType
LowCardinalityType::LowCardinalityType(TypeRef nested_type) : Type(LowCardinality), nested_type_(nested_type) {
}
Expand All @@ -456,13 +469,30 @@ LowCardinalityType::~LowCardinalityType() {

std::string TupleType::GetName() const {
std::string result("Tuple(");
bool has_complete_names = item_names_.size() == item_types_.size();
if (has_complete_names) {
for (const auto& item_name : item_names_) {
if (item_name.empty()) {
has_complete_names = false;
break;
}
}
}

if (!item_types_.empty()) {
result += item_types_[0]->GetName();
if (has_complete_names) {
result += item_names_[0] + " " + item_types_[0]->GetName();
} else {
result += item_types_[0]->GetName();
}
}

for (size_t i = 1; i < item_types_.size(); ++i) {
result += ", " + item_types_[i]->GetName();
if (has_complete_names) {
result += ", " + item_names_[i] + " " + item_types_[i]->GetName();
} else {
result += ", " + item_types_[i]->GetName();
}
}

result += ")";
Expand Down
24 changes: 24 additions & 0 deletions clickhouse/types/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@ using Int128 = absl::int128;
using UInt128 = absl::uint128;
using Int64 = int64_t;

/// 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,
};

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

class Type {
Expand Down Expand Up @@ -59,6 +67,7 @@ class Type {
MultiPolygon,
Time,
Time64,
Bool,
};

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

static TypeRef CreateTuple(const std::vector<TypeRef>& item_types);

static TypeRef CreateTuple(const std::vector<TypeRef>& item_types,
std::vector<std::string> item_names);

static TypeRef CreateEnum8(const std::vector<EnumItem>& enum_items);

static TypeRef CreateEnum16(const std::vector<EnumItem>& enum_items);
Expand Down Expand Up @@ -293,14 +305,21 @@ class NullableType : public Type {
class TupleType : public Type {
public:
explicit TupleType(const std::vector<TypeRef>& item_types);
TupleType(const std::vector<TypeRef>& item_types,
std::vector<std::string> item_names);

std::string GetName() const;

/// Type of nested Tuple element type.
std::vector<TypeRef> GetTupleType() const { return item_types_; }

/// Field names for named tuples. Same length as GetTupleType() when
/// populated, or empty when the tuple has no field names.
const std::vector<std::string>& GetItemNames() const { return item_names_; }

private:
std::vector<TypeRef> item_types_;
std::vector<std::string> item_names_;
};

class LowCardinalityType : public Type {
Expand Down Expand Up @@ -384,6 +403,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: 3 additions & 1 deletion ut/CreateColumnByType_ut.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ class CreateColumnByTypeWithName : public ::testing::TestWithParam<const char* /
TEST(CreateColumnByType, Bool) {
const auto col = CreateColumnByType("Bool");
ASSERT_NE(nullptr, col);
EXPECT_EQ(col->GetType().GetName(), "UInt8");
EXPECT_EQ(col->GetType().GetName(), "Bool");
EXPECT_NE(nullptr, col->As<ColumnBool>());
}

TEST_P(CreateColumnByTypeWithName, CreateColumnByType)
Expand All @@ -75,6 +76,7 @@ TEST_P(CreateColumnByTypeWithName, CreateColumnByType)
INSTANTIATE_TEST_SUITE_P(Basic, CreateColumnByTypeWithName, ::testing::Values(
"Int8", "Int16", "Int32", "Int64",
"UInt8", "UInt16", "UInt32", "UInt64",
"Bool",
"String", "Date", "DateTime",
"UUID", "Int128", "UInt128"
));
Expand Down
Loading