10
10
#include < string_view>
11
11
#include < type_traits>
12
12
13
+ #include < cassert>
14
+
13
15
namespace {
14
16
using namespace clickhouse ;
15
17
@@ -105,13 +107,13 @@ inline void AppendToDictionary(Column& dictionary, const ItemView & item) {
105
107
}
106
108
}
107
109
108
- // Add special NULL-item, which is expected at pos(0) in dictionary,
110
+ // A special NULL-item, which is expected at pos(0) in dictionary,
109
111
// note that we distinguish empty string from NULL-value.
110
- inline void AppendNullItemToDictionary ( ColumnRef dictionary) {
112
+ inline auto GetNullItemForDictionary ( const ColumnRef dictionary) {
111
113
if (auto n = dictionary->As <ColumnNullable>()) {
112
- AppendToDictionary (*dictionary, ItemView{}) ;
114
+ return ItemView{};
113
115
} else {
114
- AppendToDictionary (*dictionary, ItemView{dictionary->Type ()->GetCode (), std::string_view{}}) ;
116
+ return ItemView{dictionary->Type ()->GetCode (), std::string_view{}};
115
117
}
116
118
}
117
119
@@ -125,18 +127,19 @@ ColumnLowCardinality::ColumnLowCardinality(ColumnRef dictionary_column)
125
127
{
126
128
if (dictionary_column_->Size () != 0 ) {
127
129
// When dictionary column was constructed with values, re-add values by copying to update index and unique_items_map.
130
+ // TODO: eliminate data copying by coming with better solution than doing AppendUnsafe() N times.
128
131
129
132
// Steal values into temporary column.
130
133
auto values = dictionary_column_->Slice (0 , 0 );
131
134
values->Swap (*dictionary_column_);
132
135
133
- AppendNullItemToDictionary (dictionary_column_ );
136
+ AppendNullItemToEmptyColumn ( );
134
137
135
138
// Re-add values, updating index and unique_items_map.
136
139
for (size_t i = 0 ; i < values->Size (); ++i)
137
140
AppendUnsafe (values->GetItem (i));
138
141
} else {
139
- AppendNullItemToDictionary (dictionary_column_ );
142
+ AppendNullItemToEmptyColumn ( );
140
143
}
141
144
}
142
145
@@ -288,6 +291,9 @@ void ColumnLowCardinality::Save(CodedOutputStream* output) {
288
291
void ColumnLowCardinality::Clear () {
289
292
index_column_->Clear ();
290
293
dictionary_column_->Clear ();
294
+ unique_items_map_.clear ();
295
+
296
+ AppendNullItemToEmptyColumn ();
291
297
}
292
298
293
299
size_t ColumnLowCardinality::Size () const {
@@ -353,6 +359,19 @@ void ColumnLowCardinality::AppendUnsafe(const ItemView & value) {
353
359
}
354
360
}
355
361
362
+ void ColumnLowCardinality::AppendNullItemToEmptyColumn ()
363
+ {
364
+ // INVARIANT: Empty LC column has an (invisible) null-item at pos 0, which MUST be present in
365
+ // unique_items_map_ in order to reuse dictionary posistion on subsequent Append()-s.
366
+
367
+ // Should be only performed on empty LC column.
368
+ assert (dictionary_column_->Size () == 0 && unique_items_map_.empty ());
369
+
370
+ const auto null_item = GetNullItemForDictionary (dictionary_column_);
371
+ AppendToDictionary (*dictionary_column_, null_item);
372
+ unique_items_map_.emplace (computeHashKey (null_item), dictionary_column_->Size ());
373
+ }
374
+
356
375
size_t ColumnLowCardinality::GetDictionarySize () const {
357
376
return dictionary_column_->Size ();
358
377
}
0 commit comments