diff --git a/dbms/src/Columns/ColumnArray.cpp b/dbms/src/Columns/ColumnArray.cpp index dd793ce59fd..9d94613040a 100644 --- a/dbms/src/Columns/ColumnArray.cpp +++ b/dbms/src/Columns/ColumnArray.cpp @@ -3,6 +3,8 @@ #include #include #include +#include +#include #include #include @@ -374,17 +376,19 @@ void ColumnArray::insertRangeFrom(const IColumn & src, size_t start, size_t leng ColumnPtr ColumnArray::filter(const Filter & filt, ssize_t result_size_hint) const { - if (typeid_cast(data.get())) return filterNumber(filt, result_size_hint); - if (typeid_cast(data.get())) return filterNumber(filt, result_size_hint); - if (typeid_cast(data.get())) return filterNumber(filt, result_size_hint); - if (typeid_cast(data.get())) return filterNumber(filt, result_size_hint); - if (typeid_cast(data.get())) return filterNumber(filt, result_size_hint); - if (typeid_cast(data.get())) return filterNumber(filt, result_size_hint); - if (typeid_cast(data.get())) return filterNumber(filt, result_size_hint); - if (typeid_cast(data.get())) return filterNumber(filt, result_size_hint); - if (typeid_cast(data.get())) return filterNumber(filt, result_size_hint); - if (typeid_cast(data.get())) return filterNumber(filt, result_size_hint); - if (typeid_cast(data.get())) return filterString(filt, result_size_hint); + if (typeid_cast(data.get())) return filterNumber(filt, result_size_hint); + if (typeid_cast(data.get())) return filterNumber(filt, result_size_hint); + if (typeid_cast(data.get())) return filterNumber(filt, result_size_hint); + if (typeid_cast(data.get())) return filterNumber(filt, result_size_hint); + if (typeid_cast(data.get())) return filterNumber(filt, result_size_hint); + if (typeid_cast(data.get())) return filterNumber(filt, result_size_hint); + if (typeid_cast(data.get())) return filterNumber(filt, result_size_hint); + if (typeid_cast(data.get())) return filterNumber(filt, result_size_hint); + if (typeid_cast(data.get())) return filterNumber(filt, result_size_hint); + if (typeid_cast(data.get())) return filterNumber(filt, result_size_hint); + if (typeid_cast(data.get())) return filterString(filt, result_size_hint); + if (typeid_cast(data.get())) return filterTuple(filt, result_size_hint); + if (typeid_cast(data.get())) return filterNullable(filt, result_size_hint); return filterGeneric(filt, result_size_hint); } @@ -516,6 +520,56 @@ ColumnPtr ColumnArray::filterGeneric(const Filter & filt, ssize_t result_size_hi return res; } +ColumnPtr ColumnArray::filterNullable(const Filter & filt, ssize_t result_size_hint) const +{ + if (getOffsets().size() == 0) + return std::make_shared(data); + + const ColumnNullable & nullable_elems = static_cast(*data); + + auto array_of_nested = std::make_shared(nullable_elems.getNestedColumn(), offsets); + auto filtered_array_of_nested_owner = array_of_nested->filter(filt, result_size_hint); + auto & filtered_array_of_nested = static_cast(*filtered_array_of_nested_owner); + auto & filtered_offsets = filtered_array_of_nested.getOffsetsColumn(); + + auto res_null_map = std::make_shared(); + auto res = std::make_shared( + std::make_shared( + filtered_array_of_nested.getDataPtr(), + res_null_map), + filtered_offsets); + + filterArraysImplOnlyData(nullable_elems.getNullMap(), getOffsets(), res_null_map->getData(), filt, result_size_hint); + return res; +} + +ColumnPtr ColumnArray::filterTuple(const Filter & filt, ssize_t result_size_hint) const +{ + if (getOffsets().size() == 0) + return std::make_shared(data); + + const ColumnTuple & tuple = static_cast(*data); + + /// Make temporary arrays for each components of Tuple, then filter and collect back. + + size_t tuple_size = tuple.getColumns().size(); + + if (tuple_size == 0) + throw Exception("Logical error: empty tuple", ErrorCodes::LOGICAL_ERROR); + + Columns temporary_arrays(tuple_size); + for (size_t i = 0; i < tuple_size; ++i) + temporary_arrays[i] = ColumnArray(tuple.getColumns()[i], getOffsetsColumn()).filter(filt, result_size_hint); + + Block tuple_block = tuple.getData().cloneEmpty(); + for (size_t i = 0; i < tuple_size; ++i) + tuple_block.getByPosition(i).column = static_cast(*temporary_arrays[i]).getDataPtr(); + + return std::make_shared( + std::make_shared(tuple_block), + static_cast(*temporary_arrays.front()).getOffsetsColumn()); +} + ColumnPtr ColumnArray::permute(const Permutation & perm, size_t limit) const { @@ -584,22 +638,21 @@ void ColumnArray::getPermutation(bool reverse, size_t limit, int nan_direction_h ColumnPtr ColumnArray::replicate(const Offsets_t & replicate_offsets) const { - /// It does not work out in general case. - - if (typeid_cast(data.get())) return replicateNumber(replicate_offsets); - if (typeid_cast(data.get())) return replicateNumber(replicate_offsets); - if (typeid_cast(data.get())) return replicateNumber(replicate_offsets); - if (typeid_cast(data.get())) return replicateNumber(replicate_offsets); - if (typeid_cast(data.get())) return replicateNumber(replicate_offsets); - if (typeid_cast(data.get())) return replicateNumber(replicate_offsets); - if (typeid_cast(data.get())) return replicateNumber(replicate_offsets); - if (typeid_cast(data.get())) return replicateNumber(replicate_offsets); - if (typeid_cast(data.get())) return replicateNumber(replicate_offsets); - if (typeid_cast(data.get())) return replicateNumber(replicate_offsets); - if (typeid_cast(data.get())) return replicateString(replicate_offsets); - if (dynamic_cast(data.get())) return replicateConst(replicate_offsets); - - throw Exception("Replication of column " + getName() + " is not implemented.", ErrorCodes::NOT_IMPLEMENTED); + if (typeid_cast(data.get())) return replicateNumber(replicate_offsets); + if (typeid_cast(data.get())) return replicateNumber(replicate_offsets); + if (typeid_cast(data.get())) return replicateNumber(replicate_offsets); + if (typeid_cast(data.get())) return replicateNumber(replicate_offsets); + if (typeid_cast(data.get())) return replicateNumber(replicate_offsets); + if (typeid_cast(data.get())) return replicateNumber(replicate_offsets); + if (typeid_cast(data.get())) return replicateNumber(replicate_offsets); + if (typeid_cast(data.get())) return replicateNumber(replicate_offsets); + if (typeid_cast(data.get())) return replicateNumber(replicate_offsets); + if (typeid_cast(data.get())) return replicateNumber(replicate_offsets); + if (typeid_cast(data.get())) return replicateString(replicate_offsets); + if (dynamic_cast(data.get())) return replicateConst(replicate_offsets); + if (typeid_cast(data.get())) return replicateNullable(replicate_offsets); + if (typeid_cast(data.get())) return replicateTuple(replicate_offsets); + return replicateGeneric(replicate_offsets); } @@ -765,4 +818,74 @@ ColumnPtr ColumnArray::replicateConst(const Offsets_t & replicate_offsets) const } +ColumnPtr ColumnArray::replicateGeneric(const Offsets_t & replicate_offsets) const +{ + size_t col_size = size(); + if (col_size != replicate_offsets.size()) + throw Exception("Size of offsets doesn't match size of column.", ErrorCodes::SIZES_OF_COLUMNS_DOESNT_MATCH); + + ColumnPtr res = cloneEmpty(); + ColumnArray & res_concrete = static_cast(*res); + + if (0 == col_size) + return res; + + IColumn::Offset_t prev_offset = 0; + const auto & offsets_data = getOffsets(); + for (size_t i = 0; i < col_size; ++i) + { + size_t size_to_replicate = offsets_data[i] - prev_offset; + prev_offset = offsets_data[i]; + + for (size_t j = 0; j < size_to_replicate; ++j) + res_concrete.insertFrom(*this, i); + } + + return res; +} + + +ColumnPtr ColumnArray::replicateNullable(const Offsets_t & replicate_offsets) const +{ + const ColumnNullable & nullable = static_cast(*data); + + /// Make temporary arrays for each components of Nullable. Then replicate them independently and collect back to result. + /// NOTE Offsets are calculated twice and it is redundant. + + auto array_of_nested = ColumnArray(nullable.getNestedColumn(), getOffsetsColumn()).replicate(replicate_offsets); + auto array_of_null_map = ColumnArray(nullable.getNullMapColumn(), getOffsetsColumn()).replicate(replicate_offsets); + + return std::make_shared( + std::make_shared( + static_cast(*array_of_nested).getDataPtr(), + static_cast(*array_of_null_map).getDataPtr()), + static_cast(*array_of_nested).getOffsetsColumn()); +} + + +ColumnPtr ColumnArray::replicateTuple(const Offsets_t & replicate_offsets) const +{ + const ColumnTuple & tuple = static_cast(*data); + + /// Make temporary arrays for each components of Tuple. In the same way as for Nullable. + + size_t tuple_size = tuple.getColumns().size(); + + if (tuple_size == 0) + throw Exception("Logical error: empty tuple", ErrorCodes::LOGICAL_ERROR); + + Columns temporary_arrays(tuple_size); + for (size_t i = 0; i < tuple_size; ++i) + temporary_arrays[i] = ColumnArray(tuple.getColumns()[i], getOffsetsColumn()).replicate(replicate_offsets); + + Block tuple_block = tuple.getData().cloneEmpty(); + for (size_t i = 0; i < tuple_size; ++i) + tuple_block.getByPosition(i).column = static_cast(*temporary_arrays[i]).getDataPtr(); + + return std::make_shared( + std::make_shared(tuple_block), + static_cast(*temporary_arrays.front()).getOffsetsColumn()); +} + + } diff --git a/dbms/src/Columns/ColumnArray.h b/dbms/src/Columns/ColumnArray.h index 48bff9cb7f1..d5e3bb33c7c 100644 --- a/dbms/src/Columns/ColumnArray.h +++ b/dbms/src/Columns/ColumnArray.h @@ -102,12 +102,20 @@ private: */ ColumnPtr replicateConst(const Offsets_t & replicate_offsets) const; + /** The following is done by simply replicating of nested columns. + */ + ColumnPtr replicateTuple(const Offsets_t & replicate_offsets) const; + ColumnPtr replicateNullable(const Offsets_t & replicate_offsets) const; + ColumnPtr replicateGeneric(const Offsets_t & replicate_offsets) const; + /// Specializations for the filter function. template ColumnPtr filterNumber(const Filter & filt, ssize_t result_size_hint) const; ColumnPtr filterString(const Filter & filt, ssize_t result_size_hint) const; + ColumnPtr filterTuple(const Filter & filt, ssize_t result_size_hint) const; + ColumnPtr filterNullable(const Filter & filt, ssize_t result_size_hint) const; ColumnPtr filterGeneric(const Filter & filt, ssize_t result_size_hint) const; }; diff --git a/dbms/src/Columns/ColumnVector.cpp b/dbms/src/Columns/ColumnVector.cpp index 3b8ca65017e..f47856d35a6 100644 --- a/dbms/src/Columns/ColumnVector.cpp +++ b/dbms/src/Columns/ColumnVector.cpp @@ -303,7 +303,7 @@ void ColumnVector::getExtremes(Field & min, Field & max) const } -/// Explicit template instantinations - to avoid code bloat in headers. +/// Explicit template instantiations - to avoid code bloat in headers. template class ColumnVector; template class ColumnVector; template class ColumnVector; diff --git a/dbms/src/Columns/ColumnsCommon.cpp b/dbms/src/Columns/ColumnsCommon.cpp index 5e0aa6f791d..af1130b3e25 100644 --- a/dbms/src/Columns/ColumnsCommon.cpp +++ b/dbms/src/Columns/ColumnsCommon.cpp @@ -53,74 +53,39 @@ namespace ErrorCodes } -template -void filterArraysImpl( - const PaddedPODArray & src_elems, const IColumn::Offsets_t & src_offsets, - PaddedPODArray & res_elems, IColumn::Offsets_t & res_offsets, - const IColumn::Filter & filt, ssize_t result_size_hint) +namespace { - const size_t size = src_offsets.size(); - if (size != filt.size()) - throw Exception("Size of filter doesn't match size of column.", ErrorCodes::SIZES_OF_COLUMNS_DOESNT_MATCH); + /// Implementation details of filterArraysImpl function, used as template parameter. + /// Allow to build or not to build offsets array. - if (result_size_hint) + struct ResultOffsetsBuilder { - res_offsets.reserve(result_size_hint > 0 ? result_size_hint : size); + IColumn::Offsets_t & res_offsets; + IColumn::Offset_t current_src_offset = 0; - if (result_size_hint < 0) - res_elems.reserve(src_elems.size()); - else if (result_size_hint < 1000000000 && src_elems.size() < 1000000000) /// Avoid overflow. - res_elems.reserve((result_size_hint * src_elems.size() + size - 1) / size); - } + ResultOffsetsBuilder(IColumn::Offsets_t * res_offsets_) : res_offsets(*res_offsets_) {} - IColumn::Offset_t current_src_offset = 0; - - const UInt8 * filt_pos = &filt[0]; - const auto filt_end = filt_pos + size; - - auto offsets_pos = &src_offsets[0]; - const auto offsets_begin = offsets_pos; - - /// copy array ending at *end_offset_ptr - const auto copy_array = [&] (const IColumn::Offset_t * offset_ptr) - { - const auto offset = offset_ptr == offsets_begin ? 0 : offset_ptr[-1]; - const auto size = *offset_ptr - offset; - - current_src_offset += size; - res_offsets.push_back(current_src_offset); - - const auto elems_size_old = res_elems.size(); - res_elems.resize(elems_size_old + size); - memcpy(&res_elems[elems_size_old], &src_elems[offset], size * sizeof(T)); - }; - -#if __SSE2__ - const __m128i zero_vec = _mm_setzero_si128(); - static constexpr size_t SIMD_BYTES = 16; - const auto filt_end_aligned = filt_pos + size / SIMD_BYTES * SIMD_BYTES; - - while (filt_pos < filt_end_aligned) - { - const auto mask = _mm_movemask_epi8(_mm_cmpgt_epi8( - _mm_loadu_si128(reinterpret_cast(filt_pos)), - zero_vec)); - - if (mask == 0) + void reserve(size_t result_size_hint, size_t src_size) { - /// SIMD_BYTES consecutive rows do not pass the filter + res_offsets.reserve(result_size_hint > 0 ? result_size_hint : src_size); } - else if (mask == 0xffff) + + void insertOne(size_t array_size) { - /// SIMD_BYTES consecutive rows pass the filter - const auto first = offsets_pos == offsets_begin; - - const auto chunk_offset = first ? 0 : offsets_pos[-1]; - const auto chunk_size = offsets_pos[SIMD_BYTES - 1] - chunk_offset; + current_src_offset += array_size; + res_offsets.push_back(current_src_offset); + } + template + void insertChunk( + const IColumn::Offset_t * src_offsets_pos, + bool first, + IColumn::Offset_t chunk_offset, + size_t chunk_size) + { const auto offsets_size_old = res_offsets.size(); res_offsets.resize(offsets_size_old + SIMD_BYTES); - memcpy(&res_offsets[offsets_size_old], offsets_pos, SIMD_BYTES * sizeof(IColumn::Offset_t)); + memcpy(&res_offsets[offsets_size_old], src_offsets_pos, SIMD_BYTES * sizeof(IColumn::Offset_t)); if (!first) { @@ -137,75 +102,162 @@ void filterArraysImpl( } } current_src_offset += chunk_size; - - /// copy elements for SIMD_BYTES arrays at once - const auto elems_size_old = res_elems.size(); - res_elems.resize(elems_size_old + chunk_size); - memcpy(&res_elems[elems_size_old], &src_elems[chunk_offset], chunk_size * sizeof(T)); - } - else - { - for (size_t i = 0; i < SIMD_BYTES; ++i) - if (filt_pos[i]) - copy_array(offsets_pos + i); } + }; - filt_pos += SIMD_BYTES; - offsets_pos += SIMD_BYTES; - } -#endif - - while (filt_pos < filt_end) + struct NoResultOffsetsBuilder { - if (*filt_pos) - copy_array(offsets_pos); + NoResultOffsetsBuilder(IColumn::Offsets_t * res_offsets_) {} + void reserve(size_t result_size_hint, size_t src_size) {} + void insertOne(size_t array_size) {} - ++filt_pos; - ++offsets_pos; + template + void insertChunk( + const IColumn::Offset_t * src_offsets_pos, + bool first, + IColumn::Offset_t chunk_offset, + size_t chunk_size) + { + } + }; + + + template + void filterArraysImplGeneric( + const PaddedPODArray & src_elems, const IColumn::Offsets_t & src_offsets, + PaddedPODArray & res_elems, IColumn::Offsets_t * res_offsets, + const IColumn::Filter & filt, ssize_t result_size_hint) + { + const size_t size = src_offsets.size(); + if (size != filt.size()) + throw Exception("Size of filter doesn't match size of column.", ErrorCodes::SIZES_OF_COLUMNS_DOESNT_MATCH); + + ResultOffsetsBuilder result_offsets_builder(res_offsets); + + if (result_size_hint) + { + result_offsets_builder.reserve(result_size_hint, size); + + if (result_size_hint < 0) + res_elems.reserve(src_elems.size()); + else if (result_size_hint < 1000000000 && src_elems.size() < 1000000000) /// Avoid overflow. + res_elems.reserve((result_size_hint * src_elems.size() + size - 1) / size); + } + + const UInt8 * filt_pos = &filt[0]; + const auto filt_end = filt_pos + size; + + auto offsets_pos = &src_offsets[0]; + const auto offsets_begin = offsets_pos; + + /// copy array ending at *end_offset_ptr + const auto copy_array = [&] (const IColumn::Offset_t * offset_ptr) + { + const auto offset = offset_ptr == offsets_begin ? 0 : offset_ptr[-1]; + const auto size = *offset_ptr - offset; + + result_offsets_builder.insertOne(size); + + const auto elems_size_old = res_elems.size(); + res_elems.resize(elems_size_old + size); + memcpy(&res_elems[elems_size_old], &src_elems[offset], size * sizeof(T)); + }; + + #if __SSE2__ + const __m128i zero_vec = _mm_setzero_si128(); + static constexpr size_t SIMD_BYTES = 16; + const auto filt_end_aligned = filt_pos + size / SIMD_BYTES * SIMD_BYTES; + + while (filt_pos < filt_end_aligned) + { + const auto mask = _mm_movemask_epi8(_mm_cmpgt_epi8( + _mm_loadu_si128(reinterpret_cast(filt_pos)), + zero_vec)); + + if (mask == 0) + { + /// SIMD_BYTES consecutive rows do not pass the filter + } + else if (mask == 0xffff) + { + /// SIMD_BYTES consecutive rows pass the filter + const auto first = offsets_pos == offsets_begin; + + const auto chunk_offset = first ? 0 : offsets_pos[-1]; + const auto chunk_size = offsets_pos[SIMD_BYTES - 1] - chunk_offset; + + result_offsets_builder.template insertChunk(offsets_pos, first, chunk_offset, chunk_size); + + /// copy elements for SIMD_BYTES arrays at once + const auto elems_size_old = res_elems.size(); + res_elems.resize(elems_size_old + chunk_size); + memcpy(&res_elems[elems_size_old], &src_elems[chunk_offset], chunk_size * sizeof(T)); + } + else + { + for (size_t i = 0; i < SIMD_BYTES; ++i) + if (filt_pos[i]) + copy_array(offsets_pos + i); + } + + filt_pos += SIMD_BYTES; + offsets_pos += SIMD_BYTES; + } + #endif + + while (filt_pos < filt_end) + { + if (*filt_pos) + copy_array(offsets_pos); + + ++filt_pos; + ++offsets_pos; + } } } +template +void filterArraysImpl( + const PaddedPODArray & src_elems, const IColumn::Offsets_t & src_offsets, + PaddedPODArray & res_elems, IColumn::Offsets_t & res_offsets, + const IColumn::Filter & filt, ssize_t result_size_hint) +{ + return filterArraysImplGeneric(src_elems, src_offsets, res_elems, &res_offsets, filt, result_size_hint); +} + +template +void filterArraysImplOnlyData( + const PaddedPODArray & src_elems, const IColumn::Offsets_t & src_offsets, + PaddedPODArray & res_elems, + const IColumn::Filter & filt, ssize_t result_size_hint) +{ + return filterArraysImplGeneric(src_elems, src_offsets, res_elems, nullptr, filt, result_size_hint); +} + + /// Explicit instantiations - not to place the implementation of the function above in the header file. -template void filterArraysImpl( - const PaddedPODArray &, const IColumn::Offsets_t &, - PaddedPODArray &, IColumn::Offsets_t &, - const IColumn::Filter &, ssize_t); -template void filterArraysImpl( - const PaddedPODArray &, const IColumn::Offsets_t &, - PaddedPODArray &, IColumn::Offsets_t &, - const IColumn::Filter &, ssize_t); -template void filterArraysImpl( - const PaddedPODArray &, const IColumn::Offsets_t &, - PaddedPODArray &, IColumn::Offsets_t &, - const IColumn::Filter &, ssize_t); -template void filterArraysImpl( - const PaddedPODArray &, const IColumn::Offsets_t &, - PaddedPODArray &, IColumn::Offsets_t &, - const IColumn::Filter &, ssize_t); -template void filterArraysImpl( - const PaddedPODArray &, const IColumn::Offsets_t &, - PaddedPODArray &, IColumn::Offsets_t &, - const IColumn::Filter &, ssize_t); -template void filterArraysImpl( - const PaddedPODArray &, const IColumn::Offsets_t &, - PaddedPODArray &, IColumn::Offsets_t &, - const IColumn::Filter &, ssize_t); -template void filterArraysImpl( - const PaddedPODArray &, const IColumn::Offsets_t &, - PaddedPODArray &, IColumn::Offsets_t &, - const IColumn::Filter &, ssize_t); -template void filterArraysImpl( - const PaddedPODArray &, const IColumn::Offsets_t &, - PaddedPODArray &, IColumn::Offsets_t &, - const IColumn::Filter &, ssize_t); -template void filterArraysImpl( - const PaddedPODArray &, const IColumn::Offsets_t &, - PaddedPODArray &, IColumn::Offsets_t &, - const IColumn::Filter &, ssize_t); -template void filterArraysImpl( - const PaddedPODArray &, const IColumn::Offsets_t &, - PaddedPODArray &, IColumn::Offsets_t &, +#define INSTANTIATE(TYPE) \ +template void filterArraysImpl( \ + const PaddedPODArray &, const IColumn::Offsets_t &, \ + PaddedPODArray &, IColumn::Offsets_t &, \ + const IColumn::Filter &, ssize_t); \ +template void filterArraysImplOnlyData( \ + const PaddedPODArray &, const IColumn::Offsets_t &, \ + PaddedPODArray &, \ const IColumn::Filter &, ssize_t); +INSTANTIATE(UInt8) +INSTANTIATE(UInt16) +INSTANTIATE(UInt32) +INSTANTIATE(UInt64) +INSTANTIATE(Int8) +INSTANTIATE(Int16) +INSTANTIATE(Int32) +INSTANTIATE(Int64) +INSTANTIATE(Float32) +INSTANTIATE(Float64) + +#undef INSTANTIATE + } diff --git a/dbms/src/Columns/ColumnsCommon.h b/dbms/src/Columns/ColumnsCommon.h index a13afcb41d9..82b042f2779 100644 --- a/dbms/src/Columns/ColumnsCommon.h +++ b/dbms/src/Columns/ColumnsCommon.h @@ -19,4 +19,11 @@ void filterArraysImpl( PaddedPODArray & res_elems, IColumn::Offsets_t & res_offsets, const IColumn::Filter & filt, ssize_t result_size_hint); +/// Same as above, but not fills res_offsets. +template +void filterArraysImplOnlyData( + const PaddedPODArray & src_elems, const IColumn::Offsets_t & src_offsets, + PaddedPODArray & res_elems, + const IColumn::Filter & filt, ssize_t result_size_hint); + } diff --git a/dbms/src/Common/Allocator.cpp b/dbms/src/Common/Allocator.cpp index 0468efcf044..7eca841833e 100644 --- a/dbms/src/Common/Allocator.cpp +++ b/dbms/src/Common/Allocator.cpp @@ -155,6 +155,6 @@ void * Allocator::realloc(void * buf, size_t old_size, size_t new } -/// Explicit template instantinations. +/// Explicit template instantiations. template class Allocator; template class Allocator; diff --git a/dbms/src/DataTypes/DataTypeNumberBase.cpp b/dbms/src/DataTypes/DataTypeNumberBase.cpp index d7b354dade5..0a3b6caf5de 100644 --- a/dbms/src/DataTypes/DataTypeNumberBase.cpp +++ b/dbms/src/DataTypes/DataTypeNumberBase.cpp @@ -196,7 +196,7 @@ ColumnPtr DataTypeNumberBase::createConstColumn(size_t size, const Field & fi } -/// Explicit template instantinations - to avoid code bloat in headers. +/// Explicit template instantiations - to avoid code bloat in headers. template class DataTypeNumberBase; template class DataTypeNumberBase; template class DataTypeNumberBase; diff --git a/dbms/src/Interpreters/createBlockSelector.cpp b/dbms/src/Interpreters/createBlockSelector.cpp index 74a6a7fd205..df0702465ce 100644 --- a/dbms/src/Interpreters/createBlockSelector.cpp +++ b/dbms/src/Interpreters/createBlockSelector.cpp @@ -53,7 +53,7 @@ IColumn::Selector createBlockSelector( } -/// Explicit instantinations to avoid code bloat in headers. +/// Explicit instantiations to avoid code bloat in headers. template IColumn::Selector createBlockSelector(const IColumn & column, size_t num_shards, const std::vector & slots); template IColumn::Selector createBlockSelector(const IColumn & column, size_t num_shards, const std::vector & slots); template IColumn::Selector createBlockSelector(const IColumn & column, size_t num_shards, const std::vector & slots); diff --git a/dbms/tests/queries/0_stateless/00448_replicate_nullable_tuple_generic.reference b/dbms/tests/queries/0_stateless/00448_replicate_nullable_tuple_generic.reference new file mode 100644 index 00000000000..3c1307d9212 --- /dev/null +++ b/dbms/tests/queries/0_stateless/00448_replicate_nullable_tuple_generic.reference @@ -0,0 +1,8 @@ +[1,NULL,2] 1 +[1,NULL,2] \N +[1,NULL,2] 2 +[(1,2),(3,4),(5,6)] (1,2) +[(1,2),(3,4),(5,6)] (3,4) +[(1,2),(3,4),(5,6)] (5,6) +['Hello','world'] Hello +['Hello','world'] world diff --git a/dbms/tests/queries/0_stateless/00448_replicate_nullable_tuple_generic.sql b/dbms/tests/queries/0_stateless/00448_replicate_nullable_tuple_generic.sql new file mode 100644 index 00000000000..04bddd71972 --- /dev/null +++ b/dbms/tests/queries/0_stateless/00448_replicate_nullable_tuple_generic.sql @@ -0,0 +1,3 @@ +SELECT x, arrayJoin(x) FROM (SELECT materialize([1, NULL, 2]) AS x); +SELECT x, arrayJoin(x) FROM (SELECT materialize([(1, 2), (3, 4), (5, 6)]) AS x); +SELECT x, arrayJoin(x) FROM (SELECT materialize(arrayMap(x -> toFixedString(x, 5), ['Hello', 'world'])) AS x);