Skip to content

Commit c7a07fa

Browse files
committed
Test typed column As alias coherence
1 parent 0372f31 commit c7a07fa

2 files changed

Lines changed: 284 additions & 0 deletions

File tree

ut/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ SET ( clickhouse-cpp-ut-src
77
block_ut.cpp
88
client_ut.cpp
99
columns_ut.cpp
10+
column_as_ut.cpp
1011
column_array_ut.cpp
1112
itemview_ut.cpp
1213
socket_ut.cpp

ut/column_as_ut.cpp

Lines changed: 283 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,283 @@
1+
#include <clickhouse/columns/array.h>
2+
#include <clickhouse/columns/factory.h>
3+
#include <clickhouse/columns/lowcardinality.h>
4+
#include <clickhouse/columns/map.h>
5+
#include <clickhouse/columns/nullable.h>
6+
#include <clickhouse/columns/numeric.h>
7+
#include <clickhouse/columns/string.h>
8+
#include <clickhouse/columns/tuple.h>
9+
10+
#include <gtest/gtest.h>
11+
12+
#include <array>
13+
#include <map>
14+
#include <optional>
15+
#include <string>
16+
#include <string_view>
17+
#include <tuple>
18+
#include <vector>
19+
20+
namespace {
21+
using namespace clickhouse;
22+
23+
struct ArrayAsTestCase {
24+
using TypedColumn = ColumnArrayT<ColumnUInt64>;
25+
using Value = std::vector<uint64_t>;
26+
27+
static const char* TypeName() { return "Array(UInt64)"; }
28+
29+
static std::array<Value, 3> Values() {
30+
return {{{1, 2}, {7}, {9, 10, 11}}};
31+
}
32+
33+
static void Append(TypedColumn& column, const Value& value) {
34+
column.Append(value);
35+
}
36+
37+
static void ExpectValue(const TypedColumn& column, size_t index, const Value& expected) {
38+
const auto actual = column.At(index);
39+
ASSERT_EQ(expected.size(), actual.size());
40+
for (size_t i = 0; i < expected.size(); ++i) {
41+
EXPECT_EQ(expected[i], actual[i]);
42+
}
43+
}
44+
};
45+
46+
struct NullableAsTestCase {
47+
using TypedColumn = ColumnNullableT<ColumnUInt64>;
48+
using Value = std::optional<uint64_t>;
49+
50+
static const char* TypeName() { return "Nullable(UInt64)"; }
51+
52+
static std::array<Value, 3> Values() {
53+
return {{uint64_t{1}, std::nullopt, uint64_t{42}}};
54+
}
55+
56+
static void Append(TypedColumn& column, const Value& value) {
57+
column.Append(value);
58+
}
59+
60+
static void ExpectValue(const TypedColumn& column, size_t index, const Value& expected) {
61+
EXPECT_EQ(expected, column.At(index));
62+
}
63+
};
64+
65+
struct TupleAsTestCase {
66+
using TypedColumn = ColumnTupleT<ColumnUInt64, ColumnString>;
67+
using Value = std::tuple<uint64_t, std::string>;
68+
69+
static const char* TypeName() { return "Tuple(UInt64, String)"; }
70+
71+
static std::array<Value, 3> Values() {
72+
return {{{1, "one"}, {2, "two"}, {3, "three"}}};
73+
}
74+
75+
static void Append(TypedColumn& column, const Value& value) {
76+
column.Append(value);
77+
}
78+
79+
static void ExpectValue(const TypedColumn& column, size_t index, const Value& expected) {
80+
const auto actual = column.At(index);
81+
EXPECT_EQ(std::get<0>(expected), std::get<0>(actual));
82+
EXPECT_EQ(std::string_view(std::get<1>(expected)), std::get<1>(actual));
83+
}
84+
};
85+
86+
struct MapAsTestCase {
87+
using TypedColumn = ColumnMapT<ColumnString, ColumnUInt64>;
88+
using Value = std::map<std::string, uint64_t>;
89+
90+
static const char* TypeName() { return "Map(String, UInt64)"; }
91+
92+
static std::array<Value, 3> Values() {
93+
return {{{{"one", 1}}, {{"two", 2}, {"second", 20}}, {{"three", 3}}}};
94+
}
95+
96+
static void Append(TypedColumn& column, const Value& value) {
97+
column.Append(value);
98+
}
99+
100+
static void ExpectValue(const TypedColumn& column, size_t index, const Value& expected) {
101+
const auto actual = column.At(index);
102+
ASSERT_EQ(expected.size(), actual.size());
103+
for (const auto& [key, value] : expected) {
104+
EXPECT_EQ(value, actual.At(std::string_view(key)));
105+
}
106+
}
107+
};
108+
109+
struct LowCardinalityAsTestCase {
110+
using TypedColumn = ColumnLowCardinalityT<ColumnString>;
111+
using Value = std::string;
112+
113+
static const char* TypeName() { return "LowCardinality(String)"; }
114+
115+
static std::array<Value, 3> Values() {
116+
return {{"alpha", "beta", "alpha"}};
117+
}
118+
119+
static void Append(TypedColumn& column, const Value& value) {
120+
column.Append(std::string_view(value));
121+
}
122+
123+
static void ExpectValue(const TypedColumn& column, size_t index, const Value& expected) {
124+
EXPECT_EQ(std::string_view(expected), column.At(index));
125+
}
126+
};
127+
128+
template <typename T>
129+
class ColumnAsTest : public testing::Test {
130+
protected:
131+
using TypedColumn = typename T::TypedColumn;
132+
using Value = typename T::Value;
133+
134+
static ColumnRef MakeBaseColumn() {
135+
return CreateColumnByType(T::TypeName());
136+
}
137+
138+
static void AppendToBase(const ColumnRef& column, const Value& value) {
139+
auto donor = MakeBaseColumn();
140+
T::Append(*donor->template AsStrict<TypedColumn>(), value);
141+
column->Append(std::move(donor));
142+
}
143+
144+
static void ExpectValues(
145+
const std::shared_ptr<TypedColumn>& column,
146+
const std::vector<Value>& expected) {
147+
ASSERT_EQ(expected.size(), column->Size());
148+
for (size_t i = 0; i < expected.size(); ++i) {
149+
T::ExpectValue(*column, i, expected[i]);
150+
}
151+
}
152+
};
153+
154+
using ColumnAsTestCases = testing::Types<
155+
ArrayAsTestCase,
156+
NullableAsTestCase,
157+
TupleAsTestCase,
158+
MapAsTestCase,
159+
LowCardinalityAsTestCase>;
160+
161+
TYPED_TEST_SUITE(ColumnAsTest, ColumnAsTestCases);
162+
163+
TYPED_TEST(ColumnAsTest, SharesMutationsInBothDirections) {
164+
using TypedColumn = typename TestFixture::TypedColumn;
165+
const auto values = TypeParam::Values();
166+
auto base = TestFixture::MakeBaseColumn();
167+
168+
ASSERT_NE(nullptr, base);
169+
EXPECT_EQ(nullptr, std::dynamic_pointer_cast<TypedColumn>(base));
170+
171+
auto first_view = base->template As<TypedColumn>();
172+
auto second_view = base->template AsStrict<TypedColumn>();
173+
ASSERT_NE(nullptr, first_view);
174+
ASSERT_NE(nullptr, second_view);
175+
EXPECT_NE(first_view.get(), second_view.get());
176+
EXPECT_NE(base.get(), static_cast<Column*>(first_view.get()));
177+
178+
TestFixture::AppendToBase(base, values[0]);
179+
EXPECT_EQ(1u, base->Size());
180+
TestFixture::ExpectValues(first_view, {values[0]});
181+
TestFixture::ExpectValues(second_view, {values[0]});
182+
183+
TypeParam::Append(*first_view, values[1]);
184+
EXPECT_EQ(2u, base->Size());
185+
TestFixture::ExpectValues(first_view, {values[0], values[1]});
186+
TestFixture::ExpectValues(second_view, {values[0], values[1]});
187+
188+
TypeParam::Append(*second_view, values[2]);
189+
EXPECT_EQ(3u, base->Size());
190+
TestFixture::ExpectValues(first_view, {values[0], values[1], values[2]});
191+
TestFixture::ExpectValues(second_view, {values[0], values[1], values[2]});
192+
}
193+
194+
TYPED_TEST(ColumnAsTest, AsOnAliasReturnsSameInstance) {
195+
using TypedColumn = typename TestFixture::TypedColumn;
196+
const auto values = TypeParam::Values();
197+
auto base = TestFixture::MakeBaseColumn();
198+
ASSERT_NE(nullptr, base);
199+
200+
auto typed_alias = base->template AsStrict<TypedColumn>();
201+
auto same_alias = typed_alias->template As<TypedColumn>();
202+
ASSERT_NE(nullptr, same_alias);
203+
EXPECT_EQ(typed_alias.get(), same_alias.get());
204+
205+
TypeParam::Append(*same_alias, values[0]);
206+
EXPECT_EQ(1u, base->Size());
207+
TestFixture::ExpectValues(typed_alias, {values[0]});
208+
209+
TestFixture::AppendToBase(base, values[1]);
210+
EXPECT_EQ(2u, same_alias->Size());
211+
TestFixture::ExpectValues(same_alias, {values[0], values[1]});
212+
}
213+
214+
TYPED_TEST(ColumnAsTest, ClearPreservesAllViewsAndAllowsReuse) {
215+
const auto values = TypeParam::Values();
216+
auto base = TestFixture::MakeBaseColumn();
217+
ASSERT_NE(nullptr, base);
218+
219+
auto first_view = base->template AsStrict<typename TestFixture::TypedColumn>();
220+
auto second_view = base->template AsStrict<typename TestFixture::TypedColumn>();
221+
TestFixture::AppendToBase(base, values[0]);
222+
TestFixture::AppendToBase(base, values[1]);
223+
224+
base->Clear();
225+
EXPECT_EQ(0u, base->Size());
226+
TestFixture::ExpectValues(first_view, {});
227+
TestFixture::ExpectValues(second_view, {});
228+
229+
TypeParam::Append(*first_view, values[2]);
230+
EXPECT_EQ(1u, base->Size());
231+
TestFixture::ExpectValues(second_view, {values[2]});
232+
233+
second_view->Clear();
234+
EXPECT_EQ(0u, base->Size());
235+
TestFixture::ExpectValues(first_view, {});
236+
TestFixture::ExpectValues(second_view, {});
237+
238+
TestFixture::AppendToBase(base, values[1]);
239+
EXPECT_EQ(1u, base->Size());
240+
TestFixture::ExpectValues(first_view, {values[1]});
241+
TestFixture::ExpectValues(second_view, {values[1]});
242+
}
243+
244+
TYPED_TEST(ColumnAsTest, SwapPreservesAllViewsAndAllowsReuse) {
245+
const auto values = TypeParam::Values();
246+
auto first_base = TestFixture::MakeBaseColumn();
247+
auto second_base = TestFixture::MakeBaseColumn();
248+
ASSERT_NE(nullptr, first_base);
249+
ASSERT_NE(nullptr, second_base);
250+
251+
TestFixture::AppendToBase(first_base, values[0]);
252+
TestFixture::AppendToBase(second_base, values[1]);
253+
TestFixture::AppendToBase(second_base, values[2]);
254+
255+
auto first_view = first_base->template AsStrict<typename TestFixture::TypedColumn>();
256+
auto first_second_view = first_base->template AsStrict<typename TestFixture::TypedColumn>();
257+
auto second_view = second_base->template AsStrict<typename TestFixture::TypedColumn>();
258+
auto second_second_view = second_base->template AsStrict<typename TestFixture::TypedColumn>();
259+
260+
first_base->Swap(*second_base);
261+
TestFixture::ExpectValues(first_view, {values[1], values[2]});
262+
TestFixture::ExpectValues(first_second_view, {values[1], values[2]});
263+
TestFixture::ExpectValues(second_view, {values[0]});
264+
TestFixture::ExpectValues(second_second_view, {values[0]});
265+
266+
TypeParam::Append(*first_view, values[0]);
267+
EXPECT_EQ(3u, first_base->Size());
268+
TestFixture::ExpectValues(first_second_view, {values[1], values[2], values[0]});
269+
270+
first_view->Swap(*second_view);
271+
EXPECT_EQ(1u, first_base->Size());
272+
EXPECT_EQ(3u, second_base->Size());
273+
TestFixture::ExpectValues(first_view, {values[0]});
274+
TestFixture::ExpectValues(first_second_view, {values[0]});
275+
TestFixture::ExpectValues(second_view, {values[1], values[2], values[0]});
276+
TestFixture::ExpectValues(second_second_view, {values[1], values[2], values[0]});
277+
278+
TypeParam::Append(*second_second_view, values[1]);
279+
EXPECT_EQ(4u, second_base->Size());
280+
TestFixture::ExpectValues(second_view, {values[1], values[2], values[0], values[1]});
281+
}
282+
283+
} // namespace

0 commit comments

Comments
 (0)