Skip to content

Commit c9e4841

Browse files
committed
Some fixes to ColumnDateTime64 and underlying code, added unit and integration tests
Fixed DateTime64 example
1 parent 7de8b8d commit c9e4841

File tree

11 files changed

+331
-79
lines changed

11 files changed

+331
-79
lines changed

clickhouse/columns/date.cpp

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -114,20 +114,24 @@ ItemView ColumnDateTime::GetItem(size_t index) const {
114114
}
115115

116116
ColumnDateTime64::ColumnDateTime64(size_t precision)
117-
: Column(Type::CreateDateTime64(precision))
118-
, data_(std::make_shared<ColumnDecimal>(18ul, precision))
117+
: ColumnDateTime64(Type::CreateDateTime64(precision), std::make_shared<ColumnDecimal>(18ul, precision))
119118
{}
120119

120+
ColumnDateTime64::ColumnDateTime64(TypeRef type, std::shared_ptr<ColumnDecimal> data)
121+
: Column(type),
122+
data_(data),
123+
precision_(type->As<DateTime64Type>()->GetPrecision())
124+
{}
121125

122126
void ColumnDateTime64::Append(const Int64& value) {
123127
// TODO: we need a type, which safely represents datetime.
124128
// The precision of Poco.DateTime is not big enough.
125129
data_->Append(value);
126130
}
127131

128-
void ColumnDateTime64::Append(const std::string& value) {
129-
data_->Append(value);
130-
}
132+
//void ColumnDateTime64::Append(const std::string& value) {
133+
// data_->Append(value);
134+
//}
131135

132136
Int64 ColumnDateTime64::At(size_t n) const {
133137
return data_->At(n);
@@ -160,21 +164,22 @@ ItemView ColumnDateTime64::GetItem(size_t index) const {
160164

161165
void ColumnDateTime64::Swap(Column& other) {
162166
auto& col = dynamic_cast<ColumnDateTime64&>(other);
167+
if (col.GetPrecision() != GetPrecision()) {
168+
throw std::runtime_error("Can't swap DateTime64 columns when precisions are not the same: "
169+
+ std::to_string(GetPrecision()) + "(this) != " + std::to_string(col.GetPrecision()) + "(that)");
170+
}
171+
163172
data_.swap(col.data_);
164173
}
165174

166175
ColumnRef ColumnDateTime64::Slice(size_t begin, size_t len) {
167-
auto col = data_->Slice(begin, len)->As<ColumnDecimal>();
168-
size_t precision = col->Type()->As<DateTime64Type>()->GetPrecision();
169-
auto result = std::make_shared<ColumnDateTime64>(precision); // TODO FIXME
170-
171-
result->data_->Append(col);
176+
auto sliced_data = data_->Slice(begin, len)->As<ColumnDecimal>();
172177

173-
return result;
178+
return ColumnRef{new ColumnDateTime64(type_, sliced_data)};
174179
}
175180

176181
size_t ColumnDateTime64::GetPrecision() const {
177-
return data_->Type()->As<DecimalType>()->GetScale();
182+
return precision_;
178183
}
179184

180185
}

clickhouse/columns/date.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,9 @@ class ColumnDateTime64 : public Column {
9191

9292
/// Appends one element to the end of column.
9393
void Append(const Int64& value);
94-
void Append(const std::string& value);
94+
// It is a bit controversal: users might expect it to parse string of ISO8601 or some other human-friendly format,
95+
// but current implemntation parses it as fractional integer with decimal point, e.g. "123.456".
96+
// void Append(const std::string& value);
9597

9698
/// Returns element at given row number.
9799
Int64 At(size_t n) const;
@@ -120,8 +122,13 @@ class ColumnDateTime64 : public Column {
120122
ItemView GetItem(size_t index) const override;
121123

122124
size_t GetPrecision() const;
125+
126+
private:
127+
ColumnDateTime64(TypeRef type, std::shared_ptr<ColumnDecimal> data);
128+
123129
private:
124130
std::shared_ptr<ColumnDecimal> data_;
131+
const size_t precision_;
125132
};
126133

127134
}

clickhouse/columns/decimal.cpp

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#include "decimal.h"
22

3-
#include <iostream>
3+
#include <cassert>
44

55
namespace clickhouse {
66

@@ -83,12 +83,16 @@ void ColumnDecimal::Append(const std::string& value) {
8383
}
8484

8585
Int128 ColumnDecimal::At(size_t i) const {
86-
if (data_->Type()->GetCode() == Type::Int32) {
87-
return static_cast<Int128>(data_->As<ColumnInt32>()->At(i));
88-
} else if (data_->Type()->GetCode() == Type::Int64) {
89-
return static_cast<Int128>(data_->As<ColumnInt64>()->At(i));
90-
} else {
91-
return data_->As<ColumnInt128>()->At(i);
86+
switch (data_->Type()->GetCode()) {
87+
case Type::Int32:
88+
return static_cast<Int128>(data_->As<ColumnInt32>()->At(i));
89+
case Type::Int64:
90+
return static_cast<Int128>(data_->As<ColumnInt64>()->At(i));
91+
case Type::Int128:
92+
return data_->As<ColumnInt128>()->At(i);
93+
default:
94+
assert(false && "Invalid data_ column type in ColumnDecimal");
95+
return 0;
9296
}
9397
}
9498

@@ -129,4 +133,14 @@ ItemView ColumnDecimal::GetItem(size_t index) const {
129133
return data_->GetItem(index);
130134
}
131135

136+
size_t ColumnDecimal::GetScale() const
137+
{
138+
return type_->As<DecimalType>()->GetScale();
139+
}
140+
141+
size_t ColumnDecimal::GetPrecision() const
142+
{
143+
return type_->As<DecimalType>()->GetPrecision();
144+
}
145+
132146
}

clickhouse/columns/decimal.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ class ColumnDecimal : public Column {
2727
void Swap(Column& other) override;
2828
ItemView GetItem(size_t index) const override;
2929

30+
size_t GetScale() const;
31+
size_t GetPrecision() const;
32+
3033
private:
3134
/// Depending on a precision it can be one of:
3235
/// - ColumnInt32

clickhouse/columns/numeric.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ ColumnVector<T>::ColumnVector()
1010
}
1111

1212
template <typename T>
13-
ColumnVector<T>::ColumnVector(const std::vector<T>& data)
13+
ColumnVector<T>::ColumnVector(std::vector<T> data)
1414
: Column(Type::CreateSimple<T>())
15-
, data_(data)
15+
, data_(std::move(data))
1616
{
1717
}
1818

clickhouse/columns/numeric.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ class ColumnVector : public Column {
1414

1515
ColumnVector();
1616

17-
explicit ColumnVector(const std::vector<T>& data);
17+
explicit ColumnVector(std::vector<T> data);
1818

1919
/// Appends one element to the end of column.
2020
void Append(const T& value);

clickhouse/types/types.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,13 @@ EnumType::ValueToNameIterator EnumType::EndValueToName() const {
227227

228228
/// class DateTime64Type
229229

230-
DateTime64Type::DateTime64Type(size_t precision) : Type(DateTime64), precision_(precision) {}
230+
DateTime64Type::DateTime64Type(size_t precision)
231+
: Type(DateTime64), precision_(precision) {
232+
233+
if (precision_ > 18) {
234+
throw std::runtime_error("DateTime64 precision is > 18");
235+
}
236+
}
231237

232238
std::string DateTime64Type::GetName() const {
233239
std::string datetime64_representation;

clickhouse/types/types.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ class Type {
2626
String,
2727
FixedString,
2828
DateTime,
29-
DateTime64,
3029
Date,
3130
Array,
3231
Nullable,
@@ -42,6 +41,7 @@ class Type {
4241
Decimal64,
4342
Decimal128,
4443
LowCardinality,
44+
DateTime64,
4545
};
4646

4747
using EnumItem = std::pair<std::string /* name */, int16_t /* value */>;
@@ -150,7 +150,7 @@ class DecimalType : public Type {
150150

151151
class DateTime64Type: public Type {
152152
public:
153-
DateTime64Type(size_t precision);
153+
explicit DateTime64Type(size_t precision);
154154

155155
std::string GetName() const;
156156

tests/simple/main.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ inline void DateExample(Client& client) {
120120
{
121121
for (size_t c = 0; c < block.GetRowCount(); ++c) {
122122
auto col = block[0]->As<ColumnDateTime>();
123-
std::time_t t = col->As<ColumnDateTime>()->At(c);
123+
std::time_t t = col->At(c);
124124
std::cerr << std::asctime(std::localtime(&t)) << " " << std::endl;
125125
}
126126
}
@@ -134,7 +134,7 @@ inline void DateTime64Example(Client& client) {
134134
Block b;
135135

136136
/// Create a table.
137-
client.Execute("CREATE TABLE IF NOT EXISTS test.date (d DateTime64(6)) ENGINE = Memory");
137+
client.Execute("CREATE TABLE IF NOT EXISTS test.datetime64 (dt64 DateTime64(6)) ENGINE = Memory");
138138

139139
size_t precision = 6ul;
140140
auto d = std::make_shared<ColumnDateTime64>(precision);
@@ -143,21 +143,21 @@ inline void DateTime64Example(Client& client) {
143143
Int64 datetime = Int64(std::time(nullptr)) * precision_multiplier;
144144

145145
d->Append(datetime);
146-
b.AppendColumn("d", d);
146+
b.AppendColumn("dt64", d);
147147
client.Insert("test.date", b);
148148

149-
client.Select("SELECT d FROM test.date", [precision_multiplier](const Block& block)
149+
client.Select("SELECT d FROM test.datetime64", [precision_multiplier](const Block& block)
150150
{
151151
for (size_t c = 0; c < block.GetRowCount(); ++c) {
152152
auto col = block[0]->As<ColumnDateTime64>();
153-
Int64 t = col->At(c) / precision_multiplier;
153+
const time_t t = static_cast<time_t>(col->At(c) / precision_multiplier);
154154
std::cerr << std::asctime(std::localtime(&t)) << " " << std::endl;
155155
}
156156
}
157157
);
158158

159159
/// Delete table.
160-
client.Execute("DROP TABLE test.date");
160+
client.Execute("DROP TABLE test.datetime64");
161161
}
162162

163163
inline void DecimalExample(Client& client) {

ut/client_ut.cpp

Lines changed: 97 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
#include <clickhouse/client.h>
22
#include <contrib/gtest/gtest.h>
33

4+
#include <cmath>
5+
46
using namespace clickhouse;
57

6-
namespace clickhouse
7-
{
8-
std::ostream & operator<<(std::ostream & ostr, const ServerInfo & server_info)
9-
{
8+
namespace clickhouse {
9+
std::ostream & operator<<(std::ostream & ostr, const ServerInfo & server_info) {
1010
return ostr << server_info.name << "/" << server_info.display_name
1111
<< " ver "
1212
<< server_info.version_major << "."
@@ -16,6 +16,33 @@ std::ostream & operator<<(std::ostream & ostr, const ServerInfo & server_info)
1616
}
1717
}
1818

19+
namespace {
20+
21+
uint64_t versionNumber(
22+
uint64_t version_major,
23+
uint64_t version_minor,
24+
uint64_t version_patch = 0,
25+
uint64_t revision = 0) {
26+
27+
// in this case version_major can be up to 1000
28+
static auto revision_decimal_places = 8;
29+
static auto patch_decimal_places = 4;
30+
static auto minor_decimal_places = 4;
31+
32+
auto const result = version_major * static_cast<uint64_t>(std::pow(10, minor_decimal_places + patch_decimal_places + revision_decimal_places))
33+
+ version_minor * static_cast<uint64_t>(std::pow(10, patch_decimal_places + revision_decimal_places))
34+
+ version_patch * static_cast<uint64_t>(std::pow(10, revision_decimal_places))
35+
+ revision;
36+
37+
return result;
38+
}
39+
40+
uint64_t versionNumber(const ServerInfo & server_info) {
41+
return versionNumber(server_info.version_major, server_info.version_minor, server_info.version_patch, server_info.revision);
42+
}
43+
44+
}
45+
1946
// Use value-parameterized tests to run same tests with different client
2047
// options.
2148
class ClientCase : public testing::TestWithParam<ClientOptions> {
@@ -356,7 +383,7 @@ TEST_P(ClientCase, Numbers) {
356383

357384
TEST_P(ClientCase, SimpleAggregateFunction) {
358385
const auto & server_info = client_->GetServerInfo();
359-
if (server_info.version_major <= 19 && server_info.version_minor < 9) {
386+
if (versionNumber(server_info) < versionNumber(19, 9)) {
360387
std::cout << "Test is skipped since server '" << server_info << "' does not support SimpleAggregateFunction" << std::endl;
361388
return;
362389
}
@@ -688,6 +715,71 @@ TEST_P(ClientCase, Decimal) {
688715
});
689716
}
690717

718+
// Test roundtrip of DateTime64 values
719+
TEST_P(ClientCase, DateTime64) {
720+
const auto & server_info = client_->GetServerInfo();
721+
if (versionNumber(server_info) < versionNumber(20, 1)) {
722+
std::cout << "Test is skipped since server '" << server_info << "' does not support DateTime64" << std::endl;
723+
return;
724+
}
725+
726+
Block block;
727+
client_->Execute("DROP TABLE IF EXISTS test_clickhouse_cpp.datetime64;");
728+
729+
client_->Execute("CREATE TABLE IF NOT EXISTS "
730+
"test_clickhouse_cpp.datetime64 (dt DateTime64(6)) "
731+
"ENGINE = Memory");
732+
733+
auto col_dt64 = std::make_shared<ColumnDateTime64>(6);
734+
block.AppendColumn("dt", col_dt64);
735+
736+
// Empty INSERT and SELECT
737+
client_->Insert("test_clickhouse_cpp.datetime64", block);
738+
client_->Select("SELECT dt FROM test_clickhouse_cpp.datetime64",
739+
[](const Block& block) {
740+
ASSERT_EQ(0U, block.GetRowCount());
741+
}
742+
);
743+
744+
const std::vector<Int64> data{
745+
-1'234'567'890'123'456'7ll, // approx year 1578
746+
-1'234'567'890'123ll, // 1969-12-17T17:03:52.890123
747+
-1'234'567ll, // 1969-12-31T23:59:58.234567
748+
0, // epoch
749+
1'234'567ll, // 1970-01-01T00:00:01.234567
750+
1'234'567'890'123ll, // 1970-01-15T06:56:07.890123
751+
1'234'567'890'123'456'7ll // 2361-03-21T19:15:01.234567
752+
};
753+
for (const auto & d : data) {
754+
col_dt64->Append(d);
755+
}
756+
757+
block.RefreshRowCount();
758+
759+
// Non-empty INSERT and SELECT
760+
client_->Insert("test_clickhouse_cpp.datetime64", block);
761+
762+
size_t total_rows = 0;
763+
client_->Select("SELECT dt FROM test_clickhouse_cpp.datetime64",
764+
[&total_rows, &data](const Block& block) {
765+
total_rows += block.GetRowCount();
766+
if (block.GetRowCount() == 0) {
767+
return;
768+
}
769+
770+
ASSERT_EQ(1U, block.GetColumnCount());
771+
if (auto col = block[0]->As<ColumnDateTime64>()) {
772+
ASSERT_EQ(data.size(), col->Size());
773+
for (size_t i = 0; i < col->Size(); ++i) {
774+
EXPECT_EQ(data[i], col->At(i)) << " at index: " << i;
775+
}
776+
}
777+
}
778+
);
779+
780+
ASSERT_EQ(total_rows, data.size());
781+
}
782+
691783
INSTANTIATE_TEST_CASE_P(
692784
Client, ClientCase,
693785
::testing::Values(

0 commit comments

Comments
 (0)