diff --git a/src/Columns/ColumnLowCardinality.h b/src/Columns/ColumnLowCardinality.h index fc607021ccf..4af2cb2f36e 100644 --- a/src/Columns/ColumnLowCardinality.h +++ b/src/Columns/ColumnLowCardinality.h @@ -188,6 +188,7 @@ public: */ bool nestedIsNullable() const { return isColumnNullable(*dictionary.getColumnUnique().getNestedColumn()); } void nestedToNullable() { dictionary.getColumnUnique().nestedToNullable(); } + void nestedRemoveNullable() { dictionary.getColumnUnique().nestedRemoveNullable(); } const IColumnUnique & getDictionary() const { return dictionary.getColumnUnique(); } const ColumnPtr & getDictionaryPtr() const { return dictionary.getColumnUniquePtr(); } diff --git a/src/Columns/ColumnUnique.h b/src/Columns/ColumnUnique.h index 51b45be53fd..c138962cf29 100644 --- a/src/Columns/ColumnUnique.h +++ b/src/Columns/ColumnUnique.h @@ -51,6 +51,7 @@ public: const ColumnPtr & getNestedNotNullableColumn() const override { return column_holder; } bool nestedColumnIsNullable() const override { return is_nullable; } void nestedToNullable() override; + void nestedRemoveNullable() override; size_t uniqueInsert(const Field & x) override; size_t uniqueInsertFrom(const IColumn & src, size_t n) override; @@ -271,6 +272,14 @@ void ColumnUnique::nestedToNullable() createNullMask(); } +template +void ColumnUnique::nestedRemoveNullable() +{ + is_nullable = false; + nested_null_mask = nullptr; + nested_column_nullable = nullptr; +} + template const ColumnPtr & ColumnUnique::getNestedColumn() const { diff --git a/src/Columns/IColumnUnique.h b/src/Columns/IColumnUnique.h index 2c1c542fce5..5e6473219d1 100644 --- a/src/Columns/IColumnUnique.h +++ b/src/Columns/IColumnUnique.h @@ -24,6 +24,7 @@ public: virtual bool nestedColumnIsNullable() const = 0; virtual void nestedToNullable() = 0; + virtual void nestedRemoveNullable() = 0; /// Returns array with StringRefHash calculated for each row of getNestedNotNullableColumn() column. /// Returns nullptr if nested column doesn't contain strings. Otherwise calculates hash (if it wasn't). diff --git a/src/Interpreters/HashJoin.cpp b/src/Interpreters/HashJoin.cpp index 251553436c7..2af136e7350 100644 --- a/src/Interpreters/HashJoin.cpp +++ b/src/Interpreters/HashJoin.cpp @@ -160,7 +160,7 @@ static ColumnWithTypeAndName correctNullability(ColumnWithTypeAndName && column, { if (nullable) { - JoinCommon::convertColumnToNullable(column, false); + JoinCommon::convertColumnToNullable(column); if (column.type->isNullable() && !negative_null_map.empty()) { MutableColumnPtr mutable_column = IColumn::mutate(std::move(column.column)); @@ -1085,7 +1085,8 @@ void HashJoin::joinBlockImpl( ColumnWithTypeAndName right_col(col.column, col.type, right_key.name); if (right_col.type->lowCardinality() != right_key.type->lowCardinality()) JoinCommon::changeLowCardinalityInplace(right_col); - block.insert(correctNullability(std::move(right_col), is_nullable)); + right_col = correctNullability(std::move(right_col), is_nullable); + block.insert(right_col); } } else if (has_required_right_keys) @@ -1114,7 +1115,8 @@ void HashJoin::joinBlockImpl( ColumnWithTypeAndName right_col(thin_column, col.type, right_key.name); if (right_col.type->lowCardinality() != right_key.type->lowCardinality()) JoinCommon::changeLowCardinalityInplace(right_col); - block.insert(correctNullability(std::move(right_col), is_nullable, null_map_filter)); + right_col = correctNullability(std::move(right_col), is_nullable, null_map_filter); + block.insert(right_col); if constexpr (need_replication) right_keys_to_replicate.push_back(block.getPositionByName(right_key.name)); diff --git a/src/Interpreters/join_common.cpp b/src/Interpreters/join_common.cpp index c61ad62e95e..29ed2b01c3e 100644 --- a/src/Interpreters/join_common.cpp +++ b/src/Interpreters/join_common.cpp @@ -122,6 +122,20 @@ void convertColumnsToNullable(Block & block, size_t starting_pos) /// @warning It assumes that every NULL has default value in nested column (or it does not matter) void removeColumnNullability(ColumnWithTypeAndName & column) { + if (column.type->lowCardinality()) + { + /// LowCardinality(Nullable(T)) case + ColumnLowCardinality * col_as_lc = assert_cast(column.column->assumeMutable().get()); + if (col_as_lc->nestedIsNullable()) + { + col_as_lc->nestedRemoveNullable(); + const auto & dict_type = typeid_cast(column.type.get())->getDictionaryType(); + column.type = std::make_shared(removeNullable(dict_type)); + } + + return; + } + if (!column.type->isNullable()) return;