1+ #include < gtest/gtest.h>
2+ #include < clickhouse/columns/string.h>
3+ #include " clickhouse/columns/nullable.h"
4+ #include " clickhouse/columns/lowcardinality.h"
5+ #include " clickhouse/client.h"
6+ #include " utils.h"
7+ #include " clickhouse/base/wire_format.h"
8+ #include < clickhouse/base/output.h>
9+
10+ namespace
11+ {
12+ using namespace clickhouse ;
13+ }
14+
15+ static const auto localHostEndpoint = ClientOptions()
16+ .SetHost( getEnvOrDefault(" CLICKHOUSE_HOST" , " localhost" ))
17+ .SetPort( getEnvOrDefault<size_t >(" CLICKHOUSE_PORT" , " 9000" ))
18+ .SetUser( getEnvOrDefault(" CLICKHOUSE_USER" , " default" ))
19+ .SetPassword( getEnvOrDefault(" CLICKHOUSE_PASSWORD" , " " ))
20+ .SetDefaultDatabase(getEnvOrDefault(" CLICKHOUSE_DB" , " default" ));
21+
22+
23+ ColumnRef buildTestColumn (const std::vector<std::string>& rowsData, const std::vector<uint8_t >& nulls) {
24+ auto stringColumn = std::make_shared<ColumnString>(rowsData);
25+ auto nullsColumn = std::make_shared<ColumnUInt8>(nulls);
26+ auto lowCardinalityColumn = std::make_shared<ColumnLowCardinality>(
27+ std::make_shared<ColumnNullable>(stringColumn, nullsColumn)
28+ );
29+
30+ return lowCardinalityColumn;
31+ }
32+
33+ void createTable (Client& client) {
34+ client.Execute (" DROP TEMPORARY TABLE IF EXISTS lc_of_nullable" );
35+ client.Execute (" CREATE TEMPORARY TABLE IF NOT EXISTS lc_of_nullable (words LowCardinality(Nullable(String))) ENGINE = Memory" );
36+ }
37+
38+ TEST (LowCardinalityOfNullable, InsertAndQuery) {
39+ const auto rowsData = std::vector<std::string> {
40+ " eminem" ,
41+ " " ,
42+ " tupac" ,
43+ " shady" ,
44+ " fifty" ,
45+ " dre" ,
46+ " " ,
47+ " cube"
48+ };
49+
50+ const auto nulls = std::vector<uint8_t > {
51+ false , false , true , false , true , true , false , false
52+ };
53+
54+ auto column = buildTestColumn (rowsData, nulls);
55+
56+ Block block;
57+ block.AppendColumn (" words" , column);
58+
59+ Client client (ClientOptions (localHostEndpoint)
60+ .SetBakcwardCompatibilityFeatureLowCardinalityAsWrappedColumn (false )
61+ .SetPingBeforeQuery (true ));
62+
63+ createTable (client);
64+
65+ client.Insert (" lc_of_nullable" , block);
66+
67+ client.Select (" SELECT * FROM lc_of_nullable" , [&](const Block& bl) {
68+ for (size_t row = 0 ; row < bl.GetRowCount (); row++) {
69+ auto lc_col = bl[0 ]->As <ColumnLowCardinality>();
70+ auto item = lc_col->GetItem (row);
71+
72+ if (nulls[row]) {
73+ ASSERT_EQ (Type::Code::Void, item.type );
74+ } else {
75+ ASSERT_EQ (rowsData[row], item.get <std::string_view>());
76+ }
77+ }
78+ });
79+ }
80+
81+ TEST (LowCardinalityOfNullable, InsertAndQueryEmpty) {
82+ auto column = buildTestColumn ({}, {});
83+
84+ Block block;
85+ block.AppendColumn (" words" , column);
86+
87+ Client client (ClientOptions (localHostEndpoint)
88+ .SetBakcwardCompatibilityFeatureLowCardinalityAsWrappedColumn (false )
89+ .SetPingBeforeQuery (true ));
90+
91+ createTable (client);
92+
93+ EXPECT_NO_THROW (client.Insert (" lc_of_nullable" , block));
94+
95+ client.Select (" SELECT * FROM lc_of_nullable" , [&](const Block& bl) {
96+ ASSERT_EQ (bl.GetRowCount (), 0u );
97+ });
98+ }
99+
100+ TEST (LowCardinalityOfNullable, ThrowOnBackwardsCompatibleLCColumn) {
101+ auto column = buildTestColumn ({}, {});
102+
103+ Block block;
104+ block.AppendColumn (" words" , column);
105+
106+ Client client (ClientOptions (localHostEndpoint)
107+ .SetPingBeforeQuery (true ));
108+
109+ createTable (client);
110+
111+ EXPECT_THROW (client.Insert (" lc_of_nullable" , block), UnimplementedError);
112+
113+ client.Select (" SELECT * FROM lc_of_nullable" , [&](const Block& bl) {
114+ ASSERT_EQ (bl.GetRowCount (), 0u );
115+ });
116+ }
0 commit comments