1616#include < clickhouse/client.h>
1717
1818#include < gtest/gtest.h>
19+ #include < initializer_list>
20+ #include < memory>
21+ #include < type_traits>
1922
2023#include " utils.h"
2124#include " roundtrip_column.h"
@@ -46,10 +49,12 @@ std::ostream& operator<<(std::ostream& ostr, const Type::Code& type_code) {
4649template <typename ColumnTypeT,
4750 typename std::shared_ptr<ColumnTypeT> (*CreatorFunction)(),
4851 typename GeneratorValueType,
49- typename std::vector<GeneratorValueType> (*GeneratorFunc )()>
52+ typename std::vector<GeneratorValueType> (*GeneratorFunction )()>
5053struct GenericColumnTestCase
5154{
5255 using ColumnType = ColumnTypeT;
56+ static constexpr auto Creator = CreatorFunction;
57+ static constexpr auto Generator = GeneratorFunction;
5358
5459 static auto createColumn ()
5560 {
@@ -58,7 +63,7 @@ struct GenericColumnTestCase
5863
5964 static auto generateValues ()
6065 {
61- return GeneratorFunc ();
66+ return GeneratorFunction ();
6267 }
6368};
6469
@@ -92,7 +97,7 @@ class GenericColumnTest : public testing::Test {
9297 return std::tuple{column, values};
9398 }
9499
95- static std::optional<std::string> SkipTest (clickhouse::Client& client) {
100+ static std::optional<std::string> CheckIfShouldSkipTest (clickhouse::Client& client) {
96101 if constexpr (std::is_same_v<ColumnType, ColumnDate32>) {
97102 // Date32 first appeared in v21.9.2.17-stable
98103 const auto server_info = client.GetServerInfo ();
@@ -113,6 +118,33 @@ class GenericColumnTest : public testing::Test {
113118 }
114119 return std::nullopt ;
115120 }
121+
122+ template <typename ColumnType>
123+ static void TestColumnRoundtrip (const std::shared_ptr<ColumnType> & column, const ClientOptions & client_options)
124+ {
125+ SCOPED_TRACE (::testing::Message (" Column type: " ) << column->GetType ().GetName ());
126+ SCOPED_TRACE (::testing::Message (" Client options: " ) << client_options);
127+
128+ clickhouse::Client client (client_options);
129+
130+ if (auto message = CheckIfShouldSkipTest (client)) {
131+ GTEST_SKIP () << *message;
132+ }
133+
134+ auto result_typed = RoundtripColumnValues (client, column)->template AsStrict <ColumnType>();
135+ EXPECT_TRUE (CompareRecursive (*column, *result_typed));
136+ }
137+
138+
139+ template <typename ColumnType, typename CompressionMethods>
140+ static void TestColumnRoundtrip (const ColumnType & column, const ClientOptions & client_options, CompressionMethods && compression_methods)
141+ {
142+ for (auto compressionMethod : compression_methods)
143+ {
144+ ClientOptions new_options = ClientOptions (client_options).SetCompressionMethod (compressionMethod);
145+ TestColumnRoundtrip (column, new_options);
146+ }
147+ }
116148};
117149
118150// Luckily all (non-data copying/moving) constructors have size_t params.
@@ -184,7 +216,17 @@ using TestCases = ::testing::Types<
184216 DecimalColumnTestCase<ColumnDecimal, 12 , 9 >,
185217
186218 DecimalColumnTestCase<ColumnDecimal, 6 , 0 >,
187- DecimalColumnTestCase<ColumnDecimal, 6 , 3 >
219+ DecimalColumnTestCase<ColumnDecimal, 6 , 3 >,
220+
221+ GenericColumnTestCase<ColumnLowCardinalityT<ColumnString>, &makeColumn<ColumnLowCardinalityT<ColumnString>>, std::string, &MakeStrings>
222+
223+ // Array(String)
224+ // GenericColumnTestCase<ColumnArrayT<ColumnString>, &makeColumn<ColumnArrayT<ColumnString>>, std::vector<std::string>, &MakeArrays<std::string, &MakeStrings>>
225+
226+ // // Array(Array(String))
227+ // GenericColumnTestCase<ColumnArrayT<ColumnArrayT<ColumnString>>, &makeColumn<ColumnArrayT<ColumnArrayT<ColumnString>>>,
228+ // std::vector<std::vector<std::string>>,
229+ // &MakeArrays<std::vector<std::string>, &MakeArrays<std::string, &MakeStrings>>>
188230 >;
189231
190232TYPED_TEST_SUITE (GenericColumnTest, TestCases);
@@ -222,7 +264,7 @@ TYPED_TEST(GenericColumnTest, EmptyColumn) {
222264
223265TYPED_TEST (GenericColumnTest, Append) {
224266 auto column = this ->MakeColumn ();
225- const auto values = this ->GenerateValues (100 );
267+ const auto values = this ->GenerateValues (10'000 );
226268
227269 for (const auto & v : values) {
228270 EXPECT_NO_THROW (column->Append (v));
@@ -259,10 +301,17 @@ inline auto convertValueForGetItem(const ColumnType& col, ValueType&& t) {
259301}
260302
261303TYPED_TEST (GenericColumnTest, GetItem) {
262- auto [column, values] = this ->MakeColumnWithValues (100 );
304+ auto [column, values] = this ->MakeColumnWithValues (10'000 );
263305
264306 ASSERT_EQ (values.size (), column->Size ());
265- ASSERT_EQ (column->GetItem (0 ).type , column->GetType ().GetCode ());
307+ const auto wrapping_types = std::set<Type::Code>{
308+ Type::Code::LowCardinality, Type::Code::Array, Type::Code::Nullable
309+ };
310+
311+ // For wrapping types, type of ItemView can be different from type of column
312+ if (wrapping_types.find (column->GetType ().GetCode ()) == wrapping_types.end () ) {
313+ EXPECT_EQ (column->GetItem (0 ).type , column->GetType ().GetCode ());
314+ }
266315
267316 for (size_t i = 0 ; i < values.size (); ++i) {
268317 const auto v = convertValueForGetItem (*column, values[i]);
@@ -274,7 +323,7 @@ TYPED_TEST(GenericColumnTest, GetItem) {
274323}
275324
276325TYPED_TEST (GenericColumnTest, Slice) {
277- auto [column, values] = this ->MakeColumnWithValues (100 );
326+ auto [column, values] = this ->MakeColumnWithValues (10'000 );
278327
279328 auto untyped_slice = column->Slice (0 , column->Size ());
280329 auto slice = untyped_slice->template AsStrict <typename TestFixture::ColumnType>();
@@ -286,7 +335,7 @@ TYPED_TEST(GenericColumnTest, Slice) {
286335}
287336
288337TYPED_TEST (GenericColumnTest, CloneEmpty) {
289- auto [column, values] = this ->MakeColumnWithValues (100 );
338+ auto [column, values] = this ->MakeColumnWithValues (10'000 );
290339 EXPECT_EQ (values.size (), column->Size ());
291340
292341 auto clone_untyped = column->CloneEmpty ();
@@ -298,15 +347,15 @@ TYPED_TEST(GenericColumnTest, CloneEmpty) {
298347}
299348
300349TYPED_TEST (GenericColumnTest, Clear) {
301- auto [column, values] = this ->MakeColumnWithValues (100 );
350+ auto [column, values] = this ->MakeColumnWithValues (10'000 );
302351 EXPECT_EQ (values.size (), column->Size ());
303352
304353 column->Clear ();
305354 EXPECT_EQ (0u , column->Size ());
306355}
307356
308357TYPED_TEST (GenericColumnTest, Swap) {
309- auto [column_A, values] = this ->MakeColumnWithValues (100 );
358+ auto [column_A, values] = this ->MakeColumnWithValues (10'000 );
310359 auto column_B = this ->MakeColumn ();
311360
312361 column_A->Swap (*column_B);
@@ -318,18 +367,21 @@ TYPED_TEST(GenericColumnTest, Swap) {
318367TYPED_TEST (GenericColumnTest, LoadAndSave) {
319368 auto [column_A, values] = this ->MakeColumnWithValues (100 );
320369
321- char buffer[4096 ] = {' \0 ' };
370+ // large buffer since we have pretty big values for String column
371+ auto const BufferSize = 10 *1024 *1024 ;
372+ std::unique_ptr<char []> buffer = std::make_unique<char []>(BufferSize);
373+ memset (buffer.get (), 0 , BufferSize);
322374 {
323- ArrayOutput output (buffer, sizeof (buffer) );
375+ ArrayOutput output (buffer. get (), BufferSize );
324376 // Save
325- EXPECT_NO_THROW (column_A->Save (&output));
377+ ASSERT_NO_THROW (column_A->Save (&output));
326378 }
327379
328380 auto column_B = this ->MakeColumn ();
329381 {
330- ArrayInput input (buffer, sizeof (buffer) );
382+ ArrayInput input (buffer. get (), BufferSize );
331383 // Load
332- EXPECT_TRUE (column_B->Load (&input, values.size ()));
384+ ASSERT_TRUE (column_B->Load (&input, values.size ()));
333385 }
334386
335387 EXPECT_TRUE (CompareRecursive (*column_A, *column_B));
@@ -342,25 +394,28 @@ const auto LocalHostEndpoint = ClientOptions()
342394 .SetPassword( getEnvOrDefault(" CLICKHOUSE_PASSWORD" , " " ))
343395 .SetDefaultDatabase(getEnvOrDefault(" CLICKHOUSE_DB" , " default" ));
344396
397+ const auto AllCompressionMethods = {
398+ clickhouse::CompressionMethod::None,
399+ clickhouse::CompressionMethod::LZ4
400+ };
401+
345402TYPED_TEST (GenericColumnTest, RoundTrip) {
346- auto [column, values] = this ->MakeColumnWithValues (100 );
403+ auto [column, values] = this ->MakeColumnWithValues (10'000 );
347404 EXPECT_EQ (values.size (), column->Size ());
348405
349- clickhouse::Client client (LocalHostEndpoint);
350-
351- if (auto message = this ->SkipTest (client)) {
352- GTEST_SKIP () << *message;
353- }
354-
355- auto result_typed = RoundtripColumnValues (client, column)->template AsStrict <typename TestFixture::ColumnType>();
356- EXPECT_TRUE (CompareRecursive (*column, *result_typed));
406+ this ->TestColumnRoundtrip (column, LocalHostEndpoint, AllCompressionMethods);
357407}
358408
359- TYPED_TEST (GenericColumnTest, NulableT_RoundTrip ) {
409+ TYPED_TEST (GenericColumnTest, NullableT_RoundTrip ) {
360410 using NullableType = ColumnNullableT<typename TestFixture::ColumnType>;
361411
362- auto column = std::make_shared<NullableType>(this ->MakeColumn ());
363- auto values = this ->GenerateValues (100 );
412+ auto non_nullable_column = this ->MakeColumn ();
413+ if (non_nullable_column->GetType ().GetCode () == Type::Code::LowCardinality)
414+ // TODO (vnemkov): wrap as ColumnLowCardinalityT<ColumnNullableT<NestedColumn>> instead of ColumnNullableT<ColumnLowCardinalityT<NestedColumn>>
415+ GTEST_SKIP () << " Can't have " << non_nullable_column->GetType ().GetName () << " in Nullable" ;
416+
417+ auto column = std::make_shared<NullableType>(std::move (non_nullable_column));
418+ auto values = this ->GenerateValues (10'000 );
364419
365420 FromVectorGenerator<bool > is_null ({true , false });
366421 for (size_t i = 0 ; i < values.size (); ++i) {
@@ -371,12 +426,24 @@ TYPED_TEST(GenericColumnTest, NulableT_RoundTrip) {
371426 }
372427 }
373428
374- clickhouse::Client client (LocalHostEndpoint);
429+ this ->TestColumnRoundtrip (column, LocalHostEndpoint, AllCompressionMethods);
430+ }
431+
432+ TYPED_TEST (GenericColumnTest, ArrayT_RoundTrip) {
433+ using ColumnArrayType = ColumnArrayT<typename TestFixture::ColumnType>;
375434
376- if (auto message = this ->SkipTest (client)) {
377- GTEST_SKIP () << *message;
435+ auto [nested_column, values] = this ->MakeColumnWithValues (100 );
436+
437+ auto column = std::make_shared<ColumnArrayType>(nested_column->CloneEmpty ()->template As <typename TestFixture::ColumnType>());
438+ for (size_t i = 0 ; i < values.size (); ++i)
439+ {
440+ const std::vector<std::decay_t <decltype (values[0 ])>> row{values.begin (), values.begin () + i};
441+ column->Append (values.begin (), values.begin () + i);
442+
443+ EXPECT_TRUE (CompareRecursive (row, (*column)[column->Size () - 1 ]));
378444 }
445+ EXPECT_EQ (values.size (), column->Size ());
379446
380- auto result_typed = WrapColumn<NullableType>(RoundtripColumnValues (client, column));
381- EXPECT_TRUE (CompareRecursive (*column, *result_typed));
447+ this ->TestColumnRoundtrip (column, LocalHostEndpoint, AllCompressionMethods);
382448}
449+
0 commit comments